Change the way the AndroidBuilder is created and used.
Xavier Ducrohet [Thu, 23 Aug 2012 00:34:00 +0000 (17:34 -0700)]
The previous way relied on all tasks of a given
assemble<variant> to happen with no other task
(from another assemble<variant>) to happen in the
middle due to the single AndroidBuilder stored in the
plugin.

This changes this in the following way:
- There is now a map of (variant, builder).
- The configureVariant task is gone and the builder is done
  on demand
- The ApplcationVariant classes are reponsible for creating
  the builder since they own the data needed to initialize it.
- Each task receives both the AndroidBasePlugin and the variant
  in order to ask the variant to create the builder if the base
  plugin doesn't already have one.

Additionally this does the following:
- Removes the default value of the compilation target
- Fixes the runtimeJar given to the compiler. This
  queries the AndroidBuilder which replies with a list of jars
  which is then joined in a colon separated string. This
  is done in the doFirst of the compile task so that this is
  only done when the compile task runs.
- Fixes an issue exporting non signed apks.

Finally this refactors the plugin a bit moving stuff into
the AndroidBasePlugin (now a class) so that the library plugin
can use it later when I get to it.

Change-Id: I5ad05395b8cc7ad9a2aa7242ae635dca5579f8d4

23 files changed:
builder/src/main/java/com/android/builder/AndroidBuilder.java
builder/src/main/java/com/android/builder/BuildType.java
builder/src/main/java/com/android/builder/DefaultSdkParser.java
builder/src/main/java/com/android/builder/ProductFlavor.java
builder/src/main/java/com/android/builder/packaging/Packager.java
gradle/src/main/groovy/com/android/build/gradle/AndroidBasePlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidBuilderProvider.groovy [deleted file]
gradle/src/main/groovy/com/android/build/gradle/AndroidExtension.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidLibraryPlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidPlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/BaseAndroidTask.groovy
gradle/src/main/groovy/com/android/build/gradle/ConfigureVariant.groovy [deleted file]
gradle/src/main/groovy/com/android/build/gradle/CrunchResources.groovy
gradle/src/main/groovy/com/android/build/gradle/Dex.groovy
gradle/src/main/groovy/com/android/build/gradle/GenerateBuildConfigTask.groovy
gradle/src/main/groovy/com/android/build/gradle/MergeManifest.groovy
gradle/src/main/groovy/com/android/build/gradle/PackageApplication.groovy
gradle/src/main/groovy/com/android/build/gradle/ProcessResources.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/ApplicationVariant.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/ProductionAppVariant.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/TestAppVariant.groovy
testapps/basic/build.gradle
testapps/basic/src/main/java/org/gradle/sample/MainActivity.java

index 636f6f0..4d4e35b 100644 (file)
@@ -59,7 +59,7 @@ import java.util.Set;
  * {@link #generateBuildConfig(String, java.util.List)}
  * {@link #mergeLibraryManifests(File, List, File)}
  * {@link #processResources(String, String, String, String, String, AaptOptions)}
- * {@link #convertBytecode(String, String, DexOptions)}
+ * {@link #convertBytecode(java.util.List, String, DexOptions)}
  * {@link #packageApk(String, String, String, String)}
  *
  * Java compilation is not handled but the builder provides the runtime classpath with
@@ -639,7 +639,7 @@ public class AndroidBuilder {
         }
 
         SigningInfo signingInfo = null;
-        if (mBuildTypeHolder.getBuildType().isDebugSigningKey()) {
+        if (mBuildTypeHolder.getBuildType().isDebugSigned()) {
             try {
                 String storeLocation = DebugKeyHelper.defaultDebugKeyStoreLocation();
                 File storeFile = new File(storeLocation);
index ba10374..de796e4 100644 (file)
@@ -27,7 +27,7 @@ public class BuildType {
     private final String mName;
     private boolean mDebuggable;
     private boolean mDebugJniBuild;
-    private boolean mDebugSigningKey;
+    private boolean mDebugSigned;
     private String mPackageNameSuffix = null;
     private boolean mRunProguard = false;
 
@@ -45,14 +45,14 @@ public class BuildType {
     private void initDebug() {
         mDebuggable = true;
         mDebugJniBuild = true;
-        mDebugSigningKey = true;
+        mDebugSigned = true;
         mZipAlign = false;
     }
 
     private void initRelease() {
         mDebuggable = false;
         mDebugJniBuild = false;
-        mDebugSigningKey = false;
+        mDebugSigned = false;
     }
 
     public String getName() {
@@ -75,12 +75,12 @@ public class BuildType {
         return mDebugJniBuild;
     }
 
-    public void setDebugSigningKey(boolean debugSigningKey) {
-        mDebugSigningKey = debugSigningKey;
+    public void setDebugSigned(boolean debugSigned) {
+        mDebugSigned = debugSigned;
     }
 
-    public boolean isDebugSigningKey() {
-        return mDebugSigningKey;
+    public boolean isDebugSigned() {
+        return mDebugSigned;
     }
 
     public void setPackageNameSuffix(@Nullable String packageNameSuffix) {
@@ -108,7 +108,6 @@ public class BuildType {
     }
 
     /*
-proguard enabled + rules
 Buildconfig: DEBUG flag + other custom properties?
      */
 }
index 8c118ce..f8ef418 100644 (file)
@@ -30,6 +30,7 @@ import java.io.File;
 public class DefaultSdkParser implements SdkParser {
 
     private final String mSdkLocation;
+    private SdkManager mManager;
 
     public DefaultSdkParser(@NonNull String sdkLocation) {
         if (!sdkLocation.endsWith(File.separator)) {
@@ -41,12 +42,14 @@ public class DefaultSdkParser implements SdkParser {
 
     @Override
     public IAndroidTarget resolveTarget(String target, ILogger logger) {
-        SdkManager manager = SdkManager.createManager(mSdkLocation, logger);
-        if (manager != null) {
-            return manager.getTargetFromHashString(target);
-        } else {
-            throw new RuntimeException("failed to parse SDK!");
+        if (mManager == null) {
+            mManager = SdkManager.createManager(mSdkLocation, logger);
+            if (mManager == null) {
+                throw new RuntimeException("failed to parse SDK!");
+            }
         }
+
+        return mManager.getTargetFromHashString(target);
     }
 
     @Override
index 85aecba..9a825a6 100644 (file)
@@ -29,6 +29,11 @@ public class ProductFlavor {
     private String mTestPackageName = null;
     private String mTestInstrumentationRunner = null;
 
+    private String mSigningStoreLocation = null;
+    private String mSigningStorePassword = null;
+    private String mSigningKeyAlias = null;
+    private String mSigningKeyPassword = null;
+
     public ProductFlavor(String name) {
         mName = name;
     }
@@ -93,6 +98,45 @@ public class ProductFlavor {
         return mTestInstrumentationRunner;
     }
 
+    public String getSigningStoreLocation() {
+        return mSigningStoreLocation;
+    }
+
+    public void setSigningStoreLocation(String signingStoreLocation) {
+        mSigningStoreLocation = signingStoreLocation;
+    }
+
+    public String getSigningStorePassword() {
+        return mSigningStorePassword;
+    }
+
+    public void setSigningStorePassword(String signingStorePassword) {
+        mSigningStorePassword = signingStorePassword;
+    }
+
+    public String getSigningKeyAlias() {
+        return mSigningKeyAlias;
+    }
+
+    public void setSigningKeyAlias(String signingKeyAlias) {
+        mSigningKeyAlias = signingKeyAlias;
+    }
+
+    public String getSigningKeyPassword() {
+        return mSigningKeyPassword;
+    }
+
+    public void setSigningKeyPassword(String signingKeyPassword) {
+        mSigningKeyPassword = signingKeyPassword;
+    }
+
+    public boolean isSigningReady() {
+        return mSigningStoreLocation != null &&
+                mSigningStorePassword != null &&
+                mSigningKeyAlias != null &&
+                mSigningKeyPassword != null;
+    }
+
     /**
      * Merges the flavor on top of a base platform and returns a new object with the result.
      * @param base the flavor to merge on top of
@@ -113,6 +157,13 @@ public class ProductFlavor {
         flavor.mTestInstrumentationRunner = chooseString(mTestInstrumentationRunner,
                 base.mTestInstrumentationRunner);
 
+        flavor.mSigningStoreLocation = chooseString(mSigningStoreLocation,
+                base.mSigningStoreLocation);
+        flavor.mSigningStorePassword = chooseString(mSigningStorePassword,
+                base.mSigningStorePassword);
+        flavor.mSigningKeyAlias = chooseString(mSigningKeyAlias, base.mSigningKeyAlias);
+        flavor.mSigningKeyPassword = chooseString(mSigningKeyPassword, base.mSigningKeyPassword);
+
         return flavor;
     }
 
index d60ee64..ca4219f 100644 (file)
@@ -244,7 +244,8 @@ public final class Packager implements IArchiveBuilder {
 
             mBuilder = new SignedJarBuilder(
                     new FileOutputStream(apkFile, false /* append */),
-                    signingInfo.getKey(), signingInfo.getCertificate());
+                    signingInfo != null ? signingInfo.getKey() : null,
+                    signingInfo != null ? signingInfo.getCertificate() : null);
 
             mLogger.verbose("Packaging %s", apkFile.getName());
 
index 17afcc1..3128f89 100644 (file)
 package com.android.build.gradle
 
 import com.android.builder.AndroidBuilder
-import com.android.builder.ProductFlavorHolder
+import com.android.builder.DefaultSdkParser
 import com.android.builder.SdkParser
 import com.android.utils.ILogger
+import org.gradle.api.Project
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.plugins.JavaBasePlugin
+import com.android.builder.ProductFlavorHolder
+import com.android.builder.BuildTypeHolder
+import com.android.build.gradle.internal.ApplicationVariant
 
 /**
  * Base class for all Android plugins
  */
-interface AndroidBasePlugin {
+abstract class AndroidBasePlugin {
+
+    private final Map<Object, AndroidBuilder> builders = [:]
+
+    protected Project project
+    protected File sdkDir
+    private DefaultSdkParser androidSdkParser
+    private AndroidLogger androidLogger
+
+    abstract String getTarget()
+    abstract ProductFlavorHolder getMainFlavor()
+    abstract BuildTypeHolder getDebugType()
+
+    protected void apply(Project project) {
+        this.project = project
+        project.apply plugin: JavaBasePlugin
+    }
+
+    SdkParser getSdkParser() {
+        if (androidSdkParser == null) {
+            androidSdkParser = new DefaultSdkParser(sdkDir.absolutePath)
+        }
+
+        return androidSdkParser;
+    }
+
+    ILogger getLogger() {
+        if (androidLogger == null) {
+            androidLogger = new AndroidLogger(project.logger)
+        }
+
+        return androidLogger
+    }
+
+    boolean isVerbose() {
+        return project.logger.isEnabled(LogLevel.DEBUG)
+    }
+
+    AndroidBuilder getAndroidBuilder(Object key) {
+        return builders.get(key)
+    }
+
+    void setAndroidBuilder(Object key, AndroidBuilder androidBuilder) {
+        builders.put(key, androidBuilder)
+    }
+
+    protected void findSdk(Project project) {
+        def localProperties = project.file("local.properties")
+        if (localProperties.exists()) {
+            Properties properties = new Properties()
+            localProperties.withInputStream { instr ->
+                properties.load(instr)
+            }
+            def sdkDirProp = properties.getProperty('sdk.dir')
+            if (!sdkDirProp) {
+                throw new RuntimeException("No sdk.dir property defined in local.properties file.")
+            }
+            sdkDir = new File(sdkDirProp)
+        } else {
+            def envVar = System.getenv("ANDROID_HOME")
+            if (envVar != null) {
+                sdkDir = new File(envVar)
+            }
+        }
+
+        if (sdkDir == null) {
+            throw new RuntimeException("SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.")
+        }
 
-    SdkParser getSdkParser()
-    String getTarget()
-    ILogger getLogger()
-    boolean isVerbose()
+        if (!sdkDir.directory) {
+            throw new RuntimeException("The SDK directory '$sdkDir' specified in local.properties does not exist.")
+        }
+    }
 
-    ProductFlavorHolder getMainFlavor()
+    protected String getRuntimeJars(ApplicationVariant variant) {
+        AndroidBuilder androidBuilder = getAndroidBuilder(variant)
 
-    void setAndroidBuilder(AndroidBuilder androidBuilder)
+        return androidBuilder.runtimeClasspath.join(":")
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/AndroidBuilderProvider.groovy b/gradle/src/main/groovy/com/android/build/gradle/AndroidBuilderProvider.groovy
deleted file mode 100644 (file)
index 3b89eb7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2012 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
-
-import com.android.builder.AndroidBuilder
-
-/**
- * Gives access to an {@link AndroidBuilder}
- */
-public interface AndroidBuilderProvider {
-
-    /**
-     * Returns an {@link AndroidBuilder}
-     * @return an {@link AndroidBuilder}
-     */
-    AndroidBuilder getAndroidBuilder()
-}
\ No newline at end of file
index 6b280bb..68dc092 100644 (file)
@@ -25,7 +25,7 @@ import com.android.builder.DexOptions
 class AndroidExtension {
     final NamedDomainObjectContainer<BuildType> buildTypes
     final NamedDomainObjectContainer<ProductFlavor> productFlavors
-    String target = "android-16"
+    String target
     final AaptOptions aaptOptions = new AaptOptions()
     final DexOptions dexOptions = new DexOptions()
 
index b1bcc75..c121391 100644 (file)
 package com.android.build.gradle
 
 import com.android.builder.BuildType
+import com.android.builder.BuildTypeHolder
+import com.android.builder.ProductFlavorHolder
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.plugins.JavaBasePlugin
 
-class AndroidLibraryPlugin implements Plugin<Project> {
-    private Project project
+class AndroidLibraryPlugin extends AndroidBasePlugin implements Plugin<Project> {
 
     @Override
     void apply(Project project) {
-        this.project = project
-
-        project.apply plugin: JavaBasePlugin
+        super.apply(project)
 
         def buildTypes = project.container(BuildType)
 
@@ -49,4 +47,19 @@ class AndroidLibraryPlugin implements Plugin<Project> {
 
         project.tasks.assemble.dependsOn assemble
     }
+
+    @Override
+    String getTarget() {
+        return null  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    ProductFlavorHolder getMainFlavor() {
+        return null  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    BuildTypeHolder getDebugType() {
+        return null  //To change body of implemented methods use File | Settings | File Templates.
+    }
 }
index d2cc280..e82fb1a 100644 (file)
@@ -21,38 +21,29 @@ import com.android.build.gradle.internal.BuildTypeDimension
 import com.android.build.gradle.internal.ProductFlavorDimension
 import com.android.build.gradle.internal.ProductionAppVariant
 import com.android.build.gradle.internal.TestAppVariant
-import com.android.builder.AndroidBuilder
 import com.android.builder.BuildType
-import com.android.builder.DefaultSdkParser
+import com.android.builder.BuildTypeHolder
 import com.android.builder.ProductFlavor
 import com.android.builder.ProductFlavorHolder
-import com.android.builder.SdkParser
-import com.android.utils.ILogger
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.logging.LogLevel
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.compile.Compile
 import org.gradle.internal.reflect.Instantiator
 
-class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilderProvider {
+class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
     private final Set<ProductionAppVariant> variants = []
     private final Map<String, BuildTypeDimension> buildTypes = [:]
     private final Map<String, ProductFlavorDimension> productFlavors = [:]
-    private Project project
     private SourceSet main
     private SourceSet test
-    private File sdkDir
-    private DefaultSdkParser androidSdkParser
-    private AndroidBuilder androidBuilder;
-    private AndroidLogger androidLogger
     private AndroidExtension extension
     private AndroidManifest mainManifest
 
     @Override
     void apply(Project project) {
-        this.project = project
+        super.apply(project)
 
         project.apply plugin: JavaBasePlugin
 
@@ -91,14 +82,6 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         project.tasks.assemble.dependsOn { variants.collect { "assemble${it.name}" } }
     }
 
-    private File getRuntimeJar() {
-        def platformDir = new File(sdkDir, "platforms/${extension.target}")
-        if (!platformDir.exists()) {
-            throw new RuntimeException("Specified target '$extension.target' does not exist.")
-        }
-        new File(platformDir, "android.jar")
-    }
-
     private AndroidManifest getMainManifest() {
         if (mainManifest == null) {
             mainManifest = new AndroidManifest()
@@ -107,34 +90,6 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         return mainManifest
     }
 
-    private void findSdk(Project project) {
-        def localProperties = project.file("local.properties")
-        if (localProperties.exists()) {
-            Properties properties = new Properties()
-            localProperties.withInputStream { instr ->
-                properties.load(instr)
-            }
-            def sdkDirProp = properties.getProperty('sdk.dir')
-            if (!sdkDirProp) {
-                throw new RuntimeException("No sdk.dir property defined in local.properties file.")
-            }
-            sdkDir = new File(sdkDirProp)
-        } else {
-            def envVar = System.getenv("ANDROID_HOME")
-            if (envVar != null) {
-                sdkDir = new File(envVar)
-            }
-        }
-
-        if (sdkDir == null) {
-            throw new RuntimeException("SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.")
-        }
-
-        if (!sdkDir.directory) {
-            throw new RuntimeException("The SDK directory '$sdkDir' specified in local.properties does not exist.")
-        }
-    }
-
     private void addBuildType(BuildType buildType) {
         if (buildType.name.startsWith("test")) {
             throw new RuntimeException("BuildType names cannot start with 'test'")
@@ -188,30 +143,25 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
 
         assert productFlavorDimension.debugVariant != null
 
+        def testAppVariant = new TestAppVariant(productFlavorDimension)
         def flavorName = productFlavor.name.capitalize()
 
-        def configureTask = project.tasks.add("configure${flavorName}Test", ConfigureVariant)
-        configureTask.plugin = this
-        // TODO: hmm?
-        configureTask.productFlavor = productFlavors.get(productFlavor.name)
-        configureTask.buildType = buildTypes.get(BuildType.DEBUG)
-
         // Add a task to generate the test app manifest
         def generateManifestTask = project.tasks.add("generate${flavorName}TestManifest", GenerateTestManifest)
-        generateManifestTask.dependsOn configureTask
         generateManifestTask.conventionMapping.outputFile = { project.file("$project.buildDir/manifests/test/$flavorName/AndroidManifest.xml") }
         generateManifestTask.conventionMapping.packageName = { getMainManifest().packageName + '.test' }
 
         // Add a task to crunch resource files
         def crunchTask = project.tasks.add("crunch${flavorName}TestResources", CrunchResources)
-        crunchTask.dependsOn configureTask
-        crunchTask.provider = this
+        crunchTask.plugin = this
+        crunchTask.variant = testAppVariant
         crunchTask.conventionMapping.outputDir = { project.file("$project.buildDir/resources/test/$flavorName") }
 
         // Add a task to generate resource package
         def processResources = project.tasks.add("process${flavorName}TestResources", ProcessResources)
         processResources.dependsOn generateManifestTask, crunchTask
-        processResources.provider = this
+        processResources.plugin = this
+        processResources.variant = testAppVariant
         processResources.conventionMapping.manifestFile = { generateManifestTask.outputFile }
         processResources.conventionMapping.crunchDir = { crunchTask.outputDir }
         // TODO: unify with generateManifestTask somehow?
@@ -225,19 +175,21 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         testCompile.source test.java, productFlavorDimension.testSource.java
         testCompile.classpath = test.compileClasspath + productFlavorDimension.debugVariant.runtimeClasspath
         testCompile.conventionMapping.destinationDir = { project.file("$project.buildDir/classes/test/$flavorName") }
-        testCompile.options.bootClasspath = getRuntimeJar()
+        testCompile.doFirst {
+            testCompile.options.bootClasspath = getRuntimeJars(testAppVariant)
+        }
+
 
-        def testApp = new TestAppVariant(productFlavor)
-        testApp.runtimeClasspath = testCompile.outputs.files + testCompile.classpath
-        testApp.resourcePackage = project.files({processResources.packageFile}) { builtBy processResources }
-        testApp.compileTask = testCompile
-        addPackageTasks(testApp)
+        testAppVariant.runtimeClasspath = testCompile.outputs.files + testCompile.classpath
+        testAppVariant.resourcePackage = project.files({processResources.packageFile}) { builtBy processResources }
+        testAppVariant.compileTask = testCompile
+        addPackageTasks(testAppVariant)
 
-        project.tasks.check.dependsOn "assemble${testApp.name}"
+        project.tasks.check.dependsOn "assemble${testAppVariant.name}"
     }
 
     private void addVariant(BuildTypeDimension buildType, ProductFlavorDimension productFlavor) {
-        def variant = new ProductionAppVariant(buildType.buildType, productFlavor.productFlavor)
+        def variant = new ProductionAppVariant(buildType, productFlavor)
         variants << variant
         buildType.variants << variant
         productFlavor.variants << variant
@@ -245,34 +197,29 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
             productFlavor.debugVariant = variant
         }
 
-        // add the base configure task
-        def configureTask = project.tasks.add("configure${variant.name}", ConfigureVariant)
-        configureTask.plugin = this
-        configureTask.productFlavor = productFlavors.get(productFlavor.name)
-        configureTask.buildType = buildTypes.get(buildType.name)
-
         // Add a task to merge the manifest(s)
         def mergeManifestTask = project.tasks.add("merge${variant.name}Manifest", MergeManifest)
-        mergeManifestTask.dependsOn configureTask
-        mergeManifestTask.provider = this
+        mergeManifestTask.plugin = this
+        mergeManifestTask.variant = variant
         mergeManifestTask.conventionMapping.mergedManifest = { project.file("$project.buildDir/manifests/$variant.dirName/AndroidManifest.xml") }
 
         // Add a task to crunch resource files
         def crunchTask = project.tasks.add("crunch${variant.name}Resources", CrunchResources)
-        crunchTask.dependsOn configureTask
-        crunchTask.provider = this
+        crunchTask.plugin = this
+        crunchTask.variant = variant
         crunchTask.conventionMapping.outputDir = { project.file("$project.buildDir/resources/$variant.dirName/res") }
 
         // Add a task to create the BuildConfig class
         def generateBuildConfigTask = project.tasks.add("generate${variant.name}BuildConfig", GenerateBuildConfigTask)
-        generateBuildConfigTask.dependsOn configureTask
-        generateBuildConfigTask.provider = this
+        generateBuildConfigTask.plugin = this
+        generateBuildConfigTask.variant = variant
         generateBuildConfigTask.conventionMapping.sourceOutputDir = { project.file("$project.buildDir/source/${variant.dirName}") }
 
         // Add a task to generate resource source files
         def processResources = project.tasks.add("process${variant.name}Resources", ProcessResources)
         processResources.dependsOn mergeManifestTask, crunchTask
-        processResources.provider = this
+        processResources.plugin = this
+        processResources.variant = variant
         processResources.conventionMapping.manifestFile = { mergeManifestTask.mergedManifest }
         processResources.conventionMapping.crunchDir = { crunchTask.outputDir }
         // TODO: unify with generateBuilderConfig somehow?
@@ -290,7 +237,9 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         compileTask.source main.java, buildType.mainSource.java, productFlavor.mainSource.java, { processResources.sourceOutputDir }
         compileTask.classpath = main.compileClasspath
         compileTask.conventionMapping.destinationDir = { project.file("$project.buildDir/classes/$variant.dirName") }
-        compileTask.options.bootClasspath = getRuntimeJar()
+        compileTask.doFirst {
+            compileTask.options.bootClasspath = getRuntimeJars(variant)
+        }
 
         // Wire up the outputs
         variant.runtimeClasspath = project.files(compileTask.outputs, main.compileClasspath)
@@ -305,7 +254,8 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         def dexTaskName = "dex${variant.name}"
         def dexTask = project.tasks.add(dexTaskName, Dex)
         dexTask.dependsOn variant.compileTask
-        dexTask.provider = this
+        dexTask.plugin = this
+        dexTask.variant = variant
         dexTask.conventionMapping.sourceFiles = { variant.runtimeClasspath }
         dexTask.conventionMapping.outputFile = { project.file("${project.buildDir}/libs/${project.archivesBaseName}-${variant.baseName}.dex") }
         dexTask.dexOptions = extension.dexOptions
@@ -313,14 +263,23 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
         // Add a task to generate application package
         def packageApp = project.tasks.add("package${variant.name}", PackageApplication)
         packageApp.dependsOn variant.resourcePackage, dexTask
-        packageApp.provider = this
-        packageApp.conventionMapping.outputFile = { project.file("$project.buildDir/apk/${project.archivesBaseName}-${variant.baseName}-unaligned.apk") }
+        packageApp.plugin = this
+        packageApp.variant = variant
+
+        def signedApk = variant.isSigned()
+
+        def apkName = signedApk ?
+            "${project.archivesBaseName}-${variant.baseName}-unaligned.apk" :
+            "${project.archivesBaseName}-${variant.baseName}-unsigned.apk"
+
+        logger.info(apkName)
+        packageApp.conventionMapping.outputFile = { project.file("$project.buildDir/apk/${apkName}") }
         packageApp.conventionMapping.resourceFile = { variant.resourcePackage.singleFile }
         packageApp.conventionMapping.dexFile = { dexTask.outputFile }
 
         def appTask = packageApp
 
-        if (variant.zipAlign) {
+        if (signedApk && variant.zipAlign) {
             // Add a task to zip align application package
             def alignApp = project.tasks.add("zipalign${variant.name}", ZipAlign)
             alignApp.dependsOn packageApp
@@ -345,45 +304,17 @@ class AndroidPlugin implements Plugin<Project>, AndroidBasePlugin, AndroidBuilde
     }
 
     @Override
-    SdkParser getSdkParser() {
-        if (androidSdkParser == null) {
-            androidSdkParser = new DefaultSdkParser(sdkDir.absolutePath)
-        }
-
-        return androidSdkParser;
-    }
-
-    @Override
     String getTarget() {
         return extension.target;
     }
 
     @Override
-    ILogger getLogger() {
-        if (androidLogger == null) {
-            androidLogger = new AndroidLogger(project.logger)
-        }
-
-        return androidLogger
-    }
-
-    @Override
-    boolean isVerbose() {
-        return project.logger.isEnabled(LogLevel.DEBUG)
-    }
-
-    @Override
-    void setAndroidBuilder(AndroidBuilder androidBuilder) {
-        this.androidBuilder = androidBuilder
-    }
-
-    @Override
     ProductFlavorHolder getMainFlavor() {
         return productFlavors.get('main')
     }
 
     @Override
-    AndroidBuilder getAndroidBuilder() {
-        return androidBuilder
+    BuildTypeHolder getDebugType() {
+        return buildTypes.get(BuildType.DEBUG)
     }
 }
index 9f62a39..fd5a585 100644 (file)
  */
 package com.android.build.gradle
 
+import com.android.build.gradle.internal.ApplicationVariant
+import com.android.builder.AndroidBuilder
 import org.gradle.api.DefaultTask
 
 abstract class BaseAndroidTask extends DefaultTask {
 
-    AndroidBuilderProvider provider
+    AndroidBasePlugin plugin
+    ApplicationVariant variant
+
+    protected AndroidBuilder getBuilder() {
+        AndroidBuilder androidBuilder = plugin.getAndroidBuilder(variant);
+
+        if (androidBuilder == null) {
+            androidBuilder = variant.createBuilder(plugin)
+            plugin.setAndroidBuilder(variant, androidBuilder)
+        }
+
+        return androidBuilder
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/ConfigureVariant.groovy b/gradle/src/main/groovy/com/android/build/gradle/ConfigureVariant.groovy
deleted file mode 100644 (file)
index 1c913da..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2012 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
-
-import com.android.builder.AndroidBuilder
-import com.android.builder.BuildTypeHolder
-import com.android.builder.ProductFlavorHolder
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-
-/**
- */
-class ConfigureVariant extends DefaultTask {
-    AndroidBasePlugin plugin
-
-    @Input
-    BuildTypeHolder buildType
-
-    @Input @Optional
-    ProductFlavorHolder productFlavor
-
-    @TaskAction
-    void generate() {
-        AndroidBuilder androidBuilder = new AndroidBuilder(plugin.sdkParser, plugin.logger, plugin.verbose)
-
-        androidBuilder.setTarget(plugin.target)
-
-        androidBuilder.setBuildVariant(plugin.mainFlavor, buildType)
-
-        if (productFlavor != null) {
-            androidBuilder.addProductFlavor(productFlavor)
-        }
-
-        plugin.setAndroidBuilder(androidBuilder)
-    }
-}
index ca4a518..0d24df6 100644 (file)
@@ -24,6 +24,6 @@ class CrunchResources extends BaseAndroidTask {
 
     @TaskAction
     void generate() {
-        provider.androidBuilder.preprocessResources(getOutputDir().absolutePath)
+        getBuilder().preprocessResources(getOutputDir().absolutePath)
     }
 }
index 3ee28c5..ba1560d 100644 (file)
@@ -40,6 +40,6 @@ class Dex extends BaseAndroidTask {
             }
         }
 
-        provider.androidBuilder.convertBytecode(files, getOutputFile().absolutePath, dexOptions)
+        getBuilder().convertBytecode(files, getOutputFile().absolutePath, dexOptions)
     }
 }
index 3239c3c..1913e78 100644 (file)
@@ -30,7 +30,7 @@ class GenerateBuildConfigTask extends BaseAndroidTask {
 
     @TaskAction
     void generate() {
-        provider.androidBuilder.generateBuildConfig(getSourceOutputDir().absolutePath,
+        getBuilder().generateBuildConfig(getSourceOutputDir().absolutePath,
                 optionalJavaLines);
     }
 }
index 8c4fbc8..ce19a39 100644 (file)
@@ -27,6 +27,6 @@ class MergeManifest extends BaseAndroidTask {
 
     @TaskAction
     void generate() {
-        provider.androidBuilder.mergeManifest(getMergedManifest().absolutePath)
+        getBuilder().mergeManifest(getMergedManifest().absolutePath)
     }
 }
index 421ed5b..a3c1b0a 100644 (file)
@@ -39,7 +39,7 @@ class PackageApplication extends BaseAndroidTask {
     void generate() {
 
         try {
-            provider.androidBuilder.packageApk(
+            getBuilder().packageApk(
                     getResourceFile().absolutePath,
                     getDexFile().absolutePath,
                     getJniDir()?.absolutePath,
index 547a6b6..e2c432a 100644 (file)
@@ -47,7 +47,7 @@ class ProcessResources extends BaseAndroidTask {
     @TaskAction
     void generate() {
 
-        provider.androidBuilder.processResources(
+        getBuilder().processResources(
                 getManifestFile().absolutePath,
                 getCrunchDir()?.absolutePath,
                 getSourceOutputDir()?.absolutePath,
index 11374bf..da39b7e 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.android.build.gradle.internal
 
+import com.android.build.gradle.AndroidBasePlugin
+import com.android.builder.AndroidBuilder
 import org.gradle.api.file.FileCollection
 import org.gradle.api.tasks.compile.Compile
 
@@ -32,9 +34,13 @@ public interface ApplicationVariant {
 
     boolean getZipAlign()
 
+    boolean isSigned()
+
     FileCollection getRuntimeClasspath()
 
     FileCollection getResourcePackage()
 
     Compile getCompileTask()
+
+    AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin)
 }
index f427f90..043a94a 100644 (file)
  */
 package com.android.build.gradle.internal
 
-import com.android.builder.BuildType
-import com.android.builder.ProductFlavor
+import com.android.build.gradle.AndroidBasePlugin
+import com.android.builder.AndroidBuilder
+import com.android.builder.BuildTypeHolder
+import com.android.builder.ProductFlavorHolder
 import org.gradle.api.file.FileCollection
 import org.gradle.api.tasks.compile.Compile
 
 class ProductionAppVariant implements ApplicationVariant {
     final String name
-    final BuildType buildType
-    final ProductFlavor productFlavor
+    final BuildTypeHolder buildTypeHolder
+    final ProductFlavorHolder productFlavorHolder
     FileCollection runtimeClasspath
     FileCollection resourcePackage
     Compile compileTask
 
-    ProductionAppVariant(BuildType buildType, ProductFlavor productFlavor) {
-        this.name = "${productFlavor.name.capitalize()}${buildType.name.capitalize()}"
-        this.buildType = buildType
-        this.productFlavor = productFlavor
+    ProductionAppVariant(BuildTypeHolder buildTypeHolder, ProductFlavorHolder productFlavorHolder) {
+        this.name = "${productFlavorHolder.productFlavor.name.capitalize()}${buildTypeHolder.buildType.name.capitalize()}"
+        this.buildTypeHolder = buildTypeHolder
+        this.productFlavorHolder = productFlavorHolder
     }
 
     String getDescription() {
-        return "$productFlavor.name $buildType.name"
+        return "$productFlavorHolder.productFlavor.name $buildTypeHolder.buildType.name"
     }
 
     String getDirName() {
-        return "$productFlavor.name/$buildType.name"
+        return "$productFlavorHolder.productFlavor.name/$buildTypeHolder.buildType.name"
     }
 
     String getBaseName() {
-        return "$productFlavor.name-$buildType.name"
+        return "$productFlavorHolder.productFlavor.name-$buildTypeHolder.buildType.name"
     }
 
     @Override
     boolean getZipAlign() {
-        return buildType.zipAlign
+        return buildTypeHolder.buildType.zipAlign
+    }
+
+    @Override
+    boolean isSigned() {
+        return buildTypeHolder.buildType.debugSigned ||
+                productFlavorHolder.productFlavor.isSigningReady()
+    }
+
+    @Override
+    AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin) {
+        AndroidBuilder androidBuilder = new AndroidBuilder(
+                androidBasePlugin.sdkParser,
+                androidBasePlugin.logger,
+                androidBasePlugin.verbose)
+
+        androidBuilder.setTarget(androidBasePlugin.target)
+
+        androidBuilder.setBuildVariant(androidBasePlugin.mainFlavor, buildTypeHolder)
+
+        if (productFlavorHolder != null) {
+            androidBuilder.addProductFlavor(productFlavorHolder)
+        }
+
+        return androidBuilder
     }
 }
index 3bbde1e..bb5ebfb 100644 (file)
  */
 package com.android.build.gradle.internal
 
-import com.android.builder.ProductFlavor
+import com.android.build.gradle.AndroidBasePlugin
+import com.android.builder.AndroidBuilder
+import com.android.builder.ProductFlavorHolder
 import org.gradle.api.file.FileCollection
 import org.gradle.api.tasks.compile.Compile
 
 class TestAppVariant implements ApplicationVariant {
     final String name
-    final ProductFlavor productFlavor
+    final ProductFlavorHolder productFlavorHolder
     FileCollection runtimeClasspath
     FileCollection resourcePackage
     Compile compileTask
 
-    TestAppVariant(ProductFlavor productFlavor) {
-        this.name = "${productFlavor.name.capitalize()}Test"
-        this.productFlavor = productFlavor
+    TestAppVariant(ProductFlavorHolder productFlavorHolder) {
+        this.name = "${productFlavorHolder.productFlavor.name.capitalize()}Test"
+        this.productFlavorHolder = productFlavorHolder
     }
 
     @Override
     String getDescription() {
-        return "$productFlavor.name test"
+        return "$productFlavorHolder.productFlavor.name test"
     }
 
     String getDirName() {
-        return "${productFlavor.name}/test"
+        return "${productFlavorHolder.productFlavor.name}/test"
     }
 
     String getBaseName() {
-        return "$productFlavor.name-test"
+        return "$productFlavorHolder.productFlavor.name-test"
     }
 
     @Override
     boolean getZipAlign() {
         return false
     }
+
+    @Override
+    boolean isSigned() {
+        return true;
+    }
+
+    @Override
+    AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin) {
+        AndroidBuilder androidBuilder = new AndroidBuilder(
+                androidBasePlugin.sdkParser,
+                androidBasePlugin.logger,
+                androidBasePlugin.verbose)
+
+        androidBuilder.setTarget(androidBasePlugin.target)
+
+        androidBuilder.setBuildVariant(androidBasePlugin.mainFlavor,
+                androidBasePlugin.debugType)
+
+        // FIXME: I'm not sure this is what is needed for test apps
+        if (productFlavorHolder != null) {
+            androidBuilder.addProductFlavor(productFlavorHolder)
+        }
+
+        return androidBuilder
+    }
+
 }
index 8ea3adf..3e27ae4 100644 (file)
@@ -19,4 +19,8 @@ repositories {
 
 dependencies {
     compile 'com.google.guava:guava:11.0.2'
+}
+
+android {
+    target = "android-15"
 }
\ No newline at end of file
index 242c173..b6b2fb7 100644 (file)
@@ -5,6 +5,8 @@ import android.view.View;
 import android.content.Intent;
 import android.os.Bundle;
 
+import android.annotation.TargetApi;
+
 public class MainActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -12,6 +14,7 @@ public class MainActivity extends Activity {
         setContentView(R.layout.main);
     }
 
+    @TargetApi(10)
     public void sendMessage(View view) {
         Intent intent = new Intent(this, ShowPeopleActivity.class);
         startActivity(intent);