| /* |
| * 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.annotations.NonNull |
| import com.android.annotations.Nullable |
| import com.android.build.gradle.api.AndroidSourceSet |
| import com.android.build.gradle.api.BaseVariant |
| import com.android.build.gradle.internal.BadPluginException |
| import com.android.build.gradle.internal.LoggerWrapper |
| import com.android.build.gradle.internal.ProductFlavorData |
| import com.android.build.gradle.internal.Sdk |
| import com.android.build.gradle.internal.api.DefaultAndroidSourceSet |
| import com.android.build.gradle.internal.dependency.DependencyChecker |
| import com.android.build.gradle.internal.dependency.LibraryDependencyImpl |
| import com.android.build.gradle.internal.dependency.ManifestDependencyImpl |
| import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl |
| import com.android.build.gradle.internal.dependency.VariantDependencies |
| import com.android.build.gradle.internal.dsl.SigningConfigDsl |
| import com.android.build.gradle.internal.model.ArtifactMetaDataImpl |
| import com.android.build.gradle.internal.model.JavaArtifactImpl |
| import com.android.build.gradle.internal.model.ModelBuilder |
| import com.android.build.gradle.internal.tasks.AndroidReportTask |
| import com.android.build.gradle.internal.tasks.DependencyReportTask |
| import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestLibraryTask |
| import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask |
| import com.android.build.gradle.internal.tasks.InstallTask |
| import com.android.build.gradle.internal.tasks.OutputFileTask |
| import com.android.build.gradle.internal.tasks.PrepareDependenciesTask |
| import com.android.build.gradle.internal.tasks.PrepareLibraryTask |
| import com.android.build.gradle.internal.tasks.SigningReportTask |
| import com.android.build.gradle.internal.tasks.TestServerTask |
| import com.android.build.gradle.internal.tasks.UninstallTask |
| import com.android.build.gradle.internal.tasks.ValidateSigningTask |
| import com.android.build.gradle.internal.test.report.ReportType |
| import com.android.build.gradle.internal.variant.ApkVariantData |
| import com.android.build.gradle.internal.variant.ApplicationVariantData |
| import com.android.build.gradle.internal.variant.BaseVariantData |
| import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer |
| import com.android.build.gradle.internal.variant.LibraryVariantData |
| import com.android.build.gradle.internal.variant.TestVariantData |
| import com.android.build.gradle.internal.variant.TestedVariantData |
| import com.android.build.gradle.tasks.AidlCompile |
| import com.android.build.gradle.tasks.Dex |
| import com.android.build.gradle.tasks.GenerateBuildConfig |
| import com.android.build.gradle.tasks.Lint |
| 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.PackageApplication |
| import com.android.build.gradle.tasks.PreDex |
| import com.android.build.gradle.tasks.ProcessAndroidResources |
| import com.android.build.gradle.tasks.ProcessAppManifest |
| import com.android.build.gradle.tasks.ProcessTestManifest |
| import com.android.build.gradle.tasks.RenderscriptCompile |
| import com.android.build.gradle.tasks.ZipAlign |
| import com.android.builder.AndroidBuilder |
| import com.android.builder.DefaultProductFlavor |
| import com.android.builder.SdkParser |
| import com.android.builder.VariantConfiguration |
| import com.android.builder.dependency.JarDependency |
| import com.android.builder.dependency.LibraryDependency |
| import com.android.builder.model.AndroidArtifact |
| import com.android.builder.model.AndroidProject |
| import com.android.builder.model.ArtifactMetaData |
| import com.android.builder.model.BuildType |
| import com.android.builder.model.JavaArtifact |
| import com.android.builder.model.ProductFlavor |
| import com.android.builder.model.SigningConfig |
| import com.android.builder.model.SourceProvider |
| import com.android.builder.model.SourceProviderContainer |
| import com.android.builder.testing.ConnectedDeviceProvider |
| import com.android.builder.testing.api.DeviceProvider |
| import com.android.builder.testing.api.TestServer |
| import com.android.utils.ILogger |
| import com.google.common.collect.ArrayListMultimap |
| import com.google.common.collect.ListMultimap |
| import com.google.common.collect.Lists |
| import com.google.common.collect.Maps |
| import com.google.common.collect.Multimap |
| import com.google.common.collect.Sets |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.GradleException |
| import org.gradle.api.Project |
| import org.gradle.api.Task |
| import org.gradle.api.artifacts.Configuration |
| import org.gradle.api.artifacts.ModuleVersionIdentifier |
| import org.gradle.api.artifacts.ProjectDependency |
| import org.gradle.api.artifacts.ResolvedArtifact |
| import org.gradle.api.artifacts.SelfResolvingDependency |
| import org.gradle.api.artifacts.result.DependencyResult |
| import org.gradle.api.artifacts.result.ResolvedDependencyResult |
| import org.gradle.api.artifacts.result.ResolvedModuleVersionResult |
| import org.gradle.api.artifacts.result.UnresolvedDependencyResult |
| import org.gradle.api.logging.LogLevel |
| import org.gradle.api.plugins.JavaBasePlugin |
| import org.gradle.api.plugins.JavaPlugin |
| import org.gradle.api.specs.Specs |
| import org.gradle.api.tasks.Copy |
| import org.gradle.api.tasks.compile.JavaCompile |
| import org.gradle.internal.reflect.Instantiator |
| import org.gradle.language.jvm.tasks.ProcessResources |
| import org.gradle.tooling.BuildException |
| import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry |
| import org.gradle.util.GUtil |
| import proguard.gradle.ProGuardTask |
| |
| import java.util.jar.Attributes |
| import java.util.jar.Manifest |
| |
| import static com.android.builder.BuilderConstants.CONNECTED |
| import static com.android.builder.BuilderConstants.DEVICE |
| import static com.android.builder.BuilderConstants.EXT_LIB_ARCHIVE |
| import static com.android.builder.BuilderConstants.FD_FLAVORS |
| import static com.android.builder.BuilderConstants.FD_FLAVORS_ALL |
| import static com.android.builder.BuilderConstants.FD_INSTRUMENT_RESULTS |
| import static com.android.builder.BuilderConstants.FD_INSTRUMENT_TESTS |
| import static com.android.builder.BuilderConstants.FD_REPORTS |
| import static com.android.builder.BuilderConstants.INSTRUMENT_TEST |
| import static java.io.File.separator |
| |
| /** |
| * Base class for all Android plugins |
| */ |
| public abstract class BasePlugin { |
| protected final static String DIR_BUNDLES = "bundles"; |
| |
| public static final String GRADLE_MIN_VERSION = "1.9" |
| public static final String[] GRADLE_SUPPORTED_VERSIONS = [ GRADLE_MIN_VERSION ] |
| |
| public static final String INSTALL_GROUP = "Install" |
| |
| public static File TEST_SDK_DIR; |
| |
| protected Instantiator instantiator |
| private ToolingModelBuilderRegistry registry |
| |
| private final Map<Object, AndroidBuilder> builders = Maps.newIdentityHashMap() |
| |
| final List<BaseVariantData> variantDataList = [] |
| final Map<LibraryDependencyImpl, PrepareLibraryTask> prepareTaskMap = [:] |
| final Map<SigningConfig, ValidateSigningTask> validateSigningTaskMap = [:] |
| |
| protected Project project |
| private LoggerWrapper loggerWrapper |
| private Sdk sdk |
| private String creator |
| |
| private boolean hasCreatedTasks = false |
| |
| private ProductFlavorData<DefaultProductFlavor> defaultConfigData |
| private final Collection<String> unresolvedDependencies = Sets.newHashSet(); |
| |
| protected DefaultAndroidSourceSet mainSourceSet |
| protected DefaultAndroidSourceSet testSourceSet |
| |
| protected Task mainPreBuild |
| protected Task uninstallAll |
| protected Task assembleTest |
| protected Task deviceCheck |
| protected Task connectedCheck |
| protected Task lint |
| protected Task lintCompile |
| |
| protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) { |
| this.instantiator = instantiator |
| this.registry = registry |
| String pluginVersion = getLocalVersion() |
| if (pluginVersion != null) { |
| creator = "Android Gradle " + pluginVersion |
| } else { |
| creator = "Android Gradle" |
| } |
| } |
| |
| protected abstract BaseExtension getExtension() |
| protected abstract void doCreateAndroidTasks() |
| |
| protected void apply(Project project) { |
| this.project = project |
| |
| checkGradleVersion() |
| sdk = new Sdk(project, logger) |
| |
| project.apply plugin: JavaBasePlugin |
| |
| // Register a builder for the custom tooling model |
| registry.register(new ModelBuilder()); |
| |
| project.tasks.assemble.description = |
| "Assembles all variants of all applications and secondary packages." |
| |
| uninstallAll = project.tasks.create("uninstallAll") |
| uninstallAll.description = "Uninstall all applications." |
| uninstallAll.group = INSTALL_GROUP |
| |
| deviceCheck = project.tasks.create("deviceCheck") |
| deviceCheck.description = "Runs all device checks using Device Providers and Test Servers." |
| deviceCheck.group = JavaBasePlugin.VERIFICATION_GROUP |
| |
| connectedCheck = project.tasks.create("connectedCheck") |
| connectedCheck.description = "Runs all device checks on currently connected devices." |
| connectedCheck.group = JavaBasePlugin.VERIFICATION_GROUP |
| |
| mainPreBuild = project.tasks.create("preBuild") |
| |
| lint = project.tasks.create("lint", Lint) |
| lint.description = "Runs lint on all variants." |
| lint.group = JavaBasePlugin.VERIFICATION_GROUP |
| lint.setPlugin(this) |
| int count = variantDataList.size() |
| for (int i = 0 ; i < count ; i++) { |
| final BaseVariantData baseVariantData = variantDataList.get(i) |
| if (isLintVariant(baseVariantData)) { |
| lint.dependsOn baseVariantData.javaCompileTask |
| } |
| } |
| project.tasks.check.dependsOn lint |
| |
| project.afterEvaluate { |
| createAndroidTasks(false) |
| } |
| } |
| |
| protected void setBaseExtension(@NonNull BaseExtension extension) { |
| sdk.setExtension(extension) |
| mainSourceSet = (DefaultAndroidSourceSet) extension.sourceSets.create(extension.defaultConfig.name) |
| testSourceSet = (DefaultAndroidSourceSet) extension.sourceSets.create(INSTRUMENT_TEST) |
| |
| defaultConfigData = new ProductFlavorData<DefaultProductFlavor>( |
| extension.defaultConfig, mainSourceSet, |
| testSourceSet, project) |
| } |
| |
| private void checkGradleVersion() { |
| boolean foundMatch = false |
| for (String version : GRADLE_SUPPORTED_VERSIONS) { |
| if (project.getGradle().gradleVersion.startsWith(version)) { |
| foundMatch = true |
| break |
| } |
| } |
| |
| if (!foundMatch) { |
| File file = new File("gradle" + separator + "wrapper" + separator + |
| "gradle-wrapper.properties"); |
| throw new BuildException( |
| String.format( |
| "Gradle version %s is required. Current version is %s. " + |
| "If using the gradle wrapper, try editing the distributionUrl in %s " + |
| "to gradle-%s-all.zip", |
| GRADLE_MIN_VERSION, project.getGradle().gradleVersion, file.getAbsolutePath(), |
| GRADLE_MIN_VERSION), null); |
| |
| } |
| } |
| |
| final void createAndroidTasks(boolean force) { |
| // get current plugins and look for the default Java plugin. |
| if (project.plugins.hasPlugin(JavaPlugin.class)) { |
| throw new BadPluginException( |
| "The 'java' plugin has been applied, but it is not compatible with the Android plugins.") |
| } |
| |
| // don't do anything if the project was not initialized. |
| // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return. |
| // This is because project don't get evaluated in the unit test setup. |
| // See AppPluginDslTest |
| if (!force && (!project.state.executed || project.state.failure != null) && TEST_SDK_DIR == null) { |
| return |
| } |
| |
| if (hasCreatedTasks) { |
| return |
| } |
| hasCreatedTasks = true |
| |
| doCreateAndroidTasks() |
| createReportTasks() |
| } |
| |
| void checkTasksAlreadyCreated() { |
| if (hasCreatedTasks) { |
| throw new GradleException( |
| "Android tasks have already been created.\n" + |
| "This happens when calling android.applicationVariants,\n" + |
| "android.libraryVariants or android.testVariants.\n" + |
| "Once these methods are called, it is not possible to\n" + |
| "continue configuring the model.") |
| } |
| } |
| |
| |
| ProductFlavorData getDefaultConfigData() { |
| return defaultConfigData |
| } |
| |
| Collection<String> getUnresolvedDependencies() { |
| return unresolvedDependencies |
| } |
| |
| SdkParser getSdkParser() { |
| return sdk.parser |
| } |
| |
| SdkParser getLoadedSdkParser() { |
| return sdk.loadParser() |
| } |
| |
| File getSdkDirectory() { |
| return sdk.sdkDirectory |
| } |
| |
| File getNdkDirectory() { |
| return sdk.ndkDirectory |
| } |
| |
| ILogger getLogger() { |
| if (loggerWrapper == null) { |
| loggerWrapper = new LoggerWrapper(project.logger) |
| } |
| |
| return loggerWrapper |
| } |
| |
| boolean isVerbose() { |
| return project.logger.isEnabled(LogLevel.DEBUG) |
| } |
| |
| AndroidBuilder getAndroidBuilder(BaseVariantData variantData) { |
| AndroidBuilder androidBuilder = builders.get(variantData) |
| |
| if (androidBuilder == null) { |
| SdkParser parser = getLoadedSdkParser() |
| androidBuilder = new AndroidBuilder(parser, creator, logger, verbose) |
| if (this instanceof LibraryPlugin) { |
| androidBuilder.setBuildingLibrary(true); |
| } |
| |
| builders.put(variantData, androidBuilder) |
| } |
| |
| return androidBuilder |
| } |
| |
| |
| protected String getRuntimeJars() { |
| return runtimeJarList.join(File.pathSeparator) |
| } |
| |
| public List<String> getRuntimeJarList() { |
| SdkParser sdkParser = getLoadedSdkParser() |
| return AndroidBuilder.getBootClasspath(sdkParser); |
| } |
| |
| protected void createProcessManifestTask(BaseVariantData variantData, |
| String manifestOurDir) { |
| def processManifestTask = project.tasks.create( |
| "process${variantData.variantConfiguration.fullName.capitalize()}Manifest", |
| ProcessAppManifest) |
| variantData.processManifestTask = processManifestTask |
| processManifestTask.dependsOn variantData.prepareDependenciesTask |
| |
| processManifestTask.plugin = this |
| processManifestTask.variant = variantData |
| |
| VariantConfiguration config = variantData.variantConfiguration |
| ProductFlavor mergedFlavor = config.mergedFlavor |
| |
| processManifestTask.conventionMapping.mainManifest = { |
| config.mainManifest |
| } |
| processManifestTask.conventionMapping.manifestOverlays = { |
| config.manifestOverlays |
| } |
| processManifestTask.conventionMapping.packageNameOverride = { |
| config.packageOverride |
| } |
| processManifestTask.conventionMapping.versionName = { |
| config.versionName |
| } |
| processManifestTask.conventionMapping.libraries = { |
| getManifestDependencies(config.directLibraries) |
| } |
| processManifestTask.conventionMapping.versionCode = { |
| config.versionCode |
| } |
| processManifestTask.conventionMapping.minSdkVersion = { |
| mergedFlavor.minSdkVersion |
| } |
| processManifestTask.conventionMapping.targetSdkVersion = { |
| mergedFlavor.targetSdkVersion |
| } |
| processManifestTask.conventionMapping.manifestOutputFile = { |
| project.file( |
| "$project.buildDir/${manifestOurDir}/${variantData.variantConfiguration.dirName}/AndroidManifest.xml") |
| } |
| } |
| |
| protected void createProcessTestManifestTask(BaseVariantData variantData, |
| String manifestOurDir) { |
| def processTestManifestTask = project.tasks.create( |
| "process${variantData.variantConfiguration.fullName.capitalize()}Manifest", |
| ProcessTestManifest) |
| variantData.processManifestTask = processTestManifestTask |
| processTestManifestTask.dependsOn variantData.prepareDependenciesTask |
| |
| processTestManifestTask.plugin = this |
| processTestManifestTask.variant = variantData |
| |
| VariantConfiguration config = variantData.variantConfiguration |
| |
| processTestManifestTask.conventionMapping.testPackageName = { |
| config.packageName |
| } |
| processTestManifestTask.conventionMapping.minSdkVersion = { |
| config.minSdkVersion |
| } |
| processTestManifestTask.conventionMapping.targetSdkVersion = { |
| config.targetSdkVersion |
| } |
| processTestManifestTask.conventionMapping.testedPackageName = { |
| config.testedPackageName |
| } |
| processTestManifestTask.conventionMapping.instrumentationRunner = { |
| config.instrumentationRunner |
| } |
| processTestManifestTask.conventionMapping.handleProfiling = { |
| config.handleProfiling |
| } |
| processTestManifestTask.conventionMapping.functionalTest = { |
| config.functionalTest |
| } |
| processTestManifestTask.conventionMapping.libraries = { |
| getManifestDependencies(config.directLibraries) |
| } |
| processTestManifestTask.conventionMapping.manifestOutputFile = { |
| project.file( |
| "$project.buildDir/${manifestOurDir}/${variantData.variantConfiguration.dirName}/AndroidManifest.xml") |
| } |
| } |
| |
| protected void createRenderscriptTask(BaseVariantData variantData) { |
| VariantConfiguration config = variantData.variantConfiguration |
| |
| def renderscriptTask = project.tasks.create( |
| "compile${variantData.variantConfiguration.fullName.capitalize()}Renderscript", |
| RenderscriptCompile) |
| variantData.renderscriptCompileTask = renderscriptTask |
| |
| ProductFlavor mergedFlavor = config.mergedFlavor |
| boolean ndkMode = mergedFlavor.renderscriptNdkMode |
| |
| // only put this dependency if rs will generate Java code |
| if (!ndkMode) { |
| variantData.sourceGenTask.dependsOn renderscriptTask |
| } |
| |
| renderscriptTask.dependsOn variantData.prepareDependenciesTask |
| renderscriptTask.plugin = this |
| renderscriptTask.variant = variantData |
| |
| renderscriptTask.targetApi = mergedFlavor.renderscriptTargetApi |
| renderscriptTask.supportMode = mergedFlavor.renderscriptSupportMode |
| renderscriptTask.ndkMode = ndkMode |
| renderscriptTask.debugBuild = config.buildType.renderscriptDebugBuild |
| renderscriptTask.optimLevel = config.buildType.renderscriptOptimLevel |
| |
| renderscriptTask.conventionMapping.sourceDirs = { config.renderscriptSourceList } |
| renderscriptTask.conventionMapping.importDirs = { config.renderscriptImports } |
| |
| renderscriptTask.conventionMapping.sourceOutputDir = { |
| project.file("$project.buildDir/source/rs/${variantData.variantConfiguration.dirName}") |
| } |
| renderscriptTask.conventionMapping.resOutputDir = { |
| project.file("$project.buildDir/res/rs/${variantData.variantConfiguration.dirName}") |
| } |
| renderscriptTask.conventionMapping.objOutputDir = { |
| project.file("$project.buildDir/rs/${variantData.variantConfiguration.dirName}/obj") |
| } |
| renderscriptTask.conventionMapping.libOutputDir = { |
| project.file("$project.buildDir/rs/${variantData.variantConfiguration.dirName}/lib") |
| } |
| renderscriptTask.conventionMapping.ndkConfig = { config.ndkConfig } |
| } |
| |
| protected void createMergeResourcesTask(@NonNull BaseVariantData variantData, |
| final boolean process9Patch) { |
| MergeResources mergeResourcesTask = basicCreateMergeResourcesTask( |
| variantData, |
| "merge", |
| "$project.buildDir/res/all/${variantData.variantConfiguration.dirName}", |
| true /*includeDependencies*/, |
| process9Patch) |
| variantData.mergeResourcesTask = mergeResourcesTask |
| } |
| |
| protected MergeResources basicCreateMergeResourcesTask( |
| @NonNull BaseVariantData variantData, |
| @NonNull String taskNamePrefix, |
| @NonNull String outputLocation, |
| final boolean includeDependencies, |
| final boolean process9Patch) { |
| MergeResources mergeResourcesTask = project.tasks.create( |
| "$taskNamePrefix${variantData.variantConfiguration.fullName.capitalize()}Resources", |
| MergeResources) |
| |
| mergeResourcesTask.dependsOn variantData.prepareDependenciesTask, variantData.renderscriptCompileTask |
| mergeResourcesTask.plugin = this |
| mergeResourcesTask.variant = variantData |
| mergeResourcesTask.incrementalFolder = project.file( |
| "$project.buildDir/incremental/${taskNamePrefix}Resources/${variantData.variantConfiguration.dirName}") |
| |
| mergeResourcesTask.process9Patch = process9Patch |
| |
| mergeResourcesTask.conventionMapping.inputResourceSets = { |
| variantData.variantConfiguration.getResourceSets( |
| variantData.renderscriptCompileTask.getResOutputDir(), |
| includeDependencies) |
| } |
| |
| mergeResourcesTask.conventionMapping.outputDir = { project.file(outputLocation) } |
| |
| return mergeResourcesTask |
| } |
| |
| protected void createMergeAssetsTask(@NonNull BaseVariantData variantData, |
| @Nullable String outputLocation, |
| final boolean includeDependencies) { |
| if (outputLocation == null) { |
| outputLocation = "$project.buildDir/assets/${variantData.variantConfiguration.dirName}" |
| } |
| |
| def mergeAssetsTask = project.tasks.create( |
| "merge${variantData.variantConfiguration.fullName.capitalize()}Assets", |
| MergeAssets) |
| variantData.mergeAssetsTask = mergeAssetsTask |
| |
| mergeAssetsTask.dependsOn variantData.prepareDependenciesTask |
| mergeAssetsTask.plugin = this |
| mergeAssetsTask.variant = variantData |
| mergeAssetsTask.incrementalFolder = |
| project.file("$project.buildDir/incremental/mergeAssets/${variantData.variantConfiguration.dirName}") |
| |
| mergeAssetsTask.conventionMapping.inputAssetSets = { |
| variantData.variantConfiguration.getAssetSets(includeDependencies) |
| } |
| mergeAssetsTask.conventionMapping.outputDir = { project.file(outputLocation) } |
| } |
| |
| protected void createBuildConfigTask(BaseVariantData variantData) { |
| def generateBuildConfigTask = project.tasks.create( |
| "generate${variantData.variantConfiguration.fullName.capitalize()}BuildConfig", |
| GenerateBuildConfig) |
| variantData.generateBuildConfigTask = generateBuildConfigTask |
| |
| VariantConfiguration variantConfiguration = variantData.variantConfiguration |
| |
| variantData.sourceGenTask.dependsOn generateBuildConfigTask |
| if (variantConfiguration.type == VariantConfiguration.Type.TEST) { |
| // in case of a test project, the manifest is generated so we need to depend |
| // on its creation. |
| generateBuildConfigTask.dependsOn variantData.processManifestTask |
| } |
| |
| generateBuildConfigTask.plugin = this |
| generateBuildConfigTask.variant = variantData |
| |
| generateBuildConfigTask.conventionMapping.buildConfigPackageName = { |
| variantConfiguration.originalPackageName |
| } |
| |
| generateBuildConfigTask.conventionMapping.appPackageName = { |
| variantConfiguration.packageName |
| } |
| |
| generateBuildConfigTask.conventionMapping.versionName = { |
| variantConfiguration.versionName |
| } |
| |
| generateBuildConfigTask.conventionMapping.versionCode = { |
| variantConfiguration.versionCode |
| } |
| |
| generateBuildConfigTask.conventionMapping.debuggable = { |
| variantConfiguration.buildType.isDebuggable() |
| } |
| |
| generateBuildConfigTask.conventionMapping.buildTypeName = { |
| variantConfiguration.buildType.name |
| } |
| |
| generateBuildConfigTask.conventionMapping.flavorName = { |
| variantConfiguration.flavorName |
| } |
| |
| generateBuildConfigTask.conventionMapping.flavorNamesWithDimensionNames = { |
| variantConfiguration.flavorNamesWithDimensionNames |
| } |
| |
| generateBuildConfigTask.conventionMapping.items = { |
| variantConfiguration.buildConfigItems |
| } |
| |
| generateBuildConfigTask.conventionMapping.sourceOutputDir = { |
| project.file("$project.buildDir/source/buildConfig/${variantData.variantConfiguration.dirName}") |
| } |
| } |
| |
| protected void createProcessResTask(BaseVariantData variantData) { |
| createProcessResTask(variantData, "$project.buildDir/symbols/${variantData.variantConfiguration.dirName}") |
| } |
| |
| protected void createProcessResTask(BaseVariantData variantData, final String symbolLocation) { |
| def processResources = project.tasks.create( |
| "process${variantData.variantConfiguration.fullName.capitalize()}Resources", |
| ProcessAndroidResources) |
| variantData.processResourcesTask = processResources |
| |
| variantData.sourceGenTask.dependsOn processResources |
| processResources.dependsOn variantData.processManifestTask, variantData.mergeResourcesTask, variantData.mergeAssetsTask |
| |
| processResources.plugin = this |
| processResources.variant = variantData |
| |
| VariantConfiguration variantConfiguration = variantData.variantConfiguration |
| |
| processResources.conventionMapping.manifestFile = { |
| variantData.processManifestTask.manifestOutputFile |
| } |
| |
| processResources.conventionMapping.resDir = { |
| variantData.mergeResourcesTask.outputDir |
| } |
| |
| processResources.conventionMapping.assetsDir = { |
| variantData.mergeAssetsTask.outputDir |
| } |
| |
| processResources.conventionMapping.libraries = { |
| getTextSymbolDependencies(variantConfiguration.allLibraries) |
| } |
| processResources.conventionMapping.packageForR = { |
| variantConfiguration.originalPackageName |
| } |
| |
| // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow? |
| processResources.conventionMapping.sourceOutputDir = { |
| project.file("$project.buildDir/source/r/${variantData.variantConfiguration.dirName}") |
| } |
| processResources.conventionMapping.textSymbolOutputDir = { |
| project.file(symbolLocation) |
| } |
| processResources.conventionMapping.packageOutputFile = { |
| project.file( |
| "$project.buildDir/libs/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.ap_") |
| } |
| if (variantConfiguration.buildType.runProguard) { |
| processResources.conventionMapping.proguardOutputFile = { |
| project.file("$project.buildDir/proguard/${variantData.variantConfiguration.dirName}/aapt_rules.txt") |
| } |
| } |
| |
| processResources.conventionMapping.type = { variantConfiguration.type } |
| processResources.conventionMapping.debuggable = { variantConfiguration.buildType.debuggable } |
| processResources.conventionMapping.aaptOptions = { extension.aaptOptions } |
| processResources.conventionMapping.resourceConfigs = { variantConfiguration.mergedFlavor.resourceConfigurations } |
| } |
| |
| protected void createProcessJavaResTask(BaseVariantData variantData) { |
| VariantConfiguration variantConfiguration = variantData.variantConfiguration |
| |
| Copy processResources = project.tasks.create( |
| "process${variantData.variantConfiguration.fullName.capitalize()}JavaRes", |
| ProcessResources); |
| variantData.processJavaResourcesTask = processResources |
| |
| // set the input |
| processResources.from(((AndroidSourceSet) variantConfiguration.defaultSourceSet).resources) |
| |
| if (variantConfiguration.type != VariantConfiguration.Type.TEST) { |
| processResources.from( |
| ((AndroidSourceSet) variantConfiguration.buildTypeSourceSet).resources) |
| } |
| if (variantConfiguration.hasFlavors()) { |
| for (SourceProvider flavorSourceSet : variantConfiguration.flavorSourceProviders) { |
| processResources.from(((AndroidSourceSet) flavorSourceSet).resources) |
| } |
| } |
| |
| processResources.conventionMapping.destinationDir = { |
| project.file("$project.buildDir/javaResources/${variantData.variantConfiguration.dirName}") |
| } |
| } |
| |
| protected void createAidlTask(BaseVariantData variantData) { |
| VariantConfiguration variantConfiguration = variantData.variantConfiguration |
| |
| def compileTask = project.tasks.create( |
| "compile${variantData.variantConfiguration.fullName.capitalize()}Aidl", |
| AidlCompile) |
| variantData.aidlCompileTask = compileTask |
| |
| variantData.sourceGenTask.dependsOn compileTask |
| variantData.aidlCompileTask.dependsOn variantData.prepareDependenciesTask |
| |
| compileTask.plugin = this |
| compileTask.variant = variantData |
| compileTask.incrementalFolder = |
| project.file("$project.buildDir/incremental/aidl/${variantData.variantConfiguration.dirName}") |
| |
| compileTask.conventionMapping.sourceDirs = { variantConfiguration.aidlSourceList } |
| compileTask.conventionMapping.importDirs = { variantConfiguration.aidlImports } |
| |
| compileTask.conventionMapping.sourceOutputDir = { |
| project.file("$project.buildDir/source/aidl/${variantData.variantConfiguration.dirName}") |
| } |
| } |
| |
| protected void createCompileTask(BaseVariantData variantData, |
| BaseVariantData testedVariantData) { |
| def compileTask = project.tasks.create( |
| "compile${variantData.variantConfiguration.fullName.capitalize()}Java", |
| JavaCompile) |
| variantData.javaCompileTask = compileTask |
| compileTask.dependsOn variantData.sourceGenTask |
| |
| VariantConfiguration config = variantData.variantConfiguration |
| |
| List<Object> sourceList = Lists.newArrayList() |
| sourceList.add(((AndroidSourceSet) config.defaultSourceSet).java) |
| sourceList.add({ variantData.processResourcesTask.sourceOutputDir }) |
| sourceList.add({ variantData.generateBuildConfigTask.sourceOutputDir }) |
| sourceList.add({ variantData.aidlCompileTask.sourceOutputDir }) |
| if (!config.mergedFlavor.renderscriptNdkMode) { |
| sourceList.add({ variantData.renderscriptCompileTask.sourceOutputDir }) |
| } |
| |
| if (config.getType() != VariantConfiguration.Type.TEST) { |
| sourceList.add(((AndroidSourceSet) config.buildTypeSourceSet).java) |
| } |
| if (config.hasFlavors()) { |
| for (SourceProvider flavorSourceProvider : config.flavorSourceProviders) { |
| sourceList.add(((AndroidSourceSet) flavorSourceProvider).java) |
| } |
| } |
| compileTask.source = sourceList.toArray() |
| |
| // if the tested variant is an app, add its classpath. For the libraries, |
| // it's done automatically since the classpath includes the library output as a normal |
| // dependency. |
| if (testedVariantData instanceof ApplicationVariantData) { |
| compileTask.conventionMapping.classpath = { |
| project.files(getAndroidBuilder(variantData).getCompileClasspath(config)) + testedVariantData.javaCompileTask.classpath + testedVariantData.javaCompileTask.outputs.files |
| } |
| } else { |
| compileTask.conventionMapping.classpath = { |
| project.files(getAndroidBuilder(variantData).getCompileClasspath(config)) |
| } |
| } |
| |
| // TODO - dependency information for the compile classpath is being lost. |
| // Add a temporary approximation |
| compileTask.dependsOn project.configurations.compile.buildDependencies |
| |
| compileTask.conventionMapping.destinationDir = { |
| project.file("$project.buildDir/classes/${variantData.variantConfiguration.dirName}") |
| } |
| compileTask.conventionMapping.dependencyCacheDir = { |
| project.file("$project.buildDir/dependency-cache/${variantData.variantConfiguration.dirName}") |
| } |
| |
| // set source/target compatibility |
| compileTask.conventionMapping.sourceCompatibility = { |
| extension.compileOptions.sourceCompatibility.toString() |
| } |
| compileTask.conventionMapping.targetCompatibility = { |
| extension.compileOptions.targetCompatibility.toString() |
| } |
| compileTask.options.encoding = extension.compileOptions.encoding |
| |
| // setup the boot classpath just before the task actually runs since this will |
| // force the sdk to be parsed. |
| compileTask.doFirst { |
| compileTask.options.bootClasspath = getRuntimeJars() |
| } |
| } |
| |
| protected void createNdkTasks(@NonNull BaseVariantData variantData) { |
| createNdkTasks( |
| variantData, |
| { project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/lib") } |
| ) |
| } |
| |
| protected void createNdkTasks(@NonNull BaseVariantData variantData, |
| @NonNull Closure<File> soFolderClosure) { |
| NdkCompile ndkCompile = project.tasks.create( |
| "compile${variantData.variantConfiguration.fullName.capitalize()}Ndk", |
| NdkCompile) |
| |
| ndkCompile.plugin = this |
| ndkCompile.variant = variantData |
| variantData.ndkCompileTask = ndkCompile |
| |
| VariantConfiguration variantConfig = variantData.variantConfiguration |
| |
| if (variantConfig.mergedFlavor.renderscriptNdkMode) { |
| ndkCompile.ndkRenderScriptMode = true |
| ndkCompile.dependsOn variantData.renderscriptCompileTask |
| } else { |
| ndkCompile.ndkRenderScriptMode = false |
| } |
| |
| ndkCompile.conventionMapping.sourceFolders = { |
| List<File> sourceList = variantConfig.jniSourceList |
| if (variantConfig.mergedFlavor.renderscriptNdkMode) { |
| sourceList.add(variantData.renderscriptCompileTask.sourceOutputDir) |
| } |
| |
| return sourceList |
| } |
| |
| ndkCompile.conventionMapping.generatedMakefile = { |
| project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/Android.mk") |
| } |
| |
| ndkCompile.conventionMapping.ndkConfig = { variantConfig.ndkConfig } |
| |
| ndkCompile.conventionMapping.debuggable = { |
| variantConfig.buildType.jniDebugBuild |
| } |
| |
| ndkCompile.conventionMapping.objFolder = { |
| project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/obj") |
| } |
| ndkCompile.conventionMapping.soFolder = soFolderClosure |
| } |
| |
| /** |
| * Creates the tasks to build the test apk. |
| * |
| * @param variant the test variant |
| * @param testedVariant the tested variant |
| * @param configDependencies the list of config dependencies |
| */ |
| protected void createTestApkTasks(@NonNull TestVariantData variantData, |
| @NonNull BaseVariantData testedVariantData) { |
| // The test app is signed with the same info as the tested app so there's no need |
| // to test both. |
| if (!variantData.isSigned()) { |
| throw new GradleException( |
| "Tested Variant '${testedVariantData.variantConfiguration.fullName}' is not configured to create a signed APK.") |
| } |
| |
| createAnchorTasks(variantData) |
| |
| // Add a task to process the manifest |
| createProcessTestManifestTask(variantData, "manifests") |
| |
| // Add a task to compile renderscript files. |
| createRenderscriptTask(variantData) |
| |
| // Add a task to merge the resource folders |
| createMergeResourcesTask(variantData, true /*process9Patch*/) |
| |
| // Add a task to merge the assets folders |
| createMergeAssetsTask(variantData, null /*default location*/, true /*includeDependencies*/) |
| |
| if (testedVariantData.variantConfiguration.type == VariantConfiguration.Type.LIBRARY) { |
| // in this case the tested library must be fully built before test can be built! |
| if (testedVariantData.assembleTask != null) { |
| variantData.processManifestTask.dependsOn testedVariantData.assembleTask |
| variantData.mergeResourcesTask.dependsOn testedVariantData.assembleTask |
| } |
| } |
| |
| // Add a task to create the BuildConfig class |
| createBuildConfigTask(variantData) |
| |
| // Add a task to generate resource source files |
| createProcessResTask(variantData) |
| |
| // process java resources |
| createProcessJavaResTask(variantData) |
| |
| createAidlTask(variantData) |
| |
| // Add a task to compile the test application |
| createCompileTask(variantData, testedVariantData) |
| |
| // Add NDK tasks |
| createNdkTasks(variantData) |
| |
| addPackageTasks(variantData, null) |
| |
| if (assembleTest != null) { |
| assembleTest.dependsOn variantData.assembleTask |
| } |
| } |
| |
| // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar |
| protected void createLintCompileTask() { |
| lintCompile = project.tasks.create("compileLint", Task) |
| File outputDir = new File("$project.buildDir/lint") |
| |
| lintCompile.doFirst{ |
| // create the directory for lint output if it does not exist. |
| if (!outputDir.exists()) { |
| boolean mkdirs = outputDir.mkdirs(); |
| if (!mkdirs) { |
| throw new GradleException("Unable to create lint output directory.") |
| } |
| } |
| } |
| } |
| |
| /** Is the given variant relevant for lint? */ |
| private static boolean isLintVariant(@NonNull BaseVariantData baseVariantData) { |
| // Only create lint targets for variants like debug and release, not debugTest |
| VariantConfiguration config = baseVariantData.variantConfiguration |
| return config.getType() != VariantConfiguration.Type.TEST; |
| } |
| |
| // Add tasks for running lint on individual variants. We've already added a |
| // lint task earlier which runs on all variants. |
| protected void createLintTasks() { |
| int count = variantDataList.size() |
| for (int i = 0 ; i < count ; i++) { |
| final BaseVariantData baseVariantData = variantDataList.get(i) |
| if (!isLintVariant(baseVariantData)) { |
| continue; |
| } |
| |
| String variantName = baseVariantData.variantConfiguration.fullName |
| def capitalizedVariantName = variantName.capitalize() |
| Task lintCheck = project.tasks.create("lint" + capitalizedVariantName, Lint) |
| lintCheck.dependsOn baseVariantData.javaCompileTask, lintCompile |
| // Note that we don't do "lint.dependsOn lintCheck"; the "lint" target will |
| // on its own run through all variants (and compare results), it doesn't delegate |
| // to the individual tasks (since it needs to coordinate data collection and |
| // reporting) |
| lintCheck.setPlugin(this) |
| lintCheck.setVariantName(variantName) |
| lintCheck.description = "Runs lint on the " + capitalizedVariantName + " build" |
| lintCheck.group = JavaBasePlugin.VERIFICATION_GROUP |
| } |
| } |
| |
| protected void createCheckTasks(boolean hasFlavors, boolean isLibraryTest) { |
| List<AndroidReportTask> reportTasks = Lists.newArrayListWithExpectedSize(2) |
| |
| List<DeviceProvider> providers = extension.deviceProviders |
| List<TestServer> servers = extension.testServers |
| |
| Task mainConnectedTask = connectedCheck |
| String connectedRootName = "${CONNECTED}${INSTRUMENT_TEST.capitalize()}" |
| // if more than one flavor, create a report aggregator task and make this the parent |
| // task for all new connected tasks. |
| if (hasFlavors) { |
| mainConnectedTask = project.tasks.create(connectedRootName, AndroidReportTask) |
| mainConnectedTask.group = JavaBasePlugin.VERIFICATION_GROUP |
| mainConnectedTask.description = "Installs and runs instrumentation tests for all flavors on connected devices." |
| mainConnectedTask.reportType = ReportType.MULTI_FLAVOR |
| connectedCheck.dependsOn mainConnectedTask |
| |
| mainConnectedTask.conventionMapping.resultsDir = { |
| String rootLocation = extension.testOptions.resultsDir != null ? |
| extension.testOptions.resultsDir : "$project.buildDir/$FD_INSTRUMENT_RESULTS" |
| |
| project.file("$rootLocation/connected/$FD_FLAVORS_ALL") |
| } |
| mainConnectedTask.conventionMapping.reportsDir = { |
| String rootLocation = extension.testOptions.reportDir != null ? |
| extension.testOptions.reportDir : |
| "$project.buildDir/$FD_REPORTS/$FD_INSTRUMENT_TESTS" |
| |
| project.file("$rootLocation/connected/$FD_FLAVORS_ALL") |
| } |
| |
| reportTasks.add(mainConnectedTask) |
| } |
| |
| Task mainProviderTask = deviceCheck |
| // if more than one provider tasks, either because of several flavors, or because of |
| // more than one providers, then create an aggregate report tasks for all of them. |
| if (providers.size() > 1 || hasFlavors) { |
| mainProviderTask = project.tasks.create("${DEVICE}${INSTRUMENT_TEST.capitalize()}", |
| AndroidReportTask) |
| mainProviderTask.group = JavaBasePlugin.VERIFICATION_GROUP |
| mainProviderTask.description = "Installs and runs instrumentation tests using all Device Providers." |
| mainProviderTask.reportType = ReportType.MULTI_FLAVOR |
| deviceCheck.dependsOn mainProviderTask |
| |
| mainProviderTask.conventionMapping.resultsDir = { |
| String rootLocation = extension.testOptions.resultsDir != null ? |
| extension.testOptions.resultsDir : "$project.buildDir/$FD_INSTRUMENT_RESULTS" |
| |
| project.file("$rootLocation/devices/$FD_FLAVORS_ALL") |
| } |
| mainProviderTask.conventionMapping.reportsDir = { |
| String rootLocation = extension.testOptions.reportDir != null ? |
| extension.testOptions.reportDir : |
| "$project.buildDir/$FD_REPORTS/$FD_INSTRUMENT_TESTS" |
| |
| project.file("$rootLocation/devices/$FD_FLAVORS_ALL") |
| } |
| |
| reportTasks.add(mainProviderTask) |
| } |
| |
| // now look for the testedvariant and create the check tasks for them. |
| // don't use an auto loop as we can't reuse baseVariantData or the closure lower |
| // gets broken. |
| int count = variantDataList.size(); |
| for (int i = 0 ; i < count ; i++) { |
| final BaseVariantData baseVariantData = variantDataList.get(i); |
| if (baseVariantData instanceof TestedVariantData) { |
| final TestVariantData testVariantData = ((TestedVariantData) baseVariantData).testVariantData |
| if (testVariantData == null) { |
| continue |
| } |
| |
| // create the check tasks for this test |
| |
| // first the connected one. |
| def connectedTask = createDeviceProviderInstrumentTestTask( |
| hasFlavors ? |
| "${connectedRootName}${baseVariantData.variantConfiguration.fullName.capitalize()}" : connectedRootName, |
| "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' on connected devices.", |
| isLibraryTest ? |
| DeviceProviderInstrumentTestLibraryTask : |
| DeviceProviderInstrumentTestTask, |
| testVariantData, |
| baseVariantData, |
| new ConnectedDeviceProvider(getSdkParser()), |
| CONNECTED |
| ) |
| |
| mainConnectedTask.dependsOn connectedTask |
| testVariantData.connectedTestTask = connectedTask |
| |
| // now the providers. |
| for (DeviceProvider deviceProvider : providers) { |
| DefaultTask providerTask = createDeviceProviderInstrumentTestTask( |
| hasFlavors ? |
| "${deviceProvider.name}${INSTRUMENT_TEST.capitalize()}${baseVariantData.variantConfiguration.fullName.capitalize()}" : |
| "${deviceProvider.name}${INSTRUMENT_TEST.capitalize()}", |
| "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' using Provider '${deviceProvider.name.capitalize()}'.", |
| isLibraryTest ? |
| DeviceProviderInstrumentTestLibraryTask : |
| DeviceProviderInstrumentTestTask, |
| testVariantData, |
| baseVariantData, |
| deviceProvider, |
| "$DEVICE/$deviceProvider.name" |
| ) |
| |
| mainProviderTask.dependsOn providerTask |
| testVariantData.providerTestTaskList.add(providerTask) |
| |
| if (!deviceProvider.isConfigured()) { |
| providerTask.enabled = false; |
| } |
| } |
| |
| // now the test servers |
| // don't use an auto loop as it'll break the closure inside. |
| for (TestServer testServer : servers) { |
| DefaultTask serverTask = project.tasks.create( |
| hasFlavors ? |
| "${testServer.name}${"upload".capitalize()}${baseVariantData.variantConfiguration.fullName}" : |
| "${testServer.name}${"upload".capitalize()}", |
| TestServerTask) |
| serverTask.description = "Uploads APKs for Build '${baseVariantData.variantConfiguration.fullName}' to Test Server '${testServer.name.capitalize()}'." |
| serverTask.group = JavaBasePlugin.VERIFICATION_GROUP |
| serverTask.dependsOn testVariantData.assembleTask, baseVariantData.assembleTask |
| |
| serverTask.testServer = testServer |
| |
| serverTask.conventionMapping.testApk = { testVariantData.outputFile } |
| if (!(baseVariantData instanceof LibraryVariantData)) { |
| serverTask.conventionMapping.testedApk = { baseVariantData.outputFile } |
| } |
| |
| serverTask.conventionMapping.variantName = { baseVariantData.variantConfiguration.fullName } |
| |
| deviceCheck.dependsOn serverTask |
| |
| if (!testServer.isConfigured()) { |
| serverTask.enabled = false; |
| } |
| } |
| } |
| } |
| |
| // If gradle is launched with --continue, we want to run all tests and generate an |
| // aggregate report (to help with the fact that we may have several build variants, or |
| // or several device providers). |
| // To do that, the report tasks must run even if one of their dependent tasks (flavor |
| // or specific provider tasks) fails, when --continue is used, and the report task is |
| // meant to run (== is in the task graph). |
| // To do this, we make the children tasks ignore their errors (ie they won't fail and |
| // stop the build). |
| if (!reportTasks.isEmpty() && project.gradle.startParameter.continueOnFailure) { |
| project.gradle.taskGraph.whenReady { taskGraph -> |
| for (AndroidReportTask reportTask : reportTasks) { |
| if (taskGraph.hasTask(reportTask)) { |
| reportTask.setWillRun() |
| } |
| } |
| } |
| } |
| } |
| |
| private DeviceProviderInstrumentTestTask createDeviceProviderInstrumentTestTask( |
| @NonNull String taskName, |
| @NonNull String description, |
| @NonNull Class<? extends DeviceProviderInstrumentTestTask> taskClass, |
| @NonNull TestVariantData variantData, |
| @NonNull BaseVariantData testedVariantData, |
| @NonNull DeviceProvider deviceProvider, |
| @NonNull String subFolder) { |
| |
| def testTask = project.tasks.create(taskName, taskClass) |
| testTask.description = description |
| testTask.group = JavaBasePlugin.VERIFICATION_GROUP |
| testTask.dependsOn testedVariantData.assembleTask, variantData.assembleTask |
| |
| testTask.plugin = this |
| testTask.variant = variantData |
| testTask.flavorName = variantData.variantConfiguration.flavorName.capitalize() |
| testTask.deviceProvider = deviceProvider |
| |
| testTask.conventionMapping.testApp = { variantData.outputFile } |
| if (testedVariantData.variantConfiguration.type != VariantConfiguration.Type.LIBRARY) { |
| testTask.conventionMapping.testedApp = { testedVariantData.outputFile } |
| } |
| |
| testTask.conventionMapping.resultsDir = { |
| String rootLocation = extension.testOptions.resultsDir != null ? |
| extension.testOptions.resultsDir : |
| "$project.buildDir/$FD_INSTRUMENT_RESULTS" |
| |
| String flavorFolder = variantData.variantConfiguration.flavorName |
| if (!flavorFolder.isEmpty()) { |
| flavorFolder = "$FD_FLAVORS/" + flavorFolder |
| } |
| |
| project.file("$rootLocation/$subFolder/$flavorFolder") |
| } |
| testTask.conventionMapping.reportsDir = { |
| String rootLocation = extension.testOptions.reportDir != null ? |
| extension.testOptions.reportDir : |
| "$project.buildDir/$FD_REPORTS/$FD_INSTRUMENT_TESTS" |
| |
| String flavorFolder = variantData.variantConfiguration.flavorName |
| if (!flavorFolder.isEmpty()) { |
| flavorFolder = "$FD_FLAVORS/" + flavorFolder |
| } |
| |
| project.file("$rootLocation/$subFolder/$flavorFolder") |
| } |
| |
| return testTask |
| } |
| |
| /** |
| * Creates the packaging tasks for the given Variant. |
| * @param variantData the variant data. |
| * @param assembleTask an optional assembleTask to be used. If null a new one is created. The |
| * assembleTask is always set in the Variant. |
| */ |
| protected void addPackageTasks(@NonNull ApkVariantData variantData, |
| @Nullable Task assembleTask) { |
| VariantConfiguration variantConfig = variantData.variantConfiguration |
| |
| boolean runProguard = variantConfig.buildType.runProguard && |
| (variantConfig.type != VariantConfiguration.Type.TEST || |
| (variantConfig.type == VariantConfiguration.Type.TEST && |
| variantConfig.testedConfig.type != VariantConfiguration.Type.LIBRARY)) |
| |
| // common dex task configuration |
| String dexTaskName = "dex${variantData.variantConfiguration.fullName.capitalize()}" |
| Dex dexTask = project.tasks.create(dexTaskName, Dex) |
| variantData.dexTask = dexTask |
| |
| dexTask.plugin = this |
| dexTask.variant = variantData |
| |
| dexTask.conventionMapping.outputFile = { |
| project.file( |
| "${project.buildDir}/libs/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.dex") |
| } |
| dexTask.dexOptions = extension.dexOptions |
| |
| if (runProguard) { |
| |
| // first proguard task. |
| BaseVariantData testedVariantData = variantData instanceof TestVariantData ? variantData.testedVariantData : null as BaseVariantData |
| File outFile = createProguardTasks(variantData, testedVariantData) |
| |
| // then dexing task |
| dexTask.dependsOn variantData.proguardTask |
| dexTask.conventionMapping.inputFiles = { project.files(outFile) } |
| dexTask.conventionMapping.preDexedLibraries = { Collections.emptyList() } |
| |
| } else { |
| |
| // if required, pre-dexing task. |
| PreDex preDexTask = null; |
| boolean runPreDex = extension.dexOptions.preDexLibraries |
| if (runPreDex) { |
| def preDexTaskName = "preDex${variantData.variantConfiguration.fullName.capitalize()}" |
| preDexTask = project.tasks.create(preDexTaskName, PreDex) |
| |
| preDexTask.dependsOn variantData.javaCompileTask |
| preDexTask.plugin = this |
| preDexTask.dexOptions = extension.dexOptions |
| |
| preDexTask.conventionMapping.inputFiles = { |
| project.files(getAndroidBuilder(variantData).getPackagedJars(variantConfig)) |
| } |
| preDexTask.conventionMapping.outputFolder = { |
| project.file( |
| "${project.buildDir}/pre-dexed/${variantData.variantConfiguration.dirName}") |
| } |
| } |
| |
| // then dexing task |
| dexTask.dependsOn variantData.javaCompileTask |
| if (runPreDex) { |
| dexTask.dependsOn preDexTask |
| } |
| |
| dexTask.conventionMapping.inputFiles = { variantData.javaCompileTask.outputs.files } |
| if (runPreDex) { |
| dexTask.conventionMapping.preDexedLibraries = { |
| project.fileTree(preDexTask.outputFolder).files |
| } |
| } else { |
| dexTask.conventionMapping.preDexedLibraries = { |
| project.files(getAndroidBuilder(variantData).getPackagedJars(variantConfig)) |
| } |
| } |
| } |
| |
| // Add a task to generate application package |
| def packageApp = project.tasks.create( |
| "package${variantData.variantConfiguration.fullName.capitalize()}", |
| PackageApplication) |
| variantData.packageApplicationTask = packageApp |
| packageApp.dependsOn variantData.processResourcesTask, dexTask, variantData.processJavaResourcesTask, variantData.ndkCompileTask |
| |
| packageApp.plugin = this |
| packageApp.variant = variantData |
| |
| packageApp.conventionMapping.resourceFile = { |
| variantData.processResourcesTask.packageOutputFile |
| } |
| packageApp.conventionMapping.dexFile = { dexTask.outputFile } |
| packageApp.conventionMapping.packagedJars = { getAndroidBuilder(variantData).getPackagedJars(variantConfig) } |
| packageApp.conventionMapping.javaResourceDir = { |
| getOptionalDir(variantData.processJavaResourcesTask.destinationDir) |
| } |
| packageApp.conventionMapping.jniFolders = { |
| // for now only the project's compilation output. |
| Set<File> set = Sets.newHashSet() |
| set.addAll(variantData.ndkCompileTask.soFolder) |
| set.addAll(variantData.renderscriptCompileTask.libOutputDir) |
| set.addAll(variantConfig.libraryJniFolders) |
| |
| if (variantConfig.mergedFlavor.renderscriptSupportMode) { |
| File rsLibs = getAndroidBuilder(variantData).getSupportNativeLibFolder() |
| if (rsLibs.isDirectory()) { |
| set.add(rsLibs); |
| } |
| } |
| |
| return set |
| } |
| packageApp.conventionMapping.abiFilters = { variantConfig.supportedAbis } |
| packageApp.conventionMapping.jniDebugBuild = { variantConfig.buildType.jniDebugBuild } |
| |
| SigningConfigDsl sc = (SigningConfigDsl) variantConfig.signingConfig |
| packageApp.conventionMapping.signingConfig = { sc } |
| if (sc != null) { |
| ValidateSigningTask validateSigningTask = validateSigningTaskMap.get(sc) |
| if (validateSigningTask == null) { |
| validateSigningTask = project.tasks.create("validate${sc.name.capitalize()}Signing", |
| ValidateSigningTask) |
| validateSigningTask.plugin = this |
| validateSigningTask.signingConfig = sc |
| |
| validateSigningTaskMap.put(sc, validateSigningTask) |
| } |
| |
| packageApp.dependsOn validateSigningTask |
| } |
| |
| def signedApk = variantData.isSigned() |
| def apkName = signedApk ? |
| "${project.archivesBaseName}-${variantData.variantConfiguration.baseName}-unaligned.apk" : |
| "${project.archivesBaseName}-${variantData.variantConfiguration.baseName}-unsigned.apk" |
| |
| packageApp.conventionMapping.outputFile = { |
| project.file("$project.buildDir/apk/${apkName}") |
| } |
| |
| Task appTask = packageApp |
| OutputFileTask outputFileTask = packageApp |
| |
| if (signedApk) { |
| if (variantData.zipAlign) { |
| // Add a task to zip align application package |
| def zipAlignTask = project.tasks.create( |
| "zipalign${variantData.variantConfiguration.fullName.capitalize()}", |
| ZipAlign) |
| variantData.zipAlignTask = zipAlignTask |
| |
| zipAlignTask.dependsOn packageApp |
| zipAlignTask.conventionMapping.inputFile = { packageApp.outputFile } |
| zipAlignTask.conventionMapping.outputFile = { |
| project.file( |
| "$project.buildDir/apk/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.apk") |
| } |
| zipAlignTask.conventionMapping.zipAlignExe = { getSdkParser().zipAlign } |
| |
| appTask = zipAlignTask |
| outputFileTask = zipAlignTask |
| variantData.outputFile = project.file( |
| "$project.buildDir/apk/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.apk") |
| } |
| |
| // Add a task to install the application package |
| def installTask = project.tasks.create( |
| "install${variantData.variantConfiguration.fullName.capitalize()}", |
| InstallTask) |
| installTask.description = "Installs the " + variantData.description |
| installTask.group = INSTALL_GROUP |
| installTask.dependsOn appTask |
| installTask.conventionMapping.packageFile = { outputFileTask.outputFile } |
| installTask.conventionMapping.adbExe = { getSdkParser().adb } |
| |
| variantData.installTask = installTask |
| } |
| |
| // Add an assemble task |
| if (assembleTask == null) { |
| assembleTask = project.tasks.create("assemble${variantData.variantConfiguration.fullName.capitalize()}") |
| assembleTask.description = "Assembles the " + variantData.description |
| assembleTask.group = org.gradle.api.plugins.BasePlugin.BUILD_GROUP |
| } |
| assembleTask.dependsOn appTask |
| variantData.assembleTask = assembleTask |
| |
| variantData.outputFile = { outputFileTask.outputFile } |
| |
| // add an uninstall task |
| def uninstallTask = project.tasks.create( |
| "uninstall${variantData.variantConfiguration.fullName.capitalize()}", |
| UninstallTask) |
| uninstallTask.description = "Uninstalls the " + variantData.description |
| uninstallTask.group = INSTALL_GROUP |
| uninstallTask.variant = variantData |
| uninstallTask.conventionMapping.adbExe = { getSdkParser().adb } |
| |
| variantData.uninstallTask = uninstallTask |
| uninstallAll.dependsOn uninstallTask |
| } |
| |
| /** |
| * Creates the proguarding task for the given Variant. |
| * @param variantData the variant data. |
| * @param testedVariantData optional. variant data representing the tested variant, null if the |
| * variant is not a test variant |
| * @return outFile file outputted by proguard |
| */ |
| @NonNull |
| protected File createProguardTasks(@NonNull BaseVariantData variantData, |
| @Nullable BaseVariantData testedVariantData) { |
| VariantConfiguration variantConfig = variantData.variantConfiguration |
| |
| def proguardTask = project.tasks.create( |
| "proguard${variantData.variantConfiguration.fullName.capitalize()}", |
| ProGuardTask) |
| proguardTask.dependsOn variantData.javaCompileTask |
| if (testedVariantData != null) { |
| proguardTask.dependsOn testedVariantData.proguardTask |
| } |
| |
| variantData.proguardTask = proguardTask |
| |
| // --- Output File --- |
| |
| File outFile; |
| if (variantData instanceof LibraryVariantData) { |
| outFile = project.file( |
| "${project.buildDir}/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/classes.jar") |
| } else { |
| outFile = project.file( |
| "${project.buildDir}/classes-proguard/${variantData.variantConfiguration.dirName}/classes.jar") |
| } |
| |
| // --- Proguard Config --- |
| |
| if (testedVariantData != null) { |
| // don't remove any code in tested app |
| proguardTask.dontshrink() |
| proguardTask.keepnames("class * extends junit.framework.TestCase") |
| proguardTask.keepclassmembers("class * extends junit.framework.TestCase {\n" + |
| " void test*(...);\n" + |
| "}") |
| |
| // input the mapping from the tested app so that we can deal with obfuscated code |
| proguardTask.applymapping("${project.buildDir}/proguard/${testedVariantData.variantConfiguration.dirName}/mapping.txt") |
| |
| // for tested app, we only care about their aapt config since the base |
| // configs are the same files anyway. |
| proguardTask.configuration(testedVariantData.processResourcesTask.proguardOutputFile) |
| } |
| |
| // all the config files coming from build type, product flavors. |
| List<Object> proguardFiles = variantConfig.getProguardFiles(true /*includeLibs*/) |
| for (Object proguardFile : proguardFiles) { |
| proguardTask.configuration(proguardFile) |
| } |
| |
| // also the config file output by aapt |
| proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile) |
| |
| // --- InJars / LibraryJars --- |
| |
| List<File> packagedJars = getAndroidBuilder(variantData).getPackagedJars(variantConfig) |
| |
| if (variantData instanceof LibraryVariantData) { |
| String packageName = variantConfig.getPackageFromManifest() |
| if (packageName == null) { |
| throw new BuildException("Failed to read manifest", null) |
| } |
| packageName = packageName.replace('.', '/'); |
| |
| // injar: the compilation output |
| // exclude R files and such from output |
| String exclude = '!' + packageName + "/R.class" |
| exclude += (', !' + packageName + "/R\$*.class") |
| exclude += (', !' + packageName + "/Manifest.class") |
| exclude += (', !' + packageName + "/Manifest\$*.class") |
| exclude += (', !' + packageName + "/BuildConfig.class") |
| proguardTask.injars(variantData.javaCompileTask.destinationDir, filter: exclude) |
| |
| // include R files and such for compilation |
| String include = exclude.replace('!', '') |
| proguardTask.libraryjars(variantData.javaCompileTask.destinationDir, filter: include) |
| |
| // injar: the local dependencies, filter out local jars from packagedJars |
| Object[] jars = LibraryPlugin.getLocalJarFileList(variantData.variantDependency) |
| for (Object inJar : jars) { |
| if (packagedJars.contains(inJar)) { |
| packagedJars.remove(inJar); |
| } |
| proguardTask.injars((File) inJar, filter: '!META-INF/MANIFEST.MF') |
| } |
| |
| // libjar: the library dependencies |
| for (File libJar : packagedJars) { |
| proguardTask.libraryjars(libJar, filter: '!META-INF/MANIFEST.MF') |
| } |
| |
| // ensure local jars keep their package names |
| proguardTask.keeppackagenames() |
| } else { |
| // injar: the compilation output |
| proguardTask.injars(variantData.javaCompileTask.destinationDir) |
| |
| // injar: the dependencies |
| for (File inJar : packagedJars) { |
| proguardTask.injars(inJar, filter: '!META-INF/MANIFEST.MF') |
| } |
| } |
| |
| // libraryJars: the runtime jars |
| for (String runtimeJar : getRuntimeJarList()) { |
| proguardTask.libraryjars(runtimeJar) |
| } |
| |
| if (testedVariantData != null) { |
| // input the tested app as library |
| proguardTask.libraryjars(testedVariantData.javaCompileTask.destinationDir) |
| // including its dependencies |
| List<File> testedPackagedJars = getAndroidBuilder(testedVariantData).getPackagedJars(testedVariantData.variantConfiguration) |
| for (File inJar : testedPackagedJars) { |
| proguardTask.libraryjars(inJar, filter: '!META-INF/MANIFEST.MF') |
| } |
| } |
| |
| // --- Out files --- |
| |
| proguardTask.outjars(outFile) |
| |
| proguardTask.dump("${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/dump.txt") |
| proguardTask.printseeds( |
| "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/seeds.txt") |
| proguardTask.printusage( |
| "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/usage.txt") |
| proguardTask.printmapping( |
| "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/mapping.txt") |
| |
| return outFile |
| } |
| |
| private void createReportTasks() { |
| def dependencyReportTask = project.tasks.create("androidDependencies", DependencyReportTask) |
| dependencyReportTask.setDescription("Displays the Android dependencies of the project") |
| dependencyReportTask.setVariants(variantDataList) |
| dependencyReportTask.setGroup("Android") |
| |
| def signingReportTask = project.tasks.create("signingReport", SigningReportTask) |
| signingReportTask.setDescription("Displays the signing info for each variant") |
| signingReportTask.setVariants(variantDataList) |
| signingReportTask.setGroup("Android") |
| } |
| |
| protected void createAnchorTasks(@NonNull BaseVariantData variantData) { |
| variantData.preBuildTask = project.tasks.create( |
| "pre${variantData.variantConfiguration.fullName.capitalize()}Build") |
| variantData.preBuildTask.dependsOn mainPreBuild |
| |
| def prepareDependenciesTask = project.tasks.create( |
| "prepare${variantData.variantConfiguration.fullName.capitalize()}Dependencies", |
| PrepareDependenciesTask) |
| |
| variantData.prepareDependenciesTask = prepareDependenciesTask |
| prepareDependenciesTask.dependsOn variantData.preBuildTask |
| |
| prepareDependenciesTask.plugin = this |
| prepareDependenciesTask.variant = variantData |
| |
| // for all libraries required by the configurations of this variant, make this task |
| // depend on all the tasks preparing these libraries. |
| VariantDependencies configurationDependencies = variantData.variantDependency |
| prepareDependenciesTask.addChecker(configurationDependencies.checker) |
| |
| for (LibraryDependencyImpl lib : configurationDependencies.libraries) { |
| addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib) |
| } |
| |
| // also create sourceGenTask |
| variantData.sourceGenTask = project.tasks.create( |
| "generate${variantData.variantConfiguration.fullName.capitalize()}Sources") |
| } |
| |
| |
| private final Map<String, ArtifactMetaData> extraArtifactMap = Maps.newHashMap() |
| private final ListMultimap<String, AndroidArtifact> extraAndroidArtifacts = ArrayListMultimap.create() |
| private final ListMultimap<String, JavaArtifact> extraJavaArtifacts = ArrayListMultimap.create() |
| private final ListMultimap<String, SourceProviderContainer> extraVariantSourceProviders = ArrayListMultimap.create() |
| private final ListMultimap<String, SourceProviderContainer> extraBuildTypeSourceProviders = ArrayListMultimap.create() |
| private final ListMultimap<String, SourceProviderContainer> extraProductFlavorSourceProviders = ArrayListMultimap.create() |
| private final ListMultimap<String, SourceProviderContainer> extraMultiFlavorSourceProviders = ArrayListMultimap.create() |
| |
| |
| public Collection<ArtifactMetaData> getExtraArtifacts() { |
| return extraArtifactMap.values() |
| } |
| |
| public Collection<AndroidArtifact> getExtraAndroidArtifacts(@NonNull String variantName) { |
| return extraAndroidArtifacts.get(variantName) |
| } |
| |
| public Collection<JavaArtifact> getExtraJavaArtifacts(@NonNull String variantName) { |
| return extraJavaArtifacts.get(variantName) |
| } |
| |
| public Collection<SourceProviderContainer> getExtraVariantSourceProviders(@NonNull String variantName) { |
| return extraVariantSourceProviders.get(variantName) |
| } |
| |
| public Collection<SourceProviderContainer> getExtraFlavorSourceProviders(@NonNull String flavorName) { |
| return extraProductFlavorSourceProviders.get(flavorName) |
| } |
| |
| public Collection<SourceProviderContainer> getExtraBuildTypeSourceProviders(@NonNull String buildTypeName) { |
| return extraBuildTypeSourceProviders.get(buildTypeName) |
| } |
| |
| public void registerArtifactType(@NonNull String name, |
| boolean isTest, |
| int artifactType) { |
| |
| if (extraArtifactMap.get(name) != null) { |
| throw new IllegalArgumentException("Artifact with name $name already registered.") |
| } |
| |
| extraArtifactMap.put(name, new ArtifactMetaDataImpl(name, isTest, artifactType)) |
| } |
| |
| public void registerBuildTypeSourceProvider(@NonNull String name, |
| @NonNull BuildType buildType, |
| @NonNull SourceProvider sourceProvider) { |
| if (extraArtifactMap.get(name) == null) { |
| throw new IllegalArgumentException( |
| "Artifact with name $name is not yet registered. Use registerArtifactType()") |
| } |
| |
| extraBuildTypeSourceProviders.put(buildType.name, |
| new DefaultSourceProviderContainer(name, sourceProvider)) |
| |
| } |
| |
| public void registerProductFlavorSourceProvider(@NonNull String name, |
| @NonNull ProductFlavor productFlavor, |
| @NonNull SourceProvider sourceProvider) { |
| if (extraArtifactMap.get(name) == null) { |
| throw new IllegalArgumentException( |
| "Artifact with name $name is not yet registered. Use registerArtifactType()") |
| } |
| |
| extraProductFlavorSourceProviders.put(productFlavor.name, |
| new DefaultSourceProviderContainer(name, sourceProvider)) |
| |
| } |
| |
| public void registerMultiFlavorSourceProvider(@NonNull String name, |
| @NonNull String flavorName, |
| @NonNull SourceProvider sourceProvider) { |
| if (extraArtifactMap.get(name) == null) { |
| throw new IllegalArgumentException( |
| "Artifact with name $name is not yet registered. Use registerArtifactType()") |
| } |
| |
| extraMultiFlavorSourceProviders.put(flavorName, |
| new DefaultSourceProviderContainer(name, sourceProvider)) |
| } |
| |
| public void registerJavaArtifact( |
| @NonNull String name, |
| @NonNull BaseVariant variant, |
| @NonNull String assembleTaskName, |
| @NonNull String javaCompileTaskName, |
| @NonNull File classesFolder, |
| @Nullable SourceProvider sourceProvider) { |
| ArtifactMetaData artifactMetaData = extraArtifactMap.get(name) |
| if (artifactMetaData == null) { |
| throw new IllegalArgumentException( |
| "Artifact with name $name is not yet registered. Use registerArtifactType()") |
| } |
| if (artifactMetaData.type != ArtifactMetaData.TYPE_JAVA) { |
| throw new IllegalArgumentException( |
| "Artifact with name $name is not of type JAVA") |
| } |
| |
| JavaArtifact artifact = new JavaArtifactImpl( |
| name, assembleTaskName, javaCompileTaskName, classesFolder, |
| null, sourceProvider, null) |
| extraJavaArtifacts.put(variant.name, artifact) |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| //------------------------------ START DEPENDENCY STUFF ---------------------------------------- |
| //---------------------------------------------------------------------------------------------- |
| |
| def addDependencyToPrepareTask(@NonNull BaseVariantData variantData, |
| @NonNull PrepareDependenciesTask prepareDependenciesTask, |
| @NonNull LibraryDependencyImpl lib) { |
| def prepareLibTask = prepareTaskMap.get(lib) |
| if (prepareLibTask != null) { |
| prepareDependenciesTask.dependsOn prepareLibTask |
| prepareLibTask.dependsOn variantData.preBuildTask |
| } |
| |
| for (LibraryDependencyImpl childLib : lib.dependencies) { |
| addDependencyToPrepareTask(variantData, prepareDependenciesTask, childLib) |
| } |
| } |
| |
| def resolveDependencies(VariantDependencies variantDeps) { |
| Map<ModuleVersionIdentifier, List<LibraryDependencyImpl>> modules = [:] |
| Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts = [:] |
| Multimap<LibraryDependency, VariantDependencies> reverseMap = ArrayListMultimap.create() |
| |
| resolveDependencyForConfig(variantDeps, modules, artifacts, reverseMap) |
| |
| modules.values().each { List list -> |
| |
| if (!list.isEmpty()) { |
| // get the first item only |
| LibraryDependencyImpl androidDependency = (LibraryDependencyImpl) list.get(0) |
| |
| String bundleName = GUtil.toCamelCase(androidDependency.name.replaceAll("\\:", " ")) |
| |
| def prepareLibraryTask = prepareTaskMap.get(androidDependency) |
| if (prepareLibraryTask == null) { |
| prepareLibraryTask = project.tasks.create("prepare${bundleName}Library", |
| PrepareLibraryTask) |
| prepareLibraryTask.description = "Prepare ${androidDependency.name}" |
| prepareLibraryTask.bundle = androidDependency.bundle |
| prepareLibraryTask.explodedDir = androidDependency.bundleFolder |
| |
| prepareTaskMap.put(androidDependency, prepareLibraryTask) |
| } |
| |
| // Use the reverse map to find all the configurations that included this android |
| // library so that we can make sure they are built. |
| List<VariantDependencies> configDepList = reverseMap.get(androidDependency) |
| if (configDepList != null && !configDepList.isEmpty()) { |
| for (VariantDependencies configDependencies: configDepList) { |
| prepareLibraryTask.dependsOn configDependencies.compileConfiguration.buildDependencies |
| } |
| } |
| } |
| } |
| } |
| |
| def resolveDependencyForConfig( |
| VariantDependencies variantDeps, |
| Map<ModuleVersionIdentifier, List<LibraryDependencyImpl>> modules, |
| Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts, |
| Multimap<LibraryDependency, VariantDependencies> reverseMap) { |
| |
| Configuration compileClasspath = variantDeps.compileConfiguration |
| |
| // TODO - shouldn't need to do this - fix this in Gradle |
| ensureConfigured(compileClasspath) |
| |
| variantDeps.checker = new DependencyChecker(variantDeps, logger) |
| |
| Set<String> currentUnresolvedDependencies = Sets.newHashSet() |
| |
| // TODO - defer downloading until required -- This is hard to do as we need the info to build the variant config. |
| List<LibraryDependencyImpl> bundles = [] |
| List<JarDependency> jars = [] |
| List<JarDependency> localJars = [] |
| collectArtifacts(compileClasspath, artifacts) |
| def dependencies = compileClasspath.incoming.resolutionResult.root.dependencies |
| dependencies.each { DependencyResult dep -> |
| if (dep instanceof ResolvedDependencyResult) { |
| addDependency(dep.selected, variantDeps, bundles, jars, modules, artifacts, reverseMap) |
| } else if (dep instanceof UnresolvedDependencyResult) { |
| def attempted = dep.attempted; |
| if (attempted != null) { |
| currentUnresolvedDependencies.add(attempted.toString()) |
| } |
| } |
| } |
| |
| // also need to process local jar files, as they are not processed by the |
| // resolvedConfiguration result. This only includes the local jar files for this project. |
| compileClasspath.allDependencies.each { dep -> |
| if (dep instanceof SelfResolvingDependency && |
| !(dep instanceof ProjectDependency)) { |
| Set<File> files = ((SelfResolvingDependency) dep).resolve() |
| for (File f : files) { |
| // TODO: support compile only dependencies. |
| localJars << new JarDependency(f) |
| } |
| } |
| } |
| |
| // handle package dependencies. We'll refuse aar libs only in package but not |
| // in compile and remove all dependencies already in compile to get package-only jar |
| // files. |
| Configuration packageClasspath = variantDeps.packageConfiguration |
| |
| if (!compileClasspath.resolvedConfiguration.hasError()) { |
| Set<File> compileFiles = compileClasspath.files |
| Set<File> packageFiles = packageClasspath.files |
| |
| for (File f : packageFiles) { |
| if (compileFiles.contains(f)) { |
| continue |
| } |
| |
| if (f.getName().toLowerCase().endsWith(".jar")) { |
| jars.add(new JarDependency(f, false /*compiled*/, true /*packaged*/)) |
| } else { |
| throw new RuntimeException("Package-only dependency '" + |
| f.absolutePath + |
| "' is not supported") |
| } |
| } |
| } else if (!currentUnresolvedDependencies.isEmpty()) { |
| unresolvedDependencies.addAll(currentUnresolvedDependencies) |
| } |
| |
| variantDeps.addLibraries(bundles) |
| variantDeps.addJars(jars) |
| variantDeps.addLocalJars(localJars) |
| |
| // TODO - filter bundles out of source set classpath |
| |
| configureBuild(variantDeps) |
| } |
| |
| def ensureConfigured(Configuration config) { |
| config.allDependencies.withType(ProjectDependency).each { dep -> |
| project.evaluationDependsOn(dep.dependencyProject.path) |
| ensureConfigured(dep.projectConfiguration) |
| } |
| } |
| |
| static def collectArtifacts(Configuration configuration, Map<ModuleVersionIdentifier, |
| List<ResolvedArtifact>> artifacts) { |
| boolean buildModelOnly = Boolean.getBoolean(AndroidProject.BUILD_MODEL_ONLY_SYSTEM_PROPERTY); |
| def allArtifacts |
| if (buildModelOnly) { |
| allArtifacts = configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.satisfyAll()) |
| } else { |
| allArtifacts = configuration.resolvedConfiguration.resolvedArtifacts |
| } |
| |
| allArtifacts.each { ResolvedArtifact artifact -> |
| def id = artifact.moduleVersion.id |
| List<ResolvedArtifact> moduleArtifacts = artifacts[id] |
| if (moduleArtifacts == null) { |
| moduleArtifacts = [] |
| artifacts[id] = moduleArtifacts |
| } |
| moduleArtifacts << artifact |
| } |
| } |
| |
| def addDependency(ResolvedModuleVersionResult moduleVersion, |
| VariantDependencies configDependencies, |
| Collection<LibraryDependencyImpl> bundles, |
| List<JarDependency> jars, |
| Map<ModuleVersionIdentifier, List<LibraryDependencyImpl>> modules, |
| Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts, |
| Multimap<LibraryDependency, VariantDependencies> reverseMap) { |
| def id = moduleVersion.id |
| if (configDependencies.checker.excluded(id)) { |
| return |
| } |
| |
| List<LibraryDependencyImpl> bundlesForThisModule = modules[id] |
| if (bundlesForThisModule == null) { |
| bundlesForThisModule = [] |
| modules[id] = bundlesForThisModule |
| |
| def nestedBundles = [] |
| def dependencies = moduleVersion.dependencies |
| dependencies.each { DependencyResult dep -> |
| if (dep instanceof ResolvedDependencyResult) { |
| addDependency(dep.selected, configDependencies, nestedBundles, |
| jars, modules, artifacts, reverseMap) |
| } |
| } |
| |
| def moduleArtifacts = artifacts[id] |
| moduleArtifacts?.each { artifact -> |
| if (artifact.type == EXT_LIB_ARCHIVE) { |
| String bundleName = GUtil.toCamelCase(id.group + " " + id.name + " " + id.version) |
| def explodedDir = project.file( |
| "$project.buildDir/exploded-bundles/${bundleName}.aar") |
| LibraryDependencyImpl adep = new LibraryDependencyImpl( |
| artifact.file, explodedDir, nestedBundles, |
| id.group + ":" + id.name + ":" + id.version) |
| bundlesForThisModule << adep |
| reverseMap.put(adep, configDependencies) |
| } else { |
| // TODO: support compile only dependencies. |
| jars << new JarDependency(artifact.file) |
| } |
| } |
| |
| if (bundlesForThisModule.empty && !nestedBundles.empty) { |
| throw new GradleException("Module version $id depends on libraries but is not a library itself") |
| } |
| } else { |
| for (LibraryDependency adep : bundlesForThisModule) { |
| reverseMap.put(adep, configDependencies) |
| } |
| } |
| |
| bundles.addAll(bundlesForThisModule) |
| } |
| |
| private void configureBuild(VariantDependencies configurationDependencies) { |
| addDependsOnTaskInOtherProjects( |
| project.getTasks().getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME), true, |
| JavaBasePlugin.BUILD_NEEDED_TASK_NAME, "compile"); |
| addDependsOnTaskInOtherProjects( |
| project.getTasks().getByName(JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME), false, |
| JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME, "compile"); |
| } |
| |
| /** |
| * Adds a dependency on tasks with the specified name in other projects. The other projects |
| * are determined from project lib dependencies using the specified configuration name. |
| * These may be projects this project depends on or projects that depend on this project |
| * based on the useDependOn argument. |
| * |
| * @param task Task to add dependencies to |
| * @param useDependedOn if true, add tasks from projects this project depends on, otherwise |
| * use projects that depend on this one. |
| * @param otherProjectTaskName name of task in other projects |
| * @param configurationName name of configuration to use to find the other projects |
| */ |
| private static void addDependsOnTaskInOtherProjects(final Task task, boolean useDependedOn, |
| String otherProjectTaskName, |
| String configurationName) { |
| Project project = task.getProject(); |
| final Configuration configuration = project.getConfigurations().getByName( |
| configurationName); |
| task.dependsOn(configuration.getTaskDependencyFromProjectDependency( |
| useDependedOn, otherProjectTaskName)); |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| //------------------------------- END DEPENDENCY STUFF ----------------------------------------- |
| //---------------------------------------------------------------------------------------------- |
| |
| protected static File getOptionalDir(File dir) { |
| if (dir.isDirectory()) { |
| return dir |
| } |
| |
| return null |
| } |
| |
| @NonNull |
| protected List<ManifestDependencyImpl> getManifestDependencies( |
| List<LibraryDependency> libraries) { |
| |
| List<ManifestDependencyImpl> list = Lists.newArrayListWithCapacity(libraries.size()) |
| |
| for (LibraryDependency lib : libraries) { |
| // get the dependencies |
| List<ManifestDependencyImpl> children = getManifestDependencies(lib.dependencies) |
| list.add(new ManifestDependencyImpl(lib.manifest, children)) |
| } |
| |
| return list |
| } |
| |
| @NonNull |
| protected static List<SymbolFileProviderImpl> getTextSymbolDependencies( |
| List<LibraryDependency> libraries) { |
| |
| List<SymbolFileProviderImpl> list = Lists.newArrayListWithCapacity(libraries.size()) |
| |
| for (LibraryDependency lib : libraries) { |
| list.add(new SymbolFileProviderImpl(lib.manifest, lib.symbolFile)) |
| } |
| |
| return list |
| } |
| |
| private static String getLocalVersion() { |
| try { |
| Class clazz = BasePlugin.class |
| String className = clazz.getSimpleName() + ".class" |
| String classPath = clazz.getResource(className).toString() |
| if (!classPath.startsWith("jar")) { |
| // Class not from JAR, unlikely |
| return null |
| } |
| String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + |
| "/META-INF/MANIFEST.MF"; |
| Manifest manifest = new Manifest(new URL(manifestPath).openStream()); |
| Attributes attr = manifest.getMainAttributes(); |
| return attr.getValue("Plugin-Version"); |
| } catch (Throwable t) { |
| return null; |
| } |
| } |
| |
| public Project getProject() { |
| return project |
| } |
| } |