// 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import java.util.ArrayList; import java.util.List; /** * Builder for creating $android_resource_validator action. This action validates merged resources * of an android_library via aapt, and writes out an R.txt file (mostly to serve as a dependency for * the android_binary -- otherwise the merger step could have generated the R.txt). * *

This is split from merging, so that it can happen off of the compilation critical path. */ public class AndroidResourceValidatorActionBuilder { private final RuleContext ruleContext; private final AndroidSdkProvider sdk; // Inputs private ResourceContainer primary; private Artifact mergedResources; // Outputs private Artifact rTxtOut; private Artifact sourceJarOut; // Flags private String customJavaPackage; private boolean debug; private Artifact staticLibraryOut; private ResourceDependencies resourceDeps; private Artifact aapt2SourceJarOut; private Artifact aapt2RTxtOut; private Artifact compiledSymbols; /** @param ruleContext The RuleContext that was used to create the SpawnAction.Builder. */ public AndroidResourceValidatorActionBuilder(RuleContext ruleContext) { this.ruleContext = ruleContext; this.sdk = AndroidSdkProvider.fromRuleContext(ruleContext); } public AndroidResourceValidatorActionBuilder setStaticLibraryOut(Artifact staticLibraryOut) { this.staticLibraryOut = staticLibraryOut; return this; } /** The primary resource container. We mostly propagate its values, but update the R.txt. */ public AndroidResourceValidatorActionBuilder withPrimary(ResourceContainer primary) { this.primary = primary; return this; } public AndroidResourceValidatorActionBuilder setMergedResources(Artifact mergedResources) { this.mergedResources = mergedResources; return this; } public AndroidResourceValidatorActionBuilder setJavaPackage(String customJavaPackage) { this.customJavaPackage = customJavaPackage; return this; } public AndroidResourceValidatorActionBuilder setDebug(boolean debug) { this.debug = debug; return this; } public AndroidResourceValidatorActionBuilder setRTxtOut(Artifact rTxtOut) { this.rTxtOut = rTxtOut; return this; } public AndroidResourceValidatorActionBuilder setSourceJarOut(Artifact sourceJarOut) { this.sourceJarOut = sourceJarOut; return this; } /** Used to add the static library from the dependencies. */ public AndroidResourceValidatorActionBuilder withDependencies(ResourceDependencies resourceDeps) { this.resourceDeps = resourceDeps; return this; } public AndroidResourceValidatorActionBuilder setAapt2RTxtOut(Artifact aapt2RTxtOut) { this.aapt2RTxtOut = aapt2RTxtOut; return this; } public AndroidResourceValidatorActionBuilder setAapt2SourceJarOut(Artifact aapt2SourceJarOut) { this.aapt2SourceJarOut = aapt2SourceJarOut; return this; } public ResourceContainer build(ActionConstructionContext context) { ResourceContainer container = primary; if (rTxtOut != null) { container = createValidateAction(container, context); } if (compiledSymbols != null) { container = createLinkStaticLibraryAction(container, context); } return container; } public AndroidResourceValidatorActionBuilder setCompiledSymbols(Artifact compiledSymbols) { this.compiledSymbols = compiledSymbols; return this; } /** * This creates a static library using aapt2. It also generates a source jar and R.txt from aapt. * *

This allows the link action to replace the validate action for builds that use aapt2, as * opposed to executing both actions. */ private ResourceContainer createLinkStaticLibraryAction( ResourceContainer validated, ActionConstructionContext context) { Preconditions.checkNotNull(staticLibraryOut); Preconditions.checkNotNull(aapt2SourceJarOut); Preconditions.checkNotNull(aapt2RTxtOut); Preconditions.checkNotNull(resourceDeps); CustomCommandLine.Builder builder = new CustomCommandLine.Builder(); ImmutableList.Builder inputs = ImmutableList.builder(); ImmutableList.Builder outs = ImmutableList.builder(); // Set the busybox tool. builder.add("--tool").add("LINK_STATIC_LIBRARY").add("--"); builder.add("--aapt2", sdk.getAapt2().getExecutable()); FluentIterable libraries = FluentIterable.from(resourceDeps.getResources()).transform( ResourceContainer::getStaticLibrary).append(sdk.getAndroidJar()); builder .add("--libraries") .add(libraries.join(Joiner.on(context.getConfiguration().getHostPathSeparator()))); inputs.addAll(libraries); builder.add("--compiled", compiledSymbols); inputs.add(compiledSymbols); builder.add("--manifest", primary.getManifest()); inputs.add(validated.getManifest()); if (!Strings.isNullOrEmpty(customJavaPackage)) { // Sets an alternative java package for the generated R.java // this allows android rules to generate resources outside of the java{,tests} tree. builder.add("--packageForR").add(customJavaPackage); } builder.add("--sourceJarOut", aapt2SourceJarOut); outs.add(aapt2SourceJarOut); builder.add("--rTxtOut", aapt2RTxtOut); outs.add(aapt2RTxtOut); builder.add("--staticLibraryOut", staticLibraryOut); outs.add(staticLibraryOut); ruleContext.registerAction( new SpawnAction.Builder() .useParameterFile(ParameterFileType.UNQUOTED) .useDefaultShellEnvironment() .addTool(sdk.getAapt2()) .addInputs(inputs.build()) .addOutputs(outs.build()) .setCommandLine(builder.build()) .setExecutable( ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)) .setProgressMessage( "Linking static android resource library for %s", ruleContext.getLabel()) .setMnemonic("AndroidResourceLink") .build(context)); return validated .toBuilder() .setAapt2JavaSourceJar(aapt2SourceJarOut) .setAapt2RTxt(aapt2RTxtOut) .setStaticLibrary(staticLibraryOut) .build(); } private ResourceContainer createValidateAction( ResourceContainer primary, ActionConstructionContext context) { CustomCommandLine.Builder builder = new CustomCommandLine.Builder(); // Set the busybox tool. builder.add("--tool").add("VALIDATE").add("--"); if (!Strings.isNullOrEmpty(sdk.getBuildToolsVersion())) { builder.add("--buildToolsVersion").add(sdk.getBuildToolsVersion()); } builder.add("--aapt", sdk.getAapt().getExecutable()); ImmutableList.Builder inputs = ImmutableList.builder(); builder.add("--annotationJar", sdk.getAnnotationsJar()); inputs.add(sdk.getAnnotationsJar()); builder.add("--androidJar", sdk.getAndroidJar()); inputs.add(sdk.getAndroidJar()); Preconditions.checkNotNull(mergedResources); builder.add("--mergedResources", mergedResources); inputs.add(mergedResources); builder.add("--manifest", primary.getManifest()); inputs.add(primary.getManifest()); if (debug) { builder.add("--debug"); } if (!Strings.isNullOrEmpty(customJavaPackage)) { // Sets an alternative java package for the generated R.java // this allows android rules to generate resources outside of the java{,tests} tree. builder.add("--packageForR").add(customJavaPackage); } List outs = new ArrayList<>(); Preconditions.checkNotNull(rTxtOut); builder.add("--rOutput", rTxtOut); outs.add(rTxtOut); Preconditions.checkNotNull(sourceJarOut); builder.add("--srcJarOutput", sourceJarOut); outs.add(sourceJarOut); SpawnAction.Builder spawnActionBuilder = new SpawnAction.Builder(); // Create the spawn action. ruleContext.registerAction( spawnActionBuilder .useParameterFile(ParameterFileType.UNQUOTED) .useDefaultShellEnvironment() .addTool(sdk.getAapt()) .addInputs(inputs.build()) .addOutputs(ImmutableList.copyOf(outs)) .setCommandLine(builder.build()) .setExecutable( ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)) .setProgressMessage("Validating Android resources for %s", ruleContext.getLabel()) .setMnemonic("AndroidResourceValidator") .build(context)); // Return the full set of validated transitive dependencies. return primary.toBuilder() .setJavaSourceJar(sourceJarOut) .setRTxt(rTxtOut) .build(); } }