| /* |
| * Copyright (C) 2015 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; |
| |
| import static com.android.SdkConstants.FN_PUBLIC_TXT; |
| import static com.android.build.api.transform.QualifiedContent.DefaultContentType.RESOURCES; |
| import static com.android.build.gradle.internal.pipeline.ExtendedContentType.NATIVE_LIBS; |
| import static com.android.build.gradle.internal.scope.InternalArtifactType.JAVAC; |
| |
| import android.databinding.tool.DataBindingBuilder; |
| import com.android.annotations.NonNull; |
| import com.android.build.api.transform.QualifiedContent; |
| import com.android.build.api.transform.QualifiedContent.Scope; |
| import com.android.build.api.transform.QualifiedContent.ScopeType; |
| import com.android.build.api.transform.Transform; |
| import com.android.build.gradle.AndroidConfig; |
| import com.android.build.gradle.internal.core.GradleVariantConfiguration; |
| import com.android.build.gradle.internal.pipeline.OriginalStream; |
| import com.android.build.gradle.internal.pipeline.TransformManager; |
| import com.android.build.gradle.internal.publishing.AndroidArtifacts; |
| import com.android.build.gradle.internal.scope.AnchorOutputType; |
| import com.android.build.gradle.internal.scope.BuildArtifactsHolder; |
| 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.BundleLibraryClasses; |
| import com.android.build.gradle.internal.tasks.BundleLibraryJavaRes; |
| import com.android.build.gradle.internal.tasks.LibraryDexingTask; |
| import com.android.build.gradle.internal.tasks.LibraryJniLibsTask; |
| import com.android.build.gradle.internal.tasks.MergeConsumerProguardFilesTask; |
| import com.android.build.gradle.internal.tasks.MergeGeneratedProguardFilesCreationAction; |
| import com.android.build.gradle.internal.tasks.PackageRenderscriptTask; |
| import com.android.build.gradle.internal.tasks.StripDebugSymbolsTask; |
| import com.android.build.gradle.internal.tasks.factory.TaskFactoryUtils; |
| import com.android.build.gradle.internal.tasks.factory.TaskProviderCallback; |
| import com.android.build.gradle.internal.transforms.LibraryAarJarsTransform; |
| import com.android.build.gradle.internal.variant.VariantFactory; |
| import com.android.build.gradle.internal.variant.VariantHelper; |
| import com.android.build.gradle.options.BooleanOption; |
| import com.android.build.gradle.options.ProjectOptions; |
| import com.android.build.gradle.tasks.BuildArtifactReportTask; |
| import com.android.build.gradle.tasks.BundleAar; |
| import com.android.build.gradle.tasks.ExtractAnnotations; |
| import com.android.build.gradle.tasks.ExtractDeepLinksTask; |
| import com.android.build.gradle.tasks.MergeResources; |
| import com.android.build.gradle.tasks.MergeSourceSetFolders; |
| import com.android.build.gradle.tasks.VerifyLibraryResourcesTask; |
| import com.android.build.gradle.tasks.ZipMergingTask; |
| import com.android.builder.errors.EvalIssueException; |
| import com.android.builder.errors.EvalIssueReporter.Type; |
| import com.android.builder.profile.Recorder; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import java.io.File; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Supplier; |
| import org.gradle.api.Project; |
| import org.gradle.api.file.ConfigurableFileCollection; |
| import org.gradle.api.file.FileCollection; |
| import org.gradle.api.tasks.TaskProvider; |
| import org.gradle.api.tasks.compile.JavaCompile; |
| import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; |
| |
| /** TaskManager for creating tasks in an Android library project. */ |
| public class LibraryTaskManager extends TaskManager { |
| |
| public LibraryTaskManager( |
| @NonNull GlobalScope globalScope, |
| @NonNull Project project, |
| @NonNull ProjectOptions projectOptions, |
| @NonNull DataBindingBuilder dataBindingBuilder, |
| @NonNull AndroidConfig extension, |
| @NonNull VariantFactory variantFactory, |
| @NonNull ToolingModelBuilderRegistry toolingRegistry, |
| @NonNull Recorder recorder) { |
| super( |
| globalScope, |
| project, |
| projectOptions, |
| dataBindingBuilder, |
| extension, |
| variantFactory, |
| toolingRegistry, |
| recorder); |
| } |
| |
| @Override |
| public void createTasksForVariantScope(@NonNull final VariantScope variantScope) { |
| final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration(); |
| |
| GlobalScope globalScope = variantScope.getGlobalScope(); |
| |
| createAnchorTasks(variantScope); |
| |
| taskFactory.register(new ExtractDeepLinksTask.CreationAction(variantScope)); |
| |
| // Create all current streams (dependencies mostly at this point) |
| createDependencyStreams(variantScope); |
| |
| createCheckManifestTask(variantScope); |
| |
| taskFactory.register( |
| new BuildArtifactReportTask.BuildArtifactReportCreationAction(variantScope)); |
| |
| createGenerateResValuesTask(variantScope); |
| |
| createMergeLibManifestsTask(variantScope); |
| |
| createRenderscriptTask(variantScope); |
| |
| createMergeResourcesTasks(variantScope); |
| |
| createShaderTask(variantScope); |
| |
| // Add tasks to merge the assets folders |
| createMergeAssetsTask(variantScope); |
| createLibraryAssetsTask(variantScope); |
| |
| // Add a task to create the BuildConfig class |
| createBuildConfigTask(variantScope); |
| |
| // Add a task to generate resource source files, directing the location |
| // of the r.txt file to be directly in the bundle. |
| createProcessResTask( |
| variantScope, |
| variantScope.getSymbolTableFile(), |
| null, |
| // Switch to package where possible so we stop merging resources in |
| // libraries |
| MergeType.PACKAGE, |
| globalScope.getProjectBaseName()); |
| |
| // Only verify resources if in Release and not namespaced. |
| if (!variantScope.getVariantConfiguration().getBuildType().isDebuggable() |
| && !variantScope.getGlobalScope().getExtension().getAaptOptions().getNamespaced()) { |
| createVerifyLibraryResTask(variantScope); |
| } |
| registerLibraryRClassTransformStream(variantScope); |
| |
| // process java resources only, the merge is setup after |
| // the task to generate intermediate jars for project to project publishing. |
| createProcessJavaResTask(variantScope); |
| |
| createAidlTask(variantScope); |
| |
| // Add data binding tasks if enabled |
| createDataBindingTasksIfNecessary(variantScope, MergeType.PACKAGE); |
| |
| // Add a compile task |
| TaskProvider<? extends JavaCompile> javacTask = createJavacTask(variantScope); |
| addJavacClassesStream(variantScope); |
| TaskManager.setJavaCompilerTask(javacTask, variantScope); |
| |
| taskFactory.register(new MergeGeneratedProguardFilesCreationAction(variantScope)); |
| |
| // External native build |
| createExternalNativeBuildJsonGenerators(variantScope); |
| createExternalNativeBuildTasks(variantScope); |
| |
| createMergeJniLibFoldersTasks(variantScope); |
| |
| taskFactory.register(new StripDebugSymbolsTask.CreationAction(variantScope)); |
| |
| taskFactory.register(new PackageRenderscriptTask.CreationAction(variantScope)); |
| |
| // merge consumer proguard files from different build types and flavors |
| taskFactory.register(new MergeConsumerProguardFilesTask.CreationAction(variantScope)); |
| |
| // Some versions of retrolambda remove the actions from the extract annotations task. |
| // TODO: remove this hack once tests are moved to a version that doesn't do this |
| // b/37564303 |
| if (projectOptions.get(BooleanOption.ENABLE_EXTRACT_ANNOTATIONS)) { |
| taskFactory.register(new ExtractAnnotations.CreationAction(extension, variantScope)); |
| } |
| |
| final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled(); |
| |
| TransformManager transformManager = variantScope.getTransformManager(); |
| |
| // ----- Code Coverage first ----- |
| if (instrumented) { |
| createJacocoTask(variantScope); |
| } |
| |
| // ----- External Transforms ----- |
| // apply all the external transforms. |
| List<Transform> customTransforms = extension.getTransforms(); |
| List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies(); |
| |
| for (int i = 0, count = customTransforms.size(); i < count; i++) { |
| Transform transform = customTransforms.get(i); |
| |
| // Check the transform only applies to supported scopes for libraries: |
| // We cannot transform scopes that are not packaged in the library |
| // itself. |
| Sets.SetView<? super Scope> difference = |
| Sets.difference(transform.getScopes(), TransformManager.PROJECT_ONLY); |
| if (!difference.isEmpty()) { |
| String scopes = difference.toString(); |
| globalScope |
| .getErrorHandler() |
| .reportError( |
| Type.GENERIC, |
| new EvalIssueException( |
| String.format( |
| "Transforms with scopes '%s' cannot be applied to library projects.", |
| scopes))); |
| } |
| |
| List<Object> deps = customTransformsDependencies.get(i); |
| transformManager.addTransform( |
| taskFactory, |
| variantScope, |
| transform, |
| null, |
| task -> { |
| if (!deps.isEmpty()) { |
| task.dependsOn(deps); |
| } |
| }, |
| taskProvider -> { |
| // if the task is a no-op then we make assemble task |
| // depend on it. |
| if (transform.getScopes().isEmpty()) { |
| TaskFactoryUtils.dependsOn( |
| variantScope.getTaskContainer().getAssembleTask(), |
| taskProvider); |
| } |
| }); |
| } |
| |
| // Create jar with library classes used for publishing to runtime elements. |
| taskFactory.register( |
| new BundleLibraryClasses.CreationAction( |
| variantScope, |
| AndroidArtifacts.PublishedConfigType.RUNTIME_ELEMENTS, |
| excludeDataBindingClassesIfNecessary(variantScope))); |
| taskFactory.register(new BundleLibraryJavaRes.CreationAction(variantScope)); |
| |
| taskFactory.register(new LibraryDexingTask.CreationAction(variantScope)); |
| |
| // Create a jar with both classes and java resources. This artifact is not |
| // used by the Android application plugin and the task usually don't need to |
| // be executed. The artifact is useful for other Gradle users who needs the |
| // 'jar' artifact as API dependency. |
| taskFactory.register(new ZipMergingTask.CreationAction(variantScope)); |
| |
| // now add a task that will take all the native libs and package |
| // them into an intermediary folder. This processes only the PROJECT |
| // scope. |
| taskFactory.register( |
| new LibraryJniLibsTask.ProjectOnlyCreationAction( |
| variantScope, InternalArtifactType.LIBRARY_JNI)); |
| |
| // Now go back to fill the pipeline with transforms used when |
| // publishing the AAR |
| |
| // first merge the java resources. |
| createMergeJavaResTask(variantScope); |
| |
| // ----- Minify next ----- |
| maybeCreateJavaCodeShrinkerTransform(variantScope); |
| maybeCreateResourcesShrinkerTransform(variantScope); |
| |
| // now add a transform that will take all the class/res and package them |
| // into the main and secondary jar files that goes in the AAR. |
| // This transform technically does not use its transform output, but that's |
| // ok. We use the transform mechanism to get incremental data from |
| // the streams. |
| // This is used for building the AAR. |
| |
| File classesJar = variantScope.getAarClassesJar(); |
| File libsDirectory = variantScope.getAarLibsDirectory(); |
| BuildArtifactsHolder artifacts = variantScope.getArtifacts(); |
| |
| LibraryAarJarsTransform transform = |
| new LibraryAarJarsTransform( |
| classesJar, |
| libsDirectory, |
| artifacts.hasFinalProduct(InternalArtifactType.ANNOTATIONS_TYPEDEF_FILE) |
| ? artifacts.getFinalProduct( |
| InternalArtifactType.ANNOTATIONS_TYPEDEF_FILE) |
| : null, |
| variantConfig::getPackageFromManifest, |
| extension.getPackageBuildConfig()); |
| |
| transform.setExcludeListProvider(excludeDataBindingClassesIfNecessary(variantScope)); |
| |
| transformManager.addTransform( |
| taskFactory, |
| variantScope, |
| transform, |
| taskName -> { |
| artifacts.appendArtifact( |
| InternalArtifactType.AAR_MAIN_JAR, |
| ImmutableList.of(classesJar), |
| taskName); |
| artifacts.appendArtifact( |
| InternalArtifactType.AAR_LIBS_DIRECTORY, |
| ImmutableList.of(libsDirectory), |
| taskName); |
| }, |
| null, |
| null); |
| |
| // now add a task that will take all the native libs and package |
| // them into the libs folder of the bundle. This processes both the PROJECT |
| // and the LOCAL_PROJECT scopes |
| taskFactory.register( |
| new LibraryJniLibsTask.ProjectAndLocalJarsCreationAction( |
| variantScope, InternalArtifactType.LIBRARY_AND_LOCAL_JARS_JNI)); |
| |
| createLintTasks(variantScope); |
| createBundleTask(variantScope); |
| } |
| |
| private void registerLibraryRClassTransformStream(@NonNull VariantScope variantScope) { |
| InternalArtifactType rClassJar; |
| |
| if (globalScope.getExtension().getAaptOptions().getNamespaced()) { |
| rClassJar = InternalArtifactType.COMPILE_ONLY_NAMESPACED_R_CLASS_JAR; |
| } else { |
| rClassJar = InternalArtifactType.COMPILE_ONLY_NOT_NAMESPACED_R_CLASS_JAR; |
| } |
| QualifiedContent.ScopeType scopeType; |
| if (variantScope.getCodeShrinker() != null) { |
| // Add the R class classes as production classes. They are then removed by the library |
| // jar transform. |
| // TODO(b/115974418): Can we stop adding the compilation-only R class as a local classes? |
| scopeType = Scope.PROJECT; |
| } else { |
| scopeType = Scope.PROVIDED_ONLY; |
| } |
| |
| FileCollection compileRClass = |
| project.files(variantScope.getArtifacts().getFinalProduct(rClassJar)); |
| variantScope |
| .getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "compile-only-r-class") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(scopeType) |
| .setFileCollection(compileRClass) |
| .build()); |
| } |
| |
| private void createBundleTask(@NonNull VariantScope variantScope) { |
| TaskProvider<BundleAar> bundle = |
| taskFactory.register(new BundleAar.CreationAction(extension, variantScope)); |
| |
| TaskFactoryUtils.dependsOn(variantScope.getTaskContainer().getAssembleTask(), bundle); |
| |
| // if the variant is the default published, then publish the aar |
| // FIXME: only generate the tasks if this is the default published variant? |
| if (extension |
| .getDefaultPublishConfig() |
| .equals(variantScope.getVariantConfiguration().getFullName())) { |
| VariantHelper.setupArchivesConfig( |
| project, variantScope.getVariantDependencies().getRuntimeClasspath()); |
| |
| // add the artifact that will be published. |
| // it must be default so that it can be found by other library modules during |
| // publishing to a maven repo. Adding it to "archives" only allows the current |
| // module to be published by not to be found by consumer who are themselves published |
| // (leading to their pom not containing dependencies). |
| project.getArtifacts().add("default", bundle); |
| } |
| } |
| |
| @Override |
| protected void createDependencyStreams(@NonNull VariantScope variantScope) { |
| super.createDependencyStreams(variantScope); |
| |
| // add the same jars twice in the same stream as the EXTERNAL_LIB in the task manager |
| // so that filtering of duplicates in proguard can work. |
| variantScope |
| .getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "local-deps-classes") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(InternalScope.LOCAL_DEPS) |
| .setFileCollection(variantScope.getLocalPackagedJars()) |
| .build()); |
| } |
| |
| private static class MergeResourceCallback implements TaskProviderCallback<MergeResources> { |
| private final VariantScope variantScope; |
| |
| private MergeResourceCallback(VariantScope variantScope) { |
| this.variantScope = variantScope; |
| } |
| |
| @Override |
| public void handleProvider(@NonNull TaskProvider<? extends MergeResources> taskProvider) { |
| |
| variantScope |
| .getArtifacts() |
| .producesFile( |
| InternalArtifactType.PUBLIC_RES, |
| BuildArtifactsHolder.OperationType.INITIAL, |
| taskProvider, |
| MergeResources::getPublicFile, |
| FN_PUBLIC_TXT); |
| |
| } |
| } |
| |
| private void createMergeResourcesTasks(@NonNull VariantScope variantScope) { |
| ImmutableSet<MergeResources.Flag> flags; |
| if (variantScope.getGlobalScope().getExtension().getAaptOptions().getNamespaced()) { |
| flags = |
| Sets.immutableEnumSet( |
| MergeResources.Flag.REMOVE_RESOURCE_NAMESPACES, |
| MergeResources.Flag.PROCESS_VECTOR_DRAWABLES); |
| } else { |
| flags = Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES); |
| } |
| |
| MergeResourceCallback callback = new MergeResourceCallback(variantScope); |
| |
| // Create a merge task to only merge the resources from this library and not |
| // the dependencies. This is what gets packaged in the aar. |
| basicCreateMergeResourcesTask( |
| variantScope, |
| MergeType.PACKAGE, |
| variantScope.getIntermediateDir(InternalArtifactType.PACKAGED_RES), |
| false, |
| false, |
| false, |
| flags, |
| callback); |
| |
| |
| // This task merges all the resources, including the dependencies of this library. |
| // This should be unused, except that external libraries might consume it. |
| createMergeResourcesTask(variantScope, false /*processResources*/, ImmutableSet.of()); |
| } |
| |
| @Override |
| protected void postJavacCreation(@NonNull VariantScope scope) { |
| // create an anchor collection for usage inside the same module (unit tests basically) |
| ConfigurableFileCollection files = |
| scope.getGlobalScope() |
| .getProject() |
| .files( |
| scope.getArtifacts().getFinalProduct(JAVAC), |
| scope.getVariantData().getAllPreJavacGeneratedBytecode(), |
| scope.getVariantData().getAllPostJavacGeneratedBytecode()); |
| scope.getArtifacts().appendArtifact(AnchorOutputType.ALL_CLASSES, files); |
| |
| // Create jar used for publishing to API elements (for other projects to compile against). |
| taskFactory.register( |
| new BundleLibraryClasses.CreationAction( |
| scope, |
| AndroidArtifacts.PublishedConfigType.API_ELEMENTS, |
| excludeDataBindingClassesIfNecessary(scope))); |
| } |
| |
| @NonNull |
| private Supplier<List<String>> excludeDataBindingClassesIfNecessary( |
| @NonNull VariantScope variantScope) { |
| if (!extension.getDataBinding().isEnabled()) { |
| return Collections::emptyList; |
| } |
| |
| return () -> { |
| File excludeFile = |
| variantScope.getVariantData().getType().isExportDataBindingClassList() |
| ? variantScope.getGeneratedClassListOutputFileForDataBinding() |
| : null; |
| File dependencyArtifactsDir = |
| variantScope |
| .getArtifacts() |
| .getFinalProduct(InternalArtifactType.DATA_BINDING_DEPENDENCY_ARTIFACTS) |
| .get() |
| .getAsFile(); |
| return dataBindingBuilder.getJarExcludeList( |
| variantScope.getVariantData().getLayoutXmlProcessor(), |
| excludeFile, |
| dependencyArtifactsDir); |
| }; |
| } |
| |
| public void createLibraryAssetsTask(@NonNull VariantScope scope) { |
| taskFactory.register(new MergeSourceSetFolders.LibraryAssetCreationAction(scope)); |
| } |
| |
| @NonNull |
| @Override |
| protected Set<ScopeType> getJavaResMergingScopes( |
| @NonNull VariantScope variantScope, @NonNull QualifiedContent.ContentType contentType) { |
| Preconditions.checkArgument( |
| contentType == RESOURCES || contentType == NATIVE_LIBS, |
| "contentType must be RESOURCES or NATIVE_LIBS"); |
| if (variantScope.getTestedVariantData() != null) { |
| if (contentType == RESOURCES) { |
| return TransformManager.SCOPE_FULL_PROJECT_WITH_LOCAL_JARS; |
| } |
| // contentType is NATIVE_LIBS |
| return TransformManager.SCOPE_FULL_PROJECT; |
| } |
| if (contentType == RESOURCES) { |
| return TransformManager.SCOPE_FULL_LIBRARY_WITH_LOCAL_JARS; |
| } |
| // contentType is NATIVE_LIBS |
| return TransformManager.PROJECT_ONLY; |
| } |
| |
| @Override |
| protected boolean isLibrary() { |
| return true; |
| } |
| |
| public void createVerifyLibraryResTask(@NonNull VariantScope scope) { |
| TaskProvider<VerifyLibraryResourcesTask> verifyLibraryResources = |
| taskFactory.register(new VerifyLibraryResourcesTask.CreationAction(scope)); |
| |
| TaskFactoryUtils.dependsOn( |
| scope.getTaskContainer().getAssembleTask(), verifyLibraryResources); |
| } |
| } |