// Copyright 2015 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 static com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode.DEFAULT;
import static com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode.ERROR;
import static com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode.STRICT;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.OutputGroupProvider;
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.Runfiles;
import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode;
import com.google.devtools.build.lib.collect.IterablesChain;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode;
import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore;
import com.google.devtools.build.lib.rules.cpp.CcNativeLibraryProvider;
import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
import com.google.devtools.build.lib.rules.cpp.LinkerInput;
import com.google.devtools.build.lib.rules.java.ClasspathConfiguredFragment;
import com.google.devtools.build.lib.rules.java.JavaCcLinkParamsProvider;
import com.google.devtools.build.lib.rules.java.JavaCommon;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgs;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgs.ClasspathType;
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.JavaCompilationHelper;
import com.google.devtools.build.lib.rules.java.JavaNativeLibraryProvider;
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.JavaSourceJarsProvider;
import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
import com.google.devtools.build.lib.rules.java.JavaUtil;
import com.google.devtools.build.lib.rules.java.SourcesJavaCompilationArgsProvider;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.InstrumentationSpec;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
/**
* A helper class for android rules.
*
*
Helps create the java compilation as well as handling the exporting of the java compilation
* artifacts to the other rules.
*/
public class AndroidCommon {
public static final InstrumentationSpec ANDROID_COLLECTION_SPEC = JavaCommon.JAVA_COLLECTION_SPEC
.withDependencyAttributes("deps", "data", "exports", "runtime_deps", "binary_under_test");
public static final Set TRANSITIVE_ATTRIBUTES = ImmutableSet.of(
"deps",
"exports"
);
public static final Iterable getTransitivePrerequisites(
RuleContext ruleContext, Mode mode, final Class classType) {
IterablesChain.Builder builder = IterablesChain.builder();
for (String attr : TRANSITIVE_ATTRIBUTES) {
if (ruleContext.getAttribute(attr) != null) {
builder.add(ruleContext.getPrerequisites(attr, mode, classType));
}
}
return builder.build();
}
public static final Iterable collectTransitiveInfo(
RuleContext ruleContext, Mode mode) {
ImmutableList.Builder builder = ImmutableList.builder();
for (String attr : TRANSITIVE_ATTRIBUTES) {
if (ruleContext.getAttribute(attr) != null) {
builder.addAll(ruleContext.getPrerequisites(attr, mode));
}
}
return builder.build();
}
private final RuleContext ruleContext;
private final JavaCommon javaCommon;
private final boolean asNeverLink;
private final boolean exportDeps;
private NestedSet compileTimeDependencyArtifacts;
private NestedSet filesToBuild;
private NestedSet transitiveNeverlinkLibraries =
NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private Iterable topLevelSourceJars = ImmutableList.of();
private NestedSet transitiveSourceJars = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private JavaCompilationArgs javaCompilationArgs = JavaCompilationArgs.EMPTY_ARGS;
private JavaCompilationArgs recursiveJavaCompilationArgs = JavaCompilationArgs.EMPTY_ARGS;
private JackCompilationHelper jackCompilationHelper;
private ImmutableList jarsProducedForRuntime;
private Artifact classJar;
private Artifact iJar;
private Artifact srcJar;
private Artifact genClassJar;
private Artifact genSourceJar;
private Artifact resourceClassJar;
private Artifact resourceIJar;
private Artifact resourceSourceJar;
private Artifact outputDepsProto;
private Artifact manifestProtoOutput;
private AndroidIdlHelper idlHelper;
public AndroidCommon(JavaCommon javaCommon) {
this(javaCommon, JavaCommon.isNeverLink(javaCommon.getRuleContext()), false);
}
/**
* Creates a new AndroidCommon.
* @param common the JavaCommon instance
* @param asNeverLink Boolean to indicate if this rule should be treated as a compile time dep
* by consuming rules.
* @param exportDeps Boolean to indicate if the dependencies should be treated as "exported" deps.
*/
public AndroidCommon(JavaCommon common, boolean asNeverLink, boolean exportDeps) {
this.ruleContext = common.getRuleContext();
this.asNeverLink = asNeverLink;
this.exportDeps = exportDeps;
this.javaCommon = common;
}
/**
* Collects the transitive neverlink dependencies.
*
* @param ruleContext the context of the rule neverlink deps are to be computed for
* @param deps the targets to be treated as dependencies
* @param runtimeJars the runtime jars produced by the rule (non-transitive)
*
* @return a nested set of the neverlink deps.
*/
public static NestedSet collectTransitiveNeverlinkLibraries(
RuleContext ruleContext, Iterable extends TransitiveInfoCollection> deps,
ImmutableList runtimeJars) {
NestedSetBuilder builder = NestedSetBuilder.naiveLinkOrder();
for (AndroidNeverLinkLibrariesProvider provider : AnalysisUtils.getProviders(deps,
AndroidNeverLinkLibrariesProvider.class)) {
builder.addTransitive(provider.getTransitiveNeverLinkLibraries());
}
if (JavaCommon.isNeverLink(ruleContext)) {
builder.addAll(runtimeJars);
for (JavaCompilationArgsProvider provider : AnalysisUtils.getProviders(
deps, JavaCompilationArgsProvider.class)) {
builder.addTransitive(provider.getRecursiveJavaCompilationArgs().getRuntimeJars());
}
}
return builder.build();
}
/**
* Creates an action that converts {@code jarToDex} to a dex file. The output will be stored in
* the {@link com.google.devtools.build.lib.actions.Artifact} {@code dxJar}.
*/
public static void createDexAction(
RuleContext ruleContext,
Artifact jarToDex, Artifact classesDex, List dexOptions, boolean multidex,
Artifact mainDexList) {
List args = new ArrayList<>();
args.add("--dex");
// Add --no-locals to coverage builds. Older coverage tools don't correctly preserve local
// variable information in stack frame maps that are required since Java 7, so to avoid runtime
// errors we just don't add local variable info in the first place. This may no longer be
// necessary, however, as long as we use a coverage tool that generates stack frame maps.
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
args.add("--no-locals"); // TODO(bazel-team): Is this still needed?
}
// Multithreaded dex does not work when using --multi-dex.
if (!multidex) {
// Multithreaded dex tends to run faster, but only up to about 5 threads (at which point the
// law of diminishing returns kicks in). This was determined experimentally, with 5-thread dex
// performing about 25% faster than 1-thread dex.
args.add("--num-threads=5");
}
args.addAll(dexOptions);
if (multidex) {
args.add("--multi-dex");
if (mainDexList != null) {
args.add("--main-dex-list=" + mainDexList.getExecPathString());
}
}
args.add("--output=" + classesDex.getExecPathString());
args.add(jarToDex.getExecPathString());
SpawnAction.Builder builder = new SpawnAction.Builder()
.setExecutable(AndroidSdkProvider.fromRuleContext(ruleContext).getDx())
.addInput(jarToDex)
.addOutput(classesDex)
.addArguments(args)
.setProgressMessage("Converting " + jarToDex.getExecPathString() + " to dex format")
.setMnemonic("AndroidDexer")
.setResources(ResourceSet.createWithRamCpuIo(4096.0, 5.0, 0.0));
if (mainDexList != null) {
builder.addInput(mainDexList);
}
ruleContext.registerAction(builder.build(ruleContext));
}
public static AndroidIdeInfoProvider createAndroidIdeInfoProvider(
RuleContext ruleContext,
AndroidSemantics semantics,
AndroidIdlHelper idlHelper,
OutputJar resourceJar,
ResourceApk resourceApk,
Artifact zipAlignedApk,
Iterable apksUnderTest) {
AndroidIdeInfoProvider.Builder ideInfoProviderBuilder =
new AndroidIdeInfoProvider.Builder()
.setIdlClassJar(idlHelper.getIdlClassJar())
.setIdlSourceJar(idlHelper.getIdlSourceJar())
.setResourceJar(resourceJar)
.addIdlParcelables(idlHelper.getIdlParcelables())
.addIdlSrcs(idlHelper.getIdlSources())
.addIdlGeneratedJavaFiles(idlHelper.getIdlGeneratedJavaSources())
.addAllApksUnderTest(apksUnderTest);
if (zipAlignedApk != null) {
ideInfoProviderBuilder.setApk(zipAlignedApk);
}
// If the rule defines resources, put those in the IDE info. Otherwise, proxy the data coming
// from the android_resources rule in its direct dependencies, if such a thing exists.
if (LocalResourceContainer.definesAndroidResources(ruleContext.attributes())) {
ideInfoProviderBuilder
.setDefinesAndroidResources(true)
.addResourceSources(resourceApk.getPrimaryResource().getArtifacts(ResourceType.RESOURCES))
.addAssetSources(
resourceApk.getPrimaryResource().getArtifacts(ResourceType.ASSETS),
getAssetDir(ruleContext))
// Sets the possibly merged manifest and the raw manifest.
.setGeneratedManifest(resourceApk.getPrimaryResource().getManifest())
.setManifest(ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET))
.setJavaPackage(getJavaPackage(ruleContext));
} else {
semantics.addNonLocalResources(ruleContext, resourceApk, ideInfoProviderBuilder);
}
return ideInfoProviderBuilder.build();
}
public static String getJavaPackage(RuleContext ruleContext) {
if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) {
return ruleContext.attributes().get("custom_package", Type.STRING);
} else {
PathFragment nameFragment = ruleContext.getRule().getPackage().getNameFragment();
String packageName = JavaUtil.getJavaFullClassname(nameFragment);
if (packageName != null) {
return packageName;
} else {
// This is a workaround for libraries that don't follow the standard Bazel package format
return nameFragment.getPathString().replace('/', '.');
}
}
}
static PathFragment getSourceDirectoryRelativePathFromResource(Artifact resource) {
PathFragment resourceDir = LocalResourceContainer.Builder.findResourceDir(resource);
if (resourceDir == null) {
return null;
}
return trimTo(resource.getRootRelativePath(), resourceDir);
}
/**
* Finds the rightmost occurrence of the needle and returns subfragment of the haystack from
* left to the end of the occurrence inclusive of the needle.
*
*
* `Example:
* Given the haystack:
* res/research/handwriting/res/values/strings.xml
* And the needle:
* res
* Returns:
* res/research/handwriting/res
*
*/
static PathFragment trimTo(PathFragment haystack, PathFragment needle) {
if (needle.equals(PathFragment.EMPTY_FRAGMENT)) {
return haystack;
}
// Compute the overlap offset for duplicated parts of the needle.
int[] overlap = new int[needle.segmentCount() + 1];
// Start overlap at -1, as it will cancel out the increment in the search.
// See http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm for the
// details.
overlap[0] = -1;
for (int i = 0, j = -1; i < needle.segmentCount(); j++, i++, overlap[i] = j) {
while (j >= 0 && !needle.getSegment(i).equals(needle.getSegment(j))) {
// Walk the overlap until the bound is found.
j = overlap[j];
}
}
// TODO(corysmith): reverse the search algorithm.
// Keep the index of the found so that the rightmost index is taken.
int found = -1;
for (int i = 0, j = 0; i < haystack.segmentCount(); i++) {
while (j >= 0 && !haystack.getSegment(i).equals(needle.getSegment(j))) {
// Not matching, walk the needle index to attempt another match.
j = overlap[j];
}
j++;
// Needle index is exhausted, so the needle must match.
if (j == needle.segmentCount()) {
// Record the found index + 1 to be inclusive of the end index.
found = i + 1;
// Subtract one from the needle index to restart the search process
j = j - 1;
}
}
if (found != -1) {
// Return the subsection of the haystack.
return haystack.subFragment(0, found);
}
throw new IllegalArgumentException(String.format("%s was not found in %s", needle, haystack));
}
Artifact compileDexWithJack(
MultidexMode mode, Optional mainDexList, Collection proguardSpecs) {
return jackCompilationHelper.compileAsDex(mode, mainDexList, proguardSpecs);
}
private void compileResources(
JavaSemantics javaSemantics,
ResourceApk resourceApk,
Artifact resourcesJar,
JavaCompilationArtifacts.Builder artifactsBuilder,
JavaTargetAttributes.Builder attributes,
NestedSetBuilder filesBuilder,
ImmutableList.Builder jarsProducedForRuntime,
boolean useRClassGenerator) throws InterruptedException {
compileResourceJar(javaSemantics, resourceApk, resourcesJar, useRClassGenerator);
// Add the compiled resource jar to the classpath of the main compilation.
attributes.addDirectJars(NestedSetBuilder.create(Order.STABLE_ORDER, resourceClassJar));
// Add the compiled resource jar to the classpath of consuming targets.
// We don't actually use the ijar. That is almost the same as the resource class jar
// except for , but it takes time to build and waiting for that to build would
// just delay building the rest of the library.
artifactsBuilder.addCompileTimeJar(resourceClassJar);
// Combined resource constants needs to come even before our own classes that may contain
// local resource constants.
artifactsBuilder.addRuntimeJar(resourceClassJar);
jarsProducedForRuntime.add(resourceClassJar);
// Add the compiled resource jar as a declared output of the rule.
filesBuilder.add(resourceSourceJar);
filesBuilder.add(resourceClassJar);
}
private void compileResourceJar(
JavaSemantics javaSemantics, ResourceApk resourceApk, Artifact resourcesJar,
boolean useRClassGenerator)
throws InterruptedException {
resourceSourceJar = ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.ANDROID_RESOURCES_SOURCE_JAR);
resourceClassJar = ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR);
JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
JavaTargetAttributes.Builder javacAttributes = new JavaTargetAttributes.Builder(javaSemantics)
.addSourceJar(resourcesJar);
JavaCompilationHelper javacHelper = new JavaCompilationHelper(
ruleContext, javaSemantics, getJavacOpts(), javacAttributes);
if (useRClassGenerator) {
RClassGeneratorActionBuilder actionBuilder =
new RClassGeneratorActionBuilder(ruleContext)
.withPrimary(resourceApk.getPrimaryResource())
.withDependencies(resourceApk.getResourceDependencies())
.setClassJarOut(resourceClassJar);
actionBuilder.build();
} else {
Artifact outputDepsProto =
javacHelper.createOutputDepsProtoArtifact(resourceClassJar, javaArtifactsBuilder);
javacHelper.createCompileActionWithInstrumentation(
resourceClassJar,
null /* manifestProtoOutput */,
null /* genSourceJar */,
outputDepsProto,
javaArtifactsBuilder);
}
javacHelper.createSourceJarAction(resourceSourceJar, null);
resourceIJar = javacHelper.createCompileTimeJarAction(resourceClassJar, javaArtifactsBuilder);
}
private void createJarJarActions(
JavaTargetAttributes.Builder attributes,
ImmutableList.Builder jarsProducedForRuntime,
Iterable resourceContainers,
String originalPackage,
Artifact binaryResourcesJar) {
// Now use jarjar for the rest of the resources. We need to make a copy
// of the final generated resources for each of the targets included in
// the transitive closure of this binary.
for (ResourceContainer otherContainer : resourceContainers) {
if (otherContainer.getLabel().equals(ruleContext.getLabel())) {
continue;
}
Artifact resourcesJar = createResourceJarArtifact(ruleContext, otherContainer, ".jar");
// combined resource constants copy needs to come before library classes that may contain
// their local resource constants
attributes.addRuntimeClassPathEntry(resourcesJar);
Artifact jarJarRuleFile = createResourceJarArtifact(
ruleContext, otherContainer, ".jar_jarjar_rules.txt");
String jarJarRule = String.format("rule %s.* %s.@1",
originalPackage, otherContainer.getJavaPackage());
ruleContext.registerAction(new FileWriteAction(
ruleContext.getActionOwner(), jarJarRuleFile, jarJarRule, false));
FilesToRunProvider jarjar =
ruleContext.getExecutablePrerequisite("$jarjar_bin", Mode.HOST);
ruleContext.registerAction(new SpawnAction.Builder()
.setExecutable(jarjar)
.addArgument("process")
.addInputArgument(jarJarRuleFile)
.addInputArgument(binaryResourcesJar)
.addOutputArgument(resourcesJar)
.setProgressMessage("Repackaging jar")
.setMnemonic("AndroidRepackageJar")
.build(ruleContext));
jarsProducedForRuntime.add(resourcesJar);
}
}
private static Artifact createResourceJarArtifact(RuleContext ruleContext,
ResourceContainer container, String fileNameSuffix) {
String artifactName = container.getLabel().getName() + fileNameSuffix;
// Since the Java sources are generated by combining all resources with the
// ones included in the binary, the path of the artifact has to be unique
// per binary and per library (not only per library).
Artifact artifact = ruleContext.getUniqueDirectoryArtifact("resource_jars",
container.getLabel().getPackageIdentifier().getPathFragment().getRelative(artifactName),
ruleContext.getBinOrGenfilesDirectory());
return artifact;
}
public JavaTargetAttributes init(
JavaSemantics javaSemantics, AndroidSemantics androidSemantics,
ResourceApk resourceApk,
boolean addCoverageSupport, boolean collectJavaCompilationArgs, boolean isBinary)
throws InterruptedException {
classJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_CLASS_JAR);
idlHelper = new AndroidIdlHelper(ruleContext, classJar);
JavaTargetAttributes.Builder attributes =
javaCommon
.initCommon(
idlHelper.getIdlGeneratedJavaSources(),
androidSemantics.getJavacArguments(ruleContext))
.setBootClassPath(
ImmutableList.of(AndroidSdkProvider.fromRuleContext(ruleContext).getAndroidJar()));
JavaCompilationArtifacts.Builder artifactsBuilder = new JavaCompilationArtifacts.Builder();
ImmutableList.Builder jarsProducedForRuntime = ImmutableList.builder();
NestedSetBuilder filesBuilder = NestedSetBuilder.stableOrder();
Artifact resourcesJar = resourceApk.getResourceJavaSrcJar();
if (resourcesJar != null) {
filesBuilder.add(resourcesJar);
// Use a fast-path R class generator for android_binary with local resources, where there is
// a bottleneck. For legacy resources, the srcjar and R class compiler don't match up
// (the legacy srcjar requires the createJarJar step below).
boolean useRClassGenerator =
getAndroidConfig(ruleContext).useRClassGenerator()
&& isBinary && !resourceApk.isLegacy();
compileResources(javaSemantics, resourceApk, resourcesJar, artifactsBuilder, attributes,
filesBuilder, jarsProducedForRuntime, useRClassGenerator);
if (resourceApk.isLegacy()) {
// Repackages the R.java for each dependency package and places the resultant jars before
// the dependency libraries to ensure that the generated resource ids are correct.
createJarJarActions(attributes, jarsProducedForRuntime,
resourceApk.getResourceDependencies().getResources(),
resourceApk.getPrimaryResource().getJavaPackage(), resourceClassJar);
}
}
JavaCompilationHelper helper = initAttributes(attributes, javaSemantics);
if (ruleContext.hasErrors()) {
return null;
}
if (addCoverageSupport) {
androidSemantics.addCoverageSupport(ruleContext, this, javaSemantics, true,
attributes, artifactsBuilder);
if (ruleContext.hasErrors()) {
return null;
}
}
jackCompilationHelper = initJack(helper.getAttributes());
if (ruleContext.hasErrors()) {
return null;
}
initJava(helper, artifactsBuilder, collectJavaCompilationArgs, filesBuilder);
if (ruleContext.hasErrors()) {
return null;
}
this.jarsProducedForRuntime = jarsProducedForRuntime.add(classJar).build();
return helper.getAttributes();
}
private JavaCompilationHelper initAttributes(
JavaTargetAttributes.Builder attributes, JavaSemantics semantics) {
JavaCompilationHelper helper = new JavaCompilationHelper(
ruleContext, semantics, javaCommon.getJavacOpts(), attributes);
helper.addLibrariesToAttributes(javaCommon.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY));
helper.addProvidersToAttributes(
JavaCommon.compilationArgsFromSources(ruleContext), asNeverLink);
attributes.setStrictJavaDeps(getStrictAndroidDeps());
attributes.setRuleKind(ruleContext.getRule().getRuleClass());
attributes.setTargetLabel(ruleContext.getLabel());
JavaCommon.validateConstraint(ruleContext, "android",
javaCommon.targetsTreatedAsDeps(ClasspathType.BOTH));
ruleContext.checkSrcsSamePackage(true);
return helper;
}
private StrictDepsMode getStrictAndroidDeps() {
// Get command line strict_android_deps option
StrictDepsMode strict = ruleContext.getFragment(AndroidConfiguration.class).getStrictDeps();
// Use option if anything but DEFAULT, which is now equivalent to ERROR.
return (strict != DEFAULT && strict != STRICT) ? strict : ERROR;
}
JackCompilationHelper initJack(JavaTargetAttributes attributes) throws InterruptedException {
AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext);
return new JackCompilationHelper.Builder()
// blaze infrastructure
.setRuleContext(ruleContext)
// configuration
.setOutputArtifact(
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_JACK_FILE))
// tools
.setJackBinary(sdk.getJack())
.setJillBinary(sdk.getJill())
.setResourceExtractorBinary(sdk.getResourceExtractor())
.setJackBaseClasspath(sdk.getAndroidBaseClasspathForJack())
// sources
.addJavaSources(attributes.getSourceFiles())
.addSourceJars(attributes.getSourceJars())
.addResources(attributes.getResources())
.addProcessorNames(attributes.getProcessorNames())
.addProcessorClasspathJars(attributes.getProcessorPath())
.addExports(JavaCommon.getExports(ruleContext))
.addClasspathDeps(javaCommon.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY))
.addRuntimeDeps(javaCommon.targetsTreatedAsDeps(ClasspathType.RUNTIME_ONLY))
.build();
}
private void initJava(
JavaCompilationHelper helper,
JavaCompilationArtifacts.Builder javaArtifactsBuilder,
boolean collectJavaCompilationArgs,
NestedSetBuilder filesBuilder) throws InterruptedException {
JavaTargetAttributes attributes = helper.getAttributes();
if (ruleContext.hasErrors()) {
// Avoid leaving filesToBuild set to null, otherwise we'll get a NullPointerException masking
// the real error.
filesToBuild = filesBuilder.build();
return;
}
Artifact jar = null;
if (attributes.hasSourceFiles() || attributes.hasSourceJars() || attributes.hasResources()) {
// We only want to add a jar to the classpath of a dependent rule if it has content.
javaArtifactsBuilder.addRuntimeJar(classJar);
jar = classJar;
}
filesBuilder.add(classJar);
manifestProtoOutput = helper.createManifestProtoOutput(classJar);
// The gensrc jar is created only if the target uses annotation processing. Otherwise,
// it is null, and the source jar action will not depend on the compile action.
if (helper.usesAnnotationProcessing()) {
genClassJar = helper.createGenJar(classJar);
genSourceJar = helper.createGensrcJar(classJar);
helper.createGenJarAction(classJar, manifestProtoOutput, genClassJar);
}
srcJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_SOURCE_JAR);
helper.createSourceJarAction(srcJar, genSourceJar);
outputDepsProto = helper.createOutputDepsProtoArtifact(classJar, javaArtifactsBuilder);
helper.createCompileActionWithInstrumentation(classJar, manifestProtoOutput, genSourceJar,
outputDepsProto, javaArtifactsBuilder);
compileTimeDependencyArtifacts =
javaCommon.collectCompileTimeDependencyArtifacts(outputDepsProto);
filesToBuild = filesBuilder.build();
if ((attributes.hasSourceFiles() || attributes.hasSourceJars()) && jar != null) {
iJar = helper.createCompileTimeJarAction(jar, javaArtifactsBuilder);
}
javaCommon.setJavaCompilationArtifacts(javaArtifactsBuilder.build());
javaCommon.setClassPathFragment(
new ClasspathConfiguredFragment(
javaCommon.getJavaCompilationArtifacts(),
attributes,
asNeverLink,
helper.getBootclasspathOrDefault()));
transitiveNeverlinkLibraries = collectTransitiveNeverlinkLibraries(
ruleContext,
javaCommon.getDependencies(),
javaCommon.getJavaCompilationArtifacts().getRuntimeJars());
topLevelSourceJars = ImmutableList.of(srcJar);
transitiveSourceJars = javaCommon.collectTransitiveSourceJars(srcJar);
if (collectJavaCompilationArgs) {
boolean hasSources = attributes.hasSourceFiles() || attributes.hasSourceJars();
this.javaCompilationArgs =
collectJavaCompilationArgs(exportDeps, asNeverLink, hasSources);
this.recursiveJavaCompilationArgs = collectJavaCompilationArgs(
true, asNeverLink, /* hasSources */ true);
}
}
public RuleConfiguredTargetBuilder addTransitiveInfoProviders(
RuleConfiguredTargetBuilder builder,
AndroidSemantics androidSemantics,
ResourceApk resourceApk,
Artifact zipAlignedApk,
Iterable apksUnderTest) {
javaCommon.addTransitiveInfoProviders(builder, filesToBuild, classJar, ANDROID_COLLECTION_SPEC);
javaCommon.addGenJarsProvider(builder, genClassJar, genSourceJar);
idlHelper.addTransitiveInfoProviders(builder, classJar, manifestProtoOutput);
JavaRuleOutputJarsProvider.Builder outputJarsBuilder = JavaRuleOutputJarsProvider.builder()
.addOutputJar(classJar, iJar, srcJar)
.setJdeps(outputDepsProto);
OutputJar resourceJar = null;
if (resourceClassJar != null && resourceIJar != null && resourceSourceJar != null) {
resourceJar = new OutputJar(resourceClassJar, resourceIJar, resourceSourceJar);
outputJarsBuilder.addOutputJar(resourceJar);
}
return builder
.setFilesToBuild(filesToBuild)
.add(JavaRuleOutputJarsProvider.class, outputJarsBuilder.build())
.add(
JavaRuntimeJarProvider.class,
new JavaRuntimeJarProvider(javaCommon.getJavaCompilationArtifacts().getRuntimeJars()))
.add(RunfilesProvider.class, RunfilesProvider.simple(getRunfiles()))
.add(AndroidResourcesProvider.class, resourceApk.toResourceProvider(ruleContext.getLabel()))
.add(
AndroidIdeInfoProvider.class,
createAndroidIdeInfoProvider(
ruleContext,
androidSemantics,
idlHelper,
resourceJar,
resourceApk,
zipAlignedApk,
apksUnderTest))
.add(
JavaCompilationArgsProvider.class,
new JavaCompilationArgsProvider(
javaCompilationArgs,
recursiveJavaCompilationArgs,
compileTimeDependencyArtifacts,
NestedSetBuilder.emptySet(Order.STABLE_ORDER)))
.add(
JackLibraryProvider.class,
asNeverLink
? jackCompilationHelper.compileAsNeverlinkLibrary()
: jackCompilationHelper.compileAsLibrary())
.addSkylarkTransitiveInfo(AndroidSkylarkApiProvider.NAME, new AndroidSkylarkApiProvider())
.addOutputGroup(
OutputGroupProvider.HIDDEN_TOP_LEVEL, collectHiddenTopLevelArtifacts(ruleContext))
.addOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP, transitiveSourceJars);
}
private Runfiles getRunfiles() {
// TODO(bazel-team): why return any Runfiles in the neverlink case?
if (asNeverLink) {
return new Runfiles.Builder(
ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles())
.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
.build();
}
return JavaCommon.getRunfiles(
ruleContext, javaCommon.getJavaSemantics(), javaCommon.getJavaCompilationArtifacts(),
asNeverLink);
}
public static PathFragment getAssetDir(RuleContext ruleContext) {
return new PathFragment(ruleContext.attributes().get(
AndroidResourcesProvider.ResourceType.ASSETS.getAttribute() + "_dir",
Type.STRING));
}
public static NestedSet collectTransitiveNativeLibraries(
Iterable extends TransitiveInfoCollection> deps) {
NestedSetBuilder builder = NestedSetBuilder.stableOrder();
for (TransitiveInfoCollection dep : deps) {
AndroidNativeLibraryProvider android = dep.getProvider(AndroidNativeLibraryProvider.class);
if (android != null) {
builder.addTransitive(android.getTransitiveAndroidNativeLibraries());
continue;
}
JavaNativeLibraryProvider java = dep.getProvider(JavaNativeLibraryProvider.class);
if (java != null) {
builder.addTransitive(java.getTransitiveJavaNativeLibraries());
continue;
}
CcNativeLibraryProvider cc = dep.getProvider(CcNativeLibraryProvider.class);
if (cc != null) {
for (LinkerInput input : cc.getTransitiveCcNativeLibraries()) {
Artifact library = input.getOriginalLibraryArtifact();
String name = library.getFilename();
if (CppFileTypes.SHARED_LIBRARY.matches(name)
|| CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(name)) {
builder.add(input);
}
}
continue;
}
}
return builder.build();
}
public static AndroidResourcesProvider getAndroidResources(RuleContext ruleContext) {
if (!ruleContext.attributes().has("resources", BuildType.LABEL)) {
return null;
}
TransitiveInfoCollection prerequisite = ruleContext.getPrerequisite("resources", Mode.TARGET);
if (prerequisite == null) {
return null;
}
return prerequisite.getProvider(AndroidResourcesProvider.class);
}
/**
* Collects Java compilation arguments for this target.
*
* @param recursive Whether to scan dependencies recursively.
* @param isNeverLink Whether the target has the 'neverlink' attr.
* @param hasSrcs If false, deps are exported (deprecated behaviour)
*/
private JavaCompilationArgs collectJavaCompilationArgs(boolean recursive, boolean isNeverLink,
boolean hasSrcs) {
boolean exportDeps = !hasSrcs
&& ruleContext.getFragment(AndroidConfiguration.class).allowSrcsLessAndroidLibraryDeps();
Iterable fromSrcs =
ImmutableList. of();
return javaCommon.collectJavaCompilationArgs(recursive, isNeverLink, fromSrcs, exportDeps);
}
public ImmutableList getJavacOpts() {
return javaCommon.getJavacOpts();
}
public Artifact getGenClassJar() {
return genClassJar;
}
@Nullable public Artifact getGenSourceJar() {
return genSourceJar;
}
public ImmutableList getRuntimeJars() {
return javaCommon.getJavaCompilationArtifacts().getRuntimeJars();
}
/**
* Returns Jars produced by this rule that may go into the runtime classpath. By contrast
* {@link #getRuntimeJars()} returns the complete runtime classpath needed by this rule, including
* dependencies.
*/
public ImmutableList getJarsProducedForRuntime() {
return jarsProducedForRuntime;
}
public Artifact getInstrumentedJar() {
return javaCommon.getJavaCompilationArtifacts().getInstrumentedJar();
}
public NestedSet getTransitiveNeverLinkLibraries() {
return transitiveNeverlinkLibraries;
}
public Iterable getTopLevelSourceJars() {
return topLevelSourceJars;
}
public NestedSet getTransitiveSourceJars() {
return transitiveSourceJars;
}
public JavaSourceJarsProvider getJavaSourceJarsProvider() {
return new JavaSourceJarsProvider(getTransitiveSourceJars(), getTopLevelSourceJars());
}
public boolean isNeverLink() {
return asNeverLink;
}
public CcLinkParamsStore getCcLinkParamsStore() {
return getCcLinkParamsStore(javaCommon.targetsTreatedAsDeps(ClasspathType.BOTH));
}
public static CcLinkParamsStore getCcLinkParamsStore(
final Iterable extends TransitiveInfoCollection> deps) {
return new CcLinkParamsStore() {
@Override
protected void collect(CcLinkParams.Builder builder, boolean linkingStatically,
boolean linkShared) {
builder.addTransitiveTargets(deps,
// Link in Java-specific C++ code in the transitive closure
JavaCcLinkParamsProvider.TO_LINK_PARAMS,
// Link in Android-specific C++ code (e.g., android_libraries) in the transitive closure
AndroidCcLinkParamsProvider.TO_LINK_PARAMS,
// Link in non-language-specific C++ code in the transitive closure
CcLinkParamsProvider.TO_LINK_PARAMS);
}
};
}
/**
* Returns {@link AndroidConfiguration} in given context.
*/
static AndroidConfiguration getAndroidConfig(RuleContext context) {
return context.getConfiguration().getFragment(AndroidConfiguration.class);
}
private NestedSet collectHiddenTopLevelArtifacts(RuleContext ruleContext) {
NestedSetBuilder builder = NestedSetBuilder.stableOrder();
for (OutputGroupProvider provider :
getTransitivePrerequisites(ruleContext, Mode.TARGET, OutputGroupProvider.class)) {
builder.addTransitive(provider.getOutputGroup(OutputGroupProvider.HIDDEN_TOP_LEVEL));
}
return builder.build();
}
}