blob: 7c340694cb9ac9b42671966dbeb871af6e155711 [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.FileProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
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.SpawnAction;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
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.JavaHelper;
import com.google.devtools.build.lib.rules.java.JavaProvider;
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.JavaRuntimeJarProvider;
import com.google.devtools.build.lib.rules.java.JavaSemantics;
import com.google.devtools.build.lib.rules.java.JavaToolchainProvider;
import com.google.devtools.build.lib.rules.java.Jvm;
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 <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 {
if (!AndroidSdkProvider.verifyPresence(ruleContext)) {
return null;
}
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 resourcesManifest = createAarArtifact(ruleContext, "resource_manifest");
ruleContext.registerAction(
createManifestExtractorActions(ruleContext, aar, "res/.*", resourcesManifest));
Artifact resources = createAarTreeArtifact(ruleContext, "resources");
ruleContext.registerAction(
createManifestFileEntriesExtractorActions(ruleContext, aar, resourcesManifest, resources));
ApplicationManifest androidManifest =
ApplicationManifest.fromExplicitManifest(ruleContext, androidManifestArtifact);
FileProvider resourcesProvider = new FileProvider(
new NestedSetBuilder<Artifact>(Order.NAIVE_LINK_ORDER).add(resources).build());
Artifact resourcesZip =
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP);
ResourceApk resourceApk = androidManifest.packWithDataAndResources(
ruleContext,
new LocalResourceContainer.Builder(ruleContext)
.withResources(ImmutableList.of(resourcesProvider))
.build(),
ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)),
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT),
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_SYMBOLS),
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST),
resourcesZip,
/* alwaysExportManifest = */ true);
// 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.<Artifact>of());
for (TransitiveInfoCollection export : ruleContext.getPrerequisites("exports", Mode.TARGET)) {
for (OutputJar jar :
JavaProvider.getProvider(JavaRuleOutputJarsProvider.class, export).getOutputJars()) {
jarProviderBuilder.addOutputJar(jar);
filesToBuildBuilder.add(jar.getClassJar());
}
}
ImmutableList<TransitiveInfoCollection> targets =
ImmutableList.<TransitiveInfoCollection>copyOf(
ruleContext.getPrerequisites("exports", Mode.TARGET));
JavaCommon common =
new JavaCommon(
ruleContext,
javaSemantics,
/* sources = */ ImmutableList.<Artifact>of(),
/* compileDeps = */ targets,
/* runtimeDeps = */ targets,
/* bothDeps = */ targets);
common.setJavaCompilationArtifacts(
new JavaCompilationArtifacts.Builder()
.addRuntimeJar(mergedJar)
.addCompileTimeJar(mergedJar)
.build());
return ruleBuilder
.setFilesToBuild(filesToBuildBuilder.build())
.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)))
.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();
}
private static Action[] createSingleFileExtractorActions(RuleContext ruleContext, Artifact aar,
String filename, Artifact outputArtifact) {
return new SpawnAction.Builder()
.setExecutable(ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST))
.setMnemonic("AarFileExtractor")
.setProgressMessage("Extracting " + filename + " from " + aar.getFilename())
.addArgument("x")
.addInputArgument(aar)
.addArgument("-d")
.addOutput(outputArtifact)
.addArgument(outputArtifact.getExecPath().getParentDirectory().getPathString())
.addArgument(filename)
.build(ruleContext);
}
private static Action[] createManifestFileEntriesExtractorActions(RuleContext ruleContext,
Artifact aar, Artifact manifest, Artifact outputTree) {
return new SpawnAction.Builder()
.setExecutable(ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST))
.setMnemonic("AarManifestFileEntriesExtractor")
.addArgument("x")
.addInputArgument(aar)
.addArgument("-d")
.addOutputArgument(outputTree)
.addArgument("@" + manifest.getExecPathString())
.addInput(manifest)
.build(ruleContext);
}
private static Action[] createAarEmbeddedJarsExtractorActions(RuleContext ruleContext,
Artifact aar, Artifact jarsTreeArtifact, Artifact singleJarParamFile) {
return new SpawnAction.Builder()
.setExecutable(
ruleContext.getExecutablePrerequisite("$aar_embedded_jars_extractor", Mode.HOST))
.setMnemonic("AarEmbeddedJarsExtractor")
.setProgressMessage("Extracting classes.jar and libs/*.jar from " + aar.getFilename())
.addArgument("--input_aar")
.addInputArgument(aar)
.addArgument("--output_dir")
.addOutputArgument(jarsTreeArtifact)
.addArgument("--output_singlejar_param_file")
.addOutputArgument(singleJarParamFile)
.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)
.addArgument("--output")
.addOutputArgument(mergedJar)
.addArgument("--dont_change_compression")
.addInput(paramFile)
.addArgument("@" + paramFile.getExecPathString())
.build(ruleContext);
}
private static Action[] createManifestExtractorActions(RuleContext ruleContext, Artifact aar,
String filenameRegexp, Artifact manifest) {
return new SpawnAction.Builder()
.setExecutable(ruleContext.getExecutablePrerequisite("$zip_manifest_creator", Mode.HOST))
.setMnemonic("ZipManifestCreator")
.setProgressMessage(
"Creating manifest for " + aar.getFilename() + " matching " + filenameRegexp)
.addArgument(filenameRegexp)
.addInputArgument(aar)
.addOutputArgument(manifest)
.build(ruleContext);
}
private static Action[] createAarNativeLibsFilterActions(RuleContext ruleContext, Artifact aar,
Artifact outputZip) {
SpawnAction.Builder actionBuilder = new SpawnAction.Builder()
.setExecutable(
ruleContext.getExecutablePrerequisite("$aar_native_libs_zip_creator", Mode.HOST))
.setMnemonic("AarNativeLibsFilter")
.setProgressMessage("Filtering AAR native libs by architecture")
.addArgument("--input_aar")
.addInputArgument(aar)
.addArgument("--cpu")
.addArgument(ruleContext.getConfiguration().getCpu())
.addArgument("--output_zip")
.addOutputArgument(outputZip);
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();
Artifact singleJar = JavaToolchainProvider.fromRuleContext(ruleContext).getSingleJar();
if (singleJar.getFilename().endsWith(".jar")) {
builder
.setJarExecutable(
ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable(),
singleJar,
JavaToolchainProvider.fromRuleContext(ruleContext).getJvmOptions())
.addTransitiveInputs(JavaHelper.getHostJavabaseInputs(ruleContext));
} else {
builder.setExecutable(singleJar);
}
return builder;
}
}