blob: 1a99f5991ae8c67462e0f5a5b8d4c3696ac8c1db [file] [log] [blame]
/*
* Copyright (C) 2017 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.tasks;
import static com.android.build.VariantOutput.OutputType.FULL_SPLIT;
import static com.android.build.VariantOutput.OutputType.MAIN;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.JAR;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.LINT;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH;
import static com.android.build.gradle.internal.scope.InternalArtifactType.LIBRARY_MANIFEST;
import static com.android.build.gradle.internal.scope.InternalArtifactType.LINT_JAR;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MANIFEST_MERGE_REPORT;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_MANIFESTS;
import com.android.Version;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.internal.dsl.LintOptions;
import com.android.build.gradle.internal.ide.dependencies.ArtifactUtils;
import com.android.build.gradle.internal.scope.BuildArtifactsHolder;
import com.android.build.gradle.internal.scope.BuildElements;
import com.android.build.gradle.internal.scope.BuildOutput;
import com.android.build.gradle.internal.scope.ExistingBuildElements;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.factory.TaskCreationAction;
import com.android.build.gradle.internal.variant.TestVariantData;
import com.android.build.gradle.internal.variant.TestedVariantData;
import com.android.builder.core.VariantType;
import com.android.repository.Revision;
import com.android.tools.lint.gradle.api.ReflectiveLintRunner;
import java.io.File;
import java.util.Optional;
import java.util.concurrent.Callable;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ArtifactCollection;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileSystemLocation;
import org.gradle.api.file.RegularFile;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
public abstract class LintBaseTask extends DefaultTask {
public static final String LINT_CLASS_PATH = "lintClassPath";
protected static final Logger LOG = Logging.getLogger(LintBaseTask.class);
@Nullable FileCollection lintClassPath;
protected Provider<Revision> buildToolsRevisionProvider;
/** Lint classpath */
@InputFiles
@Nullable
public FileCollection getLintClassPath() {
return lintClassPath;
}
@Nullable protected LintOptions lintOptions;
@Nullable protected File sdkHome;
protected ToolingModelBuilderRegistry toolingRegistry;
@Nullable protected File reportsDir;
@Nullable
public LintOptions getLintOptions() {
return lintOptions;
}
protected void runLint(LintBaseTaskDescriptor descriptor) {
FileCollection lintClassPath = getLintClassPath();
if (lintClassPath != null) {
new ReflectiveLintRunner().runLint(getProject().getGradle(),
descriptor, lintClassPath.getFiles());
}
}
@Internal("No influence on output, this is to give access to the build tools version")
@NonNull
private Revision getBuildToolsRevision() {
return buildToolsRevisionProvider.get();
}
protected abstract class LintBaseTaskDescriptor extends
com.android.tools.lint.gradle.api.LintExecutionRequest {
@Override
@Nullable
public File getSdkHome() {
return sdkHome;
}
@NonNull
@Override
public ToolingModelBuilderRegistry getToolingRegistry() {
return toolingRegistry;
}
@Nullable
@Override
public LintOptions getLintOptions() {
return lintOptions;
}
@Override
@Nullable
public File getReportsDir() {
return reportsDir;
}
@NonNull
@Override
public Project getProject() {
return LintBaseTask.this.getProject();
}
@NonNull
@Override
public Revision getBuildToolsRevision() {
return LintBaseTask.this.getBuildToolsRevision();
}
@Override
public void warn(@NonNull String message, @NonNull Object... args) {
LOG.warn(message, args);
}
@NonNull
@Override
public String getGradlePluginVersion() {
return Version.ANDROID_GRADLE_PLUGIN_VERSION;
}
}
/**
* The jar artifacts are used in ModelBuilder eventually, we add them to inputs here so Gradle
* would make sure they are resolved before starting the task.
*/
protected static void addJarArtifactsToInputs(
@NonNull ConfigurableFileCollection inputs, @NonNull VariantScope variantScope) {
inputs.from(ArtifactUtils.computeArtifactList(variantScope, COMPILE_CLASSPATH, ALL, JAR));
inputs.from(ArtifactUtils.computeArtifactList(variantScope, RUNTIME_CLASSPATH, ALL, JAR));
if (variantScope.getVariantData() instanceof TestedVariantData) {
for (VariantType variantType : VariantType.Companion.getTestComponents()) {
TestVariantData testVariantData =
((TestedVariantData) variantScope.getVariantData())
.getTestVariantData(variantType);
if (testVariantData != null) {
inputs.from(
(Callable<ArtifactCollection>)
(() ->
ArtifactUtils.computeArtifactList(
testVariantData.getScope(),
COMPILE_CLASSPATH,
ALL,
JAR)));
inputs.from(
(Callable<ArtifactCollection>)
(() ->
ArtifactUtils.computeArtifactList(
testVariantData.getScope(),
RUNTIME_CLASSPATH,
ALL,
JAR)));
}
}
}
}
public static class VariantInputs implements com.android.tools.lint.gradle.api.VariantInputs {
@NonNull private final String name;
@NonNull private final Provider<? extends FileSystemLocation> mergedManifest;
@NonNull private final Provider<RegularFile> mergedManifestReport;
@NonNull private final FileCollection lintRuleJars;
private final ConfigurableFileCollection allInputs;
public VariantInputs(@NonNull VariantScope variantScope) {
name = variantScope.getFullVariantName();
allInputs = variantScope.getGlobalScope().getProject().files();
Provider<RegularFile> localLintJarCollection;
allInputs.from(
localLintJarCollection =
variantScope
.getGlobalScope()
.getArtifacts()
.getFinalProduct(LINT_JAR));
FileCollection dependencyLintJarCollection;
allInputs.from(
dependencyLintJarCollection =
variantScope.getArtifactFileCollection(RUNTIME_CLASSPATH, ALL, LINT));
lintRuleJars = variantScope.getGlobalScope().getProject().files(
localLintJarCollection,
dependencyLintJarCollection);
BuildArtifactsHolder artifacts = variantScope.getArtifacts();
Provider<? extends FileSystemLocation> tmpMergedManifest =
artifacts.getFinalProduct(MERGED_MANIFESTS);
if (!tmpMergedManifest.isPresent()) {
tmpMergedManifest = artifacts.getFinalProduct(LIBRARY_MANIFEST);
}
if (!tmpMergedManifest.isPresent()) {
throw new RuntimeException(
"VariantInputs initialized with no merged manifest on: "
+ variantScope.getVariantConfiguration().getType());
}
mergedManifest = tmpMergedManifest;
allInputs.from(mergedManifest);
mergedManifestReport = artifacts.getFinalProduct(MANIFEST_MERGE_REPORT);
if (mergedManifest.isPresent()) {
allInputs.from(mergedManifestReport);
} else {
throw new RuntimeException(
"VariantInputs initialized with no merged manifest report on: "
+ variantScope.getVariantConfiguration().getType());
}
// these inputs are only there to ensure that the lint task runs after these build
// intermediates are built.
allInputs.from(artifacts.getAllClasses());
addJarArtifactsToInputs(allInputs, variantScope);
}
@NonNull
public FileCollection getAllInputs() {
return allInputs;
}
@Override
@NonNull
public String getName() {
return name;
}
/** the lint rule jars */
@Override
@NonNull
public FileCollection getRuleJars() {
return lintRuleJars;
}
/** the merged manifest of the current module */
@Override
@NonNull
public File getMergedManifest() {
File file = mergedManifest.get().getAsFile();
if (file.isFile()) {
return file;
}
BuildElements manifests =
ExistingBuildElements.from(InternalArtifactType.MERGED_MANIFESTS, file);
if (manifests.isEmpty()) {
throw new RuntimeException("Can't find any manifest in folder: " + file);
}
// first search for a main manifest
Optional<File> mainManifest =
manifests
.stream()
.filter(buildOutput -> buildOutput.getApkData().getType() == MAIN)
.map(BuildOutput::getOutputFile)
.findFirst();
if (mainManifest.isPresent()) {
return mainManifest.get();
}
// else search for a full_split with no filters.
Optional<File> universalSplit =
manifests
.stream()
.filter(
output ->
output.getApkData().getType() == FULL_SPLIT
&& output.getFilters().isEmpty())
.map(BuildOutput::getOutputFile)
.findFirst();
// return the universal Manifest, or a random one if not found.
return universalSplit.orElseGet(() -> manifests.iterator().next().getOutputFile());
}
@Override
@Nullable
public File getManifestMergeReport() {
if (mergedManifestReport.isPresent()) {
return mergedManifestReport.get().getAsFile();
} else {
return null;
}
}
}
public abstract static class BaseCreationAction<T extends LintBaseTask>
extends TaskCreationAction<T> {
@NonNull private final GlobalScope globalScope;
public BaseCreationAction(@NonNull GlobalScope globalScope) {
this.globalScope = globalScope;
}
@NonNull
protected GlobalScope getGlobalScope() {
return globalScope;
}
@Override
public void configure(@NonNull T lintTask) {
lintTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
lintTask.lintOptions = globalScope.getExtension().getLintOptions();
File sdkFolder = globalScope.getSdkComponents().getSdkFolder();
if (sdkFolder != null) {
lintTask.sdkHome = sdkFolder;
}
lintTask.toolingRegistry = globalScope.getToolingRegistry();
lintTask.reportsDir = globalScope.getReportsDir();
lintTask.buildToolsRevisionProvider =
globalScope.getSdkComponents().getBuildToolsRevisionProvider();
lintTask.lintClassPath = globalScope.getProject().getConfigurations()
.getByName(LINT_CLASS_PATH);
}
}
}