|
| 1 | +#!/bin/bash |
| 2 | +##===----------------------------------------------------------------------===## |
| 3 | +## |
| 4 | +## This source file is part of the Swift.org open source project |
| 5 | +## |
| 6 | +## Copyright (c) 2025 Apple Inc. and the Swift project authors |
| 7 | +## Licensed under Apache License v2.0 with Runtime Library Exception |
| 8 | +## |
| 9 | +## See https://swift.org/LICENSE.txt for license information |
| 10 | +## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 11 | +## |
| 12 | +##===----------------------------------------------------------------------===## |
| 13 | + |
| 14 | +set -euo pipefail |
| 15 | + |
| 16 | +log() { printf -- "** %s\n" "$*" >&2; } |
| 17 | +error() { printf -- "** ERROR: %s\n" "$*" >&2; } |
| 18 | +fatal() { error "$@"; exit 1; } |
| 19 | + |
| 20 | +# Detect OS from /etc/os-release |
| 21 | +detect_os_info() { |
| 22 | + if [[ ! -f /etc/os-release ]]; then |
| 23 | + fatal "Cannot detect OS: /etc/os-release not found" |
| 24 | + fi |
| 25 | + |
| 26 | + local os_id |
| 27 | + os_id=$(grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '"' | tr '[:upper:]' '[:lower:]') |
| 28 | + local version_id |
| 29 | + version_id=$(grep '^VERSION_ID=' /etc/os-release | cut -d'=' -f2 | tr -d '"') |
| 30 | + |
| 31 | + if [[ -z "$os_id" || -z "$version_id" ]]; then |
| 32 | + fatal "Could not parse OS information from /etc/os-release" |
| 33 | + fi |
| 34 | + |
| 35 | + # Create both formats |
| 36 | + local os_name |
| 37 | + os_name="${os_id}$(echo "$version_id" | tr -d '.')" |
| 38 | + local os_dotted="${os_id}${version_id}" |
| 39 | + |
| 40 | + log "Detected OS from /etc/os-release: $os_name (dotted: $os_dotted)" |
| 41 | + |
| 42 | + echo "$os_name|$os_dotted" |
| 43 | +} |
| 44 | + |
| 45 | +OS_INFO=$(detect_os_info) |
| 46 | +OS_NAME=$(echo "$OS_INFO" | cut -d'|' -f1) |
| 47 | +OS_DOTTED_VERSION=$(echo "$OS_INFO" | cut -d'|' -f2) |
| 48 | + |
| 49 | +log "Using OS: $OS_NAME" |
| 50 | +log "Dotted version of OS for filenames: $OS_DOTTED_VERSION" |
| 51 | + |
| 52 | +# Detect architecture to adjust platform name if needed |
| 53 | +ARCH=$(uname -m) |
| 54 | +ARCH_SUFFIX="" |
| 55 | +if [[ "$ARCH" == "aarch64" ]]; then |
| 56 | + ARCH_SUFFIX="-aarch64" |
| 57 | + log "Detected aarch64 architecture, using suffix: $ARCH_SUFFIX" |
| 58 | +else |
| 59 | + log "Detected $ARCH architecture, using no suffix" |
| 60 | +fi |
| 61 | + |
| 62 | +# Install curl if not already installed |
| 63 | +check_and_install_tools() { |
| 64 | + if ! which curl >/dev/null 2>&1; then |
| 65 | + log "Installing required tools: curl" |
| 66 | + apt -q update && apt -yq install curl |
| 67 | + fi |
| 68 | +} |
| 69 | + |
| 70 | +SWIFT_WEBROOT="https://download.swift.org/swift-6.2-branch" |
| 71 | +PLATFORM_NAME="${OS_NAME}${ARCH_SUFFIX}" |
| 72 | +PLATFORM_WEBROOT="${SWIFT_WEBROOT}/${PLATFORM_NAME}" |
| 73 | +STATIC_SDK_WEBROOT="${SWIFT_WEBROOT}/static-sdk" |
| 74 | + |
| 75 | +# Directory for extracted toolchain (if needed to match the static SDK) |
| 76 | +TOOLCHAIN_DIR="${HOME}/.swift-toolchains" |
| 77 | + |
| 78 | +# Get current Swift version from /.swift_tag |
| 79 | +get_current_swift_version() { |
| 80 | + # Check /.swift_tag file |
| 81 | + if [[ -f "/.swift_tag" ]]; then |
| 82 | + local swift_tag |
| 83 | + swift_tag=$(tr -d '\n' < /.swift_tag | tr -d ' ') |
| 84 | + if [[ -n "$swift_tag" ]]; then |
| 85 | + log "Found Swift tag in /.swift_tag: $swift_tag" |
| 86 | + echo "$swift_tag" |
| 87 | + return 0 |
| 88 | + fi |
| 89 | + fi |
| 90 | + |
| 91 | + log "No Swift tag found in /.swift_tag" |
| 92 | + echo "none" |
| 93 | +} |
| 94 | + |
| 95 | +parse_yaml_value() { |
| 96 | + local key="$1" |
| 97 | + local content="$2" |
| 98 | + echo "$content" | grep "^${key}:" | sed "s/^${key}:[[:space:]]*//" |
| 99 | +} |
| 100 | + |
| 101 | +download_and_verify() { |
| 102 | + local url="$1" |
| 103 | + local sig_url="$2" |
| 104 | + local output_file="$3" |
| 105 | + local temp_sig="${output_file}.sig" |
| 106 | + |
| 107 | + log "Downloading ${url##*/}" |
| 108 | + curl -fsSL "$url" -o "$output_file" |
| 109 | + |
| 110 | + log "Downloading signature" |
| 111 | + curl -fsSL "$sig_url" -o "$temp_sig" |
| 112 | + |
| 113 | + log "Setting up GPG for verification" |
| 114 | + local gnupghome |
| 115 | + gnupghome="$(mktemp -d)" |
| 116 | + export GNUPGHOME="$gnupghome" |
| 117 | + curl -fSsL https://swift.org/keys/all-keys.asc | zcat -f | gpg --import - >/dev/null 2>&1 |
| 118 | + |
| 119 | + log "Verifying signature" |
| 120 | + if gpg --batch --verify "$temp_sig" "$output_file" >/dev/null 2>&1; then |
| 121 | + log "✅ Signature verification successful" |
| 122 | + else |
| 123 | + fatal "Signature verification failed" |
| 124 | + fi |
| 125 | + |
| 126 | + rm -rf "$GNUPGHOME" "$temp_sig" |
| 127 | +} |
| 128 | + |
| 129 | +# Uses the static SDK snapshot name to find the correct toolchain snapshot |
| 130 | +# E.g. takes "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-07-22-a" as input |
| 131 | +download_and_extract_toolchain() { |
| 132 | + local dir_name="$1" |
| 133 | + |
| 134 | + log "Downloading Swift toolchain: $dir_name" |
| 135 | + |
| 136 | + # "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-07-22-a-ubuntu22.04.tar.gz" |
| 137 | + # "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-07-22-a-ubuntu22.04.tar.gz.sig" |
| 138 | + local toolchain_filename="${dir_name}-${OS_DOTTED_VERSION}${ARCH_SUFFIX}.tar.gz" |
| 139 | + local toolchain_sig_filename="${toolchain_filename}.sig" |
| 140 | + |
| 141 | + local toolchain_url="${PLATFORM_WEBROOT}/${dir_name}/${toolchain_filename}" |
| 142 | + local toolchain_sig_url="${PLATFORM_WEBROOT}/${dir_name}/${toolchain_sig_filename}" |
| 143 | + |
| 144 | + # Check if toolchain is available |
| 145 | + local http_code |
| 146 | + http_code=$(curl -sSL --head -w "%{http_code}" -o /dev/null "$toolchain_url") |
| 147 | + if [[ "$http_code" == "404" ]]; then |
| 148 | + log "❌ Toolchain not found: ${toolchain_url##*/}" |
| 149 | + log "Exiting workflow..." |
| 150 | + # Don't fail the workflow if we can't find the right toolchain |
| 151 | + exit 0 |
| 152 | + fi |
| 153 | + |
| 154 | + # Create toolchain directory |
| 155 | + mkdir -p "$TOOLCHAIN_DIR" |
| 156 | + local toolchain_path="${TOOLCHAIN_DIR}/${dir_name}" |
| 157 | + |
| 158 | + # Check if toolchain already exists |
| 159 | + if [[ -d "$toolchain_path" && -f "${toolchain_path}/usr/bin/swift" ]]; then |
| 160 | + log "Toolchain already exists at: $toolchain_path" |
| 161 | + echo "$toolchain_path/usr/bin/swift" |
| 162 | + return 0 |
| 163 | + fi |
| 164 | + |
| 165 | + # Create temporary directory |
| 166 | + local temp_dir |
| 167 | + temp_dir=$(mktemp -d) |
| 168 | + local toolchain_file="${temp_dir}/swift_toolchain.tar.gz" |
| 169 | + |
| 170 | + # Download and verify toolchain |
| 171 | + download_and_verify "$toolchain_url" "$toolchain_sig_url" "$toolchain_file" |
| 172 | + |
| 173 | + log "Extracting toolchain to: $toolchain_path" |
| 174 | + mkdir -p "$toolchain_path" |
| 175 | + tar -xzf "$toolchain_file" --directory "$toolchain_path" --strip-components=1 |
| 176 | + |
| 177 | + # Clean up |
| 178 | + rm -rf "$temp_dir" |
| 179 | + |
| 180 | + local swift_executable="${toolchain_path}/usr/bin/swift" |
| 181 | + if [[ -f "$swift_executable" ]]; then |
| 182 | + log "✅ Swift toolchain extracted successfully" |
| 183 | + echo "$swift_executable" |
| 184 | + else |
| 185 | + fatal "Swift executable not found at expected path: $swift_executable" |
| 186 | + fi |
| 187 | +} |
| 188 | + |
| 189 | +install_static_sdk() { |
| 190 | + local sdk_info="$1" |
| 191 | + local swift_executable="$2" |
| 192 | + local download_name |
| 193 | + download_name=$(parse_yaml_value "download" "$sdk_info") |
| 194 | + local dir_name |
| 195 | + dir_name=$(parse_yaml_value "dir" "$sdk_info") |
| 196 | + local checksum |
| 197 | + checksum=$(parse_yaml_value "checksum" "$sdk_info") |
| 198 | + |
| 199 | + # Check if the static SDK is already installed |
| 200 | + if "$swift_executable" sdk list 2>/dev/null | grep -q "^$dir_name"; then |
| 201 | + log "✅ Static SDK $dir_name is already installed, skipping installation" |
| 202 | + return 0 |
| 203 | + fi |
| 204 | + |
| 205 | + log "Installing Swift Static SDK: $dir_name" |
| 206 | + |
| 207 | + local sdk_url="${STATIC_SDK_WEBROOT}/${dir_name}/${download_name}" |
| 208 | + |
| 209 | + log "Running: ${swift_executable} sdk install ${sdk_url} --checksum $checksum" |
| 210 | + |
| 211 | + if "$swift_executable" sdk install "$sdk_url" --checksum "$checksum"; then |
| 212 | + log "✅ Static SDK installed successfully" |
| 213 | + else |
| 214 | + fatal "Failed to install static SDK" |
| 215 | + fi |
| 216 | +} |
| 217 | + |
| 218 | +get_static_sdk_name() { |
| 219 | + local sdk_info="$1" |
| 220 | + local download_name |
| 221 | + download_name=$(parse_yaml_value "download" "$sdk_info") |
| 222 | + # Note: we want to keep the "_static-linux-0.0.1" |
| 223 | + echo "${download_name%.artifactbundle.tar.gz}" |
| 224 | +} |
| 225 | + |
| 226 | +run_swift_static_sdk_build() { |
| 227 | + local swift_executable="$1" |
| 228 | + local sdk_name="$2" |
| 229 | + |
| 230 | + log "Running Swift build with static SDK" |
| 231 | + log "Command: $swift_executable build --swift-sdk $sdk_name" |
| 232 | + |
| 233 | + if "$swift_executable" build --swift-sdk "$sdk_name"; then |
| 234 | + log "✅ Swift build with static SDK completed successfully" |
| 235 | + else |
| 236 | + fatal "Swift build with static SDK failed" |
| 237 | + fi |
| 238 | +} |
| 239 | + |
| 240 | +main() { |
| 241 | + log "Starting Swift 6.2 Static SDK and Toolchain setup for $OS_NAME" |
| 242 | + log "Platform URL: $PLATFORM_WEBROOT" |
| 243 | + |
| 244 | + check_and_install_tools |
| 245 | + |
| 246 | + local current_swift_version |
| 247 | + current_swift_version=$(get_current_swift_version) |
| 248 | + log "Current Swift version: $current_swift_version" |
| 249 | + |
| 250 | + log "Fetching latest 6.2 static SDK information" |
| 251 | + local sdk_info |
| 252 | + if ! sdk_info=$(curl -fsSL "${STATIC_SDK_WEBROOT}/latest-build.yml"); then |
| 253 | + fatal "Failed to fetch static SDK information" |
| 254 | + fi |
| 255 | + |
| 256 | + local sdk_dir |
| 257 | + sdk_dir=$(parse_yaml_value "dir" "$sdk_info") |
| 258 | + local sdk_checksum |
| 259 | + sdk_checksum=$(parse_yaml_value "checksum" "$sdk_info") |
| 260 | + |
| 261 | + log "Latest static SDK: $sdk_dir" |
| 262 | + log "Static SDK checksum: ${sdk_checksum:0:16}..." |
| 263 | + |
| 264 | + local swift_executable="" |
| 265 | + local sdk_name="" |
| 266 | + |
| 267 | + # Check if current Swift version matches the static SDK version |
| 268 | + if [[ "$current_swift_version" == "$sdk_dir" ]]; then |
| 269 | + log "✅ Current Swift version matches latest static SDK version" |
| 270 | + log "Using system Swift and installing static SDK" |
| 271 | + |
| 272 | + swift_executable="swift" |
| 273 | + install_static_sdk "$sdk_info" "$swift_executable" |
| 274 | + sdk_name=$(get_static_sdk_name "$sdk_info") |
| 275 | + |
| 276 | + else |
| 277 | + # Either no Swift or version mismatch, so download matching toolchain |
| 278 | + if [[ "$current_swift_version" == "none" ]]; then |
| 279 | + log "No Swift installation detected" |
| 280 | + else |
| 281 | + log "Current Swift version ($current_swift_version) does not match latest static SDK ($sdk_dir)" |
| 282 | + fi |
| 283 | + |
| 284 | + log "Downloading matching toolchain and installing static SDK" |
| 285 | + |
| 286 | + # Download the toolchain that matches the static SDK snapshot name |
| 287 | + swift_executable=$(download_and_extract_toolchain "$sdk_dir") |
| 288 | + install_static_sdk "$sdk_info" "$swift_executable" |
| 289 | + sdk_name=$(get_static_sdk_name "$sdk_info") |
| 290 | + fi |
| 291 | + |
| 292 | + # Uncomment to save paths to files for other scripting |
| 293 | + # echo "$swift_executable" > swift_executable_path.txt |
| 294 | + # echo "$sdk_name" > static_sdk_name.txt |
| 295 | + |
| 296 | + # log "Paths saved to:" |
| 297 | + # log " swift_executable_path.txt" |
| 298 | + # log " static_sdk_name.txt" |
| 299 | + |
| 300 | + # Run Swift build with static SDK |
| 301 | + log "" |
| 302 | + run_swift_static_sdk_build "$swift_executable" "$sdk_name" |
| 303 | + |
| 304 | + # Success |
| 305 | + log "" |
| 306 | + log "✅ Setup and build completed successfully!" |
| 307 | + log "" |
| 308 | + log "Swift executable path: $swift_executable" |
| 309 | + log "Static SDK name: $sdk_name" |
| 310 | + log "" |
| 311 | + log "To run manually:" |
| 312 | + log " $swift_executable build --swift-sdk $sdk_name" |
| 313 | + |
| 314 | +} |
| 315 | + |
| 316 | +main "$@" |
0 commit comments