diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java index 04664e95abd9e1..896e59e5b6fd98 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java @@ -851,6 +851,8 @@ public static class Options extends FragmentOptions { "--strategy=AndroidManifestMerger=worker", "--strategy=Aapt2Optimize=worker", "--strategy=AARGenerator=worker", + "--strategy=ProcessDatabinding=worker", + "--strategy=GenerateDataBindingBaseClasses=worker" }) public Void persistentResourceProcessor; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java index 24fcfe93190265..d990a3267beafe 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java @@ -246,6 +246,24 @@ public BusyBoxActionBuilder addTransitiveFlagForEach( return this; } + /** + * Adds an efficient flag based on transitive artifact exec paths. + * + *

The exec paths of each transitive artifact will be proceeded by the flag, for example: + * --flag execpath1 --flag execpath2 + * + *

The values will only be collapsed and turned into a flag at execution time. + * + *

The values will also be added as inputs. + */ + @CanIgnoreReturnValue + public BusyBoxActionBuilder addTransitiveExecPathsFlagForEachAndInputs( + @CompileTimeConstant String arg, NestedSet transitiveValues) { + commandLine.addExecPaths(VectorArg.addBefore(arg).each(transitiveValues)); + inputs.addTransitive(transitiveValues); + return this; + } + /** * Adds an efficient flag and inputs based on transitive values. * diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java index fc04d721b6fe15..a2aecc09a78b95 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java @@ -22,13 +22,9 @@ import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; 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.CustomCommandLine.VectorArg; -import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.BuildType; @@ -38,6 +34,7 @@ import com.google.devtools.build.lib.rules.android.AndroidDataBindingProcessorBuilder; import com.google.devtools.build.lib.rules.android.AndroidDataContext; import com.google.devtools.build.lib.rules.android.AndroidResources; +import com.google.devtools.build.lib.rules.android.BusyBoxActionBuilder; import com.google.devtools.build.lib.rules.java.JavaPluginInfo; import com.google.devtools.build.lib.starlarkbuildapi.android.DataBindingV2ProviderApi; import com.google.devtools.build.lib.starlarkbuildapi.android.DataBindingV2ProviderApi.LabelJavaPackagePair; @@ -290,44 +287,29 @@ public ImmutableList getAnnotationSourceFiles(RuleContext ruleContext) } private ImmutableList createBaseClasses(RuleContext ruleContext) { - if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) { return ImmutableList.of(); // no resource, no base classes or class info } - Artifact layoutInfo = getLayoutInfoFile(); - Artifact classInfoFile = getClassInfoFile(ruleContext); - Artifact srcOutFile = + final Artifact layoutInfo = getLayoutInfoFile(); + final Artifact classInfoFile = getClassInfoFile(ruleContext); + final Artifact srcOutFile = DataBinding.getDataBindingArtifact( ruleContext, "baseClassSrc.srcjar", /* isDirectory= */ false); - - FilesToRunProvider exec = - ruleContext.getExecutablePrerequisite(DataBinding.DATABINDING_EXEC_PROCESSOR_ATTR); - - CustomCommandLine.Builder commandLineBuilder = - CustomCommandLine.builder() - .add("GEN_BASE_CLASSES") - .addExecPath("-layoutInfoFiles", layoutInfo) - .add("-package", AndroidCommon.getJavaPackage(ruleContext)) - .addExecPath("-classInfoOut", classInfoFile) - .addExecPath("-sourceOut", srcOutFile) - .add("-zipSourceOutput", "true") - .add("-useAndroidX", useAndroidX ? "true" : "false"); - - NestedSet dependencyClassInfo = getDirectClassInfo(ruleContext); - commandLineBuilder.addExecPaths( - VectorArg.addBefore("-dependencyClassInfoList").each(dependencyClassInfo)); - - ruleContext.registerAction( - new SpawnAction.Builder() - .setExecutable(exec) - .setMnemonic("GenerateDataBindingBaseClasses") - .addInput(layoutInfo) - .addTransitiveInputs(dependencyClassInfo) - .addOutput(classInfoFile) - .addOutput(srcOutFile) - .addCommandLine(commandLineBuilder.build()) - .build(ruleContext)); + final NestedSet dependencyClassInfo = getDirectClassInfo(ruleContext); + + final BusyBoxActionBuilder builder = + BusyBoxActionBuilder.create(AndroidDataContext.forNative(ruleContext), "GEN_BASE_CLASSES") + .addInput("--layoutInfoFiles", layoutInfo) + .addFlag("--package", AndroidCommon.getJavaPackage(ruleContext)) + .addOutput("--classInfoOut", classInfoFile) + .addOutput("--sourceOut", srcOutFile) + .addFlag("--useDataBindingAndroidX", useAndroidX ? "true" : "false") + .addTransitiveExecPathsFlagForEachAndInputs( + "--dependencyClassInfoList", dependencyClassInfo); + + builder.buildAndRegister( + "Generating databinding base classes", "GenerateDataBindingBaseClasses"); return ImmutableList.of(srcOutFile); } diff --git a/src/tools/android/java/com/google/devtools/build/android/GenerateDatabindingBaseClassesAction.java b/src/tools/android/java/com/google/devtools/build/android/GenerateDatabindingBaseClassesAction.java new file mode 100644 index 00000000000000..3955a9946b3eab --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/GenerateDatabindingBaseClassesAction.java @@ -0,0 +1,154 @@ +// Copyright 2018 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.android; + +import android.databinding.AndroidDataBinding; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Streams; +import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions; +import com.google.devtools.build.android.Converters.PathConverter; +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionDocumentationCategory; +import com.google.devtools.common.options.OptionEffectTag; +import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsParser; +import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** Generates databinding base classes for an Android target. */ +public final class GenerateDatabindingBaseClassesAction { + + /** Options for GenerateDatabindingBaseClassesAction. */ + public static final class Options extends OptionsBase { + @Option( + name = "layoutInfoFiles", + defaultValue = "null", + converter = PathConverter.class, + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + help = "Path to layout-info.zip file produced by databinding processor") + public Path layoutInfoFile; + + @Option( + name = "package", + defaultValue = "null", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + help = "Package name of the android target") + public String packageName; + + @Option( + name = "classInfoOut", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "Path to write classInfo.zip file") + public Path classInfoOut; + + @Option( + name = "sourceOut", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "Path to write databinding base classes to be used in Java compilation") + public Path sourceOut; + + @Option( + name = "dependencyClassInfoList", + defaultValue = "null", + converter = PathConverter.class, + allowMultiple = true, + category = "input", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + help = "List of dependency class info zip files") + public List dependencyClassInfoList; + } + + static final Logger logger = + Logger.getLogger(GenerateDatabindingBaseClassesAction.class.getName()); + + public static void main(String[] args) throws Exception { + final OptionsParser optionsParser = + OptionsParser.builder() + .allowResidue(true) + .optionsClasses( + Options.class, AaptConfigOptions.class, ResourceProcessorCommonOptions.class) + .argsPreProcessor(new ShellQuotedParamsFilePreProcessor(FileSystems.getDefault())) + .build(); + optionsParser.parseAndExitUponError(args); + final Options options = optionsParser.getOptions(Options.class); + final AaptConfigOptions aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class); + + if (options.layoutInfoFile == null) { + throw new IllegalArgumentException("--layoutInfoFiles is required"); + } + + if (options.packageName == null) { + throw new IllegalArgumentException("--packageName is required"); + } + + if (options.classInfoOut == null) { + throw new IllegalArgumentException("--classInfoOut is required"); + } + + if (options.sourceOut == null) { + throw new IllegalArgumentException("--sourceOut is required"); + } + + final List dependencyClassInfoList = + options.dependencyClassInfoList == null + ? ImmutableList.of() + : options.dependencyClassInfoList; + + final ImmutableList.Builder dbArgsBuilder = + ImmutableList.builder() + .add("GEN_BASE_CLASSES") + .add("-layoutInfoFiles") + .add(options.layoutInfoFile.toString()) + .add("-package", options.packageName) + .add("-classInfoOut") + .add(options.classInfoOut.toString()) + .add("-sourceOut") + .add(options.sourceOut.toString()) + .add("-zipSourceOutput") + .add("true") + .add("-useAndroidX") + .add(Boolean.toString(aaptConfigOptions.useDataBindingAndroidX)); + + dependencyClassInfoList.forEach( + classInfo -> dbArgsBuilder.add("-dependencyClassInfoList").add(classInfo.toString())); + + try { + AndroidDataBinding.main( + Streams.mapWithIndex( + dbArgsBuilder.build().stream(), (arg, index) -> index == 0 ? arg : arg + " ") + .toArray(String[]::new)); + } catch (RuntimeException e) { + logger.log(Level.SEVERE, "Unexpected", e); + throw e; + } + } + + private GenerateDatabindingBaseClassesAction() {} +} diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java b/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java index 58e3b48b3df996..b3f5c42d114b19 100644 --- a/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java +++ b/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java @@ -138,6 +138,12 @@ void call(String[] args) throws Exception { void call(String[] args) throws Exception { AndroidDataBindingProcessingAction.main(args); } + }, + GEN_BASE_CLASSES { + @Override + void call(String[] args) throws Exception { + GenerateDatabindingBaseClassesAction.main(args); + } }; abstract void call(String[] args) throws Exception;