From d3cc5cac69f3e2229a353554f6f50d969610ce3d Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 12 Nov 2013 09:36:20 -0800 Subject: NDK Integration first pass. Change-Id: Ib34a975c5bbbc559fc20f3b582566e7e3d69044b --- .../android/build/gradle/AutomatedBuildTest.java | 6 +- .../groovy/com/android/build/gradle/BuildTest.java | 8 +- .../com/android/build/gradle/ManualBuildTest.java | 16 +- .../com/android/build/gradle/AppPlugin.groovy | 3 + .../com/android/build/gradle/BaseExtension.groovy | 2 +- .../com/android/build/gradle/BasePlugin.groovy | 92 +++++++++- .../android/build/gradle/LibraryExtension.groovy | 7 +- .../com/android/build/gradle/LibraryPlugin.groovy | 7 +- .../com/android/build/gradle/internal/Sdk.groovy | 21 ++- .../internal/api/ApplicationVariantImpl.java | 2 +- .../gradle/internal/api/LibraryVariantImpl.java | 2 +- .../build/gradle/internal/api/TestVariantImpl.java | 2 +- .../build/gradle/internal/dsl/BuildTypeDsl.groovy | 17 +- .../gradle/internal/dsl/BuildTypeFactory.java | 2 +- .../internal/dsl/GroupableProductFlavorDsl.groovy | 8 +- .../dsl/GroupableProductFlavorFactory.java | 3 +- .../build/gradle/internal/dsl/NdkConfigDsl.java | 142 +++++++++++++++ .../gradle/internal/dsl/ProductFlavorDsl.groovy | 43 +++-- .../gradle/internal/model/ProductFlavorImpl.java | 1 - .../gradle/internal/variant/BaseVariantData.java | 4 +- .../android/build/gradle/tasks/NdkCompile.groovy | 199 +++++++++++++++++++++ .../build/gradle/tasks/PackageApplication.groovy | 22 ++- .../gradle/internal/dsl/BuildTypeDslTest.groovy | 4 +- .../build/gradle/internal/test/BaseTest.groovy | 31 +++- 24 files changed, 582 insertions(+), 62 deletions(-) create mode 100644 gradle/src/main/groovy/com/android/build/gradle/internal/dsl/NdkConfigDsl.java create mode 100644 gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy (limited to 'gradle/src') diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java index 3167459..e06ae21 100644 --- a/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java +++ b/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java @@ -37,9 +37,9 @@ public class AutomatedBuildTest extends BuildTest { "aidl", "api", "applibtest", "assets", "attrOrder", "basic", "dependencies", "dependencyChecker", "flavored", "flavorlib", "flavors", "libProguardJarDep", "libProguardLibDep", "libTestDep", "libsTest", "localJars", "migrated", "multiproject", - "multires", "overlay1", "overlay2", "pkgOverride", "proguard", "proguardLib", - "renderscript", "renderscriptInLib", "renderscriptMultiSrc", "sameNamedLibs", - "tictactoe" /*, "autorepo"*/ + "multires", "ndkSanAngeles", "overlay1", "overlay2", "pkgOverride", "proguard", + "proguardLib", "renderscript", "renderscriptInLib", "renderscriptMultiSrc", + "sameNamedLibs", "tictactoe" /*, "autorepo"*/ }; private static final String[] sReportProjects = new String[] { diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java index 092d8eb..c81d0db 100644 --- a/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java +++ b/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java @@ -33,11 +33,13 @@ abstract class BuildTest extends BaseTest { protected File testDir; protected File sdkDir; + protected File ndkDir; @Override protected void setUp() throws Exception { testDir = getTestDir(); sdkDir = getSdkDir(); + ndkDir = getNdkDir(); } /** @@ -59,12 +61,10 @@ abstract class BuildTest extends BaseTest { File project = new File(testDir, name); File buildGradle = new File(project, "build.gradle"); - if (!buildGradle.isFile()) { - return null; - } + assertTrue("Missing build.gradle for " + name, buildGradle.isFile()); // build the project - runGradleTasks(sdkDir, gradleVersion, project, tasks); + runGradleTasks(sdkDir, ndkDir, gradleVersion, project, tasks); return project; } diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java index af0bd32..423107a 100644 --- a/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java +++ b/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java @@ -19,10 +19,10 @@ package com.android.build.gradle; import com.google.common.base.Charsets; import com.google.common.io.Files; +import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import javax.imageio.ImageIO; /** * Some manual tests for building projects. @@ -55,13 +55,13 @@ public class ManualBuildTest extends BuildTest { File repo = new File(testDir, "repo"); try { - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, new File(repo, "util"), "clean", "uploadArchives"); - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, new File(repo, "baseLibrary"), "clean", "uploadArchives"); - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, new File(repo, "library"), "clean", "uploadArchives"); - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, new File(repo, "app"), "clean", "assemble"); } finally { // clean up the test repository. @@ -75,7 +75,7 @@ public class ManualBuildTest extends BuildTest { File project = new File(testDir, "libProguard"); File fileOutput = new File(project, "build/proguard/release"); - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, project, "clean", "build"); checkFile(fileOutput, "mapping.txt", new String[]{"int proguardInt -> a"}); @@ -87,7 +87,7 @@ public class ManualBuildTest extends BuildTest { File debugFileOutput = new File(project, "build/bundles/debug"); File releaseFileOutput = new File(project, "build/bundles/release"); - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, project, "clean", "build"); checkFile(debugFileOutput, "proguard.txt", new String[]{"A"}); checkFile(releaseFileOutput, "proguard.txt", new String[]{"A", "B", "C"}); @@ -97,7 +97,7 @@ public class ManualBuildTest extends BuildTest { // custom because we want to run deviceCheck even without devices, since we use // a fake DeviceProvider that doesn't use a device, but only record the calls made // to the DeviceProvider and the DeviceConnector. - runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION, + runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION, new File(testDir, "3rdPartyTests"), "clean", "deviceCheck"); } diff --git a/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy index 19b6de7..7367c00 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy @@ -561,6 +561,9 @@ class AppPlugin extends com.android.build.gradle.BasePlugin implements Plugin sourceList = new ArrayList(); + List sourceList = Lists.newArrayList() sourceList.add(((AndroidSourceSet) config.defaultSourceSet).java) sourceList.add({ variantData.processResourcesTask.sourceOutputDir }) sourceList.add({ variantData.generateBuildConfigTask.sourceOutputDir }) @@ -718,6 +724,69 @@ public abstract class BasePlugin { } } + protected void createNdkTasks(@NonNull BaseVariantData variantData) { + NdkCompile ndkCompile = project.tasks.create( + "compile${variantData.name}Ndk", NdkCompile) + + ndkCompile.plugin = this + ndkCompile.variant = variantData + variantData.ndkCompileTask = ndkCompile + + VariantConfiguration variantConfig = variantData.variantConfiguration + + ndkCompile.conventionMapping.sourceFolders = { + + Set sourceList = Sets.newHashSet() + + sourceList.addAll(((AndroidSourceSet) variantConfig.defaultSourceSet).jni.srcDirs) + + if (variantConfig.getType() != VariantConfiguration.Type.TEST) { + sourceList.addAll(((AndroidSourceSet) variantConfig.buildTypeSourceSet).jni.srcDirs) + } + if (variantConfig.hasFlavors()) { + for (SourceProvider flavorSourceSet : variantConfig.flavorSourceSets) { + sourceList.addAll(((AndroidSourceSet) flavorSourceSet).jni.srcDirs) + } + } + + return sourceList + } + + ndkCompile.conventionMapping.generatedMakefile = { + project.file("$project.buildDir/ndk/$variantData.dirName/Android.mk") + } + + ndkCompile.conventionMapping.ndkConfig = { + + //noinspection GroovyAssignabilityCheck + NdkConfigDsl mergedConfig = new NdkConfigDsl(variantConfig.defaultConfig.ndkConfig) + if (variantConfig.hasFlavors()) { + for (DefaultProductFlavor flavorConfig : variantConfig.flavorConfigs) { + //noinspection GroovyAssignabilityCheck + mergedConfig.append(flavorConfig.ndkConfig) + } + } + if (variantConfig.getType() != VariantConfiguration.Type.TEST) { + //noinspection GroovyAssignabilityCheck + mergedConfig.append(variantConfig.buildType.ndkConfig) + } + + return mergedConfig + } + + ndkCompile.conventionMapping.debuggable = { + variantConfig.buildType.jniDebugBuild + } + + ndkCompile.conventionMapping.objFolder = { + project.file("$project.buildDir/ndk/$variantData.dirName/obj") + } + + ndkCompile.conventionMapping.soFolder = { + project.file("$project.buildDir/ndk/$variantData.dirName/lib") + } + } + /** * Creates the tasks to build the test apk. * @@ -770,6 +839,9 @@ public abstract class BasePlugin { // Add a task to compile the test application createCompileTask(variantData, testedVariantData) + // Add NDK tasks + createNdkTasks(variantData) + addPackageTasks(variantData, null) if (assembleTest != null) { @@ -1166,7 +1238,7 @@ public abstract class BasePlugin { // Add a task to generate application package def packageApp = project.tasks.create("package${variantData.name}", PackageApplication) variantData.packageApplicationTask = packageApp - packageApp.dependsOn variantData.processResourcesTask, dexTask, variantData.processJavaResources + packageApp.dependsOn variantData.processResourcesTask, dexTask, variantData.processJavaResourcesTask, variantData.ndkCompileTask packageApp.plugin = this packageApp.variant = variantData @@ -1179,7 +1251,15 @@ public abstract class BasePlugin { packageApp.conventionMapping.dexFile = { dexTask.outputFile } packageApp.conventionMapping.packagedJars = { config.packagedJars } packageApp.conventionMapping.javaResourceDir = { - getOptionalDir(variantData.processJavaResources.destinationDir) + getOptionalDir(variantData.processJavaResourcesTask.destinationDir) + } + packageApp.conventionMapping.jniFolders = { + // for now only the project's compilation output. + // TODO add dependencies + Collections.singleton(variantData.ndkCompileTask.soFolder) + } + packageApp.conventionMapping.abiFilters = { + variantData.ndkCompileTask.getNdkConfig().abiFilters } packageApp.conventionMapping.jniDebugBuild = { config.buildType.jniDebugBuild } diff --git a/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy b/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy index 6d6015b..e1cf6a3 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy @@ -45,9 +45,12 @@ public class LibraryExtension extends BaseExtension { BuilderConstants.DEBUG) debugSigningConfig.initDebug() - debug = instantiator.newInstance(BuildTypeDsl.class, BuilderConstants.DEBUG, project.fileResolver) + debug = instantiator.newInstance(BuildTypeDsl.class, + BuilderConstants.DEBUG, project.fileResolver, instantiator) debug.init(debugSigningConfig) - release = instantiator.newInstance(BuildTypeDsl.class, BuilderConstants.RELEASE, project.fileResolver) + + release = instantiator.newInstance(BuildTypeDsl.class, + BuilderConstants.RELEASE, project.fileResolver, instantiator) release.init(null) } diff --git a/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy index 9df0ce1..c45a827 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy @@ -232,6 +232,9 @@ public class LibraryPlugin extends BasePlugin implements Plugin { // Add a compile task createCompileTask(variantData, null/*testedVariant*/) + // Add NDK tasks + createNdkTasks(variantData) + // package the aidl files into the bundle folder Sync packageAidl = project.tasks.create("package${variantData.name}Aidl", Sync) // packageAidl from 3 sources. the order is important to make sure the override works well. @@ -281,9 +284,9 @@ public class LibraryPlugin extends BasePlugin implements Plugin { // jar the classes. Jar jar = project.tasks.create("package${buildType.name.capitalize()}Jar", Jar); - jar.dependsOn variantData.javaCompileTask, variantData.processJavaResources + jar.dependsOn variantData.javaCompileTask, variantData.processJavaResourcesTask jar.from(variantData.javaCompileTask.outputs); - jar.from(variantData.processJavaResources.destinationDir) + jar.from(variantData.processJavaResourcesTask.destinationDir) jar.destinationDir = project.file("$project.buildDir/$DIR_BUNDLES/${variantData.dirName}") jar.archiveName = "classes.jar" diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy index 022239c..88c8726 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy @@ -43,6 +43,7 @@ public class Sdk { private boolean isSdkParserInitialized = false private File androidSdkDir + private File androidNdkDir private boolean isPlatformSdk = false private BaseExtension extension @@ -80,7 +81,7 @@ public class Sdk { if (isPlatformSdk) { parser = new PlatformSdkParser(androidSdkDir.absolutePath) } else { - parser = new DefaultSdkParser(androidSdkDir.absolutePath) + parser = new DefaultSdkParser(androidSdkDir.absolutePath, androidNdkDir) } List repositories = parser.repositories @@ -130,11 +131,16 @@ public class Sdk { return theParser } - public File getDirectory() { + public File getSdkDirectory() { checkLocation() return androidSdkDir } + public File getNdkDirectory() { + checkLocation() + return androidNdkDir + } + private void checkLocation() { // don't complain in test mode if (TEST_SDK_DIR != null) { @@ -179,6 +185,12 @@ public class Sdk { "No sdk.dir property defined in local.properties file.") } } + + def ndkDirProp = properties.getProperty('ndk.dir') + if (ndkDirProp != null) { + androidNdkDir = new File(ndkDirProp) + } + } else { String envVar = System.getenv("ANDROID_HOME") if (envVar != null) { @@ -189,6 +201,11 @@ public class Sdk { androidSdkDir = new File(property) } } + + envVar = System.getenv("ANDROID_NDK_HOME") + if (envVar != null) { + androidNdkDir = new File(envVar) + } } } } diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java index efbbad3..ed0afe4 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java @@ -172,7 +172,7 @@ public class ApplicationVariantImpl implements ApplicationVariant { @Override @NonNull public Copy getProcessJavaResources() { - return variantData.processJavaResources; + return variantData.processJavaResourcesTask; } @Override diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java index a4acfd6..7ed4c6b 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java @@ -158,7 +158,7 @@ public class LibraryVariantImpl implements LibraryVariant { @Override @NonNull public Copy getProcessJavaResources() { - return variantData.processJavaResources; + return variantData.processJavaResourcesTask; } @Override diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java index 305c2e5..37eb618 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java @@ -170,7 +170,7 @@ public class TestVariantImpl implements TestVariant { @Override @NonNull public Copy getProcessJavaResources() { - return variantData.processJavaResources; + return variantData.processJavaResourcesTask; } @Override diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy index e8fa660..8a3cf68 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy @@ -19,7 +19,9 @@ import com.android.annotations.NonNull import com.android.builder.BuilderConstants import com.android.builder.DefaultBuildType import com.android.builder.model.SigningConfig +import org.gradle.api.Action import org.gradle.api.internal.file.FileResolver +import org.gradle.internal.reflect.Instantiator /** * DSL overlay to make methods that accept String... work. @@ -30,9 +32,14 @@ public class BuildTypeDsl extends DefaultBuildType implements Serializable { @NonNull private final FileResolver fileResolver - BuildTypeDsl(@NonNull String name, @NonNull FileResolver fileResolver) { + private final NdkConfigDsl ndkConfig + + BuildTypeDsl(@NonNull String name, + @NonNull FileResolver fileResolver, + @NonNull Instantiator instantiator) { super(name) this.fileResolver = fileResolver + ndkConfig = instantiator.newInstance(NdkConfigDsl.class) } public void init(SigningConfig debugSigningConfig) { @@ -56,6 +63,10 @@ public class BuildTypeDsl extends DefaultBuildType implements Serializable { return true } + public NdkConfigDsl getNdkConfig() { + return ndkConfig + } + // -- DSL Methods. TODO remove once the instantiator does what I expect it to do. public void buildConfig(String... lines) { @@ -101,4 +112,8 @@ public class BuildTypeDsl extends DefaultBuildType implements Serializable { } return this; } + + void ndk(Action action) { + action.execute(ndkConfig) + } } diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java index 3bdfe19..486727e 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java @@ -40,6 +40,6 @@ public class BuildTypeFactory implements NamedDomainObjectFactory abiFilters; + + public NdkConfigDsl() { + } + + public NdkConfigDsl(@NonNull NdkConfigDsl ndkConfig) { + moduleName = ndkConfig.moduleName; + cFlags = ndkConfig.cFlags; + ldLibs = ndkConfig.ldLibs; + setSrcDirs(ndkConfig.abiFilters); + } + + @Input @Optional + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + @Input @Optional + public String getcFlags() { + return cFlags; + } + + public void setcFlags(String cFlags) { + this.cFlags = cFlags; + } + + @Input @Optional + public String getLdLibs() { + return ldLibs; + } + + public void setLdLibs(String ldLibs) { + this.ldLibs = ldLibs; + } + + @Input @Optional + public Set getAbiFilters() { + return abiFilters; + } + + @NonNull + public NdkConfigDsl abiFilter(String filter) { + if (abiFilters == null) { + abiFilters = Sets.newHashSetWithExpectedSize(2); + } + abiFilters.add(filter); + return this; + } + + @NonNull + public NdkConfigDsl abiFilters(String... filters) { + if (abiFilters == null) { + abiFilters = Sets.newHashSetWithExpectedSize(2); + } + Collections.addAll(abiFilters, filters); + return this; + } + + @NonNull + public NdkConfigDsl setSrcDirs(Iterable filters) { + if (filters != null) { + if (abiFilters == null) { + abiFilters = Sets.newHashSetWithExpectedSize(2); + } else { + abiFilters.clear(); + } + for (String filter : filters) { + abiFilters.add(filter); + } + } else { + abiFilters = null; + } + return this; + } + + public void append(@NonNull NdkConfigDsl ndkConfig) { + // override + if (ndkConfig.moduleName != null) { + moduleName = ndkConfig.moduleName; + } + if (ndkConfig.abiFilters != null) { + if (abiFilters == null) { + abiFilters = Sets.newHashSetWithExpectedSize(ndkConfig.abiFilters.size()); + } else { + abiFilters.clear(); + } + abiFilters.addAll(ndkConfig.abiFilters); + } + + // append + if (cFlags == null) { + cFlags = ndkConfig.cFlags; + } else if (ndkConfig.cFlags != null) { + cFlags = cFlags + " " + ndkConfig.cFlags; + } + + if (ldLibs == null) { + ldLibs = ndkConfig.ldLibs; + } else if (ndkConfig.ldLibs != null) { + ldLibs = ldLibs + " " + ndkConfig.ldLibs; + } + } +} diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy index 4ae05cb..daeb0ff 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy @@ -17,8 +17,9 @@ package com.android.build.gradle.internal.dsl import com.android.annotations.NonNull import com.android.builder.DefaultProductFlavor +import org.gradle.api.Action import org.gradle.api.internal.file.FileResolver - +import org.gradle.internal.reflect.Instantiator /** * DSL overlay to make methods that accept String... work. */ @@ -28,9 +29,19 @@ class ProductFlavorDsl extends DefaultProductFlavor { @NonNull private final FileResolver fileResolver - ProductFlavorDsl(String name, @NonNull FileResolver fileResolver) { + private final NdkConfigDsl ndkConfig + + ProductFlavorDsl(@NonNull String name, + @NonNull FileResolver fileResolver, + @NonNull Instantiator instantiator) { super(name) this.fileResolver = fileResolver + + ndkConfig = instantiator.newInstance(NdkConfigDsl.class) + } + + public NdkConfigDsl getNdkConfig() { + return ndkConfig } // -- DSL Methods. TODO remove once the instantiator does what I expect it to do. @@ -45,37 +56,41 @@ class ProductFlavorDsl extends DefaultProductFlavor { @NonNull public ProductFlavorDsl proguardFile(Object proguardFile) { - proguardFiles.add(fileResolver.resolve(proguardFile)); - return this; + proguardFiles.add(fileResolver.resolve(proguardFile)) + return this } @NonNull public ProductFlavorDsl proguardFiles(Object... proguardFileArray) { - proguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files); - return this; + proguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files) + return this } @NonNull public ProductFlavorDsl setProguardFiles(Iterable proguardFileIterable) { - proguardFiles.clear(); + proguardFiles.clear() for (Object proguardFile : proguardFileIterable) { - proguardFiles.add(fileResolver.resolve(proguardFile)); + proguardFiles.add(fileResolver.resolve(proguardFile)) } - return this; + return this } @NonNull public ProductFlavorDsl consumerProguardFiles(Object... proguardFileArray) { - consumerProguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files); - return this; + consumerProguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files) + return this } @NonNull public ProductFlavorDsl setconsumerProguardFiles(Iterable proguardFileIterable) { - consumerProguardFiles.clear(); + consumerProguardFiles.clear() for (Object proguardFile : proguardFileIterable) { - consumerProguardFiles.add(fileResolver.resolve(proguardFile)); + consumerProguardFiles.add(fileResolver.resolve(proguardFile)) } - return this; + return this + } + + void ndk(Action action) { + action.execute(ndkConfig) } } diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java index dbd30d6..37e022d 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java @@ -44,7 +44,6 @@ class ProductFlavorImpl implements ProductFlavor, Serializable { private Boolean mTestHandleProfiling = null; private Boolean mTestFunctionalTest = null; - @NonNull static ProductFlavorImpl cloneFlavor(ProductFlavor productFlavor) { ProductFlavorImpl clonedFlavor = new ProductFlavorImpl(); diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java index db804f9..a85d8be 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java @@ -24,6 +24,7 @@ import com.android.build.gradle.tasks.AidlCompile; import com.android.build.gradle.tasks.GenerateBuildConfig; import com.android.build.gradle.tasks.MergeAssets; import com.android.build.gradle.tasks.MergeResources; +import com.android.build.gradle.tasks.NdkCompile; import com.android.build.gradle.tasks.ProcessAndroidResources; import com.android.build.gradle.tasks.ProcessManifest; import com.android.build.gradle.tasks.RenderscriptCompile; @@ -61,7 +62,8 @@ public abstract class BaseVariantData { public JavaCompile javaCompileTask; public ProGuardTask proguardTask; - public Copy processJavaResources; + public Copy processJavaResourcesTask; + public NdkCompile ndkCompileTask; private Object outputFile; diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy new file mode 100644 index 0000000..ab38543 --- /dev/null +++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.tasks + +import com.android.annotations.NonNull +import com.android.build.gradle.internal.dsl.NdkConfigDsl +import com.android.build.gradle.internal.tasks.BaseTask +import com.android.sdklib.IAndroidTarget +import com.google.common.base.Charsets +import com.google.common.base.Joiner +import com.google.common.collect.Lists +import com.google.common.io.Files +import org.gradle.api.GradleException +import org.gradle.api.file.FileTree +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.incremental.IncrementalTaskInputs +import org.gradle.api.tasks.util.PatternSet + +/** + */ +class NdkCompile extends BaseTask { + + Set sourceFolders + + @OutputFile + File generatedMakefile + + @Nested + NdkConfigDsl ndkConfig + + @Input + boolean debuggable + + @OutputDirectory + File soFolder + + @OutputDirectory + File objFolder + + @InputFiles + public FileTree getSource() { + FileTree src = null + Set sources = getSourceFolders() + if (!sources.isEmpty()) { + src = getProject().files(new ArrayList(sources)).getAsFileTree() + } + return src == null ? getProject().files().getAsFileTree() : src + } + + @TaskAction + void taskAction(IncrementalTaskInputs inputs) { + + FileTree sourceFileTree = getSource() + Set sourceFiles = sourceFileTree.matching(new PatternSet().exclude("**/*.h")).files + File makefile = getGeneratedMakefile() + + if (sourceFiles.isEmpty()) { + makefile.delete() + emptyFolder(getSoFolder()) + emptyFolder(getObjFolder()) + return + } + + File ndkDirectory = getPlugin().ndkDirectory + if (ndkDirectory == null || !ndkDirectory.isDirectory()) { + throw new GradleException("NDK not configured") + } + + boolean generateMakefile = false + + if (!inputs.isIncremental()) { + project.logger.info("Unable do incremental execution: full task run") + generateMakefile = true + emptyFolder(getSoFolder()) + emptyFolder(getObjFolder()) + } else { + // look for added or removed files *only* + + //noinspection GroovyAssignabilityCheck + inputs.outOfDate { change -> + if (change.isAdded()) { + generateMakefile = true + } + } + + //noinspection GroovyAssignabilityCheck + inputs.removed { change -> + generateMakefile = true + } + } + + if (generateMakefile) { + writeMakefile(sourceFiles, makefile) + } + + // now build + runNdkBuild(ndkDirectory, makefile) + } + + private void writeMakefile(@NonNull Set sourceFiles, @NonNull File makefile) { + NdkConfigDsl ndk = getNdkConfig() + + StringBuilder sb = new StringBuilder() + + sb.append( + "LOCAL_PATH := \$(call my-dir)\n" + + "include \$(CLEAR_VARS)\n\n") + + sb.append("LOCAL_MODULE := ").append(ndk.moduleName != null ? ndk.moduleName : project.name).append('\n') + + if (ndk.cFlags != null) { + sb.append("LOCAL_CFLAGS := ").append(ndk.cFlags).append('\n') + } + + if (ndk.ldLibs != null) { + sb.append("LOCAL_LDLIBS := ").append(ndk.ldLibs).append('\n') + } + + sb.append("LOCAL_SRC_FILES := \\\n") + for (File sourceFile : sourceFiles) { + sb.append('\t').append(sourceFile.absolutePath).append(" \\\n") + } + sb.append('\n') + + sb.append( + "\ninclude \$(BUILD_SHARED_LIBRARY)\n") + + Files.write(sb.toString(), makefile, Charsets.UTF_8) + } + + private void runNdkBuild(@NonNull File ndkLocation, @NonNull File makefile) { + NdkConfigDsl ndk = getNdkConfig() + + List commands = Lists.newArrayList() + + commands.add(ndkLocation.absolutePath + File.separator + "ndk-build") + + commands.add("NDK_PROJECT_PATH=null") + + commands.add("APP_BUILD_SCRIPT=" + makefile.absolutePath) + + // target + IAndroidTarget target = getPlugin().loadedSdkParser.target + if (!target.isPlatform()) { + target = target.parent + } + commands.add("APP_PLATFORM=" + target.hashString()) + + // include paths + for (File sourceFolder : getSourceFolders()) { + commands.add("-I") + commands.add(sourceFolder.absolutePath) + } + + // temp out + commands.add("NDK_OUT=" + getObjFolder().absolutePath) + + // libs out + commands.add("NDK_LIBS_OUT=" + getSoFolder().absolutePath) + + // debug builds + if (getDebuggable()) { + commands.add("NDK_DEBUG=1") + } + + Set abiFilters = ndk.abiFilters + if (abiFilters != null && !abiFilters.isEmpty()) { + if (abiFilters.size() == 1) { + commands.add("APP_ABI=" + abiFilters.iterator().next()) + } else { + Joiner joiner = Joiner.on(',').skipNulls() + commands.add("APP_ABI=" + joiner.join(abiFilters.iterator())) + } + } else { + commands.add("APP_ABI=all") + } + + getBuilder().commandLineRunner.runCmdLine(commands, null) + } +} diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy index 549d765..fd827c1 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy @@ -19,6 +19,7 @@ import com.android.build.gradle.internal.dsl.SigningConfigDsl import com.android.build.gradle.internal.tasks.IncrementalTask import com.android.build.gradle.internal.tasks.OutputFileTask import com.android.builder.packaging.DuplicateFileException +import org.gradle.api.file.FileTree import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile @@ -41,12 +42,14 @@ public class PackageApplication extends IncrementalTask implements OutputFileTas @InputDirectory @Optional File javaResourceDir - @InputDirectory @Optional - File jniDir + Set jniFolders @OutputFile File outputFile + @Input @Optional + Set abiFilters + // ----- PRIVATE TASK API ----- @InputFiles @@ -58,6 +61,17 @@ public class PackageApplication extends IncrementalTask implements OutputFileTas @Nested @Optional SigningConfigDsl signingConfig + @InputFiles + public FileTree getNativeLibraries() { + FileTree src = null + Set folders = getJniFolders() + if (!folders.isEmpty()) { + src = getProject().files(new ArrayList(folders)).getAsFileTree() + } + return src == null ? getProject().files().getAsFileTree() : src + } + + @Override protected void doFullTaskAction() { try { @@ -66,7 +80,8 @@ public class PackageApplication extends IncrementalTask implements OutputFileTas getDexFile().absolutePath, getPackagedJars(), getJavaResourceDir()?.absolutePath, - getJniDir()?.absolutePath, + getJniFolders(), + getAbiFilters(), getJniDebugBuild(), getSigningConfig(), getOutputFile().absolutePath) @@ -81,5 +96,4 @@ public class PackageApplication extends IncrementalTask implements OutputFileTas throw new BuildException(e.getMessage(), e); } } - } diff --git a/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeDslTest.groovy b/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeDslTest.groovy index 3744b79..c63b2e9 100644 --- a/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeDslTest.groovy +++ b/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeDslTest.groovy @@ -74,7 +74,7 @@ public class BuildTypeDslTest extends BaseTest { Project project = ProjectBuilder.builder().withProjectDir( new File(testDir, "basic")).build() - BuildTypeDsl object1 = new BuildTypeDsl("foo", project.fileResolver) + BuildTypeDsl object1 = new BuildTypeDsl("foo", project.fileResolver, project.instantiator) // change every value from their default. object1.setDebuggable(true) @@ -87,7 +87,7 @@ public class BuildTypeDslTest extends BaseTest { object1.setSigningConfig(new SigningConfigDsl("blah")) object1.setZipAlign(false) - BuildTypeDsl object2 = new BuildTypeDsl(object1.name, project.fileResolver) + BuildTypeDsl object2 = new BuildTypeDsl(object1.name, project.fileResolver, project.instantiator) object2.initWith(object1) assertEquals(object1, object2) diff --git a/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy b/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy index 5a96009..ac62126 100755 --- a/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy +++ b/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy @@ -16,6 +16,7 @@ package com.android.build.gradle.internal.test import com.android.annotations.NonNull +import com.android.annotations.Nullable import com.android.sdklib.internal.project.ProjectProperties import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy import junit.framework.TestCase @@ -100,10 +101,31 @@ public abstract class BaseTest extends TestCase { return null } - protected static File createLocalProp(File project, File sdkDir) { + /** + * Returns the SDK folder as built from the Android source tree. + * @return + */ + protected File getNdkDir() { + String androidHome = System.getenv("ANDROID_NDK_HOME"); + if (androidHome != null) { + File f = new File(androidHome); + if (f.isDirectory()) { + return f; + } else { + System.out.println("Failed to find NDK in ANDROID_NDK_HOME=" + androidHome) + } + } + } + + protected static File createLocalProp(@NonNull File project, + @NonNull File sdkDir, + @Nullable File ndkDir) { ProjectPropertiesWorkingCopy localProp = ProjectProperties.create( project.absolutePath, ProjectProperties.PropertyType.LOCAL) localProp.setProperty(ProjectProperties.PROPERTY_SDK, sdkDir.absolutePath) + if (ndkDir != null) { + localProp.setProperty(ProjectProperties.PROPERTY_NDK, ndkDir.absolutePath) + } localProp.save() return (File) localProp.file @@ -112,14 +134,15 @@ public abstract class BaseTest extends TestCase { protected File runTasksOn(String name, String gradleVersion, String... tasks) { File project = new File(testDir, name) - runGradleTasks(sdkDir, gradleVersion, project, tasks) + runGradleTasks(sdkDir, ndkDir, gradleVersion, project, tasks) return project; } - protected static void runGradleTasks(File sdkDir, String gradleVersion, + protected static void runGradleTasks(File sdkDir, File ndkDir, + String gradleVersion, File project, String... tasks) { - File localProp = createLocalProp(project, sdkDir) + File localProp = createLocalProp(project, sdkDir, ndkDir) try { -- cgit v1.2.3