blob: aa009f0173c02452455e2aebec700f0e5b9fadae [file] [log] [blame]
/*
* Copyright (C) 2019 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.internal.plugins;
import static com.android.build.gradle.internal.ManagedDeviceUtilsKt.getManagedDeviceAvdFolder;
import static com.android.build.gradle.internal.dependency.JdkImageTransformKt.CONFIG_NAME_ANDROID_JDK_IMAGE;
import static com.google.common.base.Preconditions.checkState;
import com.android.SdkConstants;
import com.android.Version;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.component.impl.TestComponentImpl;
import com.android.build.api.component.impl.TestFixturesImpl;
import com.android.build.api.dsl.CommonExtension;
import com.android.build.api.extension.impl.VariantApiOperationsRegistrar;
import com.android.build.api.variant.AndroidComponentsExtension;
import com.android.build.api.variant.Variant;
import com.android.build.api.variant.VariantBuilder;
import com.android.build.api.variant.impl.GradleProperty;
import com.android.build.api.variant.impl.VariantBuilderImpl;
import com.android.build.api.variant.impl.VariantImpl;
import com.android.build.gradle.BaseExtension;
import com.android.build.gradle.api.AndroidBasePlugin;
import com.android.build.gradle.api.BaseVariantOutput;
import com.android.build.gradle.internal.ApiObjectFactory;
import com.android.build.gradle.internal.AvdComponentsBuildService;
import com.android.build.gradle.internal.BadPluginException;
import com.android.build.gradle.internal.ClasspathVerifier;
import com.android.build.gradle.internal.DependencyConfigurator;
import com.android.build.gradle.internal.DependencyResolutionChecks;
import com.android.build.gradle.internal.ExtraModelInfo;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.build.gradle.internal.NonFinalPluginExpiry;
import com.android.build.gradle.internal.SdkComponentsBuildService;
import com.android.build.gradle.internal.SdkComponentsBuildService.VersionedSdkLoader;
import com.android.build.gradle.internal.SdkComponentsKt;
import com.android.build.gradle.internal.SdkLocator;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.VariantManager;
import com.android.build.gradle.internal.attribution.BuildAttributionService;
import com.android.build.gradle.internal.crash.CrashReporting;
import com.android.build.gradle.internal.dependency.JacocoInstrumentationService;
import com.android.build.gradle.internal.dependency.SourceSetManager;
import com.android.build.gradle.internal.dsl.AbstractPublishing;
import com.android.build.gradle.internal.dsl.ApplicationPublishingImpl;
import com.android.build.gradle.internal.dsl.BuildType;
import com.android.build.gradle.internal.dsl.DefaultConfig;
import com.android.build.gradle.internal.dsl.InternalApplicationExtension;
import com.android.build.gradle.internal.dsl.InternalLibraryExtension;
import com.android.build.gradle.internal.dsl.LibraryPublishingImpl;
import com.android.build.gradle.internal.dsl.ProductFlavor;
import com.android.build.gradle.internal.dsl.SigningConfig;
import com.android.build.gradle.internal.dsl.Splits;
import com.android.build.gradle.internal.errors.DeprecationReporterImpl;
import com.android.build.gradle.internal.errors.IncompatibleProjectOptionsReporter;
import com.android.build.gradle.internal.errors.MessageReceiverImpl;
import com.android.build.gradle.internal.errors.SyncIssueReporterImpl;
import com.android.build.gradle.internal.ide.ModelBuilder;
import com.android.build.gradle.internal.ide.dependencies.LibraryDependencyCacheBuildService;
import com.android.build.gradle.internal.ide.dependencies.MavenCoordinatesCacheBuildService;
import com.android.build.gradle.internal.ide.v2.GlobalLibraryBuildService;
import com.android.build.gradle.internal.ide.v2.NativeModelBuilder;
import com.android.build.gradle.internal.lint.LintFixBuildService;
import com.android.build.gradle.internal.lint.LintFromMaven;
import com.android.build.gradle.internal.profile.AnalyticsConfiguratorService;
import com.android.build.gradle.internal.profile.AnalyticsService;
import com.android.build.gradle.internal.profile.AnalyticsUtil;
import com.android.build.gradle.internal.profile.NoOpAnalyticsConfiguratorService;
import com.android.build.gradle.internal.profile.NoOpAnalyticsService;
import com.android.build.gradle.internal.res.Aapt2FromMaven;
import com.android.build.gradle.internal.scope.BuildFeatureValues;
import com.android.build.gradle.internal.scope.DelayedActionsExecutor;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.ProjectInfo;
import com.android.build.gradle.internal.services.Aapt2DaemonBuildService;
import com.android.build.gradle.internal.services.Aapt2ThreadPoolBuildService;
import com.android.build.gradle.internal.services.AndroidLocationsBuildService;
import com.android.build.gradle.internal.services.BuildServicesKt;
import com.android.build.gradle.internal.services.ClassesHierarchyBuildService;
import com.android.build.gradle.internal.services.DslServices;
import com.android.build.gradle.internal.services.DslServicesImpl;
import com.android.build.gradle.internal.services.LintClassLoaderBuildService;
import com.android.build.gradle.internal.services.ProjectServices;
import com.android.build.gradle.internal.services.StringCachingBuildService;
import com.android.build.gradle.internal.services.SymbolTableBuildService;
import com.android.build.gradle.internal.utils.GradlePluginUtils;
import com.android.build.gradle.internal.utils.KgpUtils;
import com.android.build.gradle.internal.utils.PublishingUtils;
import com.android.build.gradle.internal.variant.ComponentInfo;
import com.android.build.gradle.internal.variant.LegacyVariantInputManager;
import com.android.build.gradle.internal.variant.VariantFactory;
import com.android.build.gradle.internal.variant.VariantInputModel;
import com.android.build.gradle.internal.variant.VariantModel;
import com.android.build.gradle.internal.variant.VariantModelImpl;
import com.android.build.gradle.options.BooleanOption;
import com.android.build.gradle.options.ProjectOptionService;
import com.android.build.gradle.options.ProjectOptions;
import com.android.build.gradle.options.StringOption;
import com.android.build.gradle.options.SyncOptions;
import com.android.builder.errors.IssueReporter;
import com.android.builder.errors.IssueReporter.Type;
import com.android.builder.model.v2.ide.ProjectType;
import com.android.sdklib.AndroidTargetHash;
import com.android.sdklib.SdkVersionInfo;
import com.android.utils.ILogger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.CharMatcher;
import com.google.wireless.android.sdk.stats.GradleBuildProfileSpan.ExecutionType;
import com.google.wireless.android.sdk.stats.GradleBuildProject;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import org.gradle.api.JavaVersion;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
import org.gradle.api.component.SoftwareComponentFactory;
import org.gradle.api.invocation.Gradle;
import org.gradle.api.logging.Logger;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.api.tasks.StopExecutionException;
import org.gradle.build.event.BuildEventsListenerRegistry;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
/** Base class for all Android plugins */
public abstract class BasePlugin<
AndroidT extends CommonExtension<?, ?, ?, ?>,
AndroidComponentsT extends
AndroidComponentsExtension<
? extends CommonExtension<?, ?, ?, ?>,
? extends VariantBuilder,
? extends Variant>,
VariantBuilderT extends VariantBuilderImpl,
VariantT extends VariantImpl>
implements Plugin<Project> {
// TODO: BaseExtension should be changed into AndroidT
private BaseExtension extension;
private AndroidComponentsT androidComponentsExtension;
private VariantManager<AndroidT, AndroidComponentsT, VariantBuilderT, VariantT> variantManager;
private LegacyVariantInputManager variantInputModel;
protected Project project;
protected ProjectServices projectServices;
protected DslServicesImpl dslServices;
protected GlobalScope globalScope;
protected SyncIssueReporterImpl syncIssueReporter;
private VariantFactory<VariantBuilderT, VariantT> variantFactory;
@NonNull private final ToolingModelBuilderRegistry registry;
@NonNull private final SoftwareComponentFactory componentFactory;
private LoggerWrapper loggerWrapper;
protected ExtraModelInfo extraModelInfo;
private String creator;
private boolean hasCreatedTasks = false;
private AnalyticsConfiguratorService configuratorService;
private ProjectOptionService optionService;
@NonNull private final BuildEventsListenerRegistry listenerRegistry;
private final VariantApiOperationsRegistrar<
AndroidT,
VariantBuilderT,
VariantT> variantApiOperations = new VariantApiOperationsRegistrar<>();
public BasePlugin(
@NonNull ToolingModelBuilderRegistry registry,
@NonNull SoftwareComponentFactory componentFactory,
@NonNull BuildEventsListenerRegistry listenerRegistry) {
ClasspathVerifier.checkClasspathSanity();
this.registry = registry;
this.componentFactory = componentFactory;
creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION;
NonFinalPluginExpiry.verifyRetirementAge();
this.listenerRegistry = listenerRegistry;
}
@NonNull
protected abstract BaseExtension createExtension(
@NonNull DslServices dslServices,
@NonNull GlobalScope globalScope,
@NonNull
DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>
dslContainers,
@NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
@NonNull ExtraModelInfo extraModelInfo);
@NonNull
protected abstract AndroidComponentsT createComponentExtension(
@NonNull DslServices dslServices,
@NonNull
VariantApiOperationsRegistrar<AndroidT, VariantBuilderT, VariantT>
variantApiOperationsRegistrar);
@NonNull
protected abstract GradleBuildProject.PluginType getAnalyticsPluginType();
@NonNull
protected abstract VariantFactory<VariantBuilderT, VariantT> createVariantFactory(
@NonNull ProjectServices projectServices, @NonNull GlobalScope globalScope);
@NonNull
protected abstract TaskManager<VariantBuilderT, VariantT> createTaskManager(
@NonNull List<ComponentInfo<VariantBuilderT, VariantT>> variants,
@NonNull List<TestComponentImpl> testComponents,
@NonNull List<TestFixturesImpl> testFixturesComponents,
boolean hasFlavors,
@NonNull ProjectOptions projectOptions,
@NonNull GlobalScope globalScope,
@NonNull BaseExtension extension,
@NonNull ProjectInfo projectInfo);
protected abstract int getProjectType();
/** The project type of the IDE model v2. */
protected abstract ProjectType getProjectTypeV2();
@VisibleForTesting
public VariantManager<AndroidT, AndroidComponentsT, VariantBuilderT, VariantT>
getVariantManager() {
return variantManager;
}
@VisibleForTesting
public VariantInputModel<DefaultConfig, BuildType, ProductFlavor, SigningConfig>
getVariantInputModel() {
return variantInputModel;
}
public BaseExtension getExtension() {
return extension;
}
private ILogger getLogger() {
if (loggerWrapper == null) {
loggerWrapper = new LoggerWrapper(project.getLogger());
}
return loggerWrapper;
}
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
project.getPluginManager().apply(AndroidBasePlugin.class);
});
}
private void basePluginApply(@NonNull Project project) {
// We run by default in headless mode, so the JVM doesn't steal focus.
System.setProperty("java.awt.headless", "true");
this.project = project;
new AndroidLocationsBuildService.RegistrationAction(project).execute();
optionService = new ProjectOptionService.RegistrationAction(project).execute().get();
createProjectServices(project);
checkMinJvmVersion();
ProjectOptions projectOptions = projectServices.getProjectOptions();
if (projectOptions.isAnalyticsEnabled()) {
new AnalyticsService.RegistrationAction(project).execute();
configuratorService
= new AnalyticsConfiguratorService.RegistrationAction(project).execute().get();
} else {
project.getGradle().getSharedServices().registerIfAbsent(
BuildServicesKt.getBuildServiceName(AnalyticsService.class),
NoOpAnalyticsService.class,
spec -> {}
);
configuratorService = project.getGradle().getSharedServices().registerIfAbsent(
BuildServicesKt.getBuildServiceName(AnalyticsConfiguratorService.class),
NoOpAnalyticsConfiguratorService.class,
spec -> {}
).get();
}
DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
checkPathForErrors();
String attributionFileLocation =
projectOptions.get(StringOption.IDE_ATTRIBUTION_FILE_LOCATION);
if (attributionFileLocation != null) {
new BuildAttributionService.RegistrationAction(project).execute();
BuildAttributionService.Companion.init(
project, attributionFileLocation, listenerRegistry);
}
configuratorService.createAnalyticsService(project, listenerRegistry);
GradleBuildProject.Builder projectBuilder =
configuratorService.getProjectBuilder(project.getPath());
if (projectBuilder != null) {
projectBuilder
.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setAndroidPlugin(getAnalyticsPluginType())
.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
.setOptions(AnalyticsUtil.toProto(projectOptions));
}
configuratorService.recordBlock(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null,
this::configureProject);
configuratorService.recordBlock(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null,
this::configureExtension);
configuratorService.recordBlock(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
}
protected abstract void pluginSpecificApply(@NonNull Project project);
private void configureProject() {
final Gradle gradle = project.getGradle();
Provider<StringCachingBuildService> stringCachingService =
new StringCachingBuildService.RegistrationAction(project).execute();
Provider<MavenCoordinatesCacheBuildService> mavenCoordinatesCacheBuildService =
new MavenCoordinatesCacheBuildService.RegistrationAction(
project, stringCachingService)
.execute();
new LibraryDependencyCacheBuildService.RegistrationAction(
project, mavenCoordinatesCacheBuildService
).execute();
new GlobalLibraryBuildService.RegistrationAction(
project, mavenCoordinatesCacheBuildService
).execute();
extraModelInfo = new ExtraModelInfo();
ProjectOptions projectOptions = projectServices.getProjectOptions();
IssueReporter issueReporter = projectServices.getIssueReporter();
new Aapt2ThreadPoolBuildService.RegistrationAction(project, projectOptions).execute();
new Aapt2DaemonBuildService.RegistrationAction(project, projectOptions).execute();
new SyncIssueReporterImpl.GlobalSyncIssueService.RegistrationAction(
project,
SyncOptions.getModelQueryMode(projectOptions),
SyncOptions.getErrorFormatMode(projectOptions))
.execute();
Provider<SdkComponentsBuildService> sdkComponentsBuildService =
new SdkComponentsBuildService.RegistrationAction(project, projectOptions).execute();
Provider<AndroidLocationsBuildService> locationsProvider =
BuildServicesKt.getBuildService(
project.getGradle().getSharedServices(),
AndroidLocationsBuildService.class);
ProviderFactory providers = project.getProviders();
Provider<VersionedSdkLoader> versionSdkLoader =
sdkComponentsBuildService.map(
buildService ->
buildService.sdkLoader(
providers.provider(() -> extension.getCompileSdkVersion()),
providers.provider(
() -> extension.getBuildToolsRevision())));
Provider<AvdComponentsBuildService> avdComponentsBuildService =
new AvdComponentsBuildService.RegistrationAction(
project,
getManagedDeviceAvdFolder(
project.getObjects(),
project.getProviders(),
locationsProvider.get()),
sdkComponentsBuildService,
project.getProviders()
.provider(() -> extension.getCompileSdkVersion()),
project.getProviders()
.provider(() -> extension.getBuildToolsRevision()))
.execute();
new SymbolTableBuildService.RegistrationAction(project).execute();
new ClassesHierarchyBuildService.RegistrationAction(project).execute();
new LintFixBuildService.RegistrationAction(project).execute();
new LintClassLoaderBuildService.RegistrationAction(project).execute();
new JacocoInstrumentationService.RegistrationAction(project).execute();
projectOptions
.getAllOptions()
.forEach(projectServices.getDeprecationReporter()::reportOptionIssuesIfAny);
IncompatibleProjectOptionsReporter.check(
projectOptions, projectServices.getIssueReporter());
// Enforce minimum versions of certain plugins
GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, issueReporter);
// Apply the Java plugin
project.getPlugins().apply(JavaBasePlugin.class);
dslServices =
new DslServicesImpl(
projectServices,
sdkComponentsBuildService);
MessageReceiverImpl messageReceiver =
new MessageReceiverImpl(
SyncOptions.getErrorFormatMode(projectOptions),
projectServices.getLogger());
globalScope =
new GlobalScope(
project,
creator,
dslServices,
sdkComponentsBuildService,
avdComponentsBuildService,
registry,
messageReceiver,
componentFactory);
project.getTasks()
.named("assemble")
.configure(
task ->
task.setDescription(
"Assembles all variants of all applications and secondary packages."));
// As soon as project is evaluated we can clear the shared state for deprecation reporting.
gradle.projectsEvaluated(action -> DeprecationReporterImpl.Companion.clean());
createAndroidJdkImageConfiguration(project, globalScope);
}
/** Creates the androidJdkImage configuration */
public static void createAndroidJdkImageConfiguration(
@NonNull Project project, @NonNull GlobalScope globalScope) {
Configuration config = project.getConfigurations().create(CONFIG_NAME_ANDROID_JDK_IMAGE);
config.setVisible(false);
config.setCanBeConsumed(false);
config.setDescription("Configuration providing JDK image for compiling Java 9+ sources");
project.getDependencies()
.add(
CONFIG_NAME_ANDROID_JDK_IMAGE,
project.files(
globalScope
.getVersionedSdkLoader()
.flatMap(
VersionedSdkLoader
::getCoreForSystemModulesProvider)));
}
private void configureExtension() {
DslServices dslServices = globalScope.getDslServices();
final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
project.container(BaseVariantOutput.class);
project.getExtensions().add("buildOutputs", buildOutputs);
variantFactory = createVariantFactory(projectServices, globalScope);
variantInputModel =
new LegacyVariantInputManager(
dslServices,
variantFactory.getVariantType(),
new SourceSetManager(
project,
isPackagePublished(),
dslServices,
new DelayedActionsExecutor()));
extension =
createExtension(
dslServices, globalScope, variantInputModel, buildOutputs, extraModelInfo);
globalScope.setExtension(extension);
androidComponentsExtension = createComponentExtension(dslServices, variantApiOperations);
variantManager =
new VariantManager(
globalScope,
project,
projectServices.getProjectOptions(),
(CommonExtension<?, ?, ?, ?>) extension,
androidComponentsExtension,
variantApiOperations,
variantFactory,
variantInputModel,
projectServices);
registerModels(
registry,
globalScope,
variantInputModel,
extension,
extraModelInfo);
// create default Objects, signingConfig first as its used by the BuildTypes.
variantFactory.createDefaultComponents(variantInputModel);
createAndroidTestUtilConfiguration();
}
protected void registerModels(
@NonNull ToolingModelBuilderRegistry registry,
@NonNull GlobalScope globalScope,
@NonNull
VariantInputModel<DefaultConfig, BuildType, ProductFlavor, SigningConfig>
variantInputModel,
@NonNull BaseExtension extension,
@NonNull ExtraModelInfo extraModelInfo) {
// Register a builder for the custom tooling model
VariantModel variantModel = createVariantModel();
registerModelBuilder(registry, globalScope, variantModel, extension, extraModelInfo);
registry.register(
new com.android.build.gradle.internal.ide.v2.ModelBuilder(
globalScope,
projectServices.getProjectOptions(),
variantModel,
(CommonExtension) extension,
projectServices.getIssueReporter(),
getProjectTypeV2()));
// Register a builder for the native tooling model
NativeModelBuilder nativeModelBuilderV2 =
new NativeModelBuilder(
projectServices.getIssueReporter(),
projectServices.getProjectOptions(),
globalScope,
variantModel,
projectServices.getProjectInfo());
registry.register(nativeModelBuilderV2);
}
@NonNull
private VariantModel createVariantModel() {
return new VariantModelImpl(
variantInputModel,
extension::getTestBuildType,
() ->
variantManager.getMainComponents().stream()
.map(ComponentInfo::getVariant)
.collect(Collectors.toList()),
() ->
variantManager.getTestComponents(),
dslServices.getIssueReporter());
}
/** Registers a builder for the custom tooling model. */
protected void registerModelBuilder(
@NonNull ToolingModelBuilderRegistry registry,
@NonNull GlobalScope globalScope,
@NonNull VariantModel variantModel,
@NonNull BaseExtension extension,
@NonNull ExtraModelInfo extraModelInfo) {
registry.register(
new ModelBuilder<>(
globalScope,
variantModel,
extension,
extraModelInfo,
projectServices.getProjectOptions(),
projectServices.getIssueReporter(),
getProjectType(),
projectServices.getProjectInfo()));
}
private void createTasks() {
configuratorService.recordBlock(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() ->
TaskManager.createTasksBeforeEvaluate(
projectServices.getProjectOptions(),
globalScope,
variantFactory.getVariantType(),
extension.getSourceSets(),
projectServices.getProjectInfo()));
project.afterEvaluate(
CrashReporting.afterEvaluate(
p -> {
variantInputModel.getSourceSetManager().runBuildableArtifactsActions();
configuratorService.recordBlock(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
this::createAndroidTasks);
}));
}
@VisibleForTesting
final void createAndroidTasks() {
if (extension.getCompileSdkVersion() == null) {
if (SyncOptions.getModelQueryMode(projectServices.getProjectOptions())
.equals(SyncOptions.EvaluationMode.IDE)) {
String newCompileSdkVersion = findHighestSdkInstalled();
if (newCompileSdkVersion == null) {
newCompileSdkVersion = "android-" + SdkVersionInfo.HIGHEST_KNOWN_STABLE_API;
}
extension.setCompileSdkVersion(newCompileSdkVersion);
}
dslServices
.getIssueReporter()
.reportError(
Type.COMPILE_SDK_VERSION_NOT_SET,
"compileSdkVersion is not specified. Please add it to build.gradle");
}
// Make sure unit tests set the required fields.
checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.");
// get current plugins and look for the default Java plugin.
if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
throw new BadPluginException(
"The 'java' plugin has been applied, but it is not compatible with the Android plugins.");
}
if (project.getPlugins().hasPlugin("me.tatarka.retrolambda")) {
String warningMsg =
"One of the plugins you are using supports Java 8 "
+ "language features. To try the support built into"
+ " the Android plugin, remove the following from "
+ "your build.gradle:\n"
+ " apply plugin: 'me.tatarka.retrolambda'\n"
+ "To learn more, go to https://d.android.com/r/"
+ "tools/java-8-support-message.html\n";
dslServices.getIssueReporter().reportWarning(IssueReporter.Type.GENERIC, warningMsg);
}
project.getRepositories()
.forEach(
it -> {
if (it instanceof FlatDirectoryArtifactRepository) {
String warningMsg =
String.format(
"Using %s should be avoided because it doesn't support any meta-data formats.",
it.getName());
dslServices
.getIssueReporter()
.reportWarning(IssueReporter.Type.GENERIC, warningMsg);
}
});
checkMavenPublishing();
// 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 ((!project.getState().getExecuted() || project.getState().getFailure() != null)
&& SdkLocator.getSdkTestDirectory() == null) {
return;
}
if (hasCreatedTasks) {
return;
}
hasCreatedTasks = true;
variantApiOperations.executeDslFinalizationBlocks(
(AndroidT) extension
);
variantInputModel.lock();
extension.disableWrite();
GradleBuildProject.Builder projectBuilder =
configuratorService.getProjectBuilder(project.getPath());
if (projectBuilder != null) {
projectBuilder
.setCompileSdk(extension.getCompileSdkVersion())
.setBuildToolsVersion(extension.getBuildToolsRevision().toString())
.setSplits(AnalyticsUtil.toProto(extension.getSplits()));
String kotlinPluginVersion = getKotlinPluginVersion();
if (kotlinPluginVersion != null) {
projectBuilder.setKotlinPluginVersion(kotlinPluginVersion);
}
}
AnalyticsUtil.recordFirebasePerformancePluginVersion(project);
// create the build feature object that will be re-used everywhere
BuildFeatureValues buildFeatureValues =
variantFactory.createBuildFeatureValues(
extension.getBuildFeatures(), projectServices.getProjectOptions());
variantManager.createVariants(buildFeatureValues);
List<ComponentInfo<VariantBuilderT, VariantT>> variants =
variantManager.getMainComponents();
TaskManager<VariantBuilderT, VariantT> taskManager =
createTaskManager(
variants,
variantManager.getTestComponents(),
variantManager.getTestFixturesComponents(),
!variantInputModel.getProductFlavors().isEmpty(),
projectServices.getProjectOptions(),
globalScope,
extension,
projectServices.getProjectInfo());
taskManager.createTasks(variantFactory.getVariantType(), createVariantModel());
new DependencyConfigurator(
project,
project.getName(),
projectServices.getProjectOptions(),
globalScope,
variantInputModel,
projectServices)
.configureDependencySubstitutions()
.configureDependencyChecks()
.configureGeneralTransforms()
.configureVariantTransforms(variants, variantManager.getNestedComponents())
.configureAttributeMatchingStrategies();
// Run the old Variant API, after the variants and tasks have been created.
ApiObjectFactory apiObjectFactory =
new ApiObjectFactory(extension, variantFactory, globalScope);
for (ComponentInfo<VariantBuilderT, VariantT> variant : variants) {
apiObjectFactory.create(variant.getVariant());
}
// lock the Properties of the variant API after the old API because
// of the versionCode/versionName properties that are shared between the old and new APIs.
variantManager.lockVariantProperties();
// Make sure no SourceSets were added through the DSL without being properly configured
variantInputModel.getSourceSetManager().checkForUnconfiguredSourceSets();
KgpUtils.syncAgpAndKgpSources(project, extension.getSourceSets());
// configure compose related tasks.
taskManager.createPostApiTasks();
// now publish all variant artifacts for non test variants since
// tests don't publish anything.
for (ComponentInfo<VariantBuilderT, VariantT> component : variants) {
component.getVariant().publishBuildArtifacts();
}
// now publish all testFixtures components artifacts.
for (TestFixturesImpl testFixturesComponent :
variantManager.getTestFixturesComponents()) {
testFixturesComponent.publishBuildArtifacts();
}
checkSplitConfiguration();
variantManager.setHasCreatedTasks(true);
for (ComponentInfo<VariantBuilderT, VariantT> variant : variants) {
variant.getVariant().getArtifacts().ensureAllOperationsAreSatisfied();
}
// notify our properties that configuration is over for us.
GradleProperty.Companion.endOfEvaluation();
}
private String findHighestSdkInstalled() {
String highestSdk = null;
File folder =
new File(
SdkComponentsKt.getSdkDir(project.getRootDir(), syncIssueReporter),
"platforms");
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null) {
Arrays.sort(listOfFiles, Comparator.comparing(File::getName).reversed());
for (File file : listOfFiles) {
if (AndroidTargetHash.getPlatformVersion(file.getName()) != null) {
highestSdk = file.getName();
break;
}
}
}
return highestSdk;
}
private void checkSplitConfiguration() {
String configApkUrl = "https://d.android.com/topic/instant-apps/guides/config-splits.html";
boolean generatePureSplits = extension.getGeneratePureSplits();
Splits splits = extension.getSplits();
boolean splitsEnabled =
splits.getDensity().isEnable()
|| splits.getAbi().isEnable()
|| splits.getLanguage().isEnable();
// The Play Store doesn't allow Pure splits
if (generatePureSplits) {
dslServices
.getIssueReporter()
.reportWarning(
Type.GENERIC,
"Configuration APKs are supported by the Google Play Store only when publishing Android Instant Apps. To instead generate stand-alone APKs for different device configurations, set generatePureSplits=false. For more information, go to "
+ configApkUrl);
}
if (!generatePureSplits && splits.getLanguage().isEnable()) {
dslServices
.getIssueReporter()
.reportWarning(
Type.GENERIC,
"Per-language APKs are supported only when building Android Instant Apps. For more information, go to "
+ configApkUrl);
}
}
private void checkPathForErrors() {
// See if we're on Windows:
if (!System.getProperty("os.name").toLowerCase(Locale.US).contains("windows")) {
return;
}
// See if the user disabled the check:
if (projectServices.getProjectOptions().get(BooleanOption.OVERRIDE_PATH_CHECK_PROPERTY)) {
return;
}
// See if the path contains non-ASCII characters.
if (CharMatcher.ascii().matchesAllOf(project.getRootDir().getAbsolutePath())) {
return;
}
String message =
"Your project path contains non-ASCII characters. This will most likely "
+ "cause the build to fail on Windows. Please move your project to a different "
+ "directory. See http://b.android.com/95744 for details. "
+ "This warning can be disabled by adding the line '"
+ BooleanOption.OVERRIDE_PATH_CHECK_PROPERTY.getPropertyName()
+ "=true' to gradle.properties file in the project directory.";
throw new StopExecutionException(message);
}
/**
* returns the kotlin plugin version, or null if plugin is not applied to this project, or
* "unknown" if plugin is applied but version can't be determined.
*/
@Nullable
private String getKotlinPluginVersion() {
Plugin plugin = project.getPlugins().findPlugin("kotlin-android");
if (plugin == null) {
return null;
}
try {
// No null checks below because we're catching all exceptions.
@SuppressWarnings("JavaReflectionMemberAccess")
Method method = plugin.getClass().getMethod("getKotlinPluginVersion");
method.setAccessible(true);
return method.invoke(plugin).toString();
} catch (Throwable e) {
// Defensively catch all exceptions because we don't want it to crash
// if kotlin plugin code changes unexpectedly.
return "unknown";
}
}
/**
* If overridden in a subclass to return "true," the package Configuration will be named
* "publish" instead of "apk"
*/
protected boolean isPackagePublished() {
return false;
}
// Create the "special" configuration for test buddy APKs. It will be resolved by the test
// running task, so that we can install all the found APKs before running tests.
private void createAndroidTestUtilConfiguration() {
project.getLogger()
.debug(
"Creating configuration "
+ SdkConstants.GRADLE_ANDROID_TEST_UTIL_CONFIGURATION);
Configuration configuration =
project.getConfigurations()
.maybeCreate(SdkConstants.GRADLE_ANDROID_TEST_UTIL_CONFIGURATION);
configuration.setVisible(false);
configuration.setDescription("Additional APKs used during instrumentation testing.");
configuration.setCanBeConsumed(false);
configuration.setCanBeResolved(true);
}
private void createProjectServices(@NonNull Project project) {
ObjectFactory objectFactory = project.getObjects();
final Logger logger = project.getLogger();
final String projectPath = project.getPath();
ProjectOptions projectOptions = optionService.getProjectOptions();
syncIssueReporter =
new SyncIssueReporterImpl(
SyncOptions.getModelQueryMode(projectOptions),
SyncOptions.getErrorFormatMode(projectOptions),
logger);
DeprecationReporterImpl deprecationReporter =
new DeprecationReporterImpl(syncIssueReporter, projectOptions, projectPath);
Aapt2FromMaven aapt2FromMaven = Aapt2FromMaven.create(project, projectOptions);
LintFromMaven lintFromMaven =
LintFromMaven.from(project, projectOptions, syncIssueReporter);
projectServices =
new ProjectServices(
syncIssueReporter,
deprecationReporter,
objectFactory,
project.getLogger(),
project.getProviders(),
project.getLayout(),
projectOptions,
project.getGradle().getSharedServices(),
lintFromMaven,
aapt2FromMaven,
project.getGradle().getStartParameter().getMaxWorkerCount(),
new ProjectInfo(project),
project::file);
}
private void checkMinJvmVersion() {
JavaVersion current = JavaVersion.current();
JavaVersion minRequired = JavaVersion.VERSION_11;
if (!current.isCompatibleWith(minRequired)) {
syncIssueReporter.reportError(
Type.AGP_USED_JAVA_VERSION_TOO_LOW,
"Android Gradle plugin requires Java "
+ minRequired.toString()
+ " to run. You are currently using Java "
+ current.toString()
+ ".\n"
+ "You can try some of the following options:\n"
+ " - changing the IDE settings.\n"
+ " - changing the JAVA_HOME environment variable.\n"
+ " - changing `org.gradle.java.home` in `gradle.properties`.");
}
}
private void checkMavenPublishing() {
if (project.getPlugins().hasPlugin("maven-publish")) {
if (extension instanceof InternalApplicationExtension) {
checkSoftwareComponents(
(ApplicationPublishingImpl)
((InternalApplicationExtension) extension).getPublishing());
}
if (extension instanceof InternalLibraryExtension) {
checkSoftwareComponents(
(LibraryPublishingImpl)
((InternalLibraryExtension) extension).getPublishing());
}
}
}
private void checkSoftwareComponents(AbstractPublishing publishing) {
boolean optIn =
PublishingUtils.publishingFeatureOptIn(publishing, dslServices.getProjectOptions());
if (!optIn) {
String warning =
"Software Components will not be created automatically for "
+ "Maven publishing from Android Gradle Plugin 8.0. To opt-in to the "
+ "future behavior, set the Gradle property "
+ "android.disableAutomaticComponentCreation=true in the "
+ "`gradle.properties` file or use the new publishing DSL.";
dslServices.getIssueReporter().reportWarning(Type.GENERIC, warning);
}
}
}