Enable LTO iOS #5485
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: ios-ci | |
on: | |
workflow_dispatch: | |
inputs: | |
release: | |
type: choice | |
default: no | |
options: | |
- full | |
- pre | |
- no | |
description: Whether to make a release, choose full release (uses platform/ios/VERSION) or pre-release | |
pre_release_version: | |
type: string | |
default: '' | |
description: Version (only for pre-releases) | |
push: | |
branches: | |
- main | |
- ios-*.*.x | |
tags: | |
- 'ios-*' | |
pull_request: | |
branches: | |
- '*' | |
permissions: | |
id-token: write # needed for AWS | |
contents: write # allow making a release | |
jobs: | |
pre_job: | |
runs-on: ubuntu-latest | |
outputs: | |
should_skip: ${{ github.event_name != 'workflow_dispatch' && steps.changed-files-yaml.outputs.ios_any_modified != 'true' }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Get all iOS files that have changed | |
if: github.event_name != 'workflow_dispatch' | |
id: changed-files-yaml | |
uses: tj-actions/changed-files@v44 | |
with: | |
files_yaml_from_source_file: .github/changed-files.yml | |
- name: Run step if test file(s) change | |
if: steps.changed-files-yaml.outputs.ios_any_modified == 'true' | |
run: | | |
echo "One or more iOS file(s) has changed." | |
echo "List of changes: ${{ steps.changed-files-yaml.outputs.ios_all_changed_files }}" | |
ios-build: | |
needs: pre_job | |
if: needs.pre_job.outputs.should_skip != 'true' | |
runs-on: [self-hosted, macOS, ARM64] | |
concurrency: | |
# cancel jobs on PRs only | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
env: | |
BUILDTYPE: Debug | |
HOMEBREW_NO_AUTO_UPDATE: 1 | |
HOMEBREW_NO_INSTALL_CLEANUP: 1 | |
defaults: | |
run: | |
working-directory: platform/ios | |
shell: bash | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
fetch-depth: 0 | |
- name: Cache Bazel | |
uses: actions/cache@v4 | |
with: | |
key: ${{ runner.os }}-bazel-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE', 'WORKSPACE.bazel', 'MODULE.bazel') }} | |
restore-keys: | | |
${{ runner.os }}-bazel- | |
path: ~/.cache/bazel | |
- name: Build Example (Swift) App | |
run: bazel build //platform/ios/app-swift:MapLibreApp --//:renderer=metal | |
- name: Check debug symbols | |
run: bazel run //platform:check-public-symbols --//:renderer=metal | |
- name: Lint plist files | |
run: bazel run //platform/ios:lint-plists --//:renderer=metal | |
- name: Running iOS tests | |
run: bazel test //platform/ios/test:ios_test --test_output=errors --//:renderer=metal | |
# tsan run fails with 'Interceptors are not working. This may be because ThreadSanitizer is loaded too late...' | |
#- name: Running iOS UI tests (Thread Sanitizer) | |
# run: bazel test //platform/ios/iosapp-UITests:uitest --test_output=errors --//:renderer=metal --features=tsan | |
- name: Running iOS UI tests (Address+UB Sanitizer) | |
run: bazel test //platform/ios/iosapp-UITests:uitest --test_output=errors --//:renderer=metal --features=include_clang_rt --copt=-fsanitize=undefined --linkopt=-fsanitize=undefined --copt=-fsanitize-recover=null --linkopt=-fsanitize-recover=null | |
# render test | |
- name: Build RenderTest .ipa and .xctest for AWS Device Farm | |
run: | | |
set -e | |
bazel run //platform/ios:xcodeproj --@rules_xcodeproj//xcodeproj:extra_common_flags="--//:renderer=metal" | |
build_dir="$(mktemp -d)" | |
xcodebuild build-for-testing -scheme RenderTest -project MapLibre.xcodeproj -derivedDataPath "$build_dir" | |
render_test_app_dir="$(dirname "$(find "$build_dir" -name RenderTestApp.app)")" | |
cd "$render_test_app_dir" | |
mkdir Payload | |
mv RenderTestApp.app Payload | |
zip -r RenderTestApp.zip Payload | |
mv RenderTestApp.zip RenderTestApp.ipa | |
cd Payload/RenderTestApp.app/PlugIns | |
zip -r "$render_test_app_dir"/RenderTest.xctest.zip RenderTest.xctest | |
echo render_test_artifacts_dir="$render_test_app_dir" >> "$GITHUB_ENV" | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: ios-render-test | |
retention-days: 3 | |
if-no-files-found: error | |
path: | | |
${{ env.render_test_artifacts_dir }}/RenderTest.xctest.zip | |
${{ env.render_test_artifacts_dir }}/RenderTestApp.ipa | |
# C++ unit tests | |
- name: Build CppUnitTests .ipa and .xctest for AWS Device Farm | |
run: | | |
set -e | |
bazel run --//:renderer=metal //platform/ios:xcodeproj | |
build_dir="$(mktemp -d)" | |
xcodebuild build-for-testing -scheme CppUnitTests -project MapLibre.xcodeproj -derivedDataPath "$build_dir" | |
ios_cpp_test_app_dir="$(dirname "$(find "$build_dir" -name CppUnitTestsApp.app)")" | |
cd "$ios_cpp_test_app_dir" | |
mkdir Payload | |
mv CppUnitTestsApp.app Payload | |
zip -r CppUnitTestsApp.zip Payload | |
mv CppUnitTestsApp.zip CppUnitTestsApp.ipa | |
cd Payload/CppUnitTestsApp.app/PlugIns | |
zip -r "$ios_cpp_test_app_dir"/CppUnitTests.xctest.zip CppUnitTests.xctest | |
echo ios_cpp_test_artifacts_dir="$ios_cpp_test_app_dir" >> "$GITHUB_ENV" | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: ios-cpp-unit-tests | |
retention-days: 3 | |
if-no-files-found: error | |
path: | | |
${{ env.ios_cpp_test_artifacts_dir }}/CppUnitTests.xctest.zip | |
${{ env.ios_cpp_test_artifacts_dir }}/CppUnitTestsApp.ipa | |
# Size test (Bloaty) | |
- name: Build dynamic library for size test (Bloaty) | |
run: | | |
bazel build //platform/ios:MapLibre.dynamic --//:renderer=metal --compilation_mode="opt" --copt -g --copt="-Oz" --strip never --output_groups=+dsyms --apple_generate_dsym | |
bazel_bin="$(bazel info --compilation_mode="opt" bazel-bin)" | |
unzip "$bazel_bin"/platform/ios/MapLibre.dynamic.xcframework.zip | |
cp "$bazel_bin"/platform/ios/MapLibre.dynamic_dsyms/MapLibre_ios_device.framework.dSYM/Contents/Resources/DWARF/MapLibre_ios_device MapLibre_DWARF | |
cp MapLibre.xcframework/ios-arm64/MapLibre.framework/MapLibre MapLibre_dynamic | |
- name: Upload size test as artifact (Bloaty) | |
if: github.event_name == 'pull_request' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ios-size-test-files | |
retention-days: 3 | |
if-no-files-found: error | |
path: | | |
platform/ios/MapLibre_DWARF | |
platform/ios/MapLibre_dynamic | |
- name: Configure AWS Credentials | |
if: github.ref == 'refs/heads/main' && vars.OIDC_AWS_ROLE_TO_ASSUME | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-region: us-west-2 | |
role-to-assume: ${{ vars.OIDC_AWS_ROLE_TO_ASSUME }} | |
role-session-name: ${{ github.run_id }} | |
- name: Upload MapLibre_DWARF & MapLibre_dynamic to S3 | |
if: github.ref == 'refs/heads/main' && vars.OIDC_AWS_ROLE_TO_ASSUME | |
run: | | |
aws s3 cp MapLibre_DWARF s3://maplibre-native/size-test-ios/MapLibre_DWARF-main | |
aws s3 cp MapLibre_dynamic s3://maplibre-native/size-test-ios/MapLibre_dynamic-main | |
- if: github.event_name == 'pull_request' | |
uses: ./.github/actions/save-pr-number | |
- name: Build DocC documentation | |
working-directory: . | |
run: | | |
HOSTING_BASE_PATH="maplibre-native/ios/latest" platform/ios/scripts/docc.sh | |
- name: Deploy DocC documentation (main) 🚀 | |
if: github.ref == 'refs/heads/main' | |
uses: JamesIves/github-pages-deploy-action@v4.6.1 | |
with: | |
branch: gh-pages | |
folder: build/docs | |
target-folder: ios/latest/ | |
ios-release: | |
runs-on: macos-14 | |
needs: ios-build | |
if: github.ref == 'refs/heads/main' || github.event.inputs.release == 'pre' | |
defaults: | |
run: | |
working-directory: platform/ios | |
shell: bash | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
fetch-depth: 0 | |
- name: VERSION file changed | |
id: version-file-ios-changed | |
uses: tj-actions/changed-files@v44 | |
with: | |
files: platform/ios/VERSION | |
# Make Metal XCFramework release | |
- name: Should make release? | |
if: | | |
github.event.inputs.release == 'full' || github.event.inputs.release == 'pre' || | |
(github.event_name == 'push' && steps.version-file-ios-changed.outputs.any_changed == 'true') | |
run: echo make_release=true >> "$GITHUB_ENV" | |
- name: Build XCFramework | |
if: env.make_release | |
run: | | |
bazel build --compilation_mode=opt --features=dead_strip,thin_lto --objc_enable_binary_stripping \ | |
--apple_generate_dsym --output_groups=+dsyms --//:renderer=metal //platform/ios:MapLibre.dynamic --embed_label=maplibre_ios_"$(cat VERSION)" | |
echo xcframework="$(bazel info execution_root)"/"$(bazel cquery --output=files --compilation_mode=opt --//:renderer=metal //platform/ios:MapLibre.dynamic)" >> "$GITHUB_ENV" | |
- name: Create .zip with debug symbols | |
if: env.make_release | |
working-directory: ./bazel-bin/platform/ios/MapLibre.dynamic_dsyms | |
run: | | |
zip MapLibre_ios_device.framework.dSYM.zip MapLibre_ios_device.framework.dSYM/Contents/Resources/DWARF/MapLibre_ios_device MapLibre_ios_device.framework.dSYM/Contents/Info.plist | |
echo debug_symbols_ios="$(realpath MapLibre_ios_device.framework.dSYM.zip)" >> "$GITHUB_ENV" | |
- name: Get version (release) | |
if: github.event.inputs.release == 'full' || steps.version-file-ios-changed.outputs.any_changed == 'true' | |
run: | | |
echo version="$(head VERSION)" >> "$GITHUB_ENV" | |
echo changelog_version_heading="## $(head VERSION)" >> "$GITHUB_ENV" | |
- name: Get version (pre-release) | |
if: github.event.inputs.release == 'pre' | |
run: | | |
version="${{ github.event.inputs.pre_release_version }}" | |
if [[ -z "$version" ]]; then | |
version="$(head VERSION)"-pre${{ github.sha }} | |
fi | |
echo version="$version" >> "$GITHUB_ENV" | |
echo changelog_version_heading="## main" >> "$GITHUB_ENV" | |
- name: Extract changelog for version | |
if: env.make_release | |
run: | | |
awk '/^##/ { p = 0 }; p == 1 { print }; $0 == "${{ env.changelog_version_heading }}" { p = 1 };' CHANGELOG.md > changelog_for_version.md | |
cat changelog_for_version.md | |
- name: Configure AWS Credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-region: us-west-2 | |
role-to-assume: ${{ vars.OIDC_AWS_ROLE_TO_ASSUME }} | |
role-session-name: ${{ github.run_id }} | |
- name: Upload changelog to S3 | |
if: env.make_release | |
run: aws s3 cp changelog_for_version.md s3://maplibre-native/changelogs/ios-${{ env.version }}.md | |
- name: Create tag | |
if: env.make_release | |
run: | | |
git tag -a ios-v${{ env.version }} -m "Publish ios-v${{ env.version }}" ${{ github.sha }} | |
git push origin ios-v${{ env.version }} | |
- name: Add license to XCFramework zip | |
if: env.make_release | |
run: | | |
cp ${{ env.xcframework }} MapLibre.dynamic.xcframework.zip | |
chmod +w MapLibre.dynamic.xcframework.zip | |
zip MapLibre.dynamic.xcframework.zip LICENSE.md # add license to zip | |
working-directory: . | |
- name: Release (GitHub) | |
if: env.make_release | |
id: github_release | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: ios-v${{ env.version }} | |
files: | | |
MapLibre.dynamic.xcframework.zip | |
${{ env.debug_symbols_ios }} | |
tag_name: ios-v${{ env.version }} | |
prerelease: ${{ github.event.inputs.release == 'pre' }} | |
body_path: platform/ios/changelog_for_version.md | |
fail_on_unmatched_files: true | |
# needed to trigger workflow for Swift Package Index release | |
- name: Generate token | |
if: env.make_release | |
id: generate_token | |
uses: tibdex/github-app-token@v2 | |
with: | |
app_id: ${{ secrets.MAPLIBRE_NATIVE_BOT_APP_ID }} | |
private_key: ${{ secrets.MAPLIBRE_NATIVE_BOT_PRIVATE_KEY }} | |
- name: Release (Swift Package Index) | |
if: env.make_release | |
run: | | |
echo "::add-mask::${{ steps.generate_token.outputs.token }}" | |
release_workflow_id=81221759 # id of release.yml | |
curl -L \ | |
-X POST \ | |
-H "Accept: application/vnd.github+json" \ | |
-H "Authorization: token ${{ steps.generate_token.outputs.token }}" \ | |
-H "X-GitHub-Api-Version: 2022-11-28" \ | |
https://api.github.com/repos/maplibre/maplibre-gl-native-distribution/actions/workflows/$release_workflow_id/dispatches \ | |
-d '{"ref":"main","inputs":{ | |
"changelog_url": "https://maplibre-native.s3.eu-central-1.amazonaws.com/changelogs/ios-${{ env.version }}.md", | |
"version":"${{ env.version }}", | |
"download_url":"${{ fromJSON(steps.github_release.outputs.assets)[0].browser_download_url }}"}}' | |
- name: Release (CocoaPods) | |
shell: bash -leo pipefail {0} # so pod is found | |
if: env.make_release | |
run: | | |
VERSION=${{ env.version }} COCOAPODS_TRUNK_TOKEN=${{ secrets.COCOAPODS_PASSWORD }} pod trunk push MapLibre.podspec | |
ios-ci-result: | |
runs-on: ubuntu-latest | |
if: needs.pre_job.outputs.should_skip != 'true' && always() | |
needs: | |
- pre_job | |
- ios-build | |
steps: | |
- name: Mark result as failed | |
if: needs.ios-build.result != 'success' | |
run: exit 1 |