From 4f3f4284e1db76b88742230b05a319db9dd0c2ee Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 22 Dec 2014 19:49:04 -0800 Subject: [PATCH] Bug 1098239 - Rewrite mobile/android Gradle integration. r=me This is a big patch, but it's essentially NPOTB. The part that is POTB is ... removing Gradle integration from the build. I've implemented |mach gradle-install| as a substitute for the build system stuff; it's just so much easier to iterate on a mach command than a moz.build and Makefile.in. I'm landing this with self-review because this lessens the impact of the Gradle integration on the build system and because I am the only person who understands either the old or the new system. You'll need to run |mach gradle-install| at top level to configure the new Gradle integration. But |mach gradle ...| does the right thing configuration steps too. This patch rewrites most of the Gradle integration. The major changes are: * all .gradle files move into mobile/android/gradle; * all the Gradle projects live in the object directory; * mozconfig exposed to all build.gradle files; * simplification of Android configuration between build.gradle files; * support for user-specified version of build tools; * first steps towards supporting builds from the source directory; * bumps Gradle to 2.2.1; * bumps the Android-Gradle plugin to 0.14.4. This is seemingly a step backwards given that we'd prefer to ship the .idea directory in the source directory. But in fact we get closer to that; it's possible to run ./gradlew in the source directory and get a reasonable build. We'll progress with this in time. The win right now is that the projects are nested, which makes importing work better on Linux machines. Unfortunately IntelliJ 13 and 14 now have conflicting Android-Gradle plugin version requirements, so we now only support IntelliJ 14.0.2 and above. --HG-- rename : mobile/android/base/gradle_AndroidManifest.xml => mobile/android/gradle/base/AndroidManifest.xml rename : mobile/android/base/gradle_AndroidManifest.xml => mobile/android/gradle/branding/AndroidManifest.xml rename : mobile/android/gradle/omnijar/gradle_AndroidManifest.xml => mobile/android/gradle/omnijar/AndroidManifest.xml rename : mobile/android/base/gradle_AndroidManifest.xml => mobile/android/gradle/preprocessed_code/AndroidManifest.xml rename : mobile/android/base/gradle_AndroidManifest.xml => mobile/android/gradle/preprocessed_resources/AndroidManifest.xml rename : mobile/android/thirdparty/gradle_AndroidManifest.xml => mobile/android/gradle/thirdparty/AndroidManifest.xml --- .gitignore | 3 + .hgignore | 3 + mobile/android/app/build.gradle | 61 --------- mobile/android/base/build.gradle | 4 +- mobile/android/base/docs/gradle.rst | 12 +- mobile/android/gradle/Makefile.in | 59 --------- mobile/android/gradle/android.gradle | 29 +++++ mobile/android/gradle/app/build.gradle | 28 +++++ .../base/AndroidManifest.xml} | 0 mobile/android/gradle/base/build.gradle | 66 ++++++++++ .../gradle/branding/AndroidManifest.xml | 4 + mobile/android/gradle/branding/build.gradle | 16 +++ mobile/android/gradle/build.gradle | 29 +++-- mobile/android/gradle/gradle.properties | 2 + mobile/android/gradle/gradle.properties.in | 14 +-- .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 51348 -> 51018 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- mobile/android/gradle/moz.build | 5 - ...ndroidManifest.xml => AndroidManifest.xml} | 0 mobile/android/gradle/omnijar/build.gradle | 52 +++++--- .../preprocessed_code/AndroidManifest.xml | 4 + .../gradle/preprocessed_code/build.gradle | 16 +++ .../AndroidManifest.xml | 4 + .../preprocessed_resources/build.gradle | 24 ++++ mobile/android/gradle/settings.gradle | 49 +++++++- .../thirdparty/AndroidManifest.xml} | 0 mobile/android/gradle/thirdparty/build.gradle | 21 ++++ mobile/android/mach_commands.py | 119 ++++++++++++++++++ mobile/android/moz.build | 1 - mobile/android/thirdparty/build.gradle | 49 -------- 30 files changed, 448 insertions(+), 230 deletions(-) delete mode 100644 mobile/android/app/build.gradle delete mode 100644 mobile/android/gradle/Makefile.in create mode 100644 mobile/android/gradle/android.gradle create mode 100644 mobile/android/gradle/app/build.gradle rename mobile/android/{base/gradle_AndroidManifest.xml => gradle/base/AndroidManifest.xml} (100%) create mode 100644 mobile/android/gradle/base/build.gradle create mode 100644 mobile/android/gradle/branding/AndroidManifest.xml create mode 100644 mobile/android/gradle/branding/build.gradle create mode 100644 mobile/android/gradle/gradle.properties delete mode 100644 mobile/android/gradle/moz.build rename mobile/android/gradle/omnijar/{gradle_AndroidManifest.xml => AndroidManifest.xml} (100%) create mode 100644 mobile/android/gradle/preprocessed_code/AndroidManifest.xml create mode 100644 mobile/android/gradle/preprocessed_code/build.gradle create mode 100644 mobile/android/gradle/preprocessed_resources/AndroidManifest.xml create mode 100644 mobile/android/gradle/preprocessed_resources/build.gradle rename mobile/android/{thirdparty/gradle_AndroidManifest.xml => gradle/thirdparty/AndroidManifest.xml} (100%) create mode 100644 mobile/android/gradle/thirdparty/build.gradle delete mode 100644 mobile/android/thirdparty/build.gradle diff --git a/.gitignore b/.gitignore index 5d4dca0b1476..8f5f436d6cc9 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,6 @@ GPATH # Git clone directory for updating web-platform-tests testing/web-platform/sync/ + +# Android Gradle artifacts. +mobile/android/gradle/.gradle diff --git a/.hgignore b/.hgignore index 619012cedad9..2771ebb4e75f 100644 --- a/.hgignore +++ b/.hgignore @@ -93,3 +93,6 @@ GPATH # including the following three lines ^browser/components/loop/standalone/content/legal/styles/.*\.css$ ^browser/components/loop/standalone/content/legal/terms/en_US\.html$ + +# Android Gradle artifacts. +^mobile/android/gradle/.gradle diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle deleted file mode 100644 index 259254e906d3..000000000000 --- a/mobile/android/app/build.gradle +++ /dev/null @@ -1,61 +0,0 @@ -project.buildDir = "${topobjdir}/mobile/android/gradle/app/build" - -apply plugin: 'android' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - } - - buildTypes { - release { - runProguard false - proguardFile getDefaultProguardFile('proguard-android.txt') - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - - android { - lintOptions { - abortOnError false - } - } - - sourceSets { - main { - manifest { - srcFile "${topobjdir}/mobile/android/base/AndroidManifest.xml" - } - - assets { - srcDir "${topobjdir}/dist/fennec/assets" - } - - jniLibs { - srcDir "${topobjdir}/dist/fennec/lib" - } - } - - androidTest { - java { - srcDir "${topobjdir}/mobile/android/gradle/app/src/androidTest/robocop_harness/java" - srcDir "${topobjdir}/mobile/android/gradle/app/src/androidTest/robocop/java" - srcDir "${topobjdir}/mobile/android/gradle/app/src/androidTest/background/java" - srcDir "${topobjdir}/mobile/android/gradle/app/src/androidTest/browser/java" - } - } - } -} - -dependencies { - compile project(':base') - androidTestCompile fileTree(dir: "../../../build/mobile/robocop", include: ['*.jar']) -} diff --git a/mobile/android/base/build.gradle b/mobile/android/base/build.gradle index 8737f995b15b..6468135f6b1c 100644 --- a/mobile/android/base/build.gradle +++ b/mobile/android/base/build.gradle @@ -1,6 +1,6 @@ project.buildDir = "${topobjdir}/mobile/android/gradle/base/build" -apply plugin: 'android-library' +apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.compileSdkVersion @@ -14,7 +14,7 @@ android { buildTypes { release { - runProguard false + minifyEnabled false proguardFile getDefaultProguardFile('proguard-android.txt') } } diff --git a/mobile/android/base/docs/gradle.rst b/mobile/android/base/docs/gradle.rst index 02980cc06bfa..7be8b1e7f96b 100644 --- a/mobile/android/base/docs/gradle.rst +++ b/mobile/android/base/docs/gradle.rst @@ -18,10 +18,10 @@ The debug APK will be at The ``$OBJDIR/mobile/android/gradle`` directory can be imported into IntelliJ as follows: -- File > Import Project +- File > Import Project... - [select ``$OBJDIR/mobile/android/gradle``] - Import project from external model > Gradle -- [select Use default Gradle wrapper] +- [select Use customizable Gradle wrapper] When prompted, do not add any files to git. You may need to re-open the project, or restart IntelliJ, to pick up a compiler language-level change. @@ -42,11 +42,11 @@ Caveats How the Gradle project is laid out ---------------------------------- -To the greatest extent possible, the Gradle configuration lives in the source -directory. The only Gradle configuration that lives in the object directory is -installed when building the ``mobile/android/gradle`` directory. +To the greatest extent possible, the Gradle configuration lives in the object +directory. -At the time of writing, their are three sub-modules: *app*, *base*, and *thirdparty*. +At the time of writing, their are three main sub-modules: *app*, *base*, and +*thirdparty*, and several smaller sub-modules. *app* is the Fennec wrapper; it generates the **org.mozilla.fennec.R** resource package. *base* is the Gecko code; it generates the **org.mozilla.gecko.R** diff --git a/mobile/android/gradle/Makefile.in b/mobile/android/gradle/Makefile.in deleted file mode 100644 index ce217a748ece..000000000000 --- a/mobile/android/gradle/Makefile.in +++ /dev/null @@ -1,59 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# 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/. - -gradle := \ - local.properties.in \ - gradle.properties.in \ - $(NULL) - -gradle_PATH := $(CURDIR) -gradle_FLAGS += -Dtopsrcdir=$(abspath $(topsrcdir)) -gradle_FLAGS += -Dtopobjdir=$(abspath $(DEPTH)) -gradle_FLAGS += -DANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT) -gradle_KEEP_PATH := 1 -PP_TARGETS += gradle - -wrapper_FILES := \ - build.gradle \ - settings.gradle \ - gradle/wrapper/gradle-wrapper.jar \ - gradle/wrapper/gradle-wrapper.properties \ - gradlew \ - $(NULL) - -wrapper_DEST := $(CURDIR) -wrapper_KEEP_PATH := 1 -INSTALL_TARGETS += wrapper - -base/src/main/java/org/mozilla/gecko: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/mobile/android/base $@ -libs:: base/src/main/java/org/mozilla/gecko - -base/src/main/res: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/mobile/android/base/resources $@ -libs:: base/src/main/res - -app/src/androidTest/robocop_harness/java/org/mozilla/gecko: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/build/mobile/robocop $@ -libs:: app/src/androidTest/robocop_harness/java/org/mozilla/gecko - -app/src/androidTest/robocop/java/org/mozilla/gecko/tests: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/mobile/android/base/tests $@ -libs:: app/src/androidTest/robocop/java/org/mozilla/gecko/tests - -app/src/androidTest/browser/java/org/mozilla/gecko: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/mobile/android/tests/browser/junit3/src $@ -libs:: app/src/androidTest/browser/java/org/mozilla/gecko - -app/src/androidTest/background/java/org/mozilla/gecko/background: - $(NSINSTALL) -D ${@D} - ln -s $(topsrcdir)/mobile/android/tests/background/junit3/src $@ -libs:: app/src/androidTest/background/java/org/mozilla/gecko/background - -include $(topsrcdir)/config/rules.mk diff --git a/mobile/android/gradle/android.gradle b/mobile/android/gradle/android.gradle new file mode 100644 index 000000000000..2c9cd85a8e18 --- /dev/null +++ b/mobile/android/gradle/android.gradle @@ -0,0 +1,29 @@ +// Configure shared Android settings. This should be run (using "apply from") +// immediately after applying the Android plugin. Be aware that IntelliJ does +// not parse these values correctly: the Android-Gradle facet panel manually +// parses build.gradle rather than interrogating the Gradle model. + +android { + compileSdkVersion "android-${mozconfig.defines.ANDROID_TARGET_SDK}" + // Turn "android-sdk/build-tools/21.1.1" into "21.1.1". + buildToolsVersion (new File(mozconfig.substs.ANDROID_BUILD_TOOLS).getName()) + + defaultConfig { + targetSdkVersion mozconfig.defines.ANDROID_TARGET_SDK + minSdkVersion mozconfig.defines.MOZ_ANDROID_MIN_SDK_VERSION + if (mozconfig.defines.MOZ_ANDROID_MAX_SDK_VERSION) { + maxSdkVersion mozconfig.defines.MOZ_ANDROID_MAX_SDK_VERSION + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + android { + lintOptions { + abortOnError false + } + } +} diff --git a/mobile/android/gradle/app/build.gradle b/mobile/android/gradle/app/build.gradle new file mode 100644 index 000000000000..884d189b9ffc --- /dev/null +++ b/mobile/android/gradle/app/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.application' + +apply from: rootProject.file("${topsrcdir}/mobile/android/gradle/android.gradle") + +android { + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } + + sourceSets { + androidTest { + java { + srcDir "${topobjdir}/mobile/android/gradle/app/src/robocop_harness" + srcDir "${topobjdir}/mobile/android/gradle/app/src/robocop" + srcDir "${topobjdir}/mobile/android/gradle/app/src/background" + srcDir "${topobjdir}/mobile/android/gradle/app/src/browser" + } + } + } +} + +dependencies { + compile project(':base') + androidTestCompile fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/mobile/android/base/gradle_AndroidManifest.xml b/mobile/android/gradle/base/AndroidManifest.xml similarity index 100% rename from mobile/android/base/gradle_AndroidManifest.xml rename to mobile/android/gradle/base/AndroidManifest.xml diff --git a/mobile/android/gradle/base/build.gradle b/mobile/android/gradle/base/build.gradle new file mode 100644 index 000000000000..1d4d6c99ecc3 --- /dev/null +++ b/mobile/android/gradle/base/build.gradle @@ -0,0 +1,66 @@ +apply plugin: 'com.android.library' + +apply from: "${topsrcdir}/mobile/android/gradle/android.gradle" + +android { + defaultConfig { + applicationId 'org.mozilla.gecko' + } + + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } + + sourceSets { + main { + java { + exclude 'org/mozilla/gecko/tests/**' + exclude 'org/mozilla/gecko/resources/**' + if (!mozconfig.substs.MOZ_CRASHREPORTER) { + exclude 'org/mozilla/gecko/CrashReporter.java' + } + } + + res { + if (mozconfig.substs.MOZ_CRASHREPORTER) { + srcDir "src/crashreporter/res" + } + } + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:support-v4:19.1.+' + + if (mozconfig.substs.MOZ_NATIVE_DEVICES) { + compile 'com.android.support:appcompat-v7:19.1.+' + compile 'com.android.support:mediarouter-v7:19.1.+' + compile 'com.google.android.gms:play-services:5.+' + } + + compile project(':branding') + compile project(':omnijar') + compile project(':preprocessed_code') + compile project(':preprocessed_resources') + compile project(':thirdparty') +} + +android.libraryVariants.all { variant -> + variant.checkManifest.dependsOn generateCodeAndResources +} + +apply plugin: 'idea' + +idea { + module { + // excludeDirs = [] + excludeDirs += file('src/main/java/org/mozilla/gecko/tests') + excludeDirs += file('org/mozilla/gecko/tests') + excludeDirs += file('tests') + } +} diff --git a/mobile/android/gradle/branding/AndroidManifest.xml b/mobile/android/gradle/branding/AndroidManifest.xml new file mode 100644 index 000000000000..6f0ecf04b968 --- /dev/null +++ b/mobile/android/gradle/branding/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/mobile/android/gradle/branding/build.gradle b/mobile/android/gradle/branding/build.gradle new file mode 100644 index 000000000000..dde5bb96030d --- /dev/null +++ b/mobile/android/gradle/branding/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'com.android.library' + +apply from: "${topsrcdir}/mobile/android/gradle/android.gradle" + +android { + defaultConfig { + applicationId 'org.mozilla.gecko.branding' + } + + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } +} diff --git a/mobile/android/gradle/build.gradle b/mobile/android/gradle/build.gradle index 577a0e7ded92..26bc9de8873d 100644 --- a/mobile/android/gradle/build.gradle +++ b/mobile/android/gradle/build.gradle @@ -1,23 +1,34 @@ -buildDir = "${topobjdir}/mobile/android/gradle/build" - -ext { - compileSdkVersion = "${compileSdkVersion}" - buildToolsVersion = "${buildToolsVersion}" - - targetSdkVersion = "${targetSdkVersion}" - minSdkVersion = "${minSdkVersion}" +allprojects { + // Expose the per-object-directory configuration to all projects. + ext { + mozconfig = gradle.mozconfig + topsrcdir = gradle.mozconfig.topsrcdir + topobjdir = gradle.mozconfig.topobjdir + } } +buildDir "${topobjdir}/mobile/android/gradle/build" + buildscript { repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:0.14.4' } } repositories { jcenter() } + +subprojects { + task generateCodeAndResources(type:Exec) { + workingDir "${topobjdir}" + + commandLine "${topsrcdir}/mach" + args 'build' + args 'mobile/android/base/gradle-targets' + } +} diff --git a/mobile/android/gradle/gradle.properties b/mobile/android/gradle/gradle.properties new file mode 100644 index 000000000000..40ca366b2f0e --- /dev/null +++ b/mobile/android/gradle/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.parallel=true +org.gradle.daemon=true diff --git a/mobile/android/gradle/gradle.properties.in b/mobile/android/gradle/gradle.properties.in index 3f1a73f29462..faefe6dba05c 100644 --- a/mobile/android/gradle/gradle.properties.in +++ b/mobile/android/gradle/gradle.properties.in @@ -1,15 +1,5 @@ #filter substitution +#include gradle.properties + topsrcdir=@topsrcdir@ topobjdir=@topobjdir@ -compileSdkVersion=android-@ANDROID_TARGET_SDK@ -buildToolsVersion=20.0.0 -targetSdkVersion=@ANDROID_TARGET_SDK@ -minSdkVersion=@MOZ_ANDROID_MIN_SDK_VERSION@ -#ifdef MOZ_ANDROID_MAX_SDK_VERSION -maxSdkVersion=@MOZ_ANDROID_MAX_SDK_VERSION@ -#endif -#if MOZ_CRASHREPORTER -MOZ_CRASHREPORTER=true -#else -MOZ_CRASHREPORTER=false -#endif diff --git a/mobile/android/gradle/gradle/wrapper/gradle-wrapper.jar b/mobile/android/gradle/gradle/wrapper/gradle-wrapper.jar index 0087cd3b18659b5577cf6ad3ef61f8eb9416ebba..c97a8bdb9088d370da7e88784a7a093b971aa23a 100644 GIT binary patch delta 10956 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 delta 11402 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 - inputs.sourceDir srcDir - } + // project.sourceSets.main.java.srcDirs.each { srcDir -> + // inputs.sourceDir srcDir + // } + + inputs.sourceDir 'src/main/java/locales' + inputs.sourceDir 'src/main/java/chrome' + inputs.sourceDir 'src/main/java/components' + inputs.sourceDir 'src/main/java/modules' + inputs.sourceDir 'src/main/java/themes' + // Produce a single output file. outputs.file "${topobjdir}/dist/fennec/assets/omni.ja" @@ -40,3 +45,10 @@ task rebuildOmnijar(type:Exec) { // Rebuild the omnijar before the earliest Java task. tasks.compileJava.dependsOn rebuildOmnijar + +apply plugin: 'idea' + +idea { + module { + } +} diff --git a/mobile/android/gradle/preprocessed_code/AndroidManifest.xml b/mobile/android/gradle/preprocessed_code/AndroidManifest.xml new file mode 100644 index 000000000000..b51508815c4b --- /dev/null +++ b/mobile/android/gradle/preprocessed_code/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/mobile/android/gradle/preprocessed_code/build.gradle b/mobile/android/gradle/preprocessed_code/build.gradle new file mode 100644 index 000000000000..c1c5e4cbb8b2 --- /dev/null +++ b/mobile/android/gradle/preprocessed_code/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'android-library' + +apply from: "${topsrcdir}/mobile/android/gradle/android.gradle" + +android { + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } +} + +android.libraryVariants.all { variant -> + variant.checkManifest.dependsOn generateCodeAndResources +} diff --git a/mobile/android/gradle/preprocessed_resources/AndroidManifest.xml b/mobile/android/gradle/preprocessed_resources/AndroidManifest.xml new file mode 100644 index 000000000000..40744740afff --- /dev/null +++ b/mobile/android/gradle/preprocessed_resources/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/mobile/android/gradle/preprocessed_resources/build.gradle b/mobile/android/gradle/preprocessed_resources/build.gradle new file mode 100644 index 000000000000..89c470f1a2d6 --- /dev/null +++ b/mobile/android/gradle/preprocessed_resources/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' + +apply from: "${topsrcdir}/mobile/android/gradle/android.gradle" + +android { + defaultConfig { + applicationId 'org.mozilla.gecko.preprocessed_resources' + } + + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } +} + +android.libraryVariants.all { variant -> + variant.checkManifest.dependsOn generateCodeAndResources +} + +dependencies { + compile project(':branding') +} diff --git a/mobile/android/gradle/settings.gradle b/mobile/android/gradle/settings.gradle index 271edc973d00..8ce2199a406e 100644 --- a/mobile/android/gradle/settings.gradle +++ b/mobile/android/gradle/settings.gradle @@ -1,9 +1,50 @@ +// If our root project is in the object directory, we expect to be given +// topsrcdir from our environment via gradle.properties. If we don't get it, +// our root project is in the source directory, so we extract topsrcdir relative +// to the location of this script. +if (!hasProperty('topsrcdir')) { + // In the source directory, we're not worried about links crossing directories. + binding.variables['topsrcdir'] = new File("../../..").getCanonicalPath() + logger.warn("topsrcdir is undefined: assuming source directory Gradle invocation with topsrcdir=${topsrcdir}.") +} + +def command = ["${topsrcdir}/mach", "environment", "--format", "json", "--verbose"] +def proc = command.execute(null, new File(topsrcdir)) +def sout = new StringBuffer() +def serr = new StringBuffer() +proc.consumeProcessOutput(sout, serr) +proc.waitFor() + +if (proc.exitValue() != 0) { + throw new GradleException("Could not extract mozconfig/build environment from |${topsrcdir}/mach environment|!"); +} + +import groovy.json.JsonSlurper +def slurper = new JsonSlurper() +def json = slurper.parseText(sout.toString()) + include ':app' include ':base' +include ':branding' include ':omnijar' +include ':preprocessed_code' +include ':preprocessed_resources' include ':thirdparty' -project(':app').projectDir = new File("${topsrcdir}/mobile/android/app") -project(':base').projectDir = new File("${topsrcdir}/mobile/android/base") -project(':omnijar').projectDir = new File("${topsrcdir}/mobile/android/gradle/omnijar") -project(':thirdparty').projectDir = new File("${topsrcdir}/mobile/android/thirdparty") +def gradleRoot = new File("${json.topobjdir}/mobile/android/gradle") +project(':app').projectDir = new File(gradleRoot, 'app') +project(':base').projectDir = new File(gradleRoot, 'base') +project(':branding').projectDir = new File(gradleRoot, 'branding') +project(':omnijar').projectDir = new File(gradleRoot, 'omnijar') +project(':preprocessed_code').projectDir = new File(gradleRoot, 'preprocessed_code') +project(':preprocessed_resources').projectDir = new File(gradleRoot, 'preprocessed_resources') +project(':thirdparty').projectDir = new File(gradleRoot, 'thirdparty') + +// The Gradle instance is shared between settings.gradle and all the +// other build.gradle files (see +// http://forums.gradle.org/gradle/topics/define_extension_properties_from_settings_xml). +// We use this ext property to pass the per-object-directory mozconfig +// between scripts. This lets us execute set-up code before we gradle +// tries to configure the project even once, and as a side benefit +// saves invoking |mach environment| multiple times. +gradle.ext.mozconfig = json diff --git a/mobile/android/thirdparty/gradle_AndroidManifest.xml b/mobile/android/gradle/thirdparty/AndroidManifest.xml similarity index 100% rename from mobile/android/thirdparty/gradle_AndroidManifest.xml rename to mobile/android/gradle/thirdparty/AndroidManifest.xml diff --git a/mobile/android/gradle/thirdparty/build.gradle b/mobile/android/gradle/thirdparty/build.gradle new file mode 100644 index 000000000000..a4391d408c17 --- /dev/null +++ b/mobile/android/gradle/thirdparty/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'com.android.library' + +apply from: "${topsrcdir}/mobile/android/gradle/android.gradle" + +android { + defaultConfig { + applicationId 'org.mozilla.gecko.thirdparty' + } + + buildTypes { + release { + minifyEnabled false + proguardFile getDefaultProguardFile('proguard-android.txt') + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:support-v4:19.1.+' +} diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py index 91750167ff7f..19572c6993f6 100644 --- a/mobile/android/mach_commands.py +++ b/mobile/android/mach_commands.py @@ -6,6 +6,8 @@ from __future__ import print_function, unicode_literals import argparse import logging +import os + import mozpack.path as mozpath from mozbuild.base import ( @@ -13,12 +15,25 @@ from mozbuild.base import ( MachCommandConditions as conditions, ) +from mozbuild.util import ( + FileAvoidWrite, +) + from mach.decorators import ( CommandArgument, CommandProvider, Command, ) +SUCCESS = ''' +You should be ready to build with Gradle and import into IntelliJ! Test with + + ./mach gradle build + +and in IntelliJ select File > Import project... and choose + + {topobjdir}/mobile/android/gradle +''' @CommandProvider class MachCommands(MachCommandBase): @@ -30,7 +45,111 @@ class MachCommands(MachCommandBase): # Avoid logging the command self.log_manager.terminal_handler.setLevel(logging.CRITICAL) + code = self.gradle_install(quiet=True) + if code: + return code + return self.run_process(['./gradlew'] + args, pass_thru=True, # Allow user to run gradle interactively. ensure_exit_code=False, # Don't throw on non-zero exit code. cwd=mozpath.join(self.topobjdir, 'mobile', 'android', 'gradle')) + + @Command('gradle-install', category='devenv', + description='Install gradle environment.', + conditions=[conditions.is_android]) + def gradle_install(self, quiet=False): + import mozpack.manifests + m = mozpack.manifests.InstallManifest() + + def srcdir(dst, src): + m.add_symlink(os.path.join(self.topsrcdir, src), dst) + + def objdir(dst, src): + m.add_symlink(os.path.join(self.topobjdir, src), dst) + + srcdir('build.gradle', 'mobile/android/gradle/build.gradle') + srcdir('settings.gradle', 'mobile/android/gradle/settings.gradle') + + m.add_pattern_copy(os.path.join(self.topsrcdir, 'mobile/android/gradle/gradle/wrapper'), '**', 'gradle/wrapper') + m.add_copy(os.path.join(self.topsrcdir, 'mobile/android/gradle/gradlew'), 'gradlew') + + defines = { + 'topsrcdir': self.topsrcdir, + 'topobjdir': self.topobjdir, + 'ANDROID_SDK_ROOT': self.substs['ANDROID_SDK_ROOT'], + } + m.add_preprocess(os.path.join(self.topsrcdir, 'mobile/android/gradle/gradle.properties.in'), + 'gradle.properties', + defines=defines, + deps=os.path.join(self.topobjdir, 'mobile/android/gradle/.deps/gradle.properties.pp')) + m.add_preprocess(os.path.join(self.topsrcdir, 'mobile/android/gradle/local.properties.in'), + 'local.properties', + defines=defines, + deps=os.path.join(self.topobjdir, 'mobile/android/gradle/.deps/local.properties.pp')) + + srcdir('branding/build.gradle', 'mobile/android/gradle/branding/build.gradle') + srcdir('branding/src/main/AndroidManifest.xml', 'mobile/android/gradle/branding/AndroidManifest.xml') + srcdir('branding/src/main/res', os.path.join(self.substs['MOZ_BRANDING_DIRECTORY'], 'res')) + + srcdir('preprocessed_code/build.gradle', 'mobile/android/gradle/preprocessed_code/build.gradle') + srcdir('preprocessed_code/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_code/AndroidManifest.xml') + objdir('preprocessed_code/src/main/java', 'mobile/android/base/generated/preprocessed') + + srcdir('preprocessed_resources/build.gradle', 'mobile/android/gradle/preprocessed_resources/build.gradle') + srcdir('preprocessed_resources/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_resources/AndroidManifest.xml') + objdir('preprocessed_resources/src/main/res', 'mobile/android/base/res') + + srcdir('thirdparty/build.gradle', 'mobile/android/gradle/thirdparty/build.gradle') + srcdir('thirdparty/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty/AndroidManifest.xml') + srcdir('thirdparty/src/main/java', 'mobile/android/thirdparty') + + srcdir('omnijar/build.gradle', 'mobile/android/gradle/omnijar/build.gradle') + srcdir('omnijar/src/main/java/locales', 'mobile/android/locales') + srcdir('omnijar/src/main/java/chrome', 'mobile/android/chrome') + srcdir('omnijar/src/main/java/components', 'mobile/android/components') + srcdir('omnijar/src/main/java/modules', 'mobile/android/modules') + srcdir('omnijar/src/main/java/themes', 'mobile/android/themes') + + srcdir('app/build.gradle', 'mobile/android/gradle/app/build.gradle') + objdir('app/src/main/AndroidManifest.xml', 'mobile/android/base/AndroidManifest.xml') + objdir('app/src/main/assets', 'dist/fennec/assets') + objdir('app/src/main/jniLibs', 'dist/fennec/lib') + # Test code. + srcdir('app/src/robocop_harness/org/mozilla/gecko', 'build/mobile/robocop') + srcdir('app/src/robocop/org/mozilla/gecko/tests', 'mobile/android/base/tests') + srcdir('app/src/background/org/mozilla/gecko', 'mobile/android/tests/background/junit3/src') + srcdir('app/src/browser/org/mozilla/gecko', 'mobile/android/tests/browser/junit3/src') + # Test libraries. + srcdir('app/libs', 'build/mobile/robocop') + + srcdir('base/build.gradle', 'mobile/android/gradle/base/build.gradle') + srcdir('base/src/main/AndroidManifest.xml', 'mobile/android/gradle/base/AndroidManifest.xml') + srcdir('base/src/main/java/org/mozilla/gecko', 'mobile/android/base') + srcdir('base/src/main/java/org/mozilla/mozstumbler', 'mobile/android/stumbler/java/org/mozilla/mozstumbler') + srcdir('base/src/main/java/org/mozilla/search', 'mobile/android/search/java/org/mozilla/search') + srcdir('base/src/main/res', 'mobile/android/base/resources') + srcdir('base/src/newtablet/res', 'mobile/android/base/newtablet/res') + srcdir('base/src/crashreporter/res', 'mobile/android/base/crashreporter/res') + + manifest_path = os.path.join(self.topobjdir, 'mobile', 'android', 'gradle.manifest') + with FileAvoidWrite(manifest_path) as f: + m.write(fileobj=f) + + self.virtualenv_manager.ensure() + code = self.run_process([ + self.virtualenv_manager.python_path, + os.path.join(self.topsrcdir, 'python/mozbuild/mozbuild/action/process_install_manifest.py'), + '--no-remove', + '--no-remove-all-directory-symlinks', + '--no-remove-empty-directories', + os.path.join(self.topobjdir, 'mobile', 'android', 'gradle'), + manifest_path], + pass_thru=True, # Allow user to run gradle interactively. + ensure_exit_code=False, # Don't throw on non-zero exit code. + cwd=mozpath.join(self.topsrcdir, 'mobile', 'android')) + + if not quiet: + if not code: + print(SUCCESS.format(topobjdir=self.topobjdir)) + + return code diff --git a/mobile/android/moz.build b/mobile/android/moz.build index 51cd2e0a9ada..8c12accb59bb 100644 --- a/mobile/android/moz.build +++ b/mobile/android/moz.build @@ -24,7 +24,6 @@ DIRS += [ 'fonts', 'geckoview_library', 'extensions', - 'gradle', ] if not CONFIG['LIBXUL_SDK']: diff --git a/mobile/android/thirdparty/build.gradle b/mobile/android/thirdparty/build.gradle deleted file mode 100644 index 084c42d9366f..000000000000 --- a/mobile/android/thirdparty/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -project.buildDir = "${topobjdir}/mobile/android/gradle/thirdparty/build" - -apply plugin: 'android-library' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - applicationId 'org.mozilla.gecko.thirdparty' - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - } - - buildTypes { - release { - runProguard false - proguardFile getDefaultProguardFile('proguard-android.txt') - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - - android { - lintOptions { - abortOnError false - } - } - - sourceSets { - main { - manifest { - srcFile 'gradle_AndroidManifest.xml' - } - - java { - srcDir "." - } - } - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:19.1.+' -}