Support compile config for flavors and build types.
[android/platform/tools/build.git] / gradle / src / main / groovy / com / android / build / gradle / BasePlugin.groovy
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.build.gradle
17
18 import com.android.SdkConstants
19 import com.android.build.gradle.internal.AndroidDependencyImpl
20 import com.android.build.gradle.internal.ApplicationVariant
21 import com.android.build.gradle.internal.ConfigurationDependencies
22 import com.android.build.gradle.internal.ProductFlavorData
23 import com.android.build.gradle.internal.ProductionAppVariant
24 import com.android.build.gradle.internal.TestAppVariant
25 import com.android.builder.AndroidBuilder
26 import com.android.builder.AndroidDependency
27 import com.android.builder.DefaultSdkParser
28 import com.android.builder.JarDependency
29 import com.android.builder.ProductFlavor
30 import com.android.builder.SdkParser
31 import com.android.builder.SourceProvider
32 import com.android.builder.VariantConfiguration
33 import com.android.utils.ILogger
34 import org.gradle.api.DefaultTask
35 import org.gradle.api.GradleException
36 import org.gradle.api.NamedDomainObjectContainer
37 import org.gradle.api.Project
38 import org.gradle.api.Task
39 import org.gradle.api.artifacts.Configuration
40 import org.gradle.api.artifacts.ModuleVersionIdentifier
41 import org.gradle.api.artifacts.ProjectDependency
42 import org.gradle.api.artifacts.ResolvedArtifact
43 import org.gradle.api.artifacts.result.ResolvedDependencyResult
44 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult
45 import org.gradle.api.internal.plugins.ProcessResources
46 import org.gradle.api.logging.LogLevel
47 import org.gradle.api.plugins.JavaBasePlugin
48 import org.gradle.api.plugins.JavaPluginConvention
49 import org.gradle.api.tasks.Copy
50 import org.gradle.api.tasks.compile.JavaCompile
51
52 /**
53  * Base class for all Android plugins
54  */
55 abstract class BasePlugin {
56
57     public final static String INSTALL_GROUP = "Install"
58
59     private final Map<Object, AndroidBuilder> builders = [:]
60
61     final List<ApplicationVariant> variants = []
62
63     protected Project project
64     protected File sdkDir
65     private DefaultSdkParser androidSdkParser
66     private LoggerWrapper loggerWrapper
67
68     private ProductFlavorData defaultConfigData
69     protected AndroidSourceSet mainSourceSet
70     protected AndroidSourceSet testSourceSet
71
72     protected Task uninstallAll
73     protected Task assembleTest
74
75     abstract String getTarget()
76
77     protected void apply(Project project) {
78         this.project = project
79         project.apply plugin: JavaBasePlugin
80
81         project.tasks.assemble.description =
82             "Assembles all variants of all applications and secondary packages."
83
84         findSdk(project)
85
86         uninstallAll = project.tasks.add("uninstallAll")
87         uninstallAll.description = "Uninstall all applications."
88         uninstallAll.group = INSTALL_GROUP
89     }
90
91     protected setDefaultConfig(ProductFlavor defaultConfig,
92                                NamedDomainObjectContainer<AndroidSourceSet> sourceSets) {
93         mainSourceSet = sourceSets.create("main")
94         testSourceSet = sourceSets.create("test")
95
96         defaultConfigData = new ProductFlavorData(defaultConfig, mainSourceSet,
97                 testSourceSet, project)
98     }
99
100     ProductFlavorData getDefaultConfigData() {
101         return defaultConfigData
102     }
103
104     SdkParser getSdkParser() {
105         if (androidSdkParser == null) {
106             androidSdkParser = new DefaultSdkParser(sdkDir.absolutePath)
107         }
108
109         return androidSdkParser;
110     }
111
112     ILogger getLogger() {
113         if (loggerWrapper == null) {
114             loggerWrapper = new LoggerWrapper(project.logger)
115         }
116
117         return loggerWrapper
118     }
119
120     boolean isVerbose() {
121         return project.logger.isEnabled(LogLevel.DEBUG)
122     }
123
124     AndroidBuilder getAndroidBuilder(ApplicationVariant variant) {
125         AndroidBuilder androidBuilder = builders.get(variant)
126
127         if (androidBuilder == null) {
128             androidBuilder = variant.createBuilder(this)
129             builders.put(variant, androidBuilder)
130         }
131
132         return androidBuilder
133     }
134
135     private void findSdk(Project project) {
136         def rootDir = project.rootDir
137         def localProperties = new File(rootDir, SdkConstants.FN_LOCAL_PROPERTIES)
138         if (localProperties.exists()) {
139             Properties properties = new Properties()
140             localProperties.withInputStream { instr ->
141                 properties.load(instr)
142             }
143             def sdkDirProp = properties.getProperty('sdk.dir')
144             if (!sdkDirProp) {
145                 throw new RuntimeException("No sdk.dir property defined in local.properties file.")
146             }
147             sdkDir = new File(sdkDirProp)
148         } else {
149             def envVar = System.getenv("ANDROID_HOME")
150             if (envVar != null) {
151                 sdkDir = new File(envVar)
152             }
153         }
154
155         if (sdkDir == null) {
156             throw new RuntimeException(
157                     "SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.")
158         }
159
160         if (!sdkDir.directory) {
161             throw new RuntimeException(
162                     "The SDK directory '$sdkDir' specified in local.properties does not exist.")
163         }
164     }
165
166     protected String getRuntimeJars(ApplicationVariant variant) {
167         AndroidBuilder androidBuilder = getAndroidBuilder(variant)
168
169         return androidBuilder.runtimeClasspath.join(File.pathSeparator)
170     }
171
172     protected ProcessManifestTask createProcessManifestTask(ApplicationVariant variant,
173                                                         String manifestOurDir) {
174         def processManifestTask = project.tasks.add("process${variant.name}Manifest",
175                 ProcessManifestTask)
176         processManifestTask.plugin = this
177         processManifestTask.variant = variant
178         processManifestTask.configObjects = variant.configObjects
179         processManifestTask.conventionMapping.inputManifests = { variant.config.manifestInputs }
180         processManifestTask.conventionMapping.processedManifest = {
181             project.file(
182                     "$project.buildDir/${manifestOurDir}/$variant.dirName/AndroidManifest.xml")
183         }
184
185         return processManifestTask
186     }
187
188     protected CrunchResourcesTask createCrunchResTask(ApplicationVariant variant) {
189         def crunchTask = project.tasks.add("crunch${variant.name}Res", CrunchResourcesTask)
190         crunchTask.plugin = this
191         crunchTask.variant = variant
192         crunchTask.configObjects = variant.configObjects
193         crunchTask.conventionMapping.resDirectories = { variant.config.resourceInputs }
194         crunchTask.conventionMapping.outputDir = {
195             project.file("$project.buildDir/res/$variant.dirName")
196         }
197
198         return crunchTask
199     }
200
201     protected GenerateBuildConfigTask createBuildConfigTask(ApplicationVariant variant,
202                                                             ProcessManifestTask processManifestTask) {
203         def generateBuildConfigTask = project.tasks.add(
204                 "generate${variant.name}BuildConfig", GenerateBuildConfigTask)
205         if (processManifestTask != null) {
206             // This is in case the manifest is generated
207             generateBuildConfigTask.dependsOn processManifestTask
208         }
209         generateBuildConfigTask.plugin = this
210         generateBuildConfigTask.variant = variant
211         generateBuildConfigTask.configObjects = variant.configObjects
212         generateBuildConfigTask.optionalJavaLines = variant.buildConfigLines
213         generateBuildConfigTask.conventionMapping.sourceOutputDir = {
214             project.file("$project.buildDir/source/${variant.dirName}")
215         }
216         return generateBuildConfigTask
217     }
218
219     protected ProcessResourcesTask createProcessResTask(ApplicationVariant variant,
220                                                     ProcessManifestTask processManifestTask,
221                                                     CrunchResourcesTask crunchTask) {
222         def processResources = project.tasks.add("process${variant.name}Res", ProcessResourcesTask)
223         processResources.dependsOn processManifestTask
224         processResources.plugin = this
225         processResources.variant = variant
226         processResources.configObjects = variant.configObjects
227         processResources.conventionMapping.manifestFile = { processManifestTask.processedManifest }
228         // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
229         processResources.conventionMapping.sourceOutputDir = {
230             project.file("$project.buildDir/source/$variant.dirName")
231         }
232         processResources.conventionMapping.textSymbolDir = {
233             project.file("$project.buildDir/symbols/$variant.dirName")
234         }
235         processResources.conventionMapping.packageFile = {
236             project.file(
237                     "$project.buildDir/libs/${project.archivesBaseName}-${variant.baseName}.ap_")
238         }
239         if (variant.runProguard) {
240             processResources.conventionMapping.proguardFile = {
241                 project.file("$project.buildDir/proguard/${variant.dirName}/rules.txt")
242             }
243         }
244
245         if (crunchTask != null) {
246             processResources.dependsOn crunchTask
247             processResources.conventionMapping.crunchDir = { crunchTask.outputDir }
248             processResources.conventionMapping.resDirectories = { crunchTask.resDirectories }
249         } else {
250             processResources.conventionMapping.resDirectories = { variant.config.resourceInputs }
251         }
252
253         processResources.aaptOptions = extension.aaptOptions
254         return processResources
255     }
256
257     protected void createProcessJavaResTask(ApplicationVariant variant) {
258         VariantConfiguration config = variant.config
259
260         Copy processResources = project.getTasks().add("process${variant.name}JavaRes",
261                 ProcessResources.class);
262
263         // set the input
264         processResources.from(((AndroidSourceSet) config.defaultSourceSet).javaResources)
265
266         if (config.getType() != VariantConfiguration.Type.TEST) {
267             processResources.from(((AndroidSourceSet) config.buildTypeSourceSet).javaResources)
268         }
269         if (config.hasFlavors()) {
270             for (SourceProvider flavorSourceSet : config.flavorSourceSets) {
271                 processResources.from(((AndroidSourceSet) flavorSourceSet).javaResources)
272             }
273         }
274
275         processResources.conventionMapping.destinationDir = {
276             project.file("$project.buildDir/javaResources/$variant.dirName")
277         }
278
279         variant.processJavaResources = processResources
280     }
281
282     protected CompileAidlTask createAidlTask(ApplicationVariant variant) {
283
284         VariantConfiguration config = variant.config
285
286         def compileTask = project.tasks.add("compile${variant.name}Aidl", CompileAidlTask)
287         compileTask.plugin = this
288         compileTask.variant = variant
289         compileTask.configObjects = variant.configObjects
290
291         List<Object> sourceList = new ArrayList<Object>();
292         sourceList.add(config.defaultSourceSet.aidlDir)
293         if (config.getType() != VariantConfiguration.Type.TEST) {
294             sourceList.add(config.buildTypeSourceSet.aidlDir)
295         }
296         if (config.hasFlavors()) {
297             for (SourceProvider flavorSourceSet : config.flavorSourceSets) {
298                 sourceList.add(flavorSourceSet.aidlDir)
299             }
300         }
301
302         compileTask.sourceDirs = sourceList
303         compileTask.importDirs = variant.config.aidlImports
304
305         compileTask.conventionMapping.sourceOutputDir = {
306             project.file("$project.buildDir/source/$variant.dirName")
307         }
308
309         return compileTask
310     }
311
312     protected void createCompileTask(ApplicationVariant variant,
313                                      ApplicationVariant testedVariant,
314                                      ProcessResourcesTask processResources,
315                                      GenerateBuildConfigTask generateBuildConfigTask,
316                                      CompileAidlTask aidlTask) {
317         def compileTask = project.tasks.add("compile${variant.name}", JavaCompile)
318         compileTask.dependsOn processResources, generateBuildConfigTask, aidlTask
319
320         VariantConfiguration config = variant.config
321
322         List<Object> sourceList = new ArrayList<Object>();
323         sourceList.add(((AndroidSourceSet) config.defaultSourceSet).java)
324         sourceList.add({ processResources.sourceOutputDir })
325         if (config.getType() != VariantConfiguration.Type.TEST) {
326             sourceList.add(((AndroidSourceSet) config.buildTypeSourceSet).java)
327         }
328         if (config.hasFlavors()) {
329             for (SourceProvider flavorSourceSet : config.flavorSourceSets) {
330                 sourceList.add(((AndroidSourceSet) flavorSourceSet).java)
331             }
332         }
333         compileTask.source = sourceList.toArray()
334
335         if (testedVariant != null) {
336             compileTask.classpath = project.files({config.compileClasspath}) + testedVariant.compileTask.classpath + testedVariant.compileTask.outputs.files
337         } else {
338             compileTask.classpath = project.files({config.compileClasspath})
339         }
340
341         // TODO - dependency information for the compile classpath is being lost.
342         // Add a temporary approximation
343         compileTask.dependsOn project.configurations.compile.buildDependencies
344
345         compileTask.conventionMapping.destinationDir = {
346             project.file("$project.buildDir/classes/$variant.dirName")
347         }
348         compileTask.conventionMapping.dependencyCacheDir = {
349             project.file("$project.buildDir/dependency-cache/$variant.dirName")
350         }
351
352         // set source/target compatibility
353         // TODO: fix?
354         JavaPluginConvention convention = project.convention.getPlugin(JavaPluginConvention.class);
355
356         compileTask.conventionMapping.sourceCompatibility = {
357             convention.sourceCompatibility.toString()
358         }
359         compileTask.conventionMapping.targetCompatibility = {
360             convention.targetCompatibility.toString()
361         }
362
363         // setup the bootclasspath just before the task actually runs since this will
364         // force the sdk to be parsed.
365         compileTask.doFirst {
366             compileTask.options.bootClasspath = getRuntimeJars(variant)
367         }
368
369         // Wire up the outputs
370         variant.resourcePackage = project.files({processResources.packageFile}) {
371             builtBy processResources
372         }
373         variant.compileTask = compileTask
374     }
375
376     protected void createTestTasks(TestAppVariant variant, ProductionAppVariant testedVariant,
377                                    List<ConfigurationDependencies> configDependencies) {
378
379         def prepareDependenciesTask = createPrepareDependenciesTask(variant, configDependencies)
380
381         // Add a task to process the manifest
382         def processManifestTask = createProcessManifestTask(variant, "manifests")
383         // TODO - move this
384         processManifestTask.dependsOn prepareDependenciesTask
385
386         // Add a task to crunch resource files
387         def crunchTask = createCrunchResTask(variant)
388
389         if (testedVariant.config.type == VariantConfiguration.Type.LIBRARY) {
390             // in this case the tested library must be fully built before test can be built!
391             if (testedVariant.assembleTask != null) {
392                 processManifestTask.dependsOn testedVariant.assembleTask
393                 crunchTask.dependsOn testedVariant.assembleTask
394             }
395         }
396
397         // Add a task to create the BuildConfig class
398         def generateBuildConfigTask = createBuildConfigTask(variant, processManifestTask)
399
400         // Add a task to generate resource source files
401         def processResources = createProcessResTask(variant, processManifestTask, crunchTask)
402
403         // process java resources
404         createProcessJavaResTask(variant)
405
406         def compileAidl = createAidlTask(variant)
407         // TODO - move this
408         compileAidl.dependsOn prepareDependenciesTask
409
410         // Add a task to compile the test application
411         createCompileTask(variant, testedVariant, processResources, generateBuildConfigTask,
412                 compileAidl)
413
414         addPackageTasks(variant, null)
415
416         if (assembleTest != null) {
417             assembleTest.dependsOn variant.assembleTask
418         }
419
420         // create the check task for this test
421         def checkTask = project.tasks.add("check${testedVariant.name}", DefaultTask)
422         checkTask.description = "Installs and runs the checks for Build ${testedVariant.name}."
423         checkTask.group = JavaBasePlugin.VERIFICATION_GROUP
424
425         checkTask.dependsOn testedVariant.assembleTask, variant.assembleTask
426         project.tasks.check.dependsOn checkTask
427
428         // now run the test.
429         def runTestsTask = project.tasks.add("run${testedVariant.name}Tests", RunTestsTask)
430         runTestsTask.description = "Runs the checks for Build ${testedVariant.name}. Must be installed on device."
431         runTestsTask.group = JavaBasePlugin.VERIFICATION_GROUP
432         runTestsTask.sdkDir = sdkDir
433         runTestsTask.variant = variant
434         checkTask.doLast { runTestsTask }
435
436         // TODO: don't rely on dependsOn which isn't reliable for execution order.
437         if (testedVariant.config.type == VariantConfiguration.Type.DEFAULT) {
438             checkTask.dependsOn testedVariant.installTask, variant.installTask, runTestsTask, testedVariant.uninstallTask, variant.uninstallTask
439         } else {
440             checkTask.dependsOn variant.installTask, runTestsTask, variant.uninstallTask
441         }
442     }
443
444     /**
445      * Creates the packaging tasks for the given Variant.
446      * @param variant the variant.
447      * @param assembleTask an optional assembleTask to be used. If null a new one is created. The
448      *                assembleTask is always set in the Variant.
449      */
450     protected void addPackageTasks(ApplicationVariant variant, Task assembleTask) {
451         // Add a dex task
452         def dexTaskName = "dex${variant.name}"
453         def dexTask = project.tasks.add(dexTaskName, DexTask)
454         dexTask.dependsOn variant.compileTask
455         dexTask.plugin = this
456         dexTask.variant = variant
457         dexTask.conventionMapping.libraries = { project.files({ variant.config.packagedJars }) }
458         dexTask.conventionMapping.sourceFiles = { variant.compileTask.outputs.files }
459         dexTask.conventionMapping.outputFile = {
460             project.file(
461                     "${project.buildDir}/libs/${project.archivesBaseName}-${variant.baseName}.dex")
462         }
463         dexTask.dexOptions = extension.dexOptions
464
465         // Add a task to generate application package
466         def packageApp = project.tasks.add("package${variant.name}", PackageApplicationTask)
467         packageApp.dependsOn variant.resourcePackage, dexTask, variant.processJavaResources
468         packageApp.plugin = this
469         packageApp.variant = variant
470         packageApp.configObjects = variant.configObjects
471
472         def signedApk = variant.isSigned()
473
474         def apkName = signedApk ?
475             "${project.archivesBaseName}-${variant.baseName}-unaligned.apk" :
476             "${project.archivesBaseName}-${variant.baseName}-unsigned.apk"
477
478         packageApp.conventionMapping.outputFile = {
479             project.file("$project.buildDir/apk/${apkName}")
480         }
481         packageApp.conventionMapping.resourceFile = { variant.resourcePackage.singleFile }
482         packageApp.conventionMapping.dexFile = { dexTask.outputFile }
483         packageApp.conventionMapping.javaResourceDir = {
484             variant.processJavaResources.destinationDir
485         }
486
487         def appTask = packageApp
488
489         if (signedApk) {
490             if (variant.zipAlign) {
491                 // Add a task to zip align application package
492                 def alignApp = project.tasks.add("zipalign${variant.name}", ZipAlignTask)
493                 alignApp.dependsOn packageApp
494                 alignApp.conventionMapping.inputFile = { packageApp.outputFile }
495                 alignApp.conventionMapping.outputFile = {
496                     project.file(
497                             "$project.buildDir/apk/${project.archivesBaseName}-${variant.baseName}.apk")
498                 }
499                 alignApp.sdkDir = sdkDir
500
501                 appTask = alignApp
502             }
503
504             // Add a task to install the application package
505             def installTask = project.tasks.add("install${variant.name}", InstallTask)
506             installTask.description = "Installs the " + variant.description
507             installTask.group = INSTALL_GROUP
508             installTask.dependsOn appTask
509             installTask.conventionMapping.packageFile = { appTask.outputFile }
510             installTask.sdkDir = sdkDir
511
512             variant.installTask = installTask
513         }
514
515         // Add an assemble task
516         if (assembleTask == null) {
517             assembleTask = project.tasks.add("assemble${variant.name}")
518             assembleTask.description = "Assembles the " + variant.description
519             assembleTask.group = org.gradle.api.plugins.BasePlugin.BUILD_GROUP
520         }
521         assembleTask.dependsOn appTask
522         variant.assembleTask = assembleTask
523
524         // add an uninstall task
525         def uninstallTask = project.tasks.add("uninstall${variant.name}", UninstallTask)
526         uninstallTask.description = "Uninstalls the " + variant.description
527         uninstallTask.group = INSTALL_GROUP
528         uninstallTask.variant = variant
529         uninstallTask.sdkDir = sdkDir
530
531         variant.uninstallTask = uninstallTask
532         uninstallAll.dependsOn uninstallTask
533     }
534
535     protected void createDependencyReportTask() {
536         def androidDependencyTask = project.tasks.add("androidDependencies", AndroidDependencyTask)
537         androidDependencyTask.setDescription("Displays the Android dependencies of the project")
538         androidDependencyTask.setVariants(variants)
539         androidDependencyTask.setGroup("Help")
540     }
541
542     PrepareDependenciesTask createPrepareDependenciesTask(ApplicationVariant variant,
543             List<ConfigurationDependencies> configDependenciesList) {
544         def prepareDependenciesTask = project.tasks.add("prepare${variant.name}Dependencies",
545                 PrepareDependenciesTask)
546         prepareDependenciesTask.plugin = this
547         prepareDependenciesTask.variant = variant
548
549         // look at all the flavors/build types of the variant and get all the libraries
550         // to make sure they are unarchived before the build runs.
551         for (ConfigurationDependencies configDependencies : configDependenciesList) {
552             prepareDependenciesTask.dependsOn configDependencies.configuration.buildDependencies
553             for (AndroidDependencyImpl lib : configDependencies.libraries) {
554                 addDependencyToPrepareTask(prepareDependenciesTask, lib)
555                 prepareDependenciesTask.add(lib.bundle, lib.bundleFolder)
556             }
557         }
558
559         return prepareDependenciesTask
560     }
561
562     def resolveDependencies(List<ConfigurationDependencies> configs) {
563         // start with the default config and its test
564         resolveDependencyForConfig(defaultConfigData)
565         resolveDependencyForConfig(defaultConfigData.testConfigDependencies)
566
567         // and then loop on all the other configs
568         for (ConfigurationDependencies config : configs) {
569             resolveDependencyForConfig(config)
570             if (config.testConfigDependencies != null) {
571                 resolveDependencyForConfig(config.testConfigDependencies)
572             }
573         }
574     }
575
576     def resolveDependencyForConfig(
577             ConfigurationDependencies configDependencies) {
578
579         def compileClasspath = configDependencies.configuration
580
581         // TODO - shouldn't need to do this - fix this in Gradle
582         ensureConfigured(compileClasspath)
583
584         def checker = new DependencyChecker(logger)
585
586         // TODO - defer downloading until required -- This is hard to do as we need the info to build the variant config.
587         List<AndroidDependency> bundles = []
588         List<JarDependency> jars = []
589         Map<ModuleVersionIdentifier, List<AndroidDependency>> modules = [:]
590         Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts = [:]
591         collectArtifacts(compileClasspath, artifacts)
592         compileClasspath.resolvedConfiguration.resolutionResult.root.dependencies.each { ResolvedDependencyResult dep ->
593             addDependency(dep.selected, checker, bundles, jars, modules, artifacts)
594         }
595
596         configDependencies.libraries = bundles
597         configDependencies.jars = jars
598
599         // TODO - filter bundles out of source set classpath
600
601         configureBuild(configDependencies)
602     }
603
604     def addDependencyToPrepareTask(PrepareDependenciesTask prepareDependenciesTask,
605                                    AndroidDependencyImpl lib) {
606         prepareDependenciesTask.add(lib.bundle, lib.bundleFolder)
607
608         for (AndroidDependencyImpl childLib : lib.dependencies) {
609             addDependencyToPrepareTask(prepareDependenciesTask, childLib)
610         }
611     }
612
613     def ensureConfigured(Configuration config) {
614         config.allDependencies.withType(ProjectDependency).each { dep ->
615             project.evaluationDependsOn(dep.dependencyProject.path)
616             ensureConfigured(dep.projectConfiguration)
617         }
618     }
619
620     def collectArtifacts(Configuration configuration, Map<ModuleVersionIdentifier,
621                          List<ResolvedArtifact>> artifacts) {
622         configuration.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact ->
623             def id = artifact.moduleVersion.id
624             List<ResolvedArtifact> moduleArtifacts = artifacts[id]
625             if (moduleArtifacts == null) {
626                 moduleArtifacts = []
627                 artifacts[id] = moduleArtifacts
628             }
629             moduleArtifacts << artifact
630         }
631     }
632
633     def addDependency(ResolvedModuleVersionResult moduleVersion,
634                       DependencyChecker checker,
635                       Collection<AndroidDependency> bundles,
636                       Collection<JarDependency> jars,
637                       Map<ModuleVersionIdentifier, List<AndroidDependency>> modules,
638                       Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts) {
639         def id = moduleVersion.id
640         if (checker.excluded(id)) {
641             return
642         }
643
644         List<AndroidDependency> bundlesForThisModule = modules[id]
645         if (bundlesForThisModule == null) {
646             bundlesForThisModule = []
647             modules[id] = bundlesForThisModule
648
649             def nestedBundles = []
650             moduleVersion.dependencies.each { ResolvedDependencyResult dep ->
651                 addDependency(dep.selected, checker, nestedBundles,
652                         jars, modules, artifacts)
653             }
654
655             def moduleArtifacts = artifacts[id]
656             moduleArtifacts?.each { artifact ->
657                 if (artifact.type == 'alb') {
658                     def explodedDir = project.file(
659                             "$project.buildDir/exploded-bundles/$artifact.file.name")
660                     bundlesForThisModule << new AndroidDependencyImpl(
661                             id.group + ":" + id.name + ":" + id.version,
662                             explodedDir, nestedBundles, artifact.file)
663                 } else {
664                     // TODO - need the correct values for the boolean flags
665                     jars << new JarDependency(artifact.file.absolutePath, true, true, true)
666                 }
667             }
668
669             if (bundlesForThisModule.empty && !nestedBundles.empty) {
670                 throw new GradleException("Module version $id depends on libraries but is not a library itself")
671             }
672         }
673
674         bundles.addAll(bundlesForThisModule)
675     }
676
677     private void configureBuild(ConfigurationDependencies configurationDependencies) {
678         def configuration = configurationDependencies.configuration
679
680         addDependsOnTaskInOtherProjects(
681                 project.getTasks().getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME), true,
682                 JavaBasePlugin.BUILD_NEEDED_TASK_NAME, configuration);
683         addDependsOnTaskInOtherProjects(
684                 project.getTasks().getByName(JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME), false,
685                 JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME, configuration);
686     }
687
688     /**
689      * Adds a dependency on tasks with the specified name in other projects.  The other projects
690      * are determined from project lib dependencies using the specified configuration name.
691      * These may be projects this project depends on or projects that depend on this project
692      * based on the useDependOn argument.
693      *
694      * @param task Task to add dependencies to
695      * @param useDependedOn if true, add tasks from projects this project depends on, otherwise
696      * use projects that depend on this one.
697      * @param otherProjectTaskName name of task in other projects
698      * @param configurationName name of configuration to use to find the other projects
699      */
700     private void addDependsOnTaskInOtherProjects(final Task task, boolean useDependedOn,
701                                                  String otherProjectTaskName,
702                                                  Configuration configuration) {
703         Project project = task.getProject();
704         task.dependsOn(configuration.getTaskDependencyFromProjectDependency(
705                 useDependedOn, otherProjectTaskName));
706     }
707 }
708