// This script defines common setup logic for our components, such as depending // on the correct versions of android dependencies. // Absent some special need for customization, we expect each project under `/components` // to apply this script to their build process via: // // ``` // apply from: "$rootDir/build-scripts/component-common.gradle" // ``` import javax.inject.Inject apply plugin: 'com.android.library' apply plugin: 'kotlin-android' // Typed Exec subclass used in the moz-central build, where the embedded // uniffi-bindgen tool and the native megazord library are already built before // gradle runs. Exposes outputDir as a DirectoryProperty so the generated // sources can be wired into the variant via addGeneratedSourceDirectory. abstract class GenerateUniffiBindingsEmbedded extends Exec { @OutputDirectory abstract DirectoryProperty getOutputDir() } // Typed task used in the standalone app-services build, where the megazord // dynamic library is produced by a separate gradle task and only exists when // generateUniffiBindings runs (not at configuration time). abstract class GenerateUniffiBindingsCargo extends DefaultTask { @InputFiles abstract ConfigurableFileCollection getMegazordNativeFiles() @InputDirectory abstract DirectoryProperty getBindgenToolDir() @Input abstract Property getCrateName() @Input abstract Property getNativeRustTarget() @Internal abstract DirectoryProperty getWorkingDirectory() @OutputDirectory abstract DirectoryProperty getOutputDir() @Inject abstract ExecOperations getExecOperations() @TaskAction void run() { def libraryPath = megazordNativeFiles.asFileTree.matching { include "${nativeRustTarget.get()}/libmegazord.*" }.singleFile if (libraryPath == null) { throw new GradleException("libmegazord dynamic library path not found") } execOperations.exec { workingDir workingDirectory.get().asFile commandLine '/usr/bin/env', 'cargo', 'uniffi-bindgen', 'generate', '--crate', crateName.get(), '--language', 'kotlin', '--out-dir', outputDir.get().asFile, '--no-format', libraryPath } } } android { compileSdk { version = release(config.compileSdkMajorVersion) { minorApiLevel = config.compileSdkMinorVersion } } defaultConfig { ndkVersion config.ndkVersion minSdkVersion config.minSdkVersion targetSdkVersion config.targetSdkVersion testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" buildConfigField("String", "LIBRARY_VERSION", "\"${config.componentsVersion}\"") } buildFeatures { buildConfig true } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' consumerProguardFiles "$appServicesRootDir/proguard-rules-consumer-jna.pro" } } testOptions { unitTests { includeAndroidResources = true } } lint { lintConfig = file("${project.appServicesRootDir}/components/lint.xml") } } kotlin { jvmToolchain(rootProject.config.jvmTargetCompatibility) } dependencies { testImplementation platform(libs.junit.bom) testImplementation libs.junit4 testRuntimeOnly libs.junit.platform.launcher testRuntimeOnly libs.junit.vintage testImplementation libs.mockito testImplementation libs.robolectric androidTestImplementation libs.androidx.test.espresso.core androidTestImplementation libs.androidx.test.runner } // Shared logic for projects that depend on libmegazord // // This ensures that libmegazord will be in the library path so that it can be loaded. It also adds // the transitive JNA dependency. ext.dependsOnTheMegazord = { dependencies { api project(":full-megazord") // Add a JNA dependency, which is required by UniFFI. implementation(libs.jna) { artifact { type = "aar" } } } // Configurations are a somewhat mysterious Gradle concept. For our purposes, we can treat them // sets of files produced by one component and consumed by another. configurations { megazordNative { canBeConsumed = false } } dependencies { megazordNative project("path": ":full-megazord", "configuration": "megazordNative") implementation project("path": ":full-megazord", "configuration": "libsForTests") } } // Shared logic for projects that use UniFFI-generated bindings // // Make sure to also call dependsOnTheMegazord() ext.configureUniFFIBindgen = { crateName -> // This will store the uniffi-bindgen generated files for our component def uniffiOutDir = layout.buildDirectory.dir("generated/uniffi/") def generateUniffiBindings // Call `uniffi-bindgen` to generate the Kotlin bindings if (gradle.hasProperty("mozconfig")) { // in moz-central we can use an `Exec` task because we can assume the bindgen tool has already been built. generateUniffiBindings = tasks.register("generateUniffiBindings", GenerateUniffiBindingsEmbedded) { def libraryPath = "${gradle.mozconfig.topobjdir}/dist/bin/libmegazord.so" def bindgen = gradle.ext.mozconfig.substs.EMBEDDED_UNIFFI_BINDGEN outputDir.set(uniffiOutDir) workingDir project.rootDir commandLine bindgen args 'generate', "--crate", crateName, '--language', 'kotlin', '--out-dir', outputDir.get().asFile, '--no-format', libraryPath // Re-generate when the native megazord library is rebuilt inputs.files libraryPath // Re-generate if our uniffi-bindgen tooling changes. inputs.files bindgen } } else { // In app-services we can't use `Exec` because the megazord target isn't built yet; the task // resolves the library path from the megazordNative configuration when it runs. generateUniffiBindings = tasks.register("generateUniffiBindings", GenerateUniffiBindingsCargo) { // Qualify every property with `it.` because the `crateName` closure parameter shadows the // task's crateName property; a bare `crateName.set(...)` would target the String, not the task. it.megazordNativeFiles.from configurations.getByName("megazordNative") it.bindgenToolDir.set(file("${project.appServicesRootDir}/tools/embedded-uniffi-bindgen/")) it.crateName.set(crateName) it.nativeRustTarget.set(rootProject.ext.nativeRustTarget) it.workingDirectory.set(project.rootDir) it.outputDir.set(uniffiOutDir) } } androidComponents.onVariants(androidComponents.selector().all()) { variant -> variant.sources.java.addGeneratedSourceDirectory(generateUniffiBindings) { it.outputDir } } }