Skip to content

Commit

Permalink
Implement worker support for GenerateDataBindingBaseClasses
Browse files Browse the repository at this point in the history
- Moved `GenerateDataBindingBaseClasses` to `ResourceProcessorBusyBox` since Android databinding classes were already a dependency there.
- `ProcessDatabinding` is already part of `ResourceProcessorBusyBox` but it was not enabled in `persistent_android_resource_processor` expansion flag. Added that as well.

This change is multiplex compatible and probably could use expansion flags as done here #15950.

Results:
In a synthetic generated project with 30 modules here https://github.com/arunkumar9t2/bazel-playground, the worker can be turned off/on with `.bazel.sh build //android/databinding_workers:android_data_binding --strategy=GenerateDataBindingBaseClasses=[sandboxed|worker]` and worker is about 9 to 10% faster.

Closes #16067.

PiperOrigin-RevId: 557906961
Change-Id: I085d8a8e92d1882834ad90db31767ab9c7d2e5cf
  • Loading branch information
arunsampathkumar-grabtaxi authored and copybara-github committed Aug 17, 2023
1 parent 253e986 commit 9092e29
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,24 @@ public <T> BusyBoxActionBuilder addTransitiveFlagForEach(
return this;
}

/**
* Adds an efficient flag based on transitive artifact exec paths.
*
* <p>The exec paths of each transitive artifact will be proceeded by the flag, for example:
* --flag execpath1 --flag execpath2
*
* <p>The values will only be collapsed and turned into a flag at execution time.
*
* <p>The values will also be added as inputs.
*/
@CanIgnoreReturnValue
public BusyBoxActionBuilder addTransitiveExecPathsFlagForEachAndInputs(
@CompileTimeConstant String arg, NestedSet<Artifact> transitiveValues) {
commandLine.addExecPaths(VectorArg.addBefore(arg).each(transitiveValues));
inputs.addTransitive(transitiveValues);
return this;
}

/**
* Adds an efficient flag and inputs based on transitive values.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -290,44 +287,29 @@ public ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext)
}

private ImmutableList<Artifact> 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<Artifact> 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<Artifact> 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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Path> 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<Path> dependencyClassInfoList =
options.dependencyClassInfoList == null
? ImmutableList.of()
: options.dependencyClassInfoList;

final ImmutableList.Builder<String> dbArgsBuilder =
ImmutableList.<String>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() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 9092e29

Please sign in to comment.