Skip to content
This repository has been archived by the owner on May 9, 2022. It is now read-only.

CI build mobile libs #21

Merged
merged 28 commits into from
Apr 8, 2021
Merged

CI build mobile libs #21

merged 28 commits into from
Apr 8, 2021

Conversation

Robert-Steiner
Copy link
Contributor

@Robert-Steiner Robert-Steiner commented Mar 9, 2021

Goal:

We want to test our android/ios libraries on different cpu architectures
in order to identify errors early in development.

What we have achieved:

We can compile and test our android libraries on different cpu architectures by using an emulator.
However, we can only compile the iOS libraries because there is no emulator for it.
We could run our tests on the iOS simulator, but it uses an architecture (x86_64)
which is not used in any iOS device. So it is not worth it.

Implementation details

Use a build matrix for testing/building android/ios libraries on different cpu architectures

There are several advantages to using a build matrix instead of one job that builds/tests all of the libraries one at a time:

  • each job can be executed in parallel
    • this speeds up the overall execution of the ci
  • if one job fails, the others continue to run
    • this makes debugging easier because we can immediately see if an error occurs only in a specific cpu architecture
  • each job has its own build cache
    • this also speeds up the execution of the ci, since unpacking a small cache is faster than a large one

Optimised android test

We noticed that the execution time of the tests on the emulator is significantly reduced
if we compile our tests in release mode. However, in release mode the debug assertions are removed
which means that we lose some checks. So the idea was to compile it in debug mode (to keep the
debug assertions) and only set opt-level to 3 (to turn on all optimisations).
However, this turned out to be more difficult than expected. In the end we decided to use
RUSTFLAGS="-C opt-level=3 -C debug-assertions=yes". More details at the end of the description.

test-android-libs without caching / debug mode

Job Build Test rubert Test rubert_tokenizer Test xayn_ai Test xayn_ai_ffi
aarch64-linux-android 3m 30s 144.56s 0.33s 0.12s 445.52s
armv7-linux-androideabi 4m 35s 152.85s 0.33s 0.13s 499.53s
i686-linux-android 3m 27s 151.51s 0.34s 0.14s 380.79s
x86_64-linux-android 3m 42s 146.17s 0.28s 0.12s 404.44s
  • Total duration: 17m 25s
  • Billable time: 59m 5s

test-android-libs without caching / release mode

Job Build Test rubert Test rubert_tokenizer Test xayn_ai Test xayn_ai_ffi
aarch64-linux-android 7m 46s 25.27s 0.10s 0.05s 76.03s
armv7-linux-androideabi 7m 07s 13.64s 0.07s 0.04s 38.79s
i686-linux-android 7m 02s 8.62s 0.07s 0.03s 12.18s
x86_64-linux-android 7m 01s 8.81s 0.08s 0.03s 13.64s
  • Total duration: 11m 32s
  • Billable time: 37m 44s

test-android-libs with caching / release mode

Job Build Test rubert Test rubert_tokenizer Test xayn_ai Test xayn_ai_ffi
aarch64-linux-android 1m 01s 23.76s 0.10s 0.04s 69.13s
armv7-linux-androideabi 1m 03s 12.16s 0.07s 0.03s 34.73s
i686-linux-android 1m 17s 10.12s 0.08s 0.03s 15.21s
x86_64-linux-android 55.27s 9.26s 0.07s 0.03s 13.79s
  • Total duration: 4m 38s
  • Billable time: 13m 7s

We have activated the option -- -Z unstable-options --report-time so that if a test suddenly becomes slow again, we can recognize it immediately.

Using cross for testing

cross makes it easy to test our code on different cpu architectures. Under the hood it runs a docker container that runs an emulator (qemu).

Features/Limitations of cross

For example:

cd xayn_ai/xayn-ai
cross test --target aarch64-linux-android

does not work. It cannot find the directory of rubert, since it is not mounted into the container. So you have to run

cd xayn_ai
cross test --target aarch64-linux-android -p xayn-ai

Using cargo-ndk for building the release binaries

To build the final android libraries we will keep using cargo-ndk because it has some advantages over cross:

  • in ndk we can set the ndk api level (in cross it is hardcoded in the dockerfile)
  • ndk automatically strips the binary (with the right strip binary) + moves it into the jniLib folder structure
RUST_LOG=debug cargo ndk -t armeabi-v7a --platform 21 -o ./jniLibs build --release
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] Using NDK at path: /Users/robert/Library/Android/sdk/ndk/22.0.7026061
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] NDK API level: 21
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] Building targets: armeabi-v7a
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] Building armeabi-v7a (armv7-linux-androideabi)
[2021-03-18T10:03:09Z DEBUG cargo_ndk::cargo] ar: /Users/robert/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar
[2021-03-18T10:03:09Z DEBUG cargo_ndk::cargo] linker: /Users/robert/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang
[2021-03-18T10:03:09Z DEBUG cargo_ndk::cargo] cargo: /Users/robert/.rustup/toolchains/1.49.0-x86_64-apple-darwin/bin/cargo
[2021-03-18T10:03:09Z DEBUG cargo_ndk::cargo] Working directory does not match manifest-path
    Finished release [optimized] target(s) in 0.16s
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] Copying libraries to ./jniLibs...
[2021-03-18T10:03:09Z INFO  cargo_ndk::cli] /Users/robert/projects/xayn-ai/target/armv7-linux-androideabi/release/libxayn_ai_ffi.so -> ./jniLibs/armeabi-v7a/libxayn_ai_ffi.so
[2021-03-18T10:03:09Z DEBUG cargo_ndk::cargo] strip: /Users/robert/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-strip

We will do the integration of cargo-ndk in a separate pr.

Why we chose RUSTFLAGS instead of [profile.test]

TL;DR:

The test profile only applies to our library but not on the dependencies.

cargo book

Note that when using the cargo test and cargo bench commands, the test/bench profiles only apply to the final test executable.
Dependencies will continue to use the dev/release profiles. Also note that when a library is built for unit tests,
then the library is built with the test profile. However, when building an integration test target, the library target
is built with the dev profile and linked into the integration test executable.


cargo test

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
    -C embed-bitcode=no
    -C debuginfo=2
    -C metadata=79293ec08c2c0f19
    -C extra-filename=-79293ec08c2c0f19
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps

If opt-level is missing the compiler will by default use -C opt-level=0
which implies -C debug-assertions=on

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    --test
    -C metadata=ad90509da42dfc40
    -C extra-filename=-ad90509da42dfc40
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-79293ec08c2c0f19.rlib

default: -C opt-level=0 -> -C debug-assertions=on

  • dependencies debug-assertions turned on
  • library debug-assertions turned on
  • dependencies optimised
  • library optimised

cargo test &

[profile.dev]
opt-level = 3

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
-C opt-level=3
    -C embed-bitcode=no
    -C debuginfo=2
-C debug-assertions=on
    -C metadata=e55d6ac30c7cc4bc
    -C extra-filename=-e55d6ac30c7cc4bc
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps

-C opt-level=3 implies debug-assertions=off but cargo correctly uses the default value of
debug-assertions=on in profile.dev.

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    --test
    -C metadata=06e2c8a5b5ed242d
    -C extra-filename=-06e2c8a5b5ed242d
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-e55d6ac30c7cc4bc.rlib
  • dependencies debug-assertions turned on
  • library debug-assertions turned on
  • dependencies optimised
  • library optimised

cargo test &

[profile.dev]
opt-level = 3

[profile.test]
opt-level = 3

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
-C opt-level=3
    -C embed-bitcode=no
    -C debuginfo=2
-C debug-assertions=on
    -C metadata=e55d6ac30c7cc4bc
    -C extra-filename=-e55d6ac30c7cc4bc
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
-C opt-level=3
    -C embed-bitcode=no
    -C debuginfo=2
-C debug-assertions=on
    --test
    -C metadata=b8ff0b3fe541eacb
    -C extra-filename=-b8ff0b3fe541eacb
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-e55d6ac30c7cc4bc.rlib
  • dependencies debug-assertions turned on
  • library debug-assertions turned on
  • dependencies optimised
  • library optimised

Cool, we've reached our goal, haven't we? Not really.
If we add opt-level=3 to profile.dev, the complication times during local
development will be longer.

An alternative would be to use RUSTFLAGS. Let's take a look.

RUSTFLAGS="-C opt-level=3" cargo test

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
    -C embed-bitcode=no
    -C debuginfo=2
    -C metadata=79293ec08c2c0f19
    -C extra-filename=-79293ec08c2c0f19
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
-C opt-level=3

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    --test
    -C metadata=ad90509da42dfc40
    -C extra-filename=-ad90509da42dfc40
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-79293ec08c2c0f19.rlib
-C opt-level=3
  • dependencies debug-assertions turned on
  • library debug-assertions turned on
  • dependencies optimised
  • library optimised

Hmmm cargo does not apply the default values of the profiles anymore.


What happens if we and profile.dev again?

RUSTFLAGS="-C opt-level=3" cargo test &

[profile.dev]
opt-level = 3

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
-C opt-level=3
    -C embed-bitcode=no
    -C debuginfo=2
-C debug-assertions=on
    -C metadata=e55d6ac30c7cc4bc
    -C extra-filename=-e55d6ac30c7cc4bc
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
-C opt-level=3

debug-assertions=on is back and we have opt-level=3 twice.
Which of the two opt-level=3 will it actually choose?

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    --test
    -C metadata=06e2c8a5b5ed242d
    -C extra-filename=-06e2c8a5b5ed242d
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-e55d6ac30c7cc4bc.rlib
-C opt-level=3
  • dependencies debug-assertions turned on
  • library debug-assertions turned on
  • dependencies optimised
  • library optimised

Which of the two opt-level=3 will it actually choose?

Let's set opt-level to 3 in the Cargo.toml and call cargo with
RUSTFLAGS="-C debug-assertions=no".

RUSTFLAGS="-C debug-assertions=no" cargo test &

[profile.dev]
opt-level = 3

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
-C opt-level=3
    -C embed-bitcode=no
    -C debuginfo=2
-C debug-assertions=on
    -C metadata=e55d6ac30c7cc4bc
    -C extra-filename=-e55d6ac30c7cc4bc
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
-C debug-assertions=no

The test did not fail, so the debug-assertions are turned off which means that the last
debug-assertions flag overwrites the previous flag.


In order to have both opt-level=3 and debug-assertions=yes we need to run

RUSTFLAGS="-C opt-level=3 -C debug-assertions=yes" cargo test

Dependencies

rustc
    --crate-name a_dep
    --edition=2018 a-dep/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts
    --crate-type lib
    --emit=dep-info,metadata,link
    -C embed-bitcode=no
    -C debuginfo=2
    -C metadata=e55d6ac30c7cc4bc
    -C extra-filename=-e55d6ac30c7cc4bc
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
-C opt-level=3
-C debug-assertions=yes

Our Library

rustc
    --crate-name mem_leak
    --edition=2018 mem_leak/src/lib.rs
    --error-format=json
    --json=diagnostic-rendered-ansi
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    --test
    -C metadata=ad90509da42dfc40
    -C extra-filename=-ad90509da42dfc40
    --out-dir /Users/robert/projects/mem_leak_lib/target/debug/deps
    -C incremental=/Users/robert/projects/mem_leak_lib/target/debug/incremental
    -L dependency=/Users/robert/projects/mem_leak_lib/target/debug/deps
    --extern a_dep=/Users/robert/projects/mem_leak_lib/target/debug/deps/liba_dep-79293ec08c2c0f19.rlib
-C opt-level=3
-C debug-assertions=yes

@Robert-Steiner Robert-Steiner changed the title wip CI build mobile/WASM libs Mar 9, 2021
@Robert-Steiner Robert-Steiner changed the title CI build mobile/WASM libs CI build mobile libs Mar 10, 2021
@Robert-Steiner Robert-Steiner force-pushed the ffi-ci branch 2 times, most recently from de46fcc to 86e5703 Compare March 12, 2021 14:36
@janpetschexain janpetschexain force-pushed the ffi-ci branch 2 times, most recently from ebded9c to f63e059 Compare March 17, 2021 13:37
.github/workflows/ci.yml Outdated Show resolved Hide resolved
.github/workflows/ci.yml Show resolved Hide resolved
.github/workflows/ci.yml Outdated Show resolved Hide resolved
@janpetschexain janpetschexain force-pushed the ffi-ci branch 3 times, most recently from bbbe556 to 3514dd4 Compare March 25, 2021 15:22
@janpetschexain
Copy link
Contributor

your pr summary is awesome 👍 we should put a link to this readme/pr directly in the ci yaml file.

@Robert-Steiner
Copy link
Contributor Author

your pr summary is awesome 👍 we should put a link to this readme/pr directly in the ci yaml file.

Thanks 😊. I can do that!

@janpetschexain janpetschexain force-pushed the ffi-ci branch 3 times, most recently from ed08f13 to 85d6a2a Compare April 6, 2021 16:15
@Robert-Steiner Robert-Steiner marked this pull request as ready for review April 7, 2021 15:24
Copy link
Contributor

@janpetschexain janpetschexain left a comment

Choose a reason for hiding this comment

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

lgtm 👍

.github/workflows/ci.yml Outdated Show resolved Hide resolved
.github/workflows/audit.yml Show resolved Hide resolved
.github/workflows/ci.yml Show resolved Hide resolved
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants