Skip to content

Android SDK build scripts #467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 59 commits into
base: main
Choose a base branch
from
Open

Conversation

marcprux
Copy link

Following up on swiftlang/github-workflows#106 and swiftlang/swift#80788, this PR contains a new swift-docker/swift-ci/sdks/android/ folder with scripts that will build and upload a full stand-alone Swift Android Swift SDK. It is modeled after the structure of the swift-ci/sdks/static-linux build scripts.

Running

The top-level ./build script installs a host toolchain and the Android NDK, and then invokes scripts/fetch-source.sh which will fetch tagged sources for libxml2, curl, boringssl, and swift.

It then applies some patches and invokes scripts/build.sh, which will build the sources for each of the specified architectures. Finally, it combines the NDK and the newly built SDKs into a single artifactbundle.

Specifying Architectures

By default all the supported Android architectures (aarch64, x86_64, aarmv7) will be built, but this can be reduced in order to speed up the build. This can be useful, e.g., as part of a CI that validates a pull request, as building a single architecture takes around 30 minutes on a standard ubuntu-24.04 GitHub runner, whereas building for all the architectures takes over an hour.

To build an artifactbundle for just the x86_64 architecture, run:

TARGET_ARCHS=x86_64 ./build

Installing and validating the SDK

The .github/workflows/pull_request.yml workflow will create and upload an installable SDK named something like: swift-6.1-RELEASE_android-0.1.artifactbundle.tar.gz. The results of one of the workflow runs can be seen at https://github.com/swift-android-sdk/swift-docker/actions/runs/14603885089

The workflow will also install the SDK locally and use swift-android-action to build and test various Swift packages in an Android emulator.

marcprux and others added 5 commits March 31, 2025 14:28
* Build Android image

* Checkout without ssh

* Retry build if it fails

* Swift 6.1 Release Dockerfiles (swiftlang#456)

* Change binutils-gold package dependency on Debian 12 to binutils (swiftlang#457)

* Update installed packages after nightly platform expansion (swiftlang#458)

* update nightly-6.1 dependencies

* update nightly-main dependencies

* fix ubuntu images

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Fedora 41 Dockerfile (swiftlang#464)

* Build Android image

* Build Android image

* Swift 6.1 Release Dockerfiles (swiftlang#456)

* Change binutils-gold package dependency on Debian 12 to binutils (swiftlang#457)

* Build Android image

* Build Android image

* Build Android image

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Android build

* Swift Android build

* Swift Android build

* Swift Android build

---------

Co-authored-by: Mishal Shah <[email protected]>
Co-authored-by: Chris McGee <[email protected]>
Co-authored-by: Justice Adams <[email protected]>
Co-authored-by: Andrew Sukach <[email protected]>
* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Start splitting NDK out from the rest of the SDK

* Start splitting NDK out from the rest of the SDK

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2
* Swift Android build 6.2

* Swift Android build 6.2

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle
@marcprux
Copy link
Author

marcprux commented May 8, 2025

I've updated this PR to build the SDK without including the Android NDK. In order to accommodate this, there is now a post-install scripts/setup-android-sdk.sh that will need to be run manually after the swift sdk install … command. This will find the NDK based on the standard ANDROID_NDK_HOME environment variable and create links from the NDK into the path specified by swift-sdk.json's sdkRootPath, as well as pulling in the swiftrt.o to work around swiftlang/swift#79621.

The latest result of the build for the 6.2 nightly can be seen at: https://github.com/skiptools/swift-android-toolchain/releases/tag/6.2-DEVELOPMENT-SNAPSHOT-2025-05-07-a

@marcprux marcprux marked this pull request as ready for review May 8, 2025 19:30
@marcprux marcprux requested a review from shahmishal as a code owner May 8, 2025 19:30
@marcprux
Copy link
Author

marcprux commented May 8, 2025

To clarify what the post-install script is doing, following is a result of the output of the command with the default NDK directory:

zap org.swift.swiftpm/swift-sdks % tree swift-6.2-DEVELOPMENT-SNAPSHOT-2025-05-07-a-android-0.1.artifactbundle/swift-android/ndk-sysroot
└── usr
    ├── include -> ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include
    └── lib
        ├── aarch64-linux-android -> ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android
        ├── arm-linux-androideabi -> ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/arm-linux-androideabi
        ├── x86_64-linux-android -> ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/x86_64-linux-android
        ├── swift
        │   └── android
        │       ├── aarch64
        │       │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-aarch64/android/aarch64/swiftrt.o
        │       ├── armv7
        │       │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-armv7/android/armv7/swiftrt.o
        │       └── x86_64
        │           └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-x86_64/android/x86_64/swiftrt.o
        └── swift_static
            └── android
                ├── aarch64
                │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-aarch64/android/aarch64/swiftrt.o
                ├── armv7
                │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-armv7/android/armv7/swiftrt.o
                └── x86_64
                    └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-x86_64/android/x86_64/swiftrt.o

Alternative NDK locations can be specified by overriding the ANDROID_NDK_HOME, like so:

zap org.swift.swiftpm/swift-sdks % ANDROID_NDK_HOME="/opt/homebrew/share/android-ndk" swift-6.2-DEVELOPMENT-SNAPSHOT-2025-05-07-a-android-0.1.artifactbundle/swift-android/scripts/setup-android-sdk.sh
setup-android-sdk.sh: success: ndk-sysroot linked to Android SDK

zap org.swift.swiftpm/swift-sdks % tree swift-6.2-DEVELOPMENT-SNAPSHOT-2025-05-07-a-android-0.1.artifactbundle/swift-android/ndk-sysroot
└── usr
    ├── include -> /opt/homebrew/share/android-ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include
    └── lib
        ├── aarch64-linux-android -> /opt/homebrew/share/android-ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android
        ├── arm-linux-androideabi -> /opt/homebrew/share/android-ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/arm-linux-androideabi
        ├── x86_64-linux-android -> /opt/homebrew/share/android-ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/x86_64-linux-android
        ├── swift
        │   └── android
        │       ├── aarch64
        │       │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-aarch64/android/aarch64/swiftrt.o
        │       ├── armv7
        │       │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-armv7/android/armv7/swiftrt.o
        │       └── x86_64
        │           └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift-x86_64/android/x86_64/swiftrt.o
        └── swift_static
            └── android
                ├── aarch64
                │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-aarch64/android/aarch64/swiftrt.o
                ├── armv7
                │   └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-armv7/android/armv7/swiftrt.o
                └── x86_64
                    └── swiftrt.o -> ../../../../../../swift-resources/usr/lib/swift_static-x86_64/android/x86_64/swiftrt.o

marcprux and others added 2 commits May 17, 2025 20:03
* Add static libraries to post-install script

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Update submodules
@marcprux
Copy link
Author

As of 72964f5, the Android SDK is now being built in a Docker container.

@etcwilde
Copy link
Member

Working through this. One thing to note, https://github.com/swiftlang/swift-docker/blob/main/ci_test.py --

swift-docker/ci_test.py

Lines 79 to 83 in c95bc0c

cmd = [
'docker', 'build', '--no-cache=true',
'-f', dockerfile,
'-t', image_name,
docker_dir
runs the docker build commands from the repository root, not from the subdirectory so scripts will need to be able to accommodate a working directory that is different from the directory where the script lives.

@marcprux
Copy link
Author

I think it should be fine in theory to run the Dockerfile from another folder, but it does require some mount points to be set up (see https://github.com/swift-android-sdk/swift-docker/blob/main/swift-ci/sdks/android/build-docker#L63).

I also notice that the static-linux Dockerfile doesn't build out-of-the-box with a generic command like:

$ docker build --no-cache=true -f swift-ci/sdks/static-linux/Dockerfile .

…
--------------------
  86 |     RUN chmod ugo+x /scripts/*
  87 |     
  88 | >>> COPY resources /resources
  89 |     
  90 |     # Create a user
--------------------
ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref 19bdc85c-49c5-4899-bc25-2a1cffd11dfe::e0kqjvdnitebn8odzbggham6d: "/resources": not found

Is there some different command I should run to test it?

Copy link
Member

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Started going through this. I think my main concerns right now are around cloning repo's that we are already cloning with update-checkout and the patches that haven't been merged into the repos yet. Otherwise it's generally looking fine. I still need to look more closely at the build.sh though.

@@ -21,3 +21,172 @@ jobs:
name: docker-logs
path: |
*.log

android-build:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about running this on every PR against the swift-docker repository.
At the moment, testing on this repo takes a few minutes maximum, while I suspect that this will increase that it pretty substantially. It might be good to either limit running it to changes to the android subdirectory, or we should make this a dedicated job on Swift CI like the static Linux Musl SDK instead.
CC @shahmishal, @justice-adams-apple

Copy link
Author

@marcprux marcprux Jul 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about running this on every PR against the swift-docker repository.

Yeah, actually, that android-build CI part was intended to be expunged before this PR would be merged. 😊

It is just there so we can exercise updates to the pull request.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that sounds good to me. Either Justice or I can probably use it as a template for setting up the Jenkins job if we have questions later, so useful to have in history.

@marcprux
Copy link
Author

@etcwilde, did you ever manage to run this and get an Android SDK artifactbundle out of it? We're itching to start publishing 6.2 snapshots of the SDK so we can get more eyes on it and start integrating those snapshots into our Android CI workflow.

@marcprux
Copy link
Author

@etcwilde I was hoping we could get some feedback on this PR so we can discuss it at the Android Workgroup meeting on Wednesday. Did you ever run the SDK build script? The sooner we can starting publishing snapshots, the sooner we will be able to iron out any last kinks before 6.2 is released.

the same artifacts in approximately half the time, and
may be suitable to an already containerized envrionment (such as
a GitHub runner).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to include an example of the command(s) for running the build locally?

Copy link
Author

@marcprux marcprux Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-DBUILD_STATIC_LIBS=ON \
-DCURL_BUILD_TESTS=OFF

quiet_pushd ${build_dir}/$arch/curl
Copy link
Member

@etcwilde etcwilde Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, save the pushd/popd call and let CMake run the build for you.

run cmake --build ${build_dir}/$arch/curl -- -j ${parallel_jobs}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


header "Installing libcurl for $arch"
quiet_pushd ${build_dir}/$arch/curl
run ninja -j$parallel_jobs install
Copy link
Member

@etcwilde etcwilde Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run cmake --install ${build_dir}/${arch}/curl

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

groupend

groupstart "Building libcurl for ${compiler_target_host}"
quiet_pushd ${swift_source_dir}/curl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already passing -S and -B to CMake. We shouldn't need the pushd/popd.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

declare -g ${name}_license="$license"
declare -g ${name}_url="$url"

packages+=(${name})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use this to generate the spdx SBOM for the SDK like the static SDK does here:

spdx_uuid=$(uuidgen)
spdx_doc_uuid=$(uuidgen)
spdx_timestamp=$(date -Iseconds)
sdk_name=swift-${swift_version}_static-linux-${static_linux_sdk_version}
bundle="${sdk_name}.artifactbundle"
rm -rf "${build_dir}/$bundle"
mkdir -p "${build_dir}/$bundle/$sdk_name/swift-linux-musl"
quiet_pushd ${build_dir}/$bundle
# First the info.json, for SwiftPM
cat > info.json <<EOF
{
"schemaVersion": "1.0",
"artifacts": {
"$sdk_name": {
"variants": [
{
"path": "$sdk_name/swift-linux-musl"
}
],
"version": "0.0.1",
"type": "swiftSDK"
}
}
}
EOF
# Now generate SPDX data
cat > sbom.spdx.json <<EOF
{
"SPDXID": "SPDXRef-DOCUMENT",
"name": "SBOM-SPDX-${spdx_uuid}",
"spdxVersion": "SPDX-2.3",
"creationInfo": {
"created": "${spdx_timestamp}",
"creators": [
"Organization: Apple, Inc."
]
},
"dataLicense": "Apache-2.0",
"documentNamespace": "urn:uuid:${spdx_doc_uuid}",
"documentDescribes": [
"SPDXRef-Package-static-linux-sdk"
],
"packages": [
EOF
first=true
for package in ${packages[@]}; do
if [[ "$first" == "true" ]]; then
first=false
else
cat >> sbom.spdx.json <<EOF
},
EOF
fi
snake=${package}_snake; snake=${!snake}
version=${package}_version; version=${!version}
name=${package}_name; name=${!name}
license=${package}_license; license=${!license}
url=${package}_url; url=${!url}
cat >> sbom.spdx.json <<EOF
{
"SPDXID": "SPDXRef-Package-${snake}",
"name": "${name}",
"versionInfo": "${version}",
"filesAnalyzed": false,
"licenseDeclared": "${license}",
"licenseConcluded": "${license}",
"downloadLocation": "${url}",
"copyrightText": "NOASSERTION",
"checksums": []
EOF
done
cat >> sbom.spdx.json <<EOF
}
],
"relationships": [
EOF
first=true
for package in ${packages[@]}; do
if [[ "$package" == "static_linux_sdk" ]]; then
continue
fi
if [[ "$first" == "true" ]]; then
first=false
else
cat >> sbom.spdx.json <<EOF
},
EOF
fi
snake=${package}_snake; snake=${!snake}
cat >> sbom.spdx.json <<EOF
{
"spdxElementId": "SPDXRef-Package-static-linux-sdk",
"relationshipType": "GENERATED_FROM",
"relatedSpdxElement": "SPDXRef-Package-${snake}"
EOF
done
cat >> sbom.spdx.json <<EOF
}
]
}
EOF

Copy link
Author

@marcprux marcprux Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've added generation of a sbom.spdx.json file in swift-android-sdk#14, which will look like:

{
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "SBOM-SPDX-9fe120c9-d845-4586-876b-4080c9143143",
  "spdxVersion": "SPDX-2.3",
  "creationInfo": {
    "created": "2025-08-22T02:37:12+00:00",
    "creators": [
      "Organization: Apple, Inc."
    ]
  },
  "dataLicense": "Apache-2.0",
  "documentNamespace": "urn:uuid:cceb1cb9-34b6-4595-9e1f-9091c408d88b",
  "documentDescribes": [
    "SPDXRef-Package-swift-android-sdk"
  ],
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-swift-android-sdk",
      "name": "Swift SDK for Android",
      "versionInfo": "",
      "filesAnalyzed": false,
      "licenseDeclared": "Apache-2.0",
      "licenseConcluded": "Apache-2.0",
      "downloadLocation": "https://swift.org/install",
      "copyrightText": "NOASSERTION",
      "checksums": []
    },
    {
      "SPDXID": "SPDXRef-Package-swift",
      "name": "swift",
      "versionInfo": "6.2-DEVELOPMENT-SNAPSHOT-2025-08-20-a",
      "filesAnalyzed": false,
      "licenseDeclared": "Apache-2.0",
      "licenseConcluded": "Apache-2.0",
      "downloadLocation": "https://swift.org",
      "copyrightText": "NOASSERTION",
      "checksums": []
    },
    {
      "SPDXID": "SPDXRef-Package-libxml2",
      "name": "libxml2",
      "versionInfo": "2.11.5",
      "filesAnalyzed": false,
      "licenseDeclared": "MIT",
      "licenseConcluded": "MIT",
      "downloadLocation": "https://github.com/GNOME/libxml2",
      "copyrightText": "NOASSERTION",
      "checksums": []
    },
    {
      "SPDXID": "SPDXRef-Package-curl",
      "name": "curl",
      "versionInfo": "8.9.1",
      "filesAnalyzed": false,
      "licenseDeclared": "MIT",
      "licenseConcluded": "MIT",
      "downloadLocation": "https://curl.se",
      "copyrightText": "NOASSERTION",
      "checksums": []
    },
    {
      "SPDXID": "SPDXRef-Package-boringssl",
      "name": "boringssl",
      "versionInfo": "fips-20220613",
      "filesAnalyzed": false,
      "licenseDeclared": "OpenSSL AND ISC AND MIT",
      "licenseConcluded": "OpenSSL AND ISC AND MIT",
      "downloadLocation": "https://boringssl.googlesource.com/boringssl/",
      "copyrightText": "NOASSERTION",
      "checksums": []
    }
  ],
  "relationships": [
    {
      "spdxElementId": "SPDXRef-Package-swift-android-sdk",
      "relationshipType": "GENERATED_FROM",
      "relatedSpdxElement": "SPDXRef-Package-swift"
    },
    {
      "spdxElementId": "SPDXRef-Package-swift-android-sdk",
      "relationshipType": "GENERATED_FROM",
      "relatedSpdxElement": "SPDXRef-Package-libxml2"
    },
    {
      "spdxElementId": "SPDXRef-Package-swift-android-sdk",
      "relationshipType": "GENERATED_FROM",
      "relatedSpdxElement": "SPDXRef-Package-curl"
    },
    {
      "spdxElementId": "SPDXRef-Package-swift-android-sdk",
      "relationshipType": "GENERATED_FROM",
      "relatedSpdxElement": "SPDXRef-Package-boringssl"
    }
  ]
}

Copy link
Member

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed an issue in one of the shebangs.
Do we want a /usr/bin/env bash? I know these scripts are documented to work on Ubuntu 24.04 so the /bin/bash should be fine, but it looks like most of the pieces should work on other linux distros too.

finagolfin and others added 5 commits August 3, 2025 14:09
…ng and add

16 KB page linker flag to 6.2 branch
…and installing LLVM

and the Testing macros for cross-compiled hosts, then enable building and using the
Testing macros for the native host.
…directory in the post-install script instead
marcprux and others added 4 commits August 21, 2025 20:03
Wildcard the clang version link in the post-install script in order to accomodate NDK 27 and 28
* Address nits

* Eliminate unnecessaryt pushds and use run cmake install instead of ninja

* Generate SBOM with SDK dependencies

* Add example of command for building locally
@marcprux marcprux requested a review from etcwilde August 22, 2025 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants