blob: 4ee22dd6d081e8820a943ab7bd7f8d68b1495fd8 [file] [log] [blame]
// Copyright 2017 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.java;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecutionRequirements;
import com.google.devtools.build.lib.actions.ParamFileInfo;
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
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.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Map;
/** Builds the action to package the resources for a Java rule into a jar. */
public class ResourceJarActionBuilder {
public static final String MNEMONIC = "JavaResourceJar";
private static final ParamFileInfo PARAM_FILE_INFO =
ParamFileInfo.builder(ParameterFileType.SHELL_QUOTED).build();
private static final ImmutableMap<String, String> EXECUTION_INFO =
ImmutableMap.of(ExecutionRequirements.SUPPORTS_PATH_MAPPING, "1");
private Artifact outputJar;
private Map<PathFragment, Artifact> resources = ImmutableMap.of();
private NestedSet<Artifact> resourceJars = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private ImmutableList<Artifact> classpathResources = ImmutableList.of();
private ImmutableList<Artifact> messages = ImmutableList.of();
private JavaToolchainProvider javaToolchain;
private NestedSet<Artifact> additionalInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
@CanIgnoreReturnValue
public ResourceJarActionBuilder setOutputJar(Artifact outputJar) {
this.outputJar = outputJar;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setAdditionalInputs(NestedSet<Artifact> additionalInputs) {
this.additionalInputs = additionalInputs;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setClasspathResources(
ImmutableList<Artifact> classpathResources) {
this.classpathResources = classpathResources;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setResources(Map<PathFragment, Artifact> resources) {
this.resources = resources;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setResourceJars(NestedSet<Artifact> resourceJars) {
this.resourceJars = resourceJars;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setTranslations(ImmutableList<Artifact> translations) {
this.messages = translations;
return this;
}
@CanIgnoreReturnValue
public ResourceJarActionBuilder setJavaToolchain(JavaToolchainProvider javaToolchain) {
this.javaToolchain = javaToolchain;
return this;
}
public void build(JavaSemantics semantics, RuleContext ruleContext, String execGroup)
throws RuleErrorException {
checkNotNull(outputJar, "outputJar must not be null");
checkNotNull(javaToolchain, "javaToolchain must not be null");
checkNotNull(javaToolchain.getJavaRuntime(), "javabase must not be null");
SpawnAction.Builder builder = new SpawnAction.Builder();
CustomCommandLine.Builder command =
CustomCommandLine.builder()
.add("--normalize")
.add("--dont_change_compression")
.add("--exclude_build_data")
.addExecPath("--output", outputJar);
if (!resourceJars.isEmpty()) {
command.addExecPaths("--sources", resourceJars);
}
addResources(command, semantics);
if (!classpathResources.isEmpty()) {
command.addExecPaths("--classpath_resources", classpathResources);
}
ruleContext.registerAction(
builder
.setExecutable(javaToolchain.getSingleJar())
.useDefaultShellEnvironment()
.addOutput(outputJar)
.addInputs(messages)
.addInputs(resources.values())
.addTransitiveInputs(resourceJars)
.addTransitiveInputs(additionalInputs)
.addInputs(classpathResources)
.addCommandLine(command.build(), PARAM_FILE_INFO)
.setProgressMessage("Building Java resource jar")
.setMnemonic(MNEMONIC)
.setExecutionInfo(EXECUTION_INFO)
.setExecGroup(execGroup)
.build(ruleContext));
}
private void addResources(CustomCommandLine.Builder command, JavaSemantics semantics) {
if (resources.isEmpty() && messages.isEmpty()) {
return;
}
command.add("--resources");
ImmutableList<Artifact> resourcesWithDefaultPath;
// When all resources use the default path (common case), save memory by throwing away those
// path fragments. The artifacts can be lazily converted to default-prefixed strings.
if (resources.entrySet().stream()
.allMatch(e -> e.getKey().equals(defaultResourcePath(e.getValue(), semantics)))) {
resourcesWithDefaultPath =
ImmutableList.<Artifact>builderWithExpectedSize(resources.size() + messages.size())
.addAll(resources.values())
.addAll(messages)
.build();
} else {
command.addObject(
Lists.transform(
ImmutableList.copyOf(resources.entrySet()),
e -> resourcePrefixedExecPath(e.getKey(), e.getValue())));
resourcesWithDefaultPath = messages;
}
if (!resourcesWithDefaultPath.isEmpty()) {
command.addObject(
Lists.transform(
resourcesWithDefaultPath,
artifact ->
resourcePrefixedExecPath(defaultResourcePath(artifact, semantics), artifact)));
}
}
private static PathFragment defaultResourcePath(Artifact artifact, JavaSemantics semantics) {
return semantics.getDefaultJavaResourcePath(artifact.getRootRelativePath());
}
private static String resourcePrefixedExecPath(PathFragment resourcePath, Artifact artifact) {
PathFragment execPath = artifact.getExecPath();
return execPath.equals(resourcePath) ? execPath.getPathString() : execPath + ":" + resourcePath;
}
}