Build a AAR archive alongside the APK

This commit is contained in:
Paul Rouget 2018-07-18 20:48:38 +08:00
parent 78e90d6358
commit e8af185a3d
16 changed files with 396 additions and 236 deletions

View file

@ -214,10 +214,12 @@ class PackageCommands(CommandBase):
if flavor is not None:
flavor_name = flavor.title()
task_name = "assemble" + flavor_name + build_type + build_mode
variant = ":assemble" + flavor_name + build_type + build_mode
apk_task_name = ":servoapp" + variant
aar_task_name = ":servoview" + variant
try:
with cd(path.join("support", "android", "apk")):
subprocess.check_call(["./gradlew", "--no-daemon", task_name], env=env)
subprocess.check_call(["./gradlew", "--no-daemon", apk_task_name, aar_task_name], env=env)
except subprocess.CalledProcessError as e:
print("Packaging Android exited with return value %d" % e.returncode)
return e.returncode

View file

@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
import org.apache.tools.ant.taskdefs.condition.Os
buildscript {
repositories {
jcenter()
@ -17,6 +18,68 @@ allprojects {
}
google()
}
buildDir = rootDir.absolutePath + "/../../../target/gradle"
}
// Utility methods
String getTargetDir(boolean debug, String arch) {
def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath
return basePath + '/target/' + getSubTargetDir(debug, arch)
}
String getSubTargetDir(boolean debug, String arch) {
return getRustTarget(arch) + '/' + (debug ? 'debug' : 'release')
}
String getJniLibsPath(boolean debug, String arch) {
return getTargetDir(debug, arch) + '/apk/jniLibs'
}
static String getRustTarget(String arch) {
switch (arch.toLowerCase()) {
case 'arm' : return 'arm-linux-androideabi'
case 'armv7' : return 'armv7-linux-androideabi'
case 'arm64' : return 'aarch64-linux-android'
case 'x86' : return 'i686-linux-android'
default: throw new GradleException("Invalid target architecture " + arch)
}
}
static String getNDKAbi(String arch) {
switch (arch.toLowerCase()) {
case 'arm' : return 'armeabi'
case 'armv7' : return 'armeabi-v7a'
case 'arm64' : return 'arm64-v8a'
case 'x86' : return 'x86'
default: throw new GradleException("Invalid target architecture " + arch)
}
}
String getNdkDir() {
// Read environment variable used in rust build system
String ndkDir = System.getenv('ANDROID_NDK')
if (ndkDir == null) {
ndkDir = System.getenv('ANDROID_NDK_HOME')
}
if (ndkDir == null) {
ndkDir = System.getenv('ANDROID_NDK_ROOT')
}
if (ndkDir == null) {
// Fallback to ndkDir in local.properties
def rootDir = project.rootDir
def localProperties = new File(rootDir, "local.properties")
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
ndkDir = properties.getProperty('ndk.dir')
}
def cmd = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build'
def ndkbuild = new File(ndkDir + '/' + cmd)
if (!ndkbuild.exists()) {
throw new GradleException("Please set a valid NDK_HOME environment variable" +
"or ndk.dir path in local.properties file");
}
return ndkbuild.absolutePath
}

View file

@ -17,6 +17,6 @@ MY_LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PATH:= $(SERVO_TARGET_DIR)
LOCAL_MODULE := servo
LOCAL_MODULE := servojni
LOCAL_SRC_FILES := libsimpleservo.so
include $(PREBUILT_SHARED_LIBRARY)

View file

@ -1,4 +1,4 @@
NDK_TOOLCHAIN_VERSION := 4.9
APP_MODULES := c++_shared servo
APP_MODULES := c++_shared servojni
APP_PLATFORM := android-18
APP_STL:= c++_shared

View file

@ -1,7 +1,5 @@
apply plugin: 'com.android.application'
import groovy.io.FileType
import org.apache.tools.ant.taskdefs.condition.Os
import java.util.regex.Matcher
import java.util.regex.Pattern
@ -9,6 +7,8 @@ android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
buildDir = rootDir.absolutePath + "/../../../target/gradle/servoapp"
defaultConfig {
applicationId "com.mozilla.servo"
minSdkVersion 18
@ -17,24 +17,12 @@ android {
versionName "1.0.0"
}
compileOptions {
incremental false
}
splits {
density {
enable false
}
abi {
enable false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// Share all of that with servoview
flavorDimensions "default"
productFlavors {
@ -48,41 +36,25 @@ android {
}
}
splits {
density {
enable false
}
abi {
enable false
}
}
sourceSets {
main {
java.srcDirs = ['src/main/java']
assets.srcDirs = ['../../../../resources']
}
armDebug {
jniLibs.srcDirs = [getJniLibsPath(true, 'arm')]
}
armRelease {
jniLibs.srcDirs = [getJniLibsPath(false, 'arm')]
}
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')]
}
}
buildTypes {
// Default debug and release build types are used as templates
debug {
jniDebuggable true
}
release {
@ -94,51 +66,28 @@ android {
// Custom build types
armDebug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm')
}
}
armRelease {
initWith(release)
ndk {
abiFilters getNDKAbi('arm')
}
}
armv7Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('armv7')
}
}
armv7Release {
initWith(release)
ndk {
abiFilters getNDKAbi('armv7')
}
}
arm64Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm64')
}
}
arm64Release {
initWith(release)
ndk {
abiFilters getNDKAbi('arm64')
}
}
x86Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('x86')
}
}
x86Release {
initWith(release)
ndk {
abiFilters getNDKAbi('x86')
}
}
}
@ -149,165 +98,31 @@ android {
}
}
// Define apk output directory
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.afterEvaluate {
android.applicationVariants.all { variant ->
Pattern pattern = Pattern.compile(/^[\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
Matcher matcher = pattern.matcher(variant.name)
if (!matcher.find()) {
throw "Invalid variant name for output"
throw new GradleException("Invalid variant name for output: " + variant.name)
}
def arch = matcher.group(1)
def debug = variant.name.contains("Debug")
def path = "../../../../../" + getSubTargetDir(debug, arch) + "/servoapp.apk";
outputFileName = new File(path)
}
}
// Call our custom NDK Build task using flavor parameters
tasks.all {
compileTask ->
Pattern pattern = Pattern.compile(/^compile[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
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(),
'APP_BUILD_SCRIPT=../jni/Android.mk',
'NDK_APPLICATION_MK=../jni/Application.mk',
'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch),
'NDK_OUT=' + getTargetDir(debug, arch) + '/apk/obj',
'NDK_DEBUG=' + (debug ? '1' : '0'),
'APP_ABI=' + getNDKAbi(arch),
'SERVO_TARGET_DIR=' + getTargetDir(debug, arch)
}
compileTask.dependsOn taskName
def finalFolder = getTargetDir(debug, arch)
def finalFile = new File(finalFolder, "servoapp.apk")
variant.outputs.all { output ->
Task copyAndRenameAPKTask = project.task("copyAndRename${variant.name.capitalize()}APK", type: Copy) {
from output.outputFile.getParent()
into finalFolder
include output.outputFileName
rename(output.outputFileName, finalFile.getName())
}
variant.assemble.finalizedBy(copyAndRenameAPKTask)
}
}
}
}
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 = ['arm', '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)
}
}
}
}
googlevrImplementation 'com.google.vr:sdk-base:1.140.0'
googlevrImplementation(name: 'GVRService', ext: 'aar')
oculusvrImplementation(name: 'OVRService', ext: 'aar')
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation project(':servoview')
}
// Utility methods
String getTargetDir(boolean debug, String arch) {
def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath
return basePath + '/target/' + getSubTargetDir(debug, arch)
}
String getSubTargetDir(boolean debug, String arch) {
return getRustTarget(arch) + '/' + (debug ? 'debug' : 'release')
}
String getJniLibsPath(boolean debug, String arch) {
return getTargetDir(debug, arch) + '/apk/jniLibs'
}
static String getRustTarget(String arch) {
switch (arch.toLowerCase()) {
case 'arm' : return 'arm-linux-androideabi'
case 'armv7' : return 'armv7-linux-androideabi'
case 'arm64' : return 'aarch64-linux-android'
case 'x86' : return 'i686-linux-android'
default: throw new GradleException("Invalid target architecture " + arch)
}
}
static String getNDKAbi(String arch) {
switch (arch.toLowerCase()) {
case 'arm' : return 'armeabi'
case 'armv7' : return 'armeabi-v7a'
case 'arm64' : return 'arm64-v8a'
case 'x86' : return 'x86'
default: throw new GradleException("Invalid target architecture " + arch)
}
}
String getNdkDir() {
// Read environment variable used in rust build system
String ndkDir = System.getenv('ANDROID_NDK')
if (ndkDir == null) {
ndkDir = System.getenv('ANDROID_NDK_HOME')
}
if (ndkDir == null) {
// Fallback to ndkDir in local.properties
def rootDir = project.rootDir
def localProperties = new File(rootDir, "local.properties")
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
ndkDir = properties.getProperty('ndk.dir')
}
def cmd = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build'
def ndkbuild = new File(ndkDir + '/' + cmd)
if (!ndkbuild.exists()) {
throw new GradleException("Please set a valid NDK_HOME environment variable" +
"or ndk.dir path in local.properties file");
}
return ndkbuild.absolutePath
}
// 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;
}

View file

@ -3,17 +3,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.servo;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@ -44,13 +41,13 @@ public class MainActivity extends Activity implements ServoView.Client {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mServoView = (ServoView)findViewById(R.id.servoview);
mBackButton = (Button)findViewById(R.id.backbutton);
mFwdButton = (Button)findViewById(R.id.forwardbutton);
mReloadButton = (Button)findViewById(R.id.reloadbutton);
mStopButton = (Button)findViewById(R.id.stopbutton);
mUrlField = (EditText)findViewById(R.id.urlfield);
mProgressBar = (ProgressBar)findViewById(R.id.progressbar);
mServoView = findViewById(R.id.servoview);
mBackButton = findViewById(R.id.backbutton);
mFwdButton = findViewById(R.id.forwardbutton);
mReloadButton = findViewById(R.id.reloadbutton);
mStopButton = findViewById(R.id.stopbutton);
mUrlField = findViewById(R.id.urlfield);
mProgressBar = findViewById(R.id.progressbar);
mServoView.setClient(this);
mBackButton.setEnabled(false);

View file

@ -0,0 +1,258 @@
apply plugin: 'com.android.library'
import groovy.io.FileType
import java.util.regex.Matcher
import java.util.regex.Pattern
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
buildDir = rootDir.absolutePath + "/../../../target/gradle/servoview"
defaultConfig {
minSdkVersion 18
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
compileOptions {
incremental false
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
flavorDimensions "default"
productFlavors {
main {
}
googlevr {
minSdkVersion 21
}
oculusvr {
minSdkVersion 21
}
}
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
armDebug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm')
}
}
armRelease {
initWith(release)
ndk {
abiFilters getNDKAbi('arm')
}
}
armv7Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('armv7')
}
}
armv7Release {
initWith(release)
ndk {
abiFilters getNDKAbi('armv7')
}
}
arm64Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm64')
}
}
arm64Release {
initWith(release)
ndk {
abiFilters getNDKAbi('arm64')
}
}
x86Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('x86')
}
}
x86Release {
initWith(release)
ndk {
abiFilters getNDKAbi('x86')
}
}
}
sourceSets {
armDebug {
jniLibs.srcDirs = [getJniLibsPath(true, 'arm')]
}
armRelease {
jniLibs.srcDirs = [getJniLibsPath(false, 'arm')]
}
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
tasks.all {
compileTask ->
Pattern pattern = Pattern.compile(/^compile[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
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(),
'APP_BUILD_SCRIPT=../jni/Android.mk',
'NDK_APPLICATION_MK=../jni/Application.mk',
'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch),
'NDK_OUT=' + getTargetDir(debug, arch) + '/apk/obj',
'NDK_DEBUG=' + (debug ? '1' : '0'),
'APP_ABI=' + getNDKAbi(arch),
'SERVO_TARGET_DIR=' + getTargetDir(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 = ['arm', '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)
}
}
}
}
googlevrImplementation 'com.google.vr:sdk-base:1.140.0'
googlevrImplementation(name: 'GVRService', ext: 'aar')
oculusvrImplementation(name: 'OVRService', ext: 'aar')
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
}
// 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;
}

View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servo">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servoview">
<application>
<activity android:name=".MainActivity"
android:screenOrientation="landscape"

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mozilla.servoview" />

View file

@ -6,7 +6,6 @@
package com.mozilla.servoview;
import android.app.Activity;
import android.os.Build;
import android.content.Context;
import android.content.res.AssetManager;
import android.net.Uri;

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">servoview</string>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servo">>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servoview">>
<application>
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity android:name=".MainActivity" android:screenOrientation="landscape">

View file

@ -1 +1 @@
include ':servoapp'
include ':servoapp', ':servoview'