Skip to content
This repository was archived by the owner on Jul 3, 2020. It is now read-only.

Cargo apk build system rework and other enhancements #223

Merged
merged 12 commits into from
Jul 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
target/
.idea
*.iml
37 changes: 36 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
FROM tomaka/rust-android
FROM rust:latest

RUN apt-get update
RUN apt-get install -yq openjdk-8-jre unzip wget

RUN rustup target add armv7-linux-androideabi
RUN rustup target add aarch64-linux-android
RUN rustup target add i686-linux-android
RUN rustup target add x86_64-linux-android

# Install Android SDK
ENV ANDROID_HOME /opt/android-sdk-linux
RUN mkdir ${ANDROID_HOME} && \
cd ${ANDROID_HOME} && \
wget -q https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \
unzip -q sdk-tools-linux-4333796.zip && \
rm sdk-tools-linux-4333796.zip && \
chown -R root:root /opt
RUN yes | ${ANDROID_HOME}/tools/bin/sdkmanager "platform-tools"
RUN yes | ${ANDROID_HOME}/tools/bin/sdkmanager "platforms;android-29"
RUN yes | ${ANDROID_HOME}/tools/bin/sdkmanager "build-tools;29.0.0"
RUN ${ANDROID_HOME}/tools/bin/sdkmanager --update

# Install Android NDK
RUN cd /usr/local && \
wget -q http://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip && \
unzip -q android-ndk-r20-linux-x86_64.zip && \
rm android-ndk-r20-linux-x86_64.zip
ENV NDK_HOME /usr/local/android-ndk-r20

# Copy contents to container. Should only use this on a clean directory
COPY . /root/cargo-apk

# Run tests
RUN cd /root/cargo-apk/cargo-apk && cargo test --release

# Install Binary and remove source and build files
RUN cargo install --path /root/cargo-apk/cargo-apk
RUN rm -rf /root/cargo-apk

# Make directory for user code
RUN mkdir /root/src
WORKDIR /root/src
125 changes: 86 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@
## With Docker

The easiest way to compile for Android is to use [Docker](https://www.docker.com/) and the
[tomaka/cargo-apk](https://hub.docker.com/r/tomaka/cargo-apk/) image.
[philipalldredge/cargo-apk](https://hub.docker.com/r/philipalldredge/cargo-apk/) image.

In order to build an APK, simply do this:

```
docker run --rm -v <path-to-local-directory-with-Cargo.toml>:/root/src tomaka/cargo-apk cargo apk build
docker run --rm -v <path-to-local-directory-with-Cargo.toml>:/root/src philipalldredge/cargo-apk cargo apk build
```

For example if you're on Linux and you want to compile the project in the current working
directory.

```
docker run --rm -v "$(pwd):/root/src" -w /root/src tomaka/cargo-apk cargo apk build
docker run --rm -v "$(pwd):/root/src" -w /root/src philipalldredge/cargo-apk cargo apk build
```

Do not mount a volume on `/root` or you will erase the local installation of Cargo.

After the build is finished, you should get an Android package in `target/android-artifacts/app/build/outputs/apk`.
After the build is finished, you should get an Android package in `target/android-artifacts/debug/apk`.

## Manual usage

Expand All @@ -31,13 +31,15 @@ After the build is finished, you should get an Android package in `target/androi
Before you can compile for Android, you need to setup your environment. This needs to be done only once per system.

- Install [`rustup`](https://rustup.rs/).
- Run `rustup target add arm-linux-androideabi`, or any other target that you want to compile to.
- Install the Java JDK (on Ubuntu, `sudo apt-get install openjdk-8-jdk`).
- Install CMake (on Ubuntu, `sudo apt-get install cmake`).
- [Install Gradle](https://gradle.org/install/).
- Run `rustup target add <target>` for all supported targets to which you want to compile. Building will attempt to build for all supported targets unless the build targets are adjusted via `Cargo.toml`.
- `rustup target add armv7-linux-androideabi`
- `rustup target add aarch64-linux-android`
- `rustup target add i686-linux-android`
- `rustup target add x86_64-linux-android`
- Install the Java JRE or JDK (on Ubuntu, `sudo apt-get install openjdk-8-jdk`).
- Download and unzip [the Android NDK](https://developer.android.com/ndk).
- Download and unzip [the Android SDK](https://developer.android.com/studio).
- Install some components in the SDK: `./android-sdk/tools/bin/sdkmanager "platform-tools" "platforms;android-18" "build-tools;26.0.1"`.
- Install some components in the SDK: `./android-sdk/tools/bin/sdkmanager "platform-tools" "platforms;android-29" "build-tools;29.0.0"`.
- Install `cargo-apk` with `cargo install cargo-apk`.
- Set the environment variables `NDK_HOME` to the path of the NDK and `ANDROID_HOME` to the path of the SDK.

Expand All @@ -46,7 +48,15 @@ Before you can compile for Android, you need to setup your environment. This nee
In the project root for your Android crate, run `cargo apk build`. You can use the same options as
with the regular `cargo build`.

This will build an Android package in `target/android-artifacts/app/build/outputs/apk`.
This will build an Android package in `target/android-artifacts/<debug|release>/apk`.

### Compiling Multiple Binaries

`cargo apk build` supports building multiple binaries and examples using the same arguments as `cargo build`. It will produce an APK for each binary.

Android packages for bin targets are placed in `target/android-artifacts/<debug|release>/apk`.

Android packages for example targets are placed in `target/android-artifacts/<debug|release>/apk/examples`.

### Testing on an Android emulator

Expand All @@ -72,62 +82,75 @@ the stdlib.

## The build process

The build process works by invoking `cargo rustc` and:
The build process works by running rustc and:

- Always compiles your crate as a shared library.
- Injects the `android_native_app_glue` file provided by the Android NDK.
- Injects some glue libraries in Rust, which ties the link between `android_native_app_glue` and
the `main` function of your crate.
- Always compiles your crate as a static library.
- Uses `ndk-build` provided by the NDK to to build a shared library.
- Links to the `android_native_app_glue` library provided by the Android NDK.
- Injects some glue libraries in Rust, which ties the link between `android_native_app_glue` and the `main` function of your crate.

This first step outputs a shared library, and is run once per target architecture.

The command then sets up an Android build environment, which includes some Java code, in
`target/android-artifacts` and puts the shared libraries in it. Then it runs `gradle`.
The command then builds the APK using the shared library, generated manifest, and tools from the Android SDK.
It signs the APK with the default debug keystore used by Android development tools. If the keystore doesn't exist, it creates it using the keytool from the JRE or JDK.

# Supported `[package.metadata.android]` entries

```toml
[package.metadata.android]
# The target Android API level.
# "android_version" is the compile SDK version. It defaults to 29.
# (target_sdk_version defaults to the value of "android_version")
# (min_sdk_version defaults to 18) It defaults to 18 because this is the minimum supported by rustc.
android_version = 29
target_sdk_version = 29
min_sdk_version = 26

# Specifies the array of targets to build for.
# Defaults to "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android".
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android" ]

#
# The following value can be customized on a per bin/example basis. See multiple_targets example
# If a value is not specified for a secondary target, it will inherit the value defined in the `package.metadata.android`
# section unless otherwise noted.
#

# The Java package name for your application.
# Hyphens are converted to underscores.
package_name = "com.author-name.my-android-app"
# Defaults to rust.<target_name> for binaries.
# Defaults to rust.<package_name>.example.<target_name> for examples.
# For example: for a binary "my_app", the default package name will be "rust.my_app"
# Secondary targets will not inherit the value defined in the root android configuration.
package_name = "rust.cargo.apk.advanced"

# The user-friendly name for your app, as displayed in the applications menu.
# Defaults to the target name
# Secondary targets will not inherit the value defined in the root android configuration.
label = "My Android App"

# Path to your application's res/ folder. See `examples/use_icon/res`.
# Path to your application's resources folder.
# If not specified, resources will not be included in the APK
res = "path/to/res_folder"

# Virtual path your application's icon for any mipmap level. See `examples/use_icon/icon`.
icon = "@mipmap/ic_laucher"
# Virtual path your application's icon for any mipmap level.
# If not specified, an icon will not be included in the APK.
icon = "@mipmap/ic_launcher"

# Path to the folder containing your application's assets. See `examples/use_assets/assets`.
# Path to the folder containing your application's assets.
# If not specified, assets will not be included in the APK
assets = "path/to/assets_folder"

# The target Android API level.
# It defaults to 18 because this is the minimum supported by rustc.
# (target_sdk_version and min_sdk_version default to the value of "android_version")
android_version = 18
target_sdk_version = 18
min_sdk_version = 18

# If set to true, makes the app run in full-screen, by adding the following line
# as an XML attribute to the manifest's <application> tag :
# android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen
# Defaults to false.
fullscreen = false

# Specifies the array of targets to build for.
# Defaults to "arm-linux-androideabi".
# Other possible targets include "aarch64-linux-android",
# "armv7-linux-androideabi", "i686-linux-android" and "x86_64-linux-android".
build_targets = [ "arm-linux-androideabi", "armv7-linux-androideabi" ]

# The maximum supported OpenGL ES version , as claimed by the manifest. Defaults to 2.0.
# The maximum supported OpenGL ES version , as claimed by the manifest.
# Defaults to 2.0.
# See https://developer.android.com/guide/topics/graphics/opengl.html#manifest
opengles_version_major = 2
opengles_version_minor = 0
opengles_version_major = 3
opengles_version_minor = 2

# Adds extra arbitrary XML attributes to the <application> tag in the manifest.
# See https://developer.android.com/guide/topics/manifest/application-element.html
Expand All @@ -140,4 +163,28 @@ opengles_version_minor = 0
[package.metadata.android.activity_attributes]
"android:screenOrientation" = "unspecified"
"android:uiOptions" = "none"

# Adds a uses-feature element to the manifest
# Supported keys: name, required, version
# The glEsVersion attribute is not supported using this section.
# It can be specified using the opengles_version_major and opengles_version_minor values
# See https://developer.android.com/guide/topics/manifest/uses-feature-element
[[package.metadata.android.feature]]
name = "android.hardware.camera"

[[package.metadata.android.feature]]
name = "android.hardware.vulkan.level"
version = "1"
required = false

# Adds a uses-permission element to the manifest.
# Note that android_version 23 and higher, Android requires the application to request permissions at runtime.
# There is currently no way to do this using a pure NDK based application.
# See https://developer.android.com/guide/topics/manifest/uses-permission-element
[[package.metadata.android.permission]]
name = "android.permission.WRITE_EXTERNAL_STORAGE"
max_sdk_version = 18

[[package.metadata.android.permission]]
name = "android.permission.CAMERA"
```
Loading