// Copyright 2016 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.lib.rules.android; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts; import com.google.devtools.build.lib.rules.java.JavaInfo; import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider; import com.google.devtools.build.lib.rules.java.JavaRuntimeInfo; import com.google.devtools.build.lib.rules.java.JavaRuntimeJarProvider; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.JavaSkylarkApiProvider; import com.google.devtools.build.lib.rules.java.JavaToolchainProvider; import com.google.devtools.build.lib.vfs.PathFragment; /** * An implementation for the aar_import rule. * *

AAR files are zip archives that contain an Android Manifest, JARs, resources, assets, native * libraries, Proguard configuration and lint jars. Currently the aar_import rule supports AARs with * an AndroidManifest.xml, classes.jar, libs/, res/ and jni/. Assets are not yet supported. * * @see AAR Format */ public class AarImport implements RuleConfiguredTargetFactory { private static final String ANDROID_MANIFEST = "AndroidManifest.xml"; private static final String MERGED_JAR = "classes_and_libs_merged.jar"; private final JavaSemantics javaSemantics; protected AarImport(JavaSemantics javaSemantics) { this.javaSemantics = javaSemantics; } @Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException, RuleErrorException { AndroidSdkProvider.verifyPresence(ruleContext); RuleConfiguredTargetBuilder ruleBuilder = new RuleConfiguredTargetBuilder(ruleContext); Artifact aar = ruleContext.getPrerequisiteArtifact("aar", Mode.TARGET); Artifact allAarJars = createAarTreeArtifact(ruleContext, "jars"); Artifact jarMergingParams = createAarArtifact(ruleContext, "jar_merging_params"); ruleContext.registerAction( createAarEmbeddedJarsExtractorActions(ruleContext, aar, allAarJars, jarMergingParams)); Artifact mergedJar = createAarArtifact(ruleContext, MERGED_JAR); ruleContext.registerAction( createAarJarsMergingActions(ruleContext, allAarJars, mergedJar, jarMergingParams)); // AndroidManifest.xml is required in every AAR. Artifact androidManifestArtifact = createAarArtifact(ruleContext, ANDROID_MANIFEST); ruleContext.registerAction( createSingleFileExtractorActions( ruleContext, aar, ANDROID_MANIFEST, androidManifestArtifact)); Artifact resources = createAarTreeArtifact(ruleContext, "resources"); Artifact assets = createAarTreeArtifact(ruleContext, "assets"); ruleContext.registerAction( createAarResourcesExtractorActions(ruleContext, aar, resources, assets)); ApplicationManifest androidManifest = ApplicationManifest.fromExplicitManifest(ruleContext, androidManifestArtifact); Artifact resourcesZip = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP); ResourceApk resourceApk = androidManifest.packAarWithDataAndResources( ruleContext, LocalResourceContainer.forAssetsAndResourcesDirectories(assets, resources), ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LOCAL_SYMBOLS), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), resourcesZip); // There isn't really any use case for building an aar_import target on its own, so the files to // build could be empty. The resources zip and merged jars are added here as a sanity check for // Bazel developers so that `bazel build java/com/my_aar_import` will fail if the resource // processing or jar merging steps fail. NestedSet filesToBuild = NestedSetBuilder.stableOrder().add(resourcesZip).add(mergedJar).build(); Artifact nativeLibs = createAarArtifact(ruleContext, "native_libs.zip"); ruleContext.registerAction(createAarNativeLibsFilterActions(ruleContext, aar, nativeLibs)); JavaRuleOutputJarsProvider.Builder jarProviderBuilder = new JavaRuleOutputJarsProvider.Builder().addOutputJar(mergedJar, null, ImmutableList.of()); ImmutableList targets = ImmutableList.builder() .addAll(ruleContext.getPrerequisites("exports", Mode.TARGET)) .addAll(ruleContext.getPrerequisites("deps", Mode.TARGET)) .build(); JavaCommon common = new JavaCommon( ruleContext, javaSemantics, /* sources = */ ImmutableList.of(), /* compileDeps = */ targets, /* runtimeDeps = */ targets, /* bothDeps = */ targets); common.setJavaCompilationArtifacts( new JavaCompilationArtifacts.Builder() .addRuntimeJar(mergedJar) .addCompileTimeJarAsFullJar(mergedJar) .build()); JavaCompilationArgsProvider javaCompilationArgsProvider = JavaCompilationArgsProvider.create( common.collectJavaCompilationArgs( /* recursive = */ false, JavaCommon.isNeverLink(ruleContext), /* srcLessDepsExport = */ false), common.collectJavaCompilationArgs( /* recursive = */ true, JavaCommon.isNeverLink(ruleContext), /* srcLessDepsExport = */ false)); JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create() .addProvider(JavaCompilationArgsProvider.class, javaCompilationArgsProvider) .addProvider(JavaRuleOutputJarsProvider.class, jarProviderBuilder.build()); common.addTransitiveInfoProviders( ruleBuilder, javaInfoBuilder, filesToBuild, /*classJar=*/ null); return ruleBuilder .setFilesToBuild(filesToBuild) .addSkylarkTransitiveInfo( JavaSkylarkApiProvider.NAME, JavaSkylarkApiProvider.fromRuleContext()) .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) .addNativeDeclaredProvider(resourceApk.toResourceInfo(ruleContext.getLabel())) .addNativeDeclaredProvider( new AndroidNativeLibsInfo( AndroidCommon.collectTransitiveNativeLibs(ruleContext).add(nativeLibs).build())) .addProvider( JavaRuntimeJarProvider.class, new JavaRuntimeJarProvider(ImmutableList.of(mergedJar))) .addNativeDeclaredProvider(javaInfoBuilder.build()) .build(); } private static Action[] createSingleFileExtractorActions( RuleContext ruleContext, Artifact aar, String filename, Artifact outputArtifact) { return new SpawnAction.Builder() .useDefaultShellEnvironment() .setExecutable(ruleContext.getExecutablePrerequisite(AarImportBaseRule.ZIPPER, Mode.HOST)) .setMnemonic("AarFileExtractor") .setProgressMessage("Extracting %s from %s", filename, aar.getFilename()) .addInput(aar) .addOutput(outputArtifact) .addCommandLine( CustomCommandLine.builder() .addExecPath("x", aar) .addPath("-d", outputArtifact.getExecPath().getParentDirectory()) .addDynamicString(filename) .build()) .build(ruleContext); } private static Action[] createAarResourcesExtractorActions( RuleContext ruleContext, Artifact aar, Artifact resourcesDir, Artifact assetsDir) { return new SpawnAction.Builder() .useDefaultShellEnvironment() .setExecutable( ruleContext.getExecutablePrerequisite( AarImportBaseRule.AAR_RESOURCES_EXTRACTOR, Mode.HOST)) .setMnemonic("AarResourcesExtractor") .addInput(aar) .addOutput(resourcesDir) .addOutput(assetsDir) .addCommandLine( CustomCommandLine.builder() .addExecPath("--input_aar", aar) .addExecPath("--output_res_dir", resourcesDir) .addExecPath("--output_assets_dir", assetsDir) .build()) .build(ruleContext); } private static Action[] createAarEmbeddedJarsExtractorActions( RuleContext ruleContext, Artifact aar, Artifact jarsTreeArtifact, Artifact singleJarParamFile) { return new SpawnAction.Builder() .useDefaultShellEnvironment() .setExecutable( ruleContext.getExecutablePrerequisite( AarImportBaseRule.AAR_EMBEDDED_JARS_EXTACTOR, Mode.HOST)) .setMnemonic("AarEmbeddedJarsExtractor") .setProgressMessage("Extracting classes.jar and libs/*.jar from %s", aar.getFilename()) .addInput(aar) .addOutput(jarsTreeArtifact) .addOutput(singleJarParamFile) .addCommandLine( CustomCommandLine.builder() .addExecPath("--input_aar", aar) .addExecPath("--output_dir", jarsTreeArtifact) .addExecPath("--output_singlejar_param_file", singleJarParamFile) .build()) .build(ruleContext); } private static Action[] createAarJarsMergingActions( RuleContext ruleContext, Artifact jarsTreeArtifact, Artifact mergedJar, Artifact paramFile) { return singleJarSpawnActionBuilder(ruleContext) .setMnemonic("AarJarsMerger") .setProgressMessage("Merging AAR embedded jars") .addInput(jarsTreeArtifact) .addOutput(mergedJar) .addInput(paramFile) .addCommandLine( CustomCommandLine.builder() .addExecPath("--output", mergedJar) .add("--dont_change_compression") .addPrefixedExecPath("@", paramFile) .build()) .build(ruleContext); } private static Action[] createAarNativeLibsFilterActions( RuleContext ruleContext, Artifact aar, Artifact outputZip) { SpawnAction.Builder actionBuilder = new SpawnAction.Builder() .useDefaultShellEnvironment() .setExecutable( ruleContext.getExecutablePrerequisite( AarImportBaseRule.AAR_NATIVE_LIBS_ZIP_CREATOR, Mode.HOST)) .setMnemonic("AarNativeLibsFilter") .setProgressMessage("Filtering AAR native libs by architecture") .addInput(aar) .addOutput(outputZip) .addCommandLine( CustomCommandLine.builder() .addExecPath("--input_aar", aar) .add("--cpu", ruleContext.getConfiguration().getCpu()) .addExecPath("--output_zip", outputZip) .build()); return actionBuilder.build(ruleContext); } private static Artifact createAarArtifact(RuleContext ruleContext, String name) { return ruleContext.getUniqueDirectoryArtifact( "_aar", name, ruleContext.getBinOrGenfilesDirectory()); } private static Artifact createAarTreeArtifact(RuleContext ruleContext, String name) { PathFragment rootRelativePath = ruleContext.getUniqueDirectory("_aar/unzipped/" + name); return ruleContext.getTreeArtifact(rootRelativePath, ruleContext.getBinOrGenfilesDirectory()); } // Adds the appropriate SpawnAction options depending on if SingleJar is a jar or not. private static SpawnAction.Builder singleJarSpawnActionBuilder(RuleContext ruleContext) { SpawnAction.Builder builder = new SpawnAction.Builder().useDefaultShellEnvironment(); Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar(); if (singleJar.getFilename().endsWith(".jar")) { builder .setJarExecutable( JavaCommon.getHostJavaExecutable(ruleContext), singleJar, JavaToolchainProvider.from(ruleContext).getJvmOptions()) .addTransitiveInputs(JavaRuntimeInfo.forHost(ruleContext).javaBaseInputsMiddleman()); } else { builder.setExecutable(singleJar); } return builder; } }