| // 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; |
| } |
| } |