diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 32b404a..384d049 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -44,6 +44,12 @@ jobs: cd MyPackage swift package init --type library enable_wasm_sdk_build: true + # Android + android_sdk_pre_build_command: | + mkdir MyPackage + cd MyPackage + swift package init --type library + enable_android_sdk_build: true # Windows windows_build_command: | mkdir MyPackage @@ -58,6 +64,12 @@ jobs: with: # Skip Linux which doesn't currently support docker-less workflow enable_linux_checks: false + # Android + android_sdk_pre_build_command: | + mkdir MyPackage + cd MyPackage + swift package init --type library + enable_android_sdk_build: true # Windows windows_build_command: | mkdir MyPackage diff --git a/.github/workflows/scripts/install-and-build-with-sdk.sh b/.github/workflows/scripts/install-and-build-with-sdk.sh index 6111a63..986aa8c 100644 --- a/.github/workflows/scripts/install-and-build-with-sdk.sh +++ b/.github/workflows/scripts/install-and-build-with-sdk.sh @@ -18,6 +18,7 @@ error() { printf -- "** ERROR: %s\n" "$*" >&2; } fatal() { error "$@"; exit 1; } # Parse command line options +INSTALL_ANDROID=false INSTALL_STATIC_LINUX=false INSTALL_WASM=false BUILD_EMBEDDED_WASM=false @@ -27,6 +28,18 @@ SWIFT_BUILD_COMMAND="swift build" while [[ $# -gt 0 ]]; do case $1 in + --android) + INSTALL_ANDROID=true + shift + ;; + --android-ndk-version=*) + ANDROID_NDK_VERSION="${1#*=}" + shift + ;; + --android-sdk-triple=*) + ANDROID_SDK_TRIPLE="${1#*=}" + shift + ;; --static) INSTALL_STATIC_LINUX=true shift @@ -64,32 +77,32 @@ done # Validate arguments if [[ -z "$SWIFT_VERSION_INPUT" ]]; then - fatal "Usage: $0 [--static] [--wasm] [--flags=\"\"] [--build-command=\"\"] " + fatal "Usage: $0 [--android] [--static] [--wasm] [--flags=\"\"] [--build-command=\"\"] " fi -if [[ "$INSTALL_STATIC_LINUX" == false && "$INSTALL_WASM" == false ]]; then - fatal "At least one of --static or --wasm must be specified" +if [[ "$INSTALL_ANDROID" == false && "$INSTALL_STATIC_LINUX" == false && "$INSTALL_WASM" == false ]]; then + fatal "At least one of --android or --static or --wasm must be specified" fi log "Requested Swift version: $SWIFT_VERSION_INPUT" +log "Install Android Swift SDK: $INSTALL_ANDROID" log "Install Static Linux Swift SDK: $INSTALL_STATIC_LINUX" log "Install Wasm Swift SDK: $INSTALL_WASM" if [[ -n "$SWIFT_BUILD_FLAGS" ]]; then log "Additional build flags: $SWIFT_BUILD_FLAGS" fi -# Detect package manager -if command -v apt >/dev/null 2>&1; then - INSTALL_PACKAGE_COMMAND="apt update -q && apt install -yq" -elif command -v dnf >/dev/null 2>&1; then - INSTALL_PACKAGE_COMMAND="dnf install -y" -elif command -v yum >/dev/null 2>&1; then - INSTALL_PACKAGE_COMMAND="yum install -y" -else - fatal "No supported package manager found" -fi - install_package() { + # Detect package manager + if command -v apt >/dev/null 2>&1; then + INSTALL_PACKAGE_COMMAND="apt update -q && apt install -yq" + elif command -v dnf >/dev/null 2>&1; then + INSTALL_PACKAGE_COMMAND="dnf install -y" + elif command -v yum >/dev/null 2>&1; then + INSTALL_PACKAGE_COMMAND="yum install -y" + else + fatal "No supported package manager found" + fi eval "$INSTALL_PACKAGE_COMMAND $1" } @@ -103,7 +116,7 @@ SWIFT_API_INSTALL_ROOT="https://www.swift.org/api/v1/install" # and gets the checksum for the patch version's Static Linux and/or Wasm Swift SDK. # # $1 (string): A minor Swift version, e.g. "6.1" -# Output: A string of the form "|| +# Output: A string of the form "||| find_latest_swift_version() { local minor_version="$1" @@ -128,6 +141,23 @@ find_latest_swift_version() { log "Found latest patch version: $latest_version" + local android_sdk_checksum="" + if [[ "$INSTALL_ANDROID" == true ]]; then + android_sdk_checksum=$(echo "$releases_json" | jq -r --arg version "$latest_version" ' + .[] + | select(.name == $version) + | .platforms[] + | select(.platform == "android") + | .checksum + ') + + if [[ -z "$android_sdk_checksum" ]]; then + fatal "No Android Swift SDK checksum found for Swift $latest_version" + fi + + log "Found Android Swift SDK checksum: ${android_sdk_checksum:0:12}..." + fi + local static_linux_sdk_checksum="" if [[ "$INSTALL_STATIC_LINUX" == true ]]; then static_linux_sdk_checksum=$(echo "$releases_json" | jq -r --arg version "$latest_version" ' @@ -162,14 +192,15 @@ find_latest_swift_version() { log "Found Swift SDK for Wasm checksum: ${wasm_sdk_checksum:0:12}..." fi - echo "${latest_version}|${static_linux_sdk_checksum}|${wasm_sdk_checksum}" + echo "${latest_version}|${android_sdk_checksum}|${static_linux_sdk_checksum}|${wasm_sdk_checksum}" } -# Finds the latest Static Linux or Wasm Swift SDK development snapshot -# for the inputted Swift version and its checksum. +# Finds the latest Android or Static Linux or Wasm +# Swift SDK development snapshot for the inputted +# Swift version and its checksum. # # $1 (string): Nightly Swift version, e.g. "6.2" or "main" -# $2 (string): "static" or "wasm" +# $2 (string): "android" or "static" or "wasm" # Output: A string of the form "|", # e.g. "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-07-29-a|" find_latest_sdk_snapshot() { @@ -206,6 +237,12 @@ find_latest_sdk_snapshot() { } SWIFT_VERSION_BRANCH="" +ANDROID_SDK_TAG="" +ANDROID_SDK_CHECKSUM="" +# TODO: we will be removing the "-0.1" suffix in a future nightly +ANDROID_SDK_PATH_SUFFIX="-0.1" +ANDROID_SDK_PATH_SEP="-" + STATIC_LINUX_SDK_TAG="" STATIC_LINUX_SDK_CHECKSUM="" WASM_SDK_TAG="" @@ -220,6 +257,13 @@ if [[ "$SWIFT_VERSION_INPUT" == nightly-* ]]; then SWIFT_VERSION_BRANCH="swift-${version}-branch" fi + if [[ "$INSTALL_ANDROID" == true ]]; then + android_sdk_info=$(find_latest_sdk_snapshot "$version" "android") + + ANDROID_SDK_TAG=$(echo "$android_sdk_info" | cut -d'|' -f1) + ANDROID_SDK_CHECKSUM=$(echo "$android_sdk_info" | cut -d'|' -f2) + fi + if [[ "$INSTALL_STATIC_LINUX" == true ]]; then static_linux_sdk_info=$(find_latest_sdk_snapshot "$version" "static") @@ -239,14 +283,21 @@ else latest_version=$(echo "$latest_version_info" | cut -d'|' -f1) SWIFT_VERSION_BRANCH="swift-${latest_version}-release" + ANDROID_SDK_TAG="swift-${latest_version}-RELEASE" + ANDROID_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f2) + STATIC_LINUX_SDK_TAG="swift-${latest_version}-RELEASE" - STATIC_LINUX_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f2) + STATIC_LINUX_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f3) WASM_SDK_TAG="swift-${latest_version}-RELEASE" - WASM_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f3) + WASM_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f4) fi # Validate that required Swift SDK tags are set +if [[ "$INSTALL_ANDROID" == true && -z "$ANDROID_SDK_TAG" ]]; then + fatal "ANDROID_SDK_TAG is not set but Android Swift SDK installation was requested" +fi + if [[ "$INSTALL_STATIC_LINUX" == true && -z "$STATIC_LINUX_SDK_TAG" ]]; then fatal "STATIC_LINUX_SDK_TAG is not set but Static Linux Swift SDK installation was requested" fi @@ -439,9 +490,25 @@ download_and_extract_toolchain() { } INSTALLED_SWIFT_TAG=$(get_installed_swift_tag) +SWIFT_EXECUTABLE_FOR_ANDROID_SDK="" SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK="" SWIFT_EXECUTABLE_FOR_WASM_SDK="" +if [[ "$INSTALL_ANDROID" == true ]]; then + if [[ "$INSTALLED_SWIFT_TAG" == "$ANDROID_SDK_TAG" ]]; then + log "Current toolchain matches Android Swift SDK snapshot: $ANDROID_SDK_TAG" + SWIFT_EXECUTABLE_FOR_ANDROID_SDK="swift" + else + log "Installing Swift toolchain to match Android Swift SDK snapshot: $ANDROID_SDK_TAG" + initialize_os_info + SWIFT_EXECUTABLE_FOR_ANDROID_SDK=$(download_and_extract_toolchain "$ANDROID_SDK_TAG") + if [[ $? -eq $EXIT_TOOLCHAIN_NOT_FOUND ]]; then + # Don't fail the workflow if we can't find the right toolchain + exit 0 + fi + fi +fi + if [[ "$INSTALL_STATIC_LINUX" == true ]]; then if [[ "$INSTALLED_SWIFT_TAG" == "$STATIC_LINUX_SDK_TAG" ]]; then log "Current toolchain matches Static Linux Swift SDK snapshot: $STATIC_LINUX_SDK_TAG" @@ -472,9 +539,57 @@ if [[ "$INSTALL_WASM" == true ]]; then fi fi +ANDROID_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/android-sdk" STATIC_LINUX_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/static-sdk" WASM_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/wasm-sdk" +install_android_sdk() { + # Check if the Android Swift SDK is already installed + if "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" sdk list 2>/dev/null | grep -q "^${ANDROID_SDK_TAG}_android"; then + log "✅ Android Swift SDK ${ANDROID_SDK_TAG} is already installed, skipping installation" + return 0 + fi + + log "Installing Android Swift SDK: $ANDROID_SDK_TAG" + + local android_sdk_name="${ANDROID_SDK_TAG}_android${ANDROID_SDK_PATH_SUFFIX}" + local android_sdk_bundle_name="${android_sdk_name}.artifactbundle" + # TODO: remove once the next nightly changes the "-" to "_" in the name + local android_sdk_bundle_dir="${android_sdk_bundle_name//_android/${ANDROID_SDK_PATH_SEP}android}" + local android_sdk_filename="${android_sdk_bundle_name}.tar.gz" + local sdk_url="${ANDROID_SDK_DOWNLOAD_ROOT}/${ANDROID_SDK_TAG}/${android_sdk_filename}" + + log "Running: ${SWIFT_EXECUTABLE_FOR_ANDROID_SDK} sdk install ${sdk_url} --checksum ${ANDROID_SDK_CHECKSUM}" + + if "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" sdk install "$sdk_url" --checksum "$ANDROID_SDK_CHECKSUM"; then + log "✅ Android Swift SDK installed successfully" + else + fatal "Failed to install Android Swift SDK" + fi + + # now setup the link to the local ANDROID_NDK_HOME + swift sdk configure --show-configuration "$(swift sdk list | grep android | tail -n 1)" + + # guess some common places where the swift-sdks file lives + cd ~/Library/org.swift.swiftpm || cd ~/.config/swiftpm || cd ~/.local/swiftpm || cd ~/.swiftpm || cd /root/.swiftpm + + # Download and install the Android NDK. + # Note that we could use the system package manager, but it is + # named different things for different distributions + # (e.g., "google-android-ndk-r26-installer" on Debian) + if [[ ! -d "${ANDROID_NDK_HOME:-}" ]]; then + # permit the "--android-ndk" flag to override the default + local android_ndk_version="${ANDROID_NDK_VERSION:-r27d}" + curl -fsSL -o ndk.zip --retry 3 https://dl.google.com/android/repository/android-ndk-"${android_ndk_version}"-"$(uname -s)".zip + unzip -q ndk.zip + rm ndk.zip + export ANDROID_NDK_HOME="${PWD}"/android-ndk-"${android_ndk_version}" + fi + + ./swift-sdks/"${android_sdk_bundle_dir}"/swift-android/scripts/setup-android-sdk.sh + cd - +} + install_static_linux_sdk() { # Check if the Static Linux Swift SDK is already installed if "$SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK" sdk list 2>/dev/null | grep -q "^${STATIC_LINUX_SDK_TAG}_static-linux-0.0.1"; then @@ -518,6 +633,11 @@ install_wasm_sdk() { } install_sdks() { + if [[ "$INSTALL_ANDROID" == true ]]; then + log "Starting install of Swift ${SWIFT_VERSION_INPUT} Android Swift SDK" + install_android_sdk + fi + if [[ "$INSTALL_STATIC_LINUX" == true ]]; then log "Starting install of Swift ${SWIFT_VERSION_INPUT} Static Linux Swift SDK" install_static_linux_sdk @@ -533,6 +653,31 @@ build() { # Enable alias expansion to use a 'swift' alias for the executable path shopt -s expand_aliases + if [[ "$INSTALL_ANDROID" == true ]]; then + log "Running Swift build with Android Swift SDK" + + local sdk_name="${ANDROID_SDK_TAG}${ANDROID_SDK_PATH_SEP}android${ANDROID_SDK_PATH_SUFFIX}" + + alias swift='$SWIFT_EXECUTABLE_FOR_ANDROID_SDK' + local build_command="$SWIFT_BUILD_COMMAND --swift-sdk ${ANDROID_SDK_TRIPLE:-$sdk_name}" + if [[ -n "$SWIFT_BUILD_FLAGS" ]]; then + build_command="$build_command $SWIFT_BUILD_FLAGS" + fi + + log "Running: $build_command" + + # clear the ANDROID_NDK_ROOT environment variable if it is set + # due to https://github.com/swiftlang/swift-driver/pull/1879 + # otherwise build error: missing required module 'SwiftAndroid' + export ANDROID_NDK_ROOT="" + + if eval "$build_command"; then + log "✅ Swift build with Android Swift SDK completed successfully" + else + fatal "Swift build with Android Swift SDK failed" + fi + fi + if [[ "$INSTALL_STATIC_LINUX" == true ]]; then log "Running Swift build with Static Linux Swift SDK" diff --git a/.github/workflows/swift_package_test.yml b/.github/workflows/swift_package_test.yml index 68125a4..67eac73 100644 --- a/.github/workflows/swift_package_test.yml +++ b/.github/workflows/swift_package_test.yml @@ -50,6 +50,14 @@ on: type: string description: "Exclude Wasm Swift SDK version list (JSON)" default: "[{\"swift_version\": \"\"}]" + android_sdk_versions: + type: string + description: "Android Swift SDK version list (JSON)" + default: "[\"nightly-main\"]" + android_exclude_swift_versions: + type: string + description: "Exclude Android Swift SDK version list (JSON)" + default: "[{\"swift_version\": \"\"}]" windows_swift_versions: type: string description: "Include Windows Swift version list (JSON)" @@ -79,6 +87,10 @@ on: type: string description: "Linux command to execute before building the Swift package with the Embedded Swift SDK for Wasm" default: "" + android_sdk_pre_build_command: + type: string + description: "Linux command to execute before building the Swift package with the Embedded Swift SDK for Android" + default: "" macos_pre_build_command: type: string description: "macOS command to execute before building the Swift package" @@ -99,6 +111,18 @@ on: type: string description: "Command to use when building the package with the Swift SDK for Wasm" default: "swift build" + android_sdk_build_command: + type: string + description: "Command to use when building the package with the Swift SDK for Android" + default: "swift build" + android_sdk_triple: + type: string + description: "The triple to use when building with the Swift SDK for Android" + default: "[\"x86_64-unknown-linux-android28\"]" + android_ndk_version: + type: string + description: "The NDK version to use when building with the Swift SDK for Android" + default: "[\"r27d\"]" windows_pre_build_command: type: string description: "Windows Command Prompt command to execute before building the Swift package" @@ -135,6 +159,10 @@ on: type: boolean description: "Boolean to enable building with the Embedded Swift SDK for Wasm. Defaults to false" default: false + enable_android_sdk_build: + type: boolean + description: "Boolean to enable building with the Swift SDK for Android. Defaults to false" + default: false enable_macos_checks: type: boolean description: "Boolean to enable macOS testing. Defaults to false" @@ -408,6 +436,65 @@ jobs: curl -s --retry 3 https://raw.githubusercontent.com/swiftlang/github-workflows/refs/heads/main/.github/workflows/scripts/install-and-build-with-sdk.sh | \ bash -s -- --embedded-wasm --flags="$BUILD_FLAGS" ${{ matrix.swift_version }} + android-sdk-build: + name: Swift SDK for Android Build (${{ matrix.swift_version }} - ${{ matrix.os_version }}) + if: ${{ inputs.enable_android_sdk_build }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + swift_version: ${{ fromJson(inputs.android_sdk_versions) }} + sdk_triple: ${{ fromJson(inputs.android_sdk_triple) }} + ndk_version: ${{ fromJson(inputs.android_ndk_version) }} + os_version: ${{ fromJson(inputs.linux_os_versions) }} + exclude: + - ${{ fromJson(inputs.android_exclude_swift_versions) }} + container: + image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} + steps: + - name: Swift version + run: swift --version + - name: Clang version + run: clang --version + - name: Checkout repository + uses: actions/checkout@v4 + if: ${{ matrix.os_version != 'amazonlinux2' }} + - name: Checkout repository + uses: actions/checkout@v1 + if: ${{ matrix.os_version == 'amazonlinux2' }} + - name: Provide token + if: ${{ inputs.needs_token }} + run: | + echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + - name: Set environment variables + if: ${{ inputs.linux_env_vars }} + run: | + for i in "${{ inputs.linux_env_vars }}" + do + printf "%s\n" $i >> $GITHUB_ENV + done + - name: Pre-build + run: ${{ inputs.linux_pre_build_command }} + - name: Install Swift SDK for Android and build + env: + BUILD_FLAGS: ${{ (contains(matrix.swift_version, 'nightly') && inputs.swift_nightly_flags) || inputs.swift_flags }} + run: | + ${{ inputs.android_sdk_pre_build_command }} + if command -v apt-get >/dev/null 2>&1 ; then # bookworm, noble, jammy, focal + apt-get -q update && apt-get -yq install curl + elif command -v dnf >/dev/null 2>&1 ; then # rhel-ubi9 + dnf -y update + dnf -y install curl-minimal + elif command -v yum >/dev/null 2>&1 ; then # amazonlinux2 + yum -y update + yum -y install curl + else + echo "Unknown package manager (tried apt-get, dnf, yum)" >&2 + exit 1 + fi + curl -s --retry 3 https://raw.githubusercontent.com/swiftlang/github-workflows/refs/heads/main/.github/workflows/scripts/install-and-build-with-sdk.sh | \ + bash -s -- --android --flags="$BUILD_FLAGS" --build-command="${{ inputs.android_sdk_build_command }}" --android-sdk-triple="${{ inputs.sdk_triple }}" --android-ndk-version="${{ inputs.ndk_version }}" ${{ matrix.swift_version }} + windows-build: name: Windows (${{ matrix.swift_version }} - windows-2022) if: ${{ inputs.enable_windows_checks }}