blob: ef9857befe98f5c24e1149ec2443e4101285a07a [file] [log] [blame]
/*
* Copyright (C) 2014 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.model
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.api.AndroidSourceDirectorySet
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.BuildTypeData
import com.android.build.gradle.internal.ExtraModelInfo
import com.android.build.gradle.internal.LibraryCache
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.ProductFlavorData
import com.android.build.gradle.internal.SdkHandler
import com.android.build.gradle.internal.TaskManager
import com.android.build.gradle.internal.VariantManager
import com.android.build.gradle.internal.coverage.JacocoPlugin
import com.android.build.gradle.internal.dsl.BuildType
import com.android.build.gradle.internal.dsl.GroupableProductFlavor
import com.android.build.gradle.internal.dsl.SigningConfig
import com.android.build.gradle.internal.dsl.SigningConfigFactory
import com.android.build.gradle.internal.model.ModelBuilder
import com.android.build.gradle.internal.process.GradleJavaProcessExecutor
import com.android.build.gradle.internal.process.GradleProcessExecutor
import com.android.build.gradle.internal.profile.RecordingBuildListener
import com.android.build.gradle.internal.tasks.DependencyReportTask
import com.android.build.gradle.internal.tasks.SigningReportTask
import com.android.build.gradle.internal.variant.VariantFactory
import com.android.build.gradle.ndk.NdkExtension
import com.android.build.gradle.tasks.JillTask
import com.android.build.gradle.tasks.PreDex
import com.android.builder.core.AndroidBuilder
import com.android.builder.core.BuilderConstants
import com.android.builder.internal.compiler.JackConversionCache
import com.android.builder.internal.compiler.PreDexCache
import com.android.builder.profile.ExecutionType
import com.android.builder.profile.ProcessRecorderFactory
import com.android.builder.profile.Recorder
import com.android.builder.profile.ThreadRecorder
import com.android.builder.sdk.TargetInfo
import com.android.ide.common.internal.ExecutorSingleton
import com.android.ide.common.process.LoggedProcessOutputHandler
import com.android.utils.ILogger
import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.LogLevel
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.internal.reflect.Instantiator
import org.gradle.internal.service.ServiceRegistry
import org.gradle.language.base.FunctionalSourceSet
import org.gradle.language.base.LanguageSourceSet
import org.gradle.language.base.internal.SourceTransformTaskConfig
import org.gradle.language.base.internal.registry.LanguageTransform
import org.gradle.model.Model
import org.gradle.model.Mutate
import org.gradle.model.Path
import org.gradle.model.RuleSource
import org.gradle.model.collection.CollectionBuilder
import org.gradle.model.internal.core.ModelCreators
import org.gradle.model.internal.core.ModelReference
import org.gradle.model.internal.registry.ModelRegistry
import org.gradle.platform.base.BinaryContainer
import org.gradle.platform.base.BinarySpec
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import javax.inject.Inject
import static com.android.builder.core.BuilderConstants.DEBUG
import static com.android.builder.core.VariantType.ANDROID_TEST
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
@CompileStatic
public class BaseComponentModelPlugin implements Plugin<Project> {
ToolingModelBuilderRegistry toolingRegistry
ModelRegistry modelRegistry
@Inject
protected BaseComponentModelPlugin(
ToolingModelBuilderRegistry toolingRegistry,
ModelRegistry modelRegistry) {
this.toolingRegistry = toolingRegistry
this.modelRegistry = modelRegistry
}
/**
* Replace BasePlugin's apply method for component model.
*/
@Override
public void apply(Project project) {
ProcessRecorderFactory.initialize(new LoggerWrapper(project.logger), project.rootProject.
file("profiler" + System.currentTimeMillis() + ".json"))
project.gradle.addListener(new RecordingBuildListener(ThreadRecorder.get()));
project.apply plugin: AndroidComponentModelPlugin
project.apply plugin: JavaBasePlugin
project.apply plugin: JacocoPlugin
// TODO: Create configurations for build types and flavors, or migrate to new dependency
// management if it's ready.
ConfigurationContainer configurations = project.getConfigurations()
createConfiguration(
configurations,
"compile",
"Classpath for default sources.")
createConfiguration(
configurations,
"default-metadata",
"Metadata for published APKs")
createConfiguration(
configurations,
"default-mapping",
"Metadata for published APKs")
project.tasks.getByName("assemble").description =
"Assembles all variants of all applications and secondary packages."
project.apply plugin: NdkComponentModelPlugin
modelRegistry.create(
ModelCreators.bridgedInstance(
ModelReference.of("toolingRegistry", ToolingModelBuilderRegistry), toolingRegistry)
.descriptor("Tooling model builder model registry.")
.build())
}
@SuppressWarnings("GrMethodMayBeStatic")
static class Rules extends RuleSource {
@Mutate
void configureAndroidModel(
AndroidModel androidModel,
@Path("androidConfig") BaseExtension config,
@Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigs) {
androidModel.config = config
androidModel.signingConfigs = signingConfigs
}
// TODO: Remove code duplicated from BasePlugin.
@Model
ExtraModelInfo createExtraModelInfo(Project project) {
return new ExtraModelInfo(project)
}
@Model
SdkHandler createSdkHandler(Project project) {
ILogger logger = new LoggerWrapper(project.logger)
SdkHandler sdkHandler = new SdkHandler(project, logger)
// call back on execution. This is called after the whole build is done (not
// after the current project is done).
// This is will be called for each (android) projects though, so this should support
// being called 2+ times.
project.gradle.buildFinished {
ExecutorSingleton.shutdown()
sdkHandler.unload()
PreDexCache.getCache().clear(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
logger)
JackConversionCache.getCache().clear(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
logger)
LibraryCache.getCache().unload()
}
project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
for (Task task : taskGraph.allTasks) {
if (task instanceof PreDex) {
PreDexCache.getCache().load(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
break;
} else if (task instanceof JillTask) {
JackConversionCache.getCache().load(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
break;
}
}
}
return sdkHandler
}
@Model
AndroidBuilder createAndroidBuilder(Project project) {
String creator = "Android Gradle"
ILogger logger = new LoggerWrapper(project.logger)
return new AndroidBuilder(
project == project.rootProject ? project.name : project.path,
creator,
new GradleProcessExecutor(project),
new GradleJavaProcessExecutor(project),
new LoggedProcessOutputHandler(logger),
logger,
project.logger.isEnabled(LogLevel.INFO))
}
@Model("androidConfig")
BaseExtension androidConfig(
ServiceRegistry serviceRegistry,
@Path("androidBuildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
@Path("androidProductFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
@Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
@Path("isApplication") Boolean isApplication,
AndroidBuilder androidBuilder,
SdkHandler sdkHandler,
ExtraModelInfo extraModelInfo,
Project project) {
Instantiator instantiator = serviceRegistry.get(Instantiator.class);
Class extensionClass = isApplication ? AppExtension : LibraryExtension
BaseExtension extension = (BaseExtension) instantiator.newInstance(extensionClass,
(ProjectInternal) project, instantiator, androidBuilder,
sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer,
extraModelInfo, !isApplication)
return extension
}
@Mutate
void addDefaultAndroidSourceSet(AndroidComponentModelSourceSet sources) {
sources.addDefaultSourceSet("resources", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("java", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("manifest", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("res", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("assets", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("aidl", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("renderscript", AndroidLanguageSourceSet.class);
sources.addDefaultSourceSet("jniLibs", AndroidLanguageSourceSet.class);
}
@Mutate
void forwardCompileSdkVersion(
@Path("android.ndk") NdkExtension ndkExtension,
@Path("android.config") BaseExtension baseExtension) {
if (ndkExtension.compileSdkVersion.isEmpty() && baseExtension.compileSdkVersion != null) {
ndkExtension.compileSdkVersion(baseExtension.compileSdkVersion);
}
}
@Model("androidSigningConfigs")
NamedDomainObjectContainer<SigningConfig> signingConfig(ServiceRegistry serviceRegistry,
Project project) {
Instantiator instantiator = serviceRegistry.get(Instantiator.class);
def signingConfigContainer =
project.container(SigningConfig, new SigningConfigFactory(instantiator))
signingConfigContainer.create(DEBUG)
signingConfigContainer.whenObjectRemoved {
throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
}
return signingConfigContainer
}
@Mutate
void closeProjectSourceSet(AndroidComponentModelSourceSet sources) {
}
@Mutate
void createAndroidComponents(
AndroidComponentSpec androidSpec,
ServiceRegistry serviceRegistry,
@Path("android.config") BaseExtension androidExtension,
@Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
@Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
@Path("android.signingConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
VariantFactory variantFactory,
TaskManager taskManager,
Project project,
AndroidBuilder androidBuilder,
SdkHandler sdkHandler,
ExtraModelInfo extraModelInfo,
ToolingModelBuilderRegistry toolingRegistry,
@Path("isApplication") Boolean isApplication) {
Instantiator instantiator = serviceRegistry.get(Instantiator.class);
// check if the target has been set.
TargetInfo targetInfo = androidBuilder.getTargetInfo()
if (targetInfo == null) {
sdkHandler.initTarget(
androidExtension.getCompileSdkVersion(),
androidExtension.buildToolsRevision,
androidBuilder)
}
VariantManager variantManager = new VariantManager(
project,
androidBuilder,
androidExtension,
variantFactory,
taskManager,
instantiator)
signingConfigContainer.all { SigningConfig signingConfig ->
variantManager.addSigningConfig(signingConfig)
}
buildTypeContainer.all { BuildType buildType ->
variantManager.addBuildType(buildType)
}
productFlavorContainer.all { GroupableProductFlavor productFlavor ->
variantManager.addProductFlavor(productFlavor)
}
ModelBuilder modelBuilder = new ModelBuilder(
androidBuilder, variantManager, taskManager,
androidExtension, extraModelInfo, !isApplication);
toolingRegistry.register(modelBuilder);
def spec = androidSpec as DefaultAndroidComponentSpec
spec.extension = androidExtension
spec.variantManager = variantManager
}
@Mutate
void createVariantData(
CollectionBuilder<AndroidBinary> binaries,
AndroidComponentSpec spec) {
VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
binaries.all {
DefaultAndroidBinary binary = it as DefaultAndroidBinary
binary.variantData =
variantManager.createVariantData(binary.buildType, binary.productFlavors)
variantManager.getVariantDataList().add(binary.variantData);
}
}
@Mutate
void createLifeCycleTasks(
CollectionBuilder<Task> tasks,
TaskManager taskManager) {
taskManager.createTasksBeforeEvaluate(new TaskCollectionBuilderAdaptor(tasks))
}
@Mutate
void createAndroidTasks(
CollectionBuilder<Task> tasks,
AndroidComponentSpec androidSpec,
TaskManager taskManager,
SdkHandler sdkHandler,
Project project,
AndroidComponentModelSourceSet androidSources) {
DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec) androidSpec
applyProjectSourceSet(spec, androidSources, spec.extension)
// setup SDK repositories.
for (File file : sdkHandler.sdkLoader.repositories) {
project.repositories.maven { MavenArtifactRepository repo ->
repo.url = file.toURI()
}
}
// TODO: determine how to provide functionalities of variant API objects.
}
// TODO: Use @BinaryTasks after figuring how to configure non-binary specific tasks.
@Mutate
void createBinaryTasks(
CollectionBuilder<Task> tasks,
BinaryContainer binaries,
AndroidComponentSpec spec,
TaskManager taskManager) {
VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
binaries.withType(AndroidBinary) { androidBinary ->
ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
DefaultAndroidBinary binary = androidBinary as DefaultAndroidBinary
variantManager.createTasksForVariantData(
new TaskCollectionBuilderAdaptor(tasks),
binary.variantData)
return null;
}
});
}
}
/**
* Create tasks that must be created after other tasks for variants are created.
*/
@Mutate
void createRemainingTasks(
CollectionBuilder<Task> tasks,
TaskManager taskManager,
AndroidComponentSpec spec) {
VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
// create the test tasks.
taskManager.createTopLevelTestTasks (
new TaskCollectionBuilderAdaptor(tasks),
!variantManager.productFlavors.isEmpty() /*hasFlavors*/);
}
@Mutate
void createReportTasks(CollectionBuilder<Task> tasks, AndroidComponentSpec spec) {
VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
tasks.create("androidDependencies", DependencyReportTask) { DependencyReportTask dependencyReportTask ->
dependencyReportTask.setDescription("Displays the Android dependencies of the project")
dependencyReportTask.setVariants(variantManager.variantDataList)
dependencyReportTask.setGroup("Android")
}
tasks.create("signingReport", SigningReportTask) { SigningReportTask signingReportTask ->
signingReportTask.setDescription("Displays the signing info for each variant")
signingReportTask.setVariants(variantManager.variantDataList)
signingReportTask.setGroup("Android")
}
}
private static void applyProjectSourceSet(
AndroidComponentSpec androidSpec,
AndroidComponentModelSourceSet sources,
BaseExtension baseExtension) {
DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec)androidSpec
VariantManager variantManager = spec.variantManager
for (FunctionalSourceSet source : sources) {
String name = source.getName();
AndroidSourceSet androidSource = (
name.equals(BuilderConstants.MAIN)
? baseExtension.sourceSets.getByName(baseExtension.getDefaultConfig().getName())
: (name.equals(ANDROID_TEST.prefix)
? baseExtension.sourceSets.getByName(ANDROID_TEST.getPrefix())
: findAndroidSourceSet(variantManager, name)))
if (androidSource == null) {
continue;
}
convertSourceSet(androidSource.getResources(), source.findByName("resource")?.getSource())
convertSourceSet(androidSource.getJava(), source.findByName("java")?.getSource())
convertSourceSet(androidSource.getRes(), source.findByName("res")?.getSource())
convertSourceSet(androidSource.getAssets(), source.findByName("assets")?.getSource())
convertSourceSet(androidSource.getAidl(), source.findByName("aidl")?.getSource())
convertSourceSet(androidSource.getRenderscript(), source.findByName("renderscript")?.getSource())
convertSourceSet(androidSource.getJni(), source.findByName("jni")?.getSource())
convertSourceSet(androidSource.getJniLibs(), source.findByName("jniLibs")?.getSource())
}
}
private static convertSourceSet(
AndroidSourceDirectorySet androidDir,
@Nullable SourceDirectorySet dir) {
if (dir == null) {
return
}
androidDir.setSrcDirs(dir.getSrcDirs())
androidDir.include(dir.getIncludes())
androidDir.exclude(dir.getExcludes())
}
@Nullable
private static AndroidSourceSet findAndroidSourceSet(
VariantManager variantManager,
String name) {
BuildTypeData buildTypeData = variantManager.getBuildTypes().get(name)
if (buildTypeData != null) {
return buildTypeData.getSourceSet();
}
boolean isTest = name.startsWith(ANDROID_TEST.prefix)
name = name.replaceFirst(ANDROID_TEST.prefix, "")
ProductFlavorData productFlavorData = variantManager.getProductFlavors().get(name)
if (productFlavorData != null) {
return isTest ? productFlavorData.getTestSourceSet(ANDROID_TEST) : productFlavorData.getSourceSet();
}
return null;
}
}
/**
* Default Android LanguageRegistration.
*
* Allows default LanguageSourceSet to be create until specialized LanguageRegistration is
* created.
*/
private static class AndroidSource implements LanguageTransform<AndroidLanguageSourceSet, AndroidObject> {
public String getName() {
return "main";
}
public Class<AndroidLanguageSourceSet> getSourceSetType() {
return AndroidLanguageSourceSet.class;
}
public Class<? extends AndroidLanguageSourceSet> getSourceSetImplementation() {
return AndroidLanguageSourceSet.class;
}
public Map<String, Class<?>> getBinaryTools() {
return Collections.emptyMap();
}
public Class<AndroidObject> getOutputType() {
return null;
}
public SourceTransformTaskConfig getTransformTask() {
return new SourceTransformTaskConfig() {
public String getTaskPrefix() {
return "process";
}
public Class<? extends DefaultTask> getTaskType() {
return DefaultTask;
}
public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
}
};
}
public boolean applyToBinary(BinarySpec binary) {
return false
}
}
private void createConfiguration(
@NonNull ConfigurationContainer configurations,
@NonNull String configurationName,
@NonNull String configurationDescription) {
Configuration configuration = configurations.findByName(configurationName)
if (configuration == null) {
configuration = configurations.create(configurationName)
}
configuration.setVisible(false);
configuration.setDescription(configurationDescription)
}
}