blob: 45806e49ff1d61ca044554592ce965fab963cf66 [file] [log] [blame]
/*
* 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.internal.model
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.BasePlugin
import com.android.build.gradle.LibraryPlugin
import com.android.build.gradle.internal.BuildTypeData
import com.android.build.gradle.internal.ProductFlavorData
import com.android.build.gradle.internal.variant.ApplicationVariantData
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.LibraryVariantData
import com.android.build.gradle.internal.variant.TestVariantData
import com.android.builder.model.AndroidProject
import com.android.builder.model.ArtifactInfo
import com.android.builder.model.BuildTypeContainer
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.DefaultProductFlavor
import com.android.builder.SdkParser
import com.android.builder.VariantConfiguration
import com.android.builder.model.SigningConfig
import com.android.builder.model.SourceProvider
import com.google.common.collect.Lists
import com.google.common.collect.Maps
import org.gradle.api.Project
import org.gradle.api.plugins.UnknownPluginException
import org.gradle.tooling.provider.model.ToolingModelBuilder
import java.util.jar.Attributes
import java.util.jar.Manifest
/**
* Builder for the custom Android model.
*/
public class ModelBuilder implements ToolingModelBuilder {
@Override
public boolean canBuild(String modelName) {
// The default name for a model is the name of the Java interface
return modelName.equals(AndroidProject.class.getName())
}
@Override
public Object buildAll(String modelName, Project project) {
AppPlugin appPlugin = getPlugin(project, AppPlugin.class)
LibraryPlugin libPlugin = null
BasePlugin basePlugin = appPlugin
Collection<SigningConfig> signingConfigs
if (appPlugin == null) {
basePlugin = libPlugin = getPlugin(project, LibraryPlugin.class)
} else {
signingConfigs = appPlugin.extension.signingConfigs
}
if (basePlugin == null) {
project.logger.error("Failed to find Android plugin for project " + project.name)
return null
}
if (libPlugin != null) {
signingConfigs = Collections.singletonList(libPlugin.extension.debugSigningConfig)
}
SdkParser sdkParser = basePlugin.getLoadedSdkParser()
List<String> bootClasspath = basePlugin.runtimeJarList
String compileTarget = sdkParser.target.hashString()
//noinspection GroovyVariableNotAssigned
DefaultAndroidProject androidProject = new DefaultAndroidProject(
getModelVersion(),
project.name,
compileTarget,
bootClasspath,
cloneSigningConfigs(signingConfigs),
libPlugin != null)
.setDefaultConfig(createPFC(basePlugin.defaultConfigData))
if (appPlugin != null) {
for (BuildTypeData btData : appPlugin.buildTypes.values()) {
androidProject.addBuildType(createBTC(btData))
}
for (ProductFlavorData pfData : appPlugin.productFlavors.values()) {
androidProject.addProductFlavors(createPFC(pfData))
}
} else if (libPlugin != null) {
androidProject.addBuildType(createBTC(libPlugin.debugBuildTypeData))
.addBuildType(createBTC(libPlugin.releaseBuildTypeData))
}
Set<Project> gradleProjects = project.getRootProject().getAllprojects();
for (BaseVariantData variantData : basePlugin.variantDataList) {
if (!(variantData instanceof TestVariantData)) {
androidProject.addVariant(createVariant(variantData, gradleProjects))
}
}
return androidProject
}
@NonNull
private static String getModelVersion() {
Class clazz = AndroidProject.class
String className = clazz.getSimpleName() + ".class"
String classPath = clazz.getResource(className).toString()
if (!classPath.startsWith("jar")) {
// Class not from JAR, unlikely
return "unknown"
}
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
"/META-INF/MANIFEST.MF"
Manifest manifest = new Manifest(new URL(manifestPath).openStream())
Attributes attr = manifest.getMainAttributes()
String version = attr.getValue("Model-Version")
if (version != null) {
return version
}
return "unknown"
}
@NonNull
private static VariantImpl createVariant(@NonNull BaseVariantData variantData,
@NonNull Set<Project> gradleProjects) {
TestVariantData testVariantData = null
if (variantData instanceof ApplicationVariantData ||
variantData instanceof LibraryVariantData) {
testVariantData = variantData.testVariantData
}
ArtifactInfo mainArtifact = createArtifactInfo(variantData, gradleProjects)
ArtifactInfo testArtifact = testVariantData != null ?
createArtifactInfo(testVariantData, gradleProjects) : null
VariantImpl variant = new VariantImpl(
variantData.name,
variantData.baseName,
variantData.variantConfiguration.buildType.name,
getProductFlavorNames(variantData),
ProductFlavorImpl.cloneFlavor(variantData.variantConfiguration.mergedFlavor),
mainArtifact,
testArtifact)
return variant
}
private static ArtifactInfo createArtifactInfo(@NonNull BaseVariantData variantData,
@NonNull Set<Project> gradleProjects) {
VariantConfiguration vC = variantData.variantConfiguration
SigningConfig signingConfig = vC.signingConfig
String signingConfigName = null
if (signingConfig != null) {
signingConfigName = signingConfig.name
}
return new ArtifactInfoImpl(
variantData.assembleTask.name,
variantData.outputFile,
vC.isSigningReady(),
signingConfigName,
vC.packageName,
getGeneratedSourceFolders(variantData),
getGeneratedResourceFolders(variantData),
variantData.javaCompileTask.destinationDir,
DependenciesImpl.cloneDependencies(variantData.variantDependency, gradleProjects)
)
}
@NonNull
private static List<String> getProductFlavorNames(@NonNull BaseVariantData variantData) {
List<String> flavorNames = Lists.newArrayList()
for (DefaultProductFlavor flavor : variantData.variantConfiguration.flavorConfigs) {
flavorNames.add(flavor.name)
}
return flavorNames
}
@NonNull
private static List<File> getGeneratedSourceFolders(@Nullable BaseVariantData variantData) {
if (variantData == null) {
return Collections.emptyList()
}
List<File> folders = Lists.newArrayList()
folders.add(variantData.processResourcesTask.sourceOutputDir)
folders.add(variantData.aidlCompileTask.sourceOutputDir)
folders.add(variantData.renderscriptCompileTask.sourceOutputDir)
folders.add(variantData.generateBuildConfigTask.sourceOutputDir)
return folders
}
@NonNull
private static List<File> getGeneratedResourceFolders(@Nullable BaseVariantData variantData) {
if (variantData == null) {
return Collections.emptyList()
}
return Collections.singletonList(variantData.renderscriptCompileTask.resOutputDir)
}
/**
* Create a ProductFlavorContainer from a ProductFlavorData
* @param productFlavorData the product flavor data
* @return a non-null ProductFlavorContainer
*/
@NonNull
private static ProductFlavorContainer createPFC(@NonNull ProductFlavorData productFlavorData) {
return new ProductFlavorContainerImpl(
ProductFlavorImpl.cloneFlavor(productFlavorData.productFlavor),
SourceProviderImpl.cloneProvider((SourceProvider) productFlavorData.sourceSet),
SourceProviderImpl.cloneProvider((SourceProvider) productFlavorData.testSourceSet))
}
/**
* Create a BuildTypeContainer from a BuildTypeData
* @param buildTypeData the build type data
* @return a non-null BuildTypeContainer
*/
@NonNull
private static BuildTypeContainer createBTC(@NonNull BuildTypeData buildTypeData) {
return new BuildTypeContainerImpl(
BuildTypeImpl.cloneBuildType(buildTypeData.buildType),
SourceProviderImpl.cloneProvider((SourceProvider) buildTypeData.sourceSet))
}
@NonNull
private static Map<String, SigningConfig> cloneSigningConfigs(
@NonNull Collection<SigningConfig> signingConfigs) {
Map<String, SigningConfig> results = Maps.newHashMapWithExpectedSize(signingConfigs.size())
for (SigningConfig signingConfig : signingConfigs) {
SigningConfig clonedSigningConfig = createSigningConfig(signingConfig)
results.put(clonedSigningConfig.name, clonedSigningConfig)
}
return results
}
@NonNull
private static SigningConfig createSigningConfig(@NonNull SigningConfig signingConfig) {
return new SigningConfigImpl(
signingConfig.getName(),
signingConfig.getStoreFile(),
signingConfig.getStorePassword(),
signingConfig.getKeyAlias(),
signingConfig.getKeyPassword(),
signingConfig.getStoreType(),
signingConfig.isSigningReady())
}
/**
* Safely queries a project for a given plugin class.
* @param project the project to query
* @param pluginClass the plugin class.
* @return the plugin instance or null if it is not applied.
*/
private static <T> T getPlugin(@NonNull Project project, @NonNull Class<T> pluginClass) {
try {
return project.getPlugins().findPlugin(pluginClass)
} catch (UnknownPluginException ignored) {
// ignore, return null below.
}
return null
}
}