Skip to content
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

Implement gradle rust plugin #7319

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
85 changes: 49 additions & 36 deletions .github/workflows/android-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,16 @@ jobs:

- name: Generate
if: steps.cache-relay-list.outputs.cache-hit != 'true'
env:
RUSTFLAGS: --deny warnings
run: |
mkdir -p android/app/build/extraAssets
cargo run --bin relay_list > android/app/build/extraAssets/relays.json
uses: burrunan/gradle-cache-action@v1
with:
job-id: jdk17
arguments: generateRelayList
gradle-version: wrapper
build-root-directory: android
execution-only-caches: false
# Disable if logs are hard to follow.
concurrent: true
read-only: ${{ github.ref != 'refs/heads/main' }}

- name: Upload
uses: actions/upload-artifact@v4
Expand All @@ -152,18 +157,15 @@ jobs:
strategy:
matrix:
include:
- arch: "x86_64"
abi: "x86_64"
target: "x86_64-linux-android"
- arch: "i686"
abi: "x86"
target: "i686-linux-android"
- arch: "aarch64"
abi: "arm64-v8a"
target: "aarch64-linux-android"
- arch: "armv7"
abi: "armeabi-v7a"
target: "armv7-linux-androideabi"
- abi: "x86_64"
taskVariant: "X86_64"
- abi: "x86"
taskVariant: "X86"
- abi: "arm64-v8a"
taskVariant: "Arm64"
- abi: "armeabi-v7a"
taskVariant: "Arm"
# name: build-native-${{ matrix.abi }}
steps:
# Fix for HOME path overridden by GH runners when building in containers, see:
# https://github.com/actions/runner/issues/863
Expand Down Expand Up @@ -197,27 +199,28 @@ jobs:
env:
cache_hash: ${{ steps.native-lib-cache-hash.outputs.native_lib_hash }}
with:
path: ./android/app/build/extraJni
path: ./android/app/build/rustJniLibs/android
key: android-native-libs-${{ runner.os }}-${{ matrix.abi }}-${{ env.cache_hash }}

- name: Build native libraries
if: steps.cache-native-libs.outputs.cache-hit != 'true'
env:
RUSTFLAGS: --deny warnings
BUILD_TYPE: debug
run: |
ARCHITECTURES="${{ matrix.abi }}"
UNSTRIPPED_LIB_PATH="$CARGO_TARGET_DIR/${{ matrix.target }}/$BUILD_TYPE/libmullvad_jni.so"
STRIPPED_LIB_PATH="./android/app/build/extraJni/${{ matrix.abi }}/libmullvad_jni.so"
NDK_TOOLCHAIN_STRIP_TOOL="$NDK_TOOLCHAIN_DIR/llvm-strip"
cargo build --target ${{ matrix.target }} --verbose --package mullvad-jni --features api-override
$NDK_TOOLCHAIN_STRIP_TOOL --strip-debug --strip-unneeded -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH"
uses: burrunan/gradle-cache-action@v1
with:
job-id: jdk17
arguments: cargoBuild${{ matrix.taskVariant }}
gradle-version: wrapper
build-root-directory: android
execution-only-caches: false
# Disable if logs are hard to follow.
concurrent: true
read-only: ${{ github.ref != 'refs/heads/main' }}


- name: Upload native libs
uses: actions/upload-artifact@v4
with:
name: native-libs-${{ matrix.arch }}
path: android/app/build/extraJni
path: android/app/build/rustJniLibs/android
if-no-files-found: error
retention-days: 7

Expand Down Expand Up @@ -290,7 +293,9 @@ jobs:
uses: burrunan/gradle-cache-action@v1
with:
job-id: jdk17
arguments: compileOssProdDebugKotlin
arguments: |
compileOssProdDebugKotlin
-x cargoBuild
gradle-version: wrapper
build-root-directory: android
execution-only-caches: false
Expand All @@ -305,18 +310,19 @@ jobs:
[
{
"workflowFile": "android-app.yml",
"jobName": "build-native"
"jobName": "build-native-x86_64"
},
{
"workflowFile": "android-app.yml",
"jobName": "generate-relay-list"
"jobMatchMode": "prefix",
"jobName": "build-native"
}
]

- uses: actions/download-artifact@v4
with:
pattern: native-libs-*
path: android/app/build/extraJni
path: android/app/build/rustJniLibs/android
merge-multiple: true

- uses: actions/download-artifact@v4
Expand All @@ -328,7 +334,9 @@ jobs:
uses: burrunan/gradle-cache-action@v1
with:
job-id: jdk17
arguments: assembleOssProdDebug
arguments: |
assembleOssProdDebug
-x cargoBuild
gradle-version: wrapper
build-root-directory: android
execution-only-caches: true
Expand All @@ -341,7 +349,9 @@ jobs:
if: github.event.inputs.run_firebase_tests == 'true'
with:
job-id: jdk17
arguments: assemblePlayStagemoleDebug
arguments: |
assemblePlayStagemoleDebug
-x cargoBuild
gradle-version: wrapper
build-root-directory: android
execution-only-caches: true
Expand Down Expand Up @@ -396,7 +406,10 @@ jobs:
uses: burrunan/gradle-cache-action@v1
with:
job-id: jdk17
arguments: ${{ matrix.assemble-command }}
arguments: |
${{ matrix.assemble-command }}
-x cargoBuild
-x generateRelayList
gradle-version: wrapper
build-root-directory: android
execution-only-caches: false
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/daemon.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Daemon+CLI - Build and test
on:

Check warning on line 3 in .github/workflows/daemon.yml

View workflow job for this annotation

GitHub Actions / check-formatting

3:1 [truthy] truthy value should be one of [false, true]
pull_request:
paths:
- '**'
Expand All @@ -10,7 +10,6 @@
- '!.github/CODEOWNERS'
- '!android/**'
- '!audits/**'
- '!build-apk.sh'
- '!build.sh'
- '!ci/**'
- 'ci/check-rust.sh'
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/testframework.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ on:
- '.github/workflows/daemon.yml'
- '!android/**'
- '!audits/**'
- '!build-apk.sh'
- '!build.sh'
- '!ci/**'
- '!clippy.toml'
Expand Down
4 changes: 2 additions & 2 deletions android/BuildInstructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,15 @@ Run the following command to download wireguard-go-rs submodule: `git submodule
### Debug build
Run the following command to build a debug build:
```bash
../build-apk.sh --dev-build
../android/build-apk.sh --dev-build
```

### Release build
1. Configure a signing key by following [these instructions](#configure-signing-key).
2. Move, copy or symlink the directory from step 1 to [./credentials/](./credentials/) (`<repository>/android/credentials/`).
3. Run the following command to build:
```bash
../build-apk.sh --app-bundle
../android/build-apk.sh --app-bundle
```

## Configure signing key
Expand Down
104 changes: 81 additions & 23 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
import java.io.ByteArrayOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.Properties
import org.gradle.internal.extensions.stdlib.capitalized

Expand All @@ -12,6 +14,7 @@ plugins {
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.ksp)
alias(libs.plugins.compose)
alias(libs.plugins.rust.android.gradle)

id(Dependencies.junit5AndroidPluginId) version Versions.junit5Plugin
}
Expand All @@ -20,7 +23,6 @@ val repoRootPath = rootProject.projectDir.absoluteFile.parentFile.absolutePath
val extraAssetsDirectory = layout.buildDirectory.dir("extraAssets").get()
val relayListPath = extraAssetsDirectory.file("relays.json").asFile
val defaultChangelogAssetsDirectory = "$repoRootPath/android/src/main/play/release-notes/"
val extraJniDirectory = layout.buildDirectory.dir("extraJni").get()

val credentialsPath = "${rootProject.projectDir}/credentials"
val keystorePropertiesFile = file("$credentialsPath/keystore.properties")
Expand All @@ -34,6 +36,7 @@ android {
namespace = "net.mullvad.mullvadvpn"
compileSdk = Versions.compileSdkVersion
buildToolsVersion = Versions.buildToolsVersion
ndkVersion = Versions.ndkVersion

defaultConfig {
val localProperties = gradleLocalProperties(rootProject.projectDir, providers)
Expand Down Expand Up @@ -129,7 +132,6 @@ android {
.getOrDefault("OVERRIDE_CHANGELOG_DIR", defaultChangelogAssetsDirectory)

assets.srcDirs(extraAssetsDirectory, changelogDir)
jniLibs.srcDirs(extraJniDirectory)
}
}

Expand Down Expand Up @@ -222,8 +224,7 @@ android {
}

val variantName = name
val capitalizedVariantName =
variantName.toString().capitalized()
val capitalizedVariantName = variantName.toString().capitalized()
val artifactName = "MullvadVPN-${versionName}${artifactSuffix}"

tasks.register<Copy>("create${capitalizedVariantName}DistApk") {
Expand All @@ -243,10 +244,14 @@ android {

createDistBundle.dependsOn("bundle$capitalizedVariantName")

// Ensure all relevant assemble tasks depend on our ensure tasks.
// Ensure that we have all the JNI libs before merging them.
tasks["merge${capitalizedVariantName}JniLibFolders"].apply {
dependsOn(tasks["generateRelayList"])
dependsOn("cargoBuild")
}

// Ensure all relevant assemble tasks depend on our ensure task.
tasks["assemble$capitalizedVariantName"].apply {
dependsOn(tasks["ensureRelayListExist"])
dependsOn(tasks["ensureJniDirectoryExist"])
dependsOn(tasks["ensureValidVersionCode"])
}
}
Expand All @@ -259,6 +264,75 @@ junitPlatform {
}
}

cargo {
val isReleaseBuild = isReleaseBuild()
val enableApiOverride = !isReleaseBuild || isAlphaOrDevBuild()
module = repoRootPath
libname = "mullvad-jni"
// All available targets:
// https://github.com/mozilla/rust-android-gradle/tree/master?tab=readme-ov-file#targets
targets =
gradleLocalProperties(rootProject.projectDir, providers)
.getProperty("CARGO_TARGETS")
?.split(",") ?: listOf("arm", "arm64", "x86", "x86_64")
profile =
if (isReleaseBuild) {
"release"
} else {
"debug"
}
prebuiltToolchains = true
targetDirectory = "$repoRootPath/target"
features {
if (enableApiOverride) {
defaultAnd(arrayOf("api-override"))
}
}
targetIncludes = arrayOf("libmullvad_jni.so")
extraCargoBuildArguments = buildList {
add("--package=mullvad-jni")
if (isReleaseBuild) {
add("--locked")
}
}
exec = { spec, _ -> println(spec.commandLine) }
}

tasks.register<Exec>("generateRelayList") {
workingDir = File(repoRootPath)
standardOutput = ByteArrayOutputStream()

// Set this if you get a cargo not found error
// environment =

onlyIf { isReleaseBuild() || !relayListPath.exists() }

commandLine("cargo", "run", "--bin", "relay_list")

doLast {
val output = standardOutput as ByteArrayOutputStream
// Create file if needed
File("$extraAssetsDirectory").mkdirs()
File("$extraAssetsDirectory/relays.json").createNewFile()
FileOutputStream("$extraAssetsDirectory/relays.json").use { it.write(output.toByteArray()) }

// Old ensure exists tasks
if (!relayListPath.exists()) {
throw GradleException("Failed to generate relay list")
}
}
}

fun isReleaseBuild() =
gradle.startParameter.getTaskNames().any { it.contains("release", ignoreCase = true) }

fun isAlphaOrDevBuild() : Boolean {
val localProperties = gradleLocalProperties(rootProject.projectDir, providers)
val versionName = generateVersionName(localProperties)
return versionName.contains("dev", ignoreCase = true) ||
versionName.contains("alpha", ignoreCase = true)
}

androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.enable =
Expand All @@ -280,22 +354,6 @@ configure<org.owasp.dependencycheck.gradle.extension.DependencyCheckExtension> {
skipConfigurations = listOf("lintClassPath")
}

tasks.register("ensureRelayListExist") {
doLast {
if (!relayListPath.exists()) {
throw GradleException("Missing relay list: $relayListPath")
}
}
}

tasks.register("ensureJniDirectoryExist") {
doLast {
if (!extraJniDirectory.asFile.exists()) {
throw GradleException("Missing JNI directory: $extraJniDirectory")
}
}
}

// This is a safety net to avoid generating too big version codes, since that could potentially be
// hard and inconvenient to recover from.
tasks.register("ensureValidVersionCode") {
Expand Down
Loading
Loading