com.android.build.gradle.internal.LintGradleProject Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-core Show documentation
Show all versions of gradle-core Show documentation
Core library to build Android Gradle plugin.
The newest version!
package com.android.build.gradle.internal;
import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT;
import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT;
import static java.io.File.separatorChar;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.tasks.Lint;
import com.android.builder.dependency.MavenCoordinatesImpl;
import com.android.builder.model.AndroidArtifact;
import com.android.builder.model.AndroidLibrary;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.ApiVersion;
import com.android.builder.model.BuildTypeContainer;
import com.android.builder.model.Dependencies;
import com.android.builder.model.JavaLibrary;
import com.android.builder.model.MavenCoordinates;
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.ProductFlavorContainer;
import com.android.builder.model.SourceProvider;
import com.android.builder.model.SourceProviderContainer;
import com.android.builder.model.Variant;
import com.android.sdklib.AndroidTargetHash;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Project;
import com.android.utils.Pair;
import com.android.utils.XmlUtils;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.gradle.api.Plugin;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ExternalDependency;
import org.gradle.api.artifacts.FileCollectionDependency;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.PluginContainer;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.SourceSetOutput;
import org.gradle.tooling.provider.model.ToolingModelBuilder;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
import org.w3c.dom.Document;
/**
* An implementation of Lint's {@link Project} class wrapping a Gradle model (project or
* library)
*/
public class LintGradleProject extends Project {
protected AndroidVersion minSdkVersion;
protected AndroidVersion targetSdkVersion;
private LintGradleProject(
@NonNull LintGradleClient client,
@NonNull File dir,
@NonNull File referenceDir,
@Nullable File manifest) {
super(client, dir, referenceDir);
gradleProject = true;
mergeManifests = true;
directLibraries = Lists.newArrayList();
if (manifest != null) {
readManifest(manifest);
}
}
private static void addJarsFromJavaLibrariesTransitively(
@NonNull Collection extends JavaLibrary> libraries,
@NonNull List list,
boolean skipProvided) {
for (JavaLibrary library : libraries) {
if (library.isSkipped()) {
continue;
}
if (skipProvided && library.isProvided()) {
continue;
}
File jar = library.getJarFile();
if (!list.contains(jar)) {
if (jar.exists()) {
list.add(jar);
}
}
addJarsFromJavaLibrariesTransitively(library.getDependencies(), list, skipProvided);
}
}
private static void addJarsFromAndroidLibrariesTransitively(
@NonNull Collection extends AndroidLibrary> libraries,
@NonNull List list,
boolean skipProvided) {
for (AndroidLibrary library : libraries) {
if (library.getProject() != null) {
continue;
}
if (library.isSkipped()) {
continue;
}
if (skipProvided && library.isProvided()) {
continue;
}
File jar = library.getJarFile();
if (!list.contains(jar)) {
if (jar.exists()) {
list.add(jar);
}
}
addJarsFromJavaLibrariesTransitively(library.getJavaDependencies(), list, skipProvided);
addJarsFromAndroidLibrariesTransitively(library.getLibraryDependencies(), list, skipProvided);
}
}
/**
* Creates a {@link com.android.build.gradle.internal.LintGradleProject} from
* the given {@link com.android.builder.model.AndroidProject} definition for
* a given {@link com.android.builder.model.Variant}, and returns it along with
* a set of lint custom rule jars applicable for the given model project.
*
* @param client the client
* @param project the model project
* @param variant the variant
* @param gradleProject the gradle project
* @return a pair of new project and list of custom rule jars
*/
@NonNull
public static Pair> create(
@NonNull LintGradleClient client,
@NonNull AndroidProject project,
@NonNull Variant variant,
@NonNull org.gradle.api.Project gradleProject) {
assert !Lint.MODEL_LIBRARIES;
File dir = gradleProject.getProjectDir();
AppGradleProject lintProject = new AppGradleProject(client, dir,
dir, project, variant);
List customRules = Lists.newArrayList();
File appLintJar = new File(gradleProject.getBuildDir(),
"lint" + separatorChar + "lint.jar");
if (appLintJar.exists()) {
customRules.add(appLintJar);
}
Set libraries = Sets.newHashSet();
Dependencies dependencies = variant.getMainArtifact().getDependencies();
for (AndroidLibrary library : dependencies.getLibraries()) {
lintProject.addDirectLibrary(createLibrary(client, library, libraries, customRules));
}
return Pair.of(lintProject, customRules);
}
@Override
protected void initialize() {
// Deliberately not calling super; that code is for ADT compatibility
}
protected void readManifest(File manifest) {
if (manifest.exists()) {
try {
String xml = Files.toString(manifest, Charsets.UTF_8);
Document document = XmlUtils.parseDocumentSilently(xml, true);
if (document != null) {
readManifest(document);
}
} catch (IOException e) {
client.log(e, "Could not read manifest %1$s", manifest);
}
}
}
@Override
public boolean isGradleProject() {
return true;
}
protected static boolean dependsOn(@NonNull Dependencies dependencies,
@NonNull String artifact) {
for (AndroidLibrary library : dependencies.getLibraries()) {
if (dependsOn(library, artifact)) {
return true;
}
}
return false;
}
protected static boolean dependsOn(@NonNull AndroidLibrary library, @NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (library.getJarFile().getName().startsWith("support-v4-")) {
return true;
}
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
File bundle = library.getBundle();
if (bundle.getName().startsWith("appcompat-v7-")) {
return true;
}
}
for (AndroidLibrary dependency : library.getLibraryDependencies()) {
if (dependsOn(dependency, artifact)) {
return true;
}
}
return false;
}
void addDirectLibrary(@NonNull Project project) {
directLibraries.add(project);
}
@NonNull
private static LibraryProject createLibrary(@NonNull LintGradleClient client,
@NonNull AndroidLibrary library,
@NonNull Set seen, List customRules) {
seen.add(library);
File dir = library.getFolder();
LibraryProject project = new LibraryProject(client, dir, dir, library);
File ruleJar = library.getLintJar();
if (ruleJar.exists()) {
customRules.add(ruleJar);
}
for (AndroidLibrary dependent : library.getLibraryDependencies()) {
if (!seen.contains(dependent)) {
project.addDirectLibrary(createLibrary(client, dependent, seen, customRules));
}
}
return project;
}
// TODO: Rename: this isn't really an "App" project (it could be a library) too; it's a "project"
// (e.g. not a remote artifact) - LocalGradleProject
private static class AppGradleProject extends LintGradleProject {
private final AndroidProject mProject;
private final Variant mVariant;
private List mProviders;
private List mTestProviders;
private AppGradleProject(
@NonNull LintGradleClient client,
@NonNull File dir,
@NonNull File referenceDir,
@NonNull AndroidProject project,
@NonNull Variant variant) {
//TODO FIXME: handle multi-apk
super(client, dir, referenceDir,
variant.getMainArtifact().getOutputs().iterator().next().getGeneratedManifest());
mProject = project;
mVariant = variant;
}
@Override
public boolean isLibrary() {
return mProject.isLibrary();
}
@Override
public AndroidProject getGradleProjectModel() {
return mProject;
}
@Override
public Variant getCurrentVariant() {
return mVariant;
}
private List getSourceProviders() {
if (mProviders == null) {
List providers = Lists.newArrayList();
AndroidArtifact mainArtifact = mVariant.getMainArtifact();
providers.add(mProject.getDefaultConfig().getSourceProvider());
for (String flavorName : mVariant.getProductFlavors()) {
for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
if (flavorName.equals(flavor.getProductFlavor().getName())) {
providers.add(flavor.getSourceProvider());
break;
}
}
}
SourceProvider multiProvider = mainArtifact.getMultiFlavorSourceProvider();
if (multiProvider != null) {
providers.add(multiProvider);
}
String buildTypeName = mVariant.getBuildType();
for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
if (buildTypeName.equals(buildType.getBuildType().getName())) {
providers.add(buildType.getSourceProvider());
break;
}
}
SourceProvider variantProvider = mainArtifact.getVariantSourceProvider();
if (variantProvider != null) {
providers.add(variantProvider);
}
mProviders = providers;
}
return mProviders;
}
private List getTestSourceProviders() {
if (mTestProviders == null) {
List providers = Lists.newArrayList();
ProductFlavorContainer defaultConfig = mProject.getDefaultConfig();
for (SourceProviderContainer extra : defaultConfig.getExtraSourceProviders()) {
String artifactName = extra.getArtifactName();
if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
providers.add(extra.getSourceProvider());
}
}
for (String flavorName : mVariant.getProductFlavors()) {
for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
if (flavorName.equals(flavor.getProductFlavor().getName())) {
for (SourceProviderContainer extra : flavor.getExtraSourceProviders()) {
String artifactName = extra.getArtifactName();
if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
providers.add(extra.getSourceProvider());
}
}
}
}
}
String buildTypeName = mVariant.getBuildType();
for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
if (buildTypeName.equals(buildType.getBuildType().getName())) {
for (SourceProviderContainer extra : buildType.getExtraSourceProviders()) {
String artifactName = extra.getArtifactName();
if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
providers.add(extra.getSourceProvider());
}
}
}
}
mTestProviders = providers;
}
return mTestProviders;
}
@NonNull
@Override
public List getManifestFiles() {
if (manifestFiles == null) {
manifestFiles = Lists.newArrayList();
for (SourceProvider provider : getSourceProviders()) {
File manifestFile = provider.getManifestFile();
if (manifestFile.exists()) { // model returns path whether or not it exists
manifestFiles.add(manifestFile);
}
}
}
return manifestFiles;
}
@NonNull
@Override
public List getProguardFiles() {
if (proguardFiles == null) {
ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
proguardFiles = flavor.getProguardFiles().stream()
.filter(File::exists)
.collect(Collectors.toList());
try {
proguardFiles.addAll(
flavor.getConsumerProguardFiles().stream()
.filter(File::exists)
.collect(Collectors.toList()));
} catch (Throwable t) {
// On some models, this threw
// org.gradle.tooling.model.UnsupportedMethodException:
// Unsupported method: BaseConfig.getConsumerProguardFiles().
// Playing it safe for a while.
}
}
return proguardFiles;
}
@NonNull
@Override
public List getResourceFolders() {
if (resourceFolders == null) {
resourceFolders = Lists.newArrayList();
for (SourceProvider provider : getSourceProviders()) {
Collection resDirs = provider.getResDirectories();
// model returns path whether or not it exists
resourceFolders.addAll(resDirs.stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
resourceFolders.addAll(
mVariant.getMainArtifact().getGeneratedResourceFolders().stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
return resourceFolders;
}
@NonNull
@Override
public List getAssetFolders() {
if (assetFolders == null) {
assetFolders = Lists.newArrayList();
for (SourceProvider provider : getSourceProviders()) {
Collection dirs = provider.getAssetsDirectories();
// model returns path whether or not it exists
assetFolders.addAll(dirs.stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
}
return assetFolders;
}
@NonNull
@Override
public List getJavaSourceFolders() {
if (javaSourceFolders == null) {
javaSourceFolders = Lists.newArrayList();
for (SourceProvider provider : getSourceProviders()) {
Collection srcDirs = provider.getJavaDirectories();
// model returns path whether or not it exists
javaSourceFolders.addAll(srcDirs.stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
javaSourceFolders.addAll(
mVariant.getMainArtifact().getGeneratedSourceFolders().stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
return javaSourceFolders;
}
@NonNull
@Override
public List getTestSourceFolders() {
if (testSourceFolders == null) {
testSourceFolders = Lists.newArrayList();
for (SourceProvider provider : getTestSourceProviders()) {
// model returns path whether or not it exists
testSourceFolders.addAll(provider.getJavaDirectories().stream()
.filter(File::exists)
.collect(Collectors.toList()));
}
}
return testSourceFolders;
}
@NonNull
@Override
public List getJavaClassFolders() {
if (javaClassFolders == null) {
javaClassFolders = new ArrayList<>(1);
File outputClassFolder = mVariant.getMainArtifact().getClassesFolder();
if (outputClassFolder.exists()) {
javaClassFolders.add(outputClassFolder);
} else if (isLibrary()) {
// For libraries we build the release variant instead
for (Variant variant : mProject.getVariants()) {
if (variant != mVariant) {
outputClassFolder = variant.getMainArtifact().getClassesFolder();
if (outputClassFolder.exists()) {
javaClassFolders.add(outputClassFolder);
break;
}
}
}
}
}
return javaClassFolders;
}
@NonNull
@Override
public List getJavaLibraries(boolean includeProvided) {
if (includeProvided) {
if (javaLibraries == null) {
Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
Collection libs = dependencies.getJavaLibraries();
javaLibraries = Lists.newArrayListWithExpectedSize(libs.size());
for (JavaLibrary lib : libs) {
File jar = lib.getJarFile();
if (jar.exists()) {
javaLibraries.add(jar);
}
}
}
return javaLibraries;
} else {
// Skip provided libraries?
if (nonProvidedJavaLibraries == null) {
Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
Collection libs = dependencies.getJavaLibraries();
nonProvidedJavaLibraries = Lists.newArrayListWithExpectedSize(libs.size());
for (JavaLibrary lib : libs) {
File jar = lib.getJarFile();
if (jar.exists()) {
if (lib.isProvided()) {
continue;
}
nonProvidedJavaLibraries.add(jar);
}
}
}
return nonProvidedJavaLibraries;
}
}
@NonNull
@Override
public List getTestLibraries() {
if (testLibraries == null) {
testLibraries = Lists.newArrayListWithExpectedSize(6);
for (AndroidArtifact artifact : mVariant.getExtraAndroidArtifacts()) {
if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifact.getName())
|| AndroidProject.ARTIFACT_UNIT_TEST.equals(artifact.getName())) {
Dependencies dependencies = artifact.getDependencies();
addJarsFromJavaLibrariesTransitively(dependencies.getJavaLibraries(),
testLibraries, false);
// Note that we don't include these for getJavaLibraries, but we need to
// for tests since we don't keep them otherwise
addJarsFromAndroidLibrariesTransitively(dependencies.getLibraries(),
testLibraries, false);
}
}
}
return testLibraries;
}
@Nullable
@Override
public String getPackage() {
// For now, lint only needs the manifest package; not the potentially variant specific
// package. As part of the Gradle work on the Lint API we should make two separate
// package lookup methods -- one for the manifest package, one for the build package
if (pkg == null) { // only used as a fallback in case manifest somehow is null
String packageName = mProject.getDefaultConfig().getProductFlavor()
.getApplicationId();
if (packageName != null) {
return packageName;
}
}
return pkg; // from manifest
}
@Override
@NonNull
public AndroidVersion getMinSdkVersion() {
if (minSdkVersion == null) {
ApiVersion minSdk = mVariant.getMergedFlavor().getMinSdkVersion();
if (minSdk == null) {
ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
minSdk = flavor.getMinSdkVersion();
}
if (minSdk != null) {
minSdkVersion = LintUtils.convertVersion(minSdk, client.getTargets());
} else {
minSdkVersion = super.getMinSdkVersion(); // from manifest
}
}
return minSdkVersion;
}
@Override
@NonNull
public AndroidVersion getTargetSdkVersion() {
if (targetSdkVersion == null) {
ApiVersion targetSdk = mVariant.getMergedFlavor().getTargetSdkVersion();
if (targetSdk == null) {
ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
targetSdk = flavor.getTargetSdkVersion();
}
if (targetSdk != null) {
targetSdkVersion = LintUtils.convertVersion(targetSdk, client.getTargets());
} else {
targetSdkVersion = super.getTargetSdkVersion(); // from manifest
}
}
return targetSdkVersion;
}
@Override
public int getBuildSdk() {
String compileTarget = mProject.getCompileTarget();
AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget);
if (version != null) {
return version.getFeatureLevel();
}
return super.getBuildSdk();
}
@Nullable
@Override
public Boolean dependsOn(@NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (supportLib == null) {
Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
supportLib = dependsOn(dependencies, artifact);
}
return supportLib;
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
if (appCompat == null) {
Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
appCompat = dependsOn(dependencies, artifact);
}
return appCompat;
} else {
return super.dependsOn(artifact);
}
}
}
private static class LibraryProject extends LintGradleProject {
private final AndroidLibrary mLibrary;
private LibraryProject(
@NonNull LintGradleClient client,
@NonNull File dir,
@NonNull File referenceDir,
@NonNull AndroidLibrary library) {
super(client, dir, referenceDir, library.getManifest());
mLibrary = library;
// TODO: Make sure we don't use this project for any source library projects!
reportIssues = false;
}
@Override
public boolean isLibrary() {
return true;
}
@Override
public AndroidLibrary getGradleLibraryModel() {
return mLibrary;
}
@Override
public Variant getCurrentVariant() {
return null;
}
@NonNull
@Override
public List getManifestFiles() {
if (manifestFiles == null) {
File manifest = mLibrary.getManifest();
if (manifest.exists()) {
manifestFiles = Collections.singletonList(manifest);
} else {
manifestFiles = Collections.emptyList();
}
}
return manifestFiles;
}
@NonNull
@Override
public List getProguardFiles() {
if (proguardFiles == null) {
File proguardRules = mLibrary.getProguardRules();
if (proguardRules.exists()) {
proguardFiles = Collections.singletonList(proguardRules);
} else {
proguardFiles = Collections.emptyList();
}
}
return proguardFiles;
}
@NonNull
@Override
public List getResourceFolders() {
if (resourceFolders == null) {
File folder = mLibrary.getResFolder();
if (folder.exists()) {
resourceFolders = Collections.singletonList(folder);
} else {
resourceFolders = Collections.emptyList();
}
}
return resourceFolders;
}
@NonNull
@Override
public List getAssetFolders() {
if (assetFolders == null) {
File folder = mLibrary.getAssetsFolder();
if (folder.exists()) {
assetFolders = Collections.singletonList(folder);
} else {
assetFolders = Collections.emptyList();
}
}
return assetFolders;
}
@NonNull
@Override
public List getJavaSourceFolders() {
return Collections.emptyList();
}
@NonNull
@Override
public List getTestSourceFolders() {
return Collections.emptyList();
}
@NonNull
@Override
public List getJavaClassFolders() {
return Collections.emptyList();
}
@NonNull
@Override
public List getJavaLibraries(boolean includeProvided) {
if (!includeProvided && mLibrary.isProvided()) {
return Collections.emptyList();
}
if (javaLibraries == null) {
javaLibraries =
Stream.concat(
Stream.of(mLibrary.getJarFile()),
mLibrary.getLocalJars().stream())
.filter(File::exists)
.collect(Collectors.toList());
}
return javaLibraries;
}
@Nullable
@Override
public Boolean dependsOn(@NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (supportLib == null) {
supportLib = dependsOn(mLibrary, artifact);
}
return supportLib;
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
if (appCompat == null) {
appCompat = dependsOn(mLibrary, artifact);
}
return appCompat;
} else {
return super.dependsOn(artifact);
}
}
}
/**
* Class which creates a lint project hierarchy based on a corresponding
* Gradle project hierarchy, looking up project dependencies by name, creating
* wrapper projects for Java libraries, looking up tooling models for each project,
* etc.
*/
static class ProjectSearch {
public final Map appProjects = Maps.newHashMap();
public final Map libraryProjects = Maps.newHashMap();
public final Map libraryProjectsByCoordinate = Maps.newHashMap();
public final Map namedProjects = Maps.newHashMap();
public final Map javaLibraryProjects = Maps.newHashMap();
public final Map javaLibraryProjectsByCoordinate = Maps.newHashMap();
public final Map gradleProjects = Maps.newHashMap();
public final List customViewRuleJars = Lists.newArrayList();
private final Set