| // 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.java; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.ActionRegistry; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.CommandLine; |
| import com.google.devtools.build.lib.actions.CommandLineItem; |
| 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.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.collect.nestedset.Order; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import java.util.function.Consumer; |
| |
| /** |
| * Helper class to create singlejar actions - singlejar can merge multiple zip files without |
| * uncompressing all the entries, making it much faster than uncompressing and then recompressing |
| * the files. |
| */ |
| @Immutable |
| public final class SingleJarActionBuilder { |
| |
| private static final ImmutableList<String> SOURCE_JAR_COMMAND_LINE_ARGS = |
| ImmutableList.of( |
| "--compression", "--normalize", "--exclude_build_data", "--warn_duplicate_resources"); |
| |
| /** Constructs the base spawn for a singlejar action. */ |
| private static SpawnAction.Builder singleJarActionBuilder( |
| JavaToolchainProvider provider, JavaRuntimeInfo hostJavabase) { |
| Artifact singleJar = provider.getSingleJar(); |
| SpawnAction.Builder builder = new SpawnAction.Builder(); |
| // If singlejar's name ends with .jar, it is Java application, otherwise it is native. |
| // TODO(asmundak): once https://github.com/bazelbuild/bazel/issues/2241 is fixed (that is, |
| // the native singlejar is used on windows) remove support for the Java implementation |
| if (singleJar.getFilename().endsWith(".jar")) { |
| builder |
| .addTransitiveInputs(hostJavabase.javaBaseInputsMiddleman()) |
| .setJarExecutable(hostJavabase.javaBinaryExecPath(), singleJar, provider.getJvmOptions()) |
| .setExecutionInfo(ExecutionRequirements.WORKER_MODE_ENABLED); |
| } else { |
| builder.setExecutable(singleJar); |
| } |
| return builder; |
| } |
| |
| /** |
| * Creates an Action that packages files into a Jar file. |
| * |
| * @param resources the resources to put into the Jar. |
| * @param resourceJars the resource jars to merge into the jar |
| * @param outputJar the Jar to create |
| */ |
| public static void createSourceJarAction( |
| RuleContext ruleContext, |
| JavaSemantics semantics, |
| NestedSet<Artifact> resources, |
| NestedSet<Artifact> resourceJars, |
| Artifact outputJar) { |
| createSourceJarAction( |
| ruleContext, |
| ruleContext, |
| semantics, |
| resources, |
| resourceJars, |
| outputJar, |
| JavaToolchainProvider.from(ruleContext), |
| JavaRuntimeInfo.forHost(ruleContext)); |
| } |
| |
| /** |
| * Creates an Action that packages files into a Jar file. |
| * |
| * @param actionRegistry serves for registering action,, |
| * @param actionConstructionContext bundles items commonly needed to construct action instances, |
| * @param resources the resources to put into the Jar. |
| * @param resourceJars the resource jars to merge into the jar |
| * @param outputJar the Jar to create |
| * @param toolchainProvider is used to retrieve jvm options |
| * @param hostJavabase the Java runtime to run the tools under |
| */ |
| public static void createSourceJarAction( |
| ActionRegistry actionRegistry, |
| ActionConstructionContext actionConstructionContext, |
| JavaSemantics semantics, |
| NestedSet<Artifact> resources, |
| NestedSet<Artifact> resourceJars, |
| Artifact outputJar, |
| JavaToolchainProvider toolchainProvider, |
| JavaRuntimeInfo hostJavabase) { |
| requireNonNull(resourceJars); |
| requireNonNull(outputJar); |
| if (!resources.isEmpty()) { |
| requireNonNull(semantics); |
| } |
| SpawnAction.Builder builder = |
| singleJarActionBuilder(toolchainProvider, hostJavabase) |
| .addOutput(outputJar) |
| .addInputs(resources) |
| .addTransitiveInputs(resourceJars) |
| .addCommandLine( |
| sourceJarCommandLine(outputJar, semantics, resources, resourceJars), |
| ParamFileInfo.builder(ParameterFileType.SHELL_QUOTED).setUseAlways(true).build()) |
| .setProgressMessage("Building source jar %s", outputJar.prettyPrint()) |
| .setMnemonic("JavaSourceJar"); |
| |
| actionRegistry.registerAction(builder.build(actionConstructionContext)); |
| } |
| |
| /** |
| * Creates an Action that merges jars into a single archive. |
| * |
| * @param jars the jars to merge. |
| * @param output the output jar to create |
| */ |
| public static void createSingleJarAction( |
| RuleContext ruleContext, NestedSet<Artifact> jars, Artifact output) { |
| requireNonNull(ruleContext); |
| requireNonNull(jars); |
| requireNonNull(output); |
| SpawnAction.Builder builder = |
| singleJarActionBuilder( |
| JavaToolchainProvider.from(ruleContext), JavaRuntimeInfo.forHost(ruleContext)) |
| .addOutput(output) |
| .addInputs(jars) |
| .addCommandLine( |
| sourceJarCommandLine( |
| output, |
| /* semantics= */ null, |
| /* resources= */ NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), |
| jars), |
| ParamFileInfo.builder(ParameterFileType.SHELL_QUOTED).setUseAlways(true).build()) |
| .setProgressMessage("Building singlejar jar %s", output.prettyPrint()) |
| .setMnemonic("JavaSingleJar"); |
| ruleContext.registerAction(builder.build(ruleContext)); |
| } |
| |
| private static CommandLine sourceJarCommandLine( |
| Artifact outputJar, |
| JavaSemantics semantics, |
| NestedSet<Artifact> resources, |
| NestedSet<Artifact> resourceJars) { |
| CustomCommandLine.Builder args = CustomCommandLine.builder(); |
| args.addExecPath("--output", outputJar); |
| args.addAll(SOURCE_JAR_COMMAND_LINE_ARGS); |
| args.addExecPaths("--sources", resourceJars); |
| if (!resources.isEmpty()) { |
| args.add("--resources"); |
| args.addAll(VectorArg.of(resources).mapped(new ResourceArgMapFn(semantics))); |
| } |
| return args.build(); |
| } |
| |
| @AutoCodec.VisibleForSerialization |
| @AutoCodec |
| static class ResourceArgMapFn extends CommandLineItem.ParametrizedMapFn<Artifact> { |
| private final JavaSemantics semantics; |
| |
| ResourceArgMapFn(JavaSemantics semantics) { |
| this.semantics = Preconditions.checkNotNull(semantics); |
| } |
| |
| @Override |
| public void expandToCommandLine(Artifact resource, Consumer<String> args) { |
| String execPath = resource.getExecPathString(); |
| String resourcePath = |
| semantics.getDefaultJavaResourcePath(resource.getRootRelativePath()).getPathString(); |
| StringBuilder sb = new StringBuilder(execPath.length() + resourcePath.length() + 1); |
| sb.append(execPath).append(":").append(resourcePath); |
| args.accept(sb.toString()); |
| } |
| |
| @Override |
| public int maxInstancesAllowed() { |
| // Expect only one semantics object. |
| return 1; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof ResourceArgMapFn)) { |
| return false; |
| } |
| ResourceArgMapFn that = (ResourceArgMapFn) o; |
| return semantics.equals(that.semantics); |
| } |
| |
| @Override |
| public int hashCode() { |
| return semantics.hashCode(); |
| } |
| } |
| } |