plugins { id 'com.android.library' } import groovy.io.FileType import java.util.regex.Matcher import java.util.regex.Pattern android { compileSdk 33 buildToolsVersion "33.0.2" namespace 'org.mozilla.servoview' buildDir = rootDir.absolutePath + "/../../../target/android/gradle/servoview" ndkPath = getNdkDir() defaultConfig { minSdk 30 targetSdk 30 versionCode 1 versionName "1.0" } compileOptions { incremental false sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } flavorDimensions "default" productFlavors { basic { } } splits { density { enable false } abi { enable false } } buildTypes { // Default debug and release build types are used as templates debug { jniDebuggable true } release { signingConfig signingConfigs.debug // Change this to sign with a production key minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } // Custom build types armv7Debug { initWith(debug) } armv7Release { initWith(release) } arm64Debug { initWith(debug) } arm64Release { initWith(release) } x86Debug { initWith(debug) } x86Release { initWith(release) } } sourceSets { main { } armv7Debug { jniLibs.srcDirs = [getJniLibsPath(true, 'armv7')] } armv7Release { jniLibs.srcDirs = [getJniLibsPath(false, 'armv7')] } arm64Debug { jniLibs.srcDirs = [getJniLibsPath(true, 'arm64')] } arm64Release { jniLibs.srcDirs = [getJniLibsPath(false, 'arm64')] } x86Debug { jniLibs.srcDirs = [getJniLibsPath(true, 'x86')] } x86Release { jniLibs.srcDirs = [getJniLibsPath(false, 'x86')] } } // Ignore default 'debug' and 'release' build types variantFilter { variant -> if(variant.buildType.name.equals('release') || variant.buildType.name.equals('debug')) { variant.setIgnore(true); } } // Call our custom NDK Build task using flavor parameters. // This step is needed because the Android Gradle Plugin system's // integration with native C/C++ shared objects (based on the // `android.externalNativeBuild` dsl object) assumes that we // actually execute compiler commands to produced the shared // objects. We already have the libsimpleservo.so produced by rustc. // We could simply copy the .so to the `sourceSet.jniLibs` folder // to make AGP bundle it with the APK, but this doesn't copy the STL // (libc++_shared.so) as well. So we use ndk-build as a glorified // `cp` command to copy the libsimpleservo.so from target/ // to target/android and crucially also include libc++_shared.so // as well. // // FIXME(mukilan): According to the AGP docs, we should not be // relying on task names used by the plugin system to hook into // the build process, but instead we should use officially supported // extension points such as `androidComponents.beforeVariants` tasks.all { compileTask -> // This matches the task `mergeBasicArmv7DebugJniLibFolders`. Pattern pattern = Pattern.compile(/^merge[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)JniLibFolders/) Matcher matcher = pattern.matcher(compileTask.name) if (!matcher.find()) { return } def taskName = "ndkbuild" + compileTask.name tasks.create(name: taskName, type: Exec) { def debug = compileTask.name.contains("Debug") def arch = matcher.group(1) commandLine getNdkDir() + "/ndk-build", 'APP_BUILD_SCRIPT=../jni/Android.mk', 'NDK_APPLICATION_MK=../jni/Application.mk', 'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch), 'NDK_DEBUG=' + (debug ? '1' : '0'), 'APP_ABI=' + getNDKAbi(arch), 'NDK_LOG=1', 'SERVO_TARGET_DIR=' + getNativeTargetDir(debug, arch) } compileTask.dependsOn taskName } project.afterEvaluate { android.libraryVariants.all { variant -> Pattern pattern = Pattern.compile(/^[\w\d]+([A-Z][\w\d]+)(Debug|Release)/) Matcher matcher = pattern.matcher(variant.name) if (!matcher.find()) { throw new GradleException("Invalid variant name for output: " + variant.name) } def arch = matcher.group(1) def debug = variant.name.contains("Debug") def finalFolder = getTargetDir(debug, arch) def finalFile = new File(finalFolder, "servoview.aar") variant.outputs.all { output -> Task copyAndRenameAARTask = project.task("copyAndRename${variant.name.capitalize()}AAR", type: Copy) { from output.outputFile.getParent() into finalFolder include output.outputFileName rename(output.outputFileName, finalFile.getName()) } variant.assemble.finalizedBy(copyAndRenameAARTask) } } } } dependencies { //Dependency list def deps = [ new ServoDependency("blurdroid.jar", "blurdroid") ] // Iterate all build types and dependencies // For each dependency call the proper implementation command and set the correct dependency path def list = ['armv7', 'arm64', 'x86'] for (arch in list) { for (debug in [true, false]) { String basePath = getTargetDir(debug, arch) + "/build" String cmd = arch + (debug ? "Debug" : "Release") + "Implementation" for (ServoDependency dep : deps) { String path = findDependencyPath(basePath, dep.fileName, dep.folderFilter) if (path) { "${cmd}" files(path) } } } } implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' } // folderFilter can be used to improve search performance static String findDependencyPath(String basePath, String filename, String folderFilter) { File path = new File(basePath); if (!path.exists()) { return '' } if (folderFilter) { path.eachDir { if (it.name.contains(folderFilter)) { path = new File(it.absolutePath) } } } def result = '' path.eachFileRecurse(FileType.FILES) { if(it.name.equals(filename)) { result = it.absolutePath } } return result } class ServoDependency { ServoDependency(String fileName, String folderFilter = null) { this.fileName = fileName; this.folderFilter = folderFilter; } public String fileName; public String folderFilter; }