| /* |
| * 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.dsl.LintOptionsImpl |
| import com.android.build.gradle.internal.variant.ApkVariantOutputData |
| import com.android.build.gradle.internal.variant.ApplicationVariantData |
| import com.android.build.gradle.internal.variant.BaseVariantData |
| import com.android.build.gradle.internal.variant.BaseVariantOutputData |
| import com.android.build.gradle.internal.variant.LibraryVariantData |
| import com.android.build.gradle.internal.variant.TestVariantData |
| import com.android.builder.core.DefaultProductFlavor |
| import com.android.builder.core.VariantConfiguration |
| import com.android.builder.model.AndroidArtifact |
| import com.android.builder.model.AndroidArtifactOutput |
| import com.android.builder.model.AndroidProject |
| import com.android.builder.model.ApiVersion |
| import com.android.builder.model.ArtifactMetaData |
| import com.android.builder.model.JavaArtifact |
| import com.android.builder.model.LintOptions |
| import com.android.builder.model.SigningConfig |
| import com.android.builder.model.SourceProvider |
| import com.android.builder.model.SourceProviderContainer |
| import com.android.sdklib.AndroidVersion |
| import com.android.sdklib.IAndroidTarget |
| import com.google.common.collect.Lists |
| import org.gradle.api.Project |
| import org.gradle.api.plugins.UnknownPluginException |
| import org.gradle.api.tasks.compile.AbstractCompile |
| import org.gradle.tooling.provider.model.ToolingModelBuilder |
| |
| import java.util.jar.Attributes |
| import java.util.jar.Manifest |
| |
| import static com.android.builder.model.AndroidProject.ARTIFACT_ANDROID_TEST |
| import static com.android.builder.model.AndroidProject.ARTIFACT_MAIN |
| /** |
| * 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) |
| } |
| |
| if (basePlugin == null) { |
| project.logger.error("Failed to find Android plugin for project " + project.name) |
| return null |
| } |
| |
| signingConfigs = basePlugin.extension.signingConfigs |
| |
| // get the boot classpath. This will ensure the target is configured. |
| List<String> bootClasspath = basePlugin.bootClasspathAsStrings |
| |
| List<File> frameworkSource = Collections.emptyList(); |
| |
| // list of extra artifacts |
| List<ArtifactMetaData> artifactMetaDataList = Lists.newArrayList(basePlugin.extraArtifacts) |
| // plus the instrumentation test one. |
| artifactMetaDataList.add( |
| new ArtifactMetaDataImpl( |
| ARTIFACT_ANDROID_TEST, |
| true /*isTest*/, |
| ArtifactMetaData.TYPE_ANDROID)); |
| |
| LintOptions lintOptions = LintOptionsImpl.create(basePlugin.extension.lintOptions) |
| |
| //noinspection GroovyVariableNotAssigned |
| DefaultAndroidProject androidProject = new DefaultAndroidProject( |
| getModelVersion(), |
| project.name, |
| basePlugin.getAndroidBuilder().getTarget().hashString(), |
| bootClasspath, |
| frameworkSource, |
| cloneSigningConfigs(signingConfigs), |
| artifactMetaDataList, |
| basePlugin.unresolvedDependencies, |
| basePlugin.extension.compileOptions, |
| lintOptions, |
| project.getBuildDir(), |
| basePlugin.extension.resourcePrefix, |
| libPlugin != null) |
| .setDefaultConfig(ProductFlavorContainerImpl.createPFC( |
| basePlugin.defaultConfigData, |
| basePlugin.getExtraFlavorSourceProviders(basePlugin.defaultConfigData.productFlavor.name))) |
| |
| for (BuildTypeData btData : basePlugin.variantManager.buildTypes.values()) { |
| androidProject.addBuildType(BuildTypeContainerImpl.createBTC( |
| btData, |
| basePlugin.getExtraBuildTypeSourceProviders(btData.buildType.name))) |
| } |
| for (ProductFlavorData pfData : basePlugin.variantManager.productFlavors.values()) { |
| androidProject.addProductFlavors(ProductFlavorContainerImpl.createPFC( |
| pfData, |
| basePlugin.getExtraFlavorSourceProviders(pfData.productFlavor.name))) |
| } |
| |
| Set<Project> gradleProjects = project.getRootProject().getAllprojects(); |
| |
| for (BaseVariantData variantData : basePlugin.variantDataList) { |
| if (!(variantData instanceof TestVariantData)) { |
| androidProject.addVariant(createVariant(variantData, basePlugin, 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 BasePlugin basePlugin, |
| @NonNull Set<Project> gradleProjects) { |
| TestVariantData testVariantData = null |
| if (variantData instanceof ApplicationVariantData || |
| variantData instanceof LibraryVariantData) { |
| testVariantData = variantData.testVariantData |
| } |
| |
| AndroidArtifact mainArtifact = createArtifactInfo( |
| ARTIFACT_MAIN, variantData, basePlugin, gradleProjects) |
| |
| String variantName = variantData.variantConfiguration.fullName |
| |
| // extra Android Artifacts |
| AndroidArtifact testArtifact = testVariantData != null ? |
| createArtifactInfo(ARTIFACT_ANDROID_TEST, testVariantData, basePlugin, gradleProjects) : null |
| |
| List<AndroidArtifact> extraAndroidArtifacts = Lists.newArrayList( |
| basePlugin.getExtraAndroidArtifacts(variantName)) |
| if (testArtifact != null) { |
| extraAndroidArtifacts.add(testArtifact) |
| } |
| |
| // clone the Java Artifacts |
| Collection<JavaArtifact> javaArtifacts = basePlugin.getExtraJavaArtifacts(variantName) |
| List<JavaArtifact> clonedJavaArtifacts = Lists.newArrayListWithCapacity(javaArtifacts.size()) |
| for (JavaArtifact javaArtifact : javaArtifacts) { |
| clonedJavaArtifacts.add(JavaArtifactImpl.clone(javaArtifact)) |
| } |
| |
| // if the target is a codename, override the model value. |
| ApiVersion sdkVersionOverride = null |
| IAndroidTarget androidTarget = basePlugin.androidBuilder.getTargetInfo().target |
| AndroidVersion version = androidTarget.getVersion() |
| if (version.codename != null) { |
| sdkVersionOverride = ApiVersionImpl.clone(version) |
| } |
| |
| VariantImpl variant = new VariantImpl( |
| variantName, |
| variantData.variantConfiguration.baseName, |
| variantData.variantConfiguration.buildType.name, |
| getProductFlavorNames(variantData), |
| ProductFlavorImpl.cloneFlavor( |
| variantData.variantConfiguration.mergedFlavor, |
| sdkVersionOverride, |
| sdkVersionOverride), |
| mainArtifact, |
| extraAndroidArtifacts, |
| clonedJavaArtifacts) |
| |
| return variant |
| } |
| |
| private static AndroidArtifact createArtifactInfo( |
| @NonNull String name, |
| @NonNull BaseVariantData variantData, |
| @NonNull BasePlugin basePlugin, |
| @NonNull Set<Project> gradleProjects) { |
| VariantConfiguration vC = variantData.variantConfiguration |
| |
| SigningConfig signingConfig = vC.signingConfig |
| String signingConfigName = null |
| if (signingConfig != null) { |
| signingConfigName = signingConfig.name |
| } |
| |
| SourceProvider variantSourceProvider = null; |
| SourceProvider multiFlavorSourceProvider = null; |
| |
| if (ARTIFACT_MAIN.equals(name)) { |
| variantSourceProvider = variantData.variantConfiguration.variantSourceProvider |
| multiFlavorSourceProvider = variantData.variantConfiguration.multiFlavorSourceProvider |
| } else { |
| SourceProviderContainer container = getSourceProviderContainer( |
| basePlugin.getExtraVariantSourceProviders(variantData.getVariantConfiguration().getFullName()), |
| name) |
| if (container != null) { |
| variantSourceProvider = container.sourceProvider |
| } |
| } |
| |
| variantSourceProvider = variantSourceProvider != null ? SourceProviderImpl.cloneProvider(variantSourceProvider) : null |
| multiFlavorSourceProvider = multiFlavorSourceProvider != null ? SourceProviderImpl.cloneProvider(multiFlavorSourceProvider) : null |
| |
| // get the outputs |
| List<? extends BaseVariantOutputData> variantOutputs = variantData.outputs |
| List<AndroidArtifactOutput> outputs = Lists.newArrayListWithCapacity(variantOutputs.size()) |
| |
| for (BaseVariantOutputData variantOutputData : variantOutputs) { |
| Integer versionCode = (variantOutputData instanceof ApkVariantOutputData) ? |
| ((ApkVariantOutputData) variantOutputData).versionCode : |
| vC.mergedFlavor.versionCode |
| |
| int intVersionCode = versionCode != null ? versionCode.intValue() : 1; |
| |
| AndroidArtifactOutput output = new AndroidArtifactOutputImpl( |
| variantOutputData.outputFile, |
| variantOutputData.assembleTask.name, |
| variantOutputData.manifestProcessorTask.manifestOutputFile, |
| intVersionCode, |
| variantOutputData.densityFilter, |
| variantOutputData.abiFilter |
| ); |
| |
| outputs.add(output) |
| } |
| |
| AbstractCompile compileTask = variantData.javaCompileTask |
| if (compileTask == null) { |
| compileTask = variantData.jackTask |
| } |
| |
| return new AndroidArtifactImpl( |
| name, |
| outputs, |
| variantData.assembleVariantTask.name, |
| vC.isSigningReady(), |
| signingConfigName, |
| vC.applicationId, |
| variantData.sourceGenTask.name, |
| variantData.compileTask.name, |
| getGeneratedSourceFolders(variantData), |
| getGeneratedResourceFolders(variantData), |
| compileTask.destinationDir, |
| DependenciesImpl.cloneDependencies(variantData, basePlugin, gradleProjects), |
| variantSourceProvider, |
| multiFlavorSourceProvider, |
| vC.supportedAbis) |
| } |
| |
| @NonNull |
| private static List<String> getProductFlavorNames(@NonNull BaseVariantData variantData) { |
| List<String> flavorNames = Lists.newArrayList() |
| |
| for (DefaultProductFlavor flavor : variantData.variantConfiguration.productFlavors) { |
| flavorNames.add(flavor.name) |
| } |
| |
| return flavorNames |
| } |
| |
| @NonNull |
| private static List<File> getGeneratedSourceFolders( |
| @Nullable BaseVariantData<? extends BaseVariantOutputData> variantData) { |
| if (variantData == null) { |
| return Collections.emptyList() |
| } |
| |
| List<File> folders = Lists.newArrayList() |
| |
| // The R class is only generated by the first output. |
| BaseVariantOutputData variantOutputData = variantData.outputs.get(0) |
| folders.add(variantOutputData.processResourcesTask.sourceOutputDir) |
| |
| folders.add(variantData.aidlCompileTask.sourceOutputDir) |
| folders.add(variantData.generateBuildConfigTask.sourceOutputDir) |
| if (!variantData.variantConfiguration.mergedFlavor.renderscriptNdkMode) { |
| folders.add(variantData.renderscriptCompileTask.sourceOutputDir) |
| } |
| |
| List<File> extraFolders = variantData.extraGeneratedSourceFolders |
| if (extraFolders != null) { |
| folders.addAll(extraFolders) |
| } |
| |
| return folders |
| } |
| |
| @NonNull |
| private static List<File> getGeneratedResourceFolders(@Nullable BaseVariantData variantData) { |
| if (variantData == null) { |
| return Collections.emptyList() |
| } |
| |
| return Lists.asList( |
| variantData.renderscriptCompileTask.resOutputDir, |
| variantData.generateResValuesTask.resOutputDir) |
| } |
| |
| @NonNull |
| private static Collection<SigningConfig> cloneSigningConfigs( |
| @NonNull Collection<SigningConfig> signingConfigs) { |
| Collection<SigningConfig> results = Lists.newArrayListWithCapacity(signingConfigs.size()) |
| |
| for (SigningConfig signingConfig : signingConfigs) { |
| results.add(SigningConfigImpl.createSigningConfig(signingConfig)) |
| } |
| |
| return results |
| } |
| |
| @Nullable |
| private static SourceProviderContainer getSourceProviderContainer( |
| @NonNull Collection<SourceProviderContainer> items, |
| @NonNull String name) { |
| for (SourceProviderContainer item : items) { |
| if (name.equals(item.getArtifactName())) { |
| return item; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * 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 |
| } |
| } |