blob: b20e645c17513f66803ff4d409b369009f4fadc9 [file] [log] [blame]
// 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.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.JavaRuleOutputJarsProvider.OutputJar;
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.
*
* <p>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 <a href="http://tools.android.com/tech-docs/new-build-system/aar-format">AAR Format</a>
*/
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.
NestedSetBuilder<Artifact> filesToBuildBuilder =
NestedSetBuilder.<Artifact>stableOrder().add(resourcesZip).add(mergedJar);
Artifact nativeLibs = createAarArtifact(ruleContext, "native_libs.zip");
ruleContext.registerAction(createAarNativeLibsFilterActions(ruleContext, aar, nativeLibs));
JavaRuleOutputJarsProvider.Builder jarProviderBuilder =
new JavaRuleOutputJarsProvider.Builder().addOutputJar(mergedJar, null, ImmutableList.of());
for (TransitiveInfoCollection export : ruleContext.getPrerequisites("exports", Mode.TARGET)) {
for (OutputJar jar :
JavaInfo.getProvider(JavaRuleOutputJarsProvider.class, export).getOutputJars()) {
jarProviderBuilder.addOutputJar(jar);
filesToBuildBuilder.add(jar.getClassJar());
}
}
ImmutableList<TransitiveInfoCollection> targets =
ImmutableList.<TransitiveInfoCollection>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());
JavaInfo javaInfo =
JavaInfo.Builder.create()
.addProvider(
JavaCompilationArgsProvider.class,
JavaCompilationArgsProvider.create(
common.collectJavaCompilationArgs(
/* recursive = */ false,
JavaCommon.isNeverLink(ruleContext),
/* srcLessDepsExport = */ false),
common.collectJavaCompilationArgs(
/* recursive = */ true,
JavaCommon.isNeverLink(ruleContext),
/* srcLessDepsExport = */ false)))
.addProvider(JavaRuleOutputJarsProvider.class, jarProviderBuilder.build())
.build();
return ruleBuilder
.setFilesToBuild(filesToBuildBuilder.build())
.addSkylarkTransitiveInfo(
JavaSkylarkApiProvider.NAME, JavaSkylarkApiProvider.fromRuleContext())
.addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY)
.addProvider(
AndroidResourcesProvider.class,
resourceApk.toResourceProvider(ruleContext.getLabel()))
.addProvider(
NativeLibsZipsProvider.class,
new NativeLibsZipsProvider(
AndroidCommon.collectTransitiveNativeLibsZips(ruleContext).add(nativeLibs).build()))
.addProvider(
JavaRuntimeJarProvider.class, new JavaRuntimeJarProvider(ImmutableList.of(mergedJar)))
.addNativeDeclaredProvider(javaInfo)
.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;
}
}