| // Copyright 2014 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 static com.google.common.base.Preconditions.checkState; |
| import static com.google.common.collect.ImmutableList.toImmutableList; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; |
| import com.google.devtools.build.lib.actions.ActionEnvironment; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.EmptyRunfilesSupplier; |
| import com.google.devtools.build.lib.actions.ExecutionRequirements; |
| import com.google.devtools.build.lib.actions.PathStripper; |
| import com.google.devtools.build.lib.actions.extra.ExtraActionInfo; |
| import com.google.devtools.build.lib.actions.extra.JavaCompileInfo; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; |
| import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.StrictDepsMode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| 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.concurrent.ThreadSafety.ThreadCompatible; |
| import com.google.devtools.build.lib.rules.java.JavaCompileAction.ProgressMessage; |
| import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaClasspathMode; |
| import com.google.devtools.build.lib.rules.java.JavaPluginInfo.JavaPluginData; |
| import com.google.devtools.build.lib.util.StringCanonicalizer; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.Collections; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import javax.annotation.Nullable; |
| |
| /** Java compilation action builder. */ |
| public final class JavaCompileActionBuilder { |
| |
| private static final String JACOCO_INSTRUMENTATION_PROCESSOR = "jacoco"; |
| |
| /** Environment variable that sets the UTF-8 charset. */ |
| static final ImmutableMap<String, String> UTF8_ENVIRONMENT = |
| ImmutableMap.of("LC_CTYPE", "en_US.UTF-8"); |
| |
| static final String MNEMONIC = "Javac"; |
| |
| /** Returns true if this is a Java compile action. */ |
| public static boolean isJavaCompileAction(ActionAnalysisMetadata action) { |
| return action != null && action.getMnemonic().equals(MNEMONIC); |
| } |
| |
| @ThreadCompatible |
| @Immutable |
| private static final class JavaCompileExtraActionInfoSupplier |
| implements JavaCompileAction.ExtraActionInfoSupplier { |
| |
| private final Artifact outputJar; |
| |
| /** The list of classpath entries to specify to javac. */ |
| private final NestedSet<Artifact> classpathEntries; |
| |
| /** The list of bootclasspath entries to specify to javac. */ |
| private final NestedSet<Artifact> bootclasspathEntries; |
| |
| /** An argument to the javac >= 9 {@code --system} flag. */ |
| @Nullable private final Optional<PathFragment> system; |
| |
| /** The list of classpath entries to search for annotation processors. */ |
| private final NestedSet<Artifact> processorPath; |
| |
| /** The list of annotation processor classes to run. */ |
| private final NestedSet<String> processorNames; |
| |
| /** Set of additional Java source files to compile. */ |
| private final ImmutableList<Artifact> sourceJars; |
| |
| /** The set of explicit Java source files to compile. */ |
| private final ImmutableSet<Artifact> sourceFiles; |
| |
| /** The compiler options to pass to javac. */ |
| private final ImmutableList<String> javacOpts; |
| |
| JavaCompileExtraActionInfoSupplier( |
| Artifact outputJar, |
| NestedSet<Artifact> classpathEntries, |
| NestedSet<Artifact> bootclasspathEntries, |
| Optional<PathFragment> system, |
| NestedSet<Artifact> processorPath, |
| NestedSet<String> processorNames, |
| ImmutableList<Artifact> sourceJars, |
| ImmutableSet<Artifact> sourceFiles, |
| ImmutableList<String> javacOpts) { |
| this.outputJar = outputJar; |
| this.classpathEntries = classpathEntries; |
| this.bootclasspathEntries = bootclasspathEntries; |
| this.system = system; |
| this.processorPath = processorPath; |
| this.processorNames = processorNames; |
| this.sourceJars = sourceJars; |
| this.sourceFiles = sourceFiles; |
| this.javacOpts = javacOpts; |
| } |
| |
| @Override |
| public void extend(ExtraActionInfo.Builder builder, ImmutableList<String> arguments) { |
| JavaCompileInfo.Builder info = |
| JavaCompileInfo.newBuilder() |
| .addAllSourceFile(Artifact.toExecPaths(sourceFiles)) |
| .addAllClasspath(Artifact.toExecPaths(classpathEntries.toList())) |
| .addAllBootclasspath(Artifact.toExecPaths(bootclasspathEntries.toList())) |
| .addAllSourcepath(Artifact.toExecPaths(sourceJars)) |
| .addAllJavacOpt(javacOpts) |
| .addAllProcessor(processorNames.toList()) |
| .addAllProcessorpath(Artifact.toExecPaths(processorPath.toList())) |
| .setOutputjar(outputJar.getExecPathString()); |
| if (system.isPresent()) { |
| info.setSystem(system.get().toString()); |
| } |
| info.addAllArgument(arguments); |
| builder.setExtension(JavaCompileInfo.javaCompileInfo, info.build()); |
| } |
| } |
| |
| private final RuleContext ruleContext; |
| private final JavaToolchainProvider toolchain; |
| private ImmutableSet<Artifact> additionalOutputs = ImmutableSet.of(); |
| private Artifact coverageArtifact; |
| private ImmutableSet<Artifact> sourceFiles = ImmutableSet.of(); |
| private ImmutableList<Artifact> sourceJars = ImmutableList.of(); |
| private StrictDepsMode strictJavaDeps = StrictDepsMode.ERROR; |
| private String fixDepsTool = "add_dep"; |
| private NestedSet<Artifact> directJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER); |
| private NestedSet<Artifact> compileTimeDependencyArtifacts = |
| NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| private ImmutableList<String> javacOpts = ImmutableList.of(); |
| private ImmutableMap<String, String> executionInfo = ImmutableMap.of(); |
| private boolean compressJar; |
| private NestedSet<Artifact> classpathEntries = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER); |
| private BootClassPathInfo bootClassPath = BootClassPathInfo.empty(); |
| private ImmutableList<Artifact> sourcePathEntries = ImmutableList.of(); |
| private JavaToolchainTool javaBuilder; |
| private NestedSet<Artifact> toolsJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER); |
| private JavaPluginData plugins = JavaPluginData.empty(); |
| private ImmutableSet<String> builtinProcessorNames = ImmutableSet.of(); |
| private NestedSet<Artifact> extraData = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER); |
| private Label targetLabel; |
| @Nullable private String injectingRuleKind; |
| private ImmutableList<Artifact> additionalInputs = ImmutableList.of(); |
| private Artifact genSourceOutput; |
| private JavaCompileOutputs<Artifact> outputs; |
| private JavaClasspathMode classpathMode; |
| private Artifact manifestOutput; |
| |
| public JavaCompileActionBuilder(RuleContext ruleContext, JavaToolchainProvider toolchain) { |
| this.ruleContext = ruleContext; |
| this.toolchain = toolchain; |
| } |
| |
| public JavaCompileAction build() { |
| // TODO(bazel-team): all the params should be calculated before getting here, and the various |
| // aggregation code below should go away. |
| ImmutableList<String> internedJcopts = |
| javacOpts.stream().map(StringCanonicalizer::intern).collect(toImmutableList()); |
| |
| // Invariant: if strictJavaDeps is OFF, then directJars and |
| // dependencyArtifacts are ignored |
| if (strictJavaDeps == StrictDepsMode.OFF) { |
| directJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER); |
| compileTimeDependencyArtifacts = NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| } |
| |
| // Invariant: if java_classpath is set to 'off', dependencyArtifacts are ignored |
| if (!Collections.disjoint( |
| plugins.processorClasses().toSet(), |
| toolchain.getReducedClasspathIncompatibleProcessors())) { |
| classpathMode = JavaClasspathMode.OFF; |
| } |
| if (classpathMode == JavaClasspathMode.OFF) { |
| compileTimeDependencyArtifacts = NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| } |
| |
| NestedSetBuilder<Artifact> toolsBuilder = NestedSetBuilder.compileOrder(); |
| |
| CustomCommandLine.Builder executableLine = |
| javaBuilder.buildCommandLine(toolchain, toolsBuilder); |
| toolsBuilder.addTransitive(toolsJars); |
| |
| ActionEnvironment actionEnvironment = |
| ruleContext |
| .getConfiguration() |
| .getActionEnvironment() |
| .withAdditionalFixedVariables(UTF8_ENVIRONMENT); |
| |
| NestedSetBuilder<Artifact> mandatoryInputsBuilder = NestedSetBuilder.stableOrder(); |
| mandatoryInputsBuilder |
| .addTransitive(plugins.processorClasspath()) |
| .addTransitive(plugins.data()) |
| .addTransitive(extraData) |
| .addAll(sourceJars) |
| .addAll(sourceFiles) |
| .addTransitive(toolchain.getJavaRuntime().javaBaseInputs()) |
| .addTransitive(bootClassPath.bootclasspath()) |
| .addAll(sourcePathEntries) |
| .addAll(additionalInputs) |
| .addTransitive(bootClassPath.systemInputs()); |
| if (coverageArtifact != null) { |
| mandatoryInputsBuilder.add(coverageArtifact); |
| } |
| |
| JavaCompileExtraActionInfoSupplier extraActionInfoSupplier = |
| new JavaCompileExtraActionInfoSupplier( |
| outputs.output(), |
| classpathEntries, |
| bootClassPath.bootclasspath(), |
| bootClassPath.systemPath(), |
| plugins.processorClasspath(), |
| plugins.processorClasses(), |
| sourceJars, |
| sourceFiles, |
| internedJcopts); |
| |
| // TODO(b/123076347): outputDepsProto should never be null if SJD is enabled |
| if (strictJavaDeps == StrictDepsMode.OFF || outputs.depsProto() == null) { |
| classpathMode = JavaClasspathMode.OFF; |
| } |
| |
| if (outputs.depsProto() != null) { |
| JavaConfiguration javaConfiguration = |
| ruleContext.getConfiguration().getFragment(JavaConfiguration.class); |
| if (javaConfiguration.inmemoryJdepsFiles()) { |
| executionInfo = |
| ImmutableMap.<String, String>builderWithExpectedSize(this.executionInfo.size() + 1) |
| .putAll(this.executionInfo) |
| .put( |
| ExecutionRequirements.REMOTE_EXECUTION_INLINE_OUTPUTS, |
| outputs.depsProto().getExecPathString()) |
| .buildOrThrow(); |
| } |
| } |
| |
| NestedSet<Artifact> tools = toolsBuilder.build(); |
| mandatoryInputsBuilder.addTransitive(tools); |
| NestedSet<Artifact> mandatoryInputs = mandatoryInputsBuilder.build(); |
| |
| boolean stripOutputPaths = |
| JavaCompilationHelper.stripOutputPaths( |
| JavaCompileAction.allInputs( |
| mandatoryInputs, classpathEntries, compileTimeDependencyArtifacts), |
| ruleContext.getConfiguration()); |
| if (stripOutputPaths) { |
| executableLine.stripOutputPaths(JavaCompilationHelper.outputBase(outputs.output())); |
| } |
| |
| return new JavaCompileAction( |
| /* compilationType= */ JavaCompileAction.CompilationType.JAVAC, |
| /* owner= */ ruleContext.getActionOwner(), |
| /* env= */ actionEnvironment, |
| /* tools= */ tools, |
| /* runfilesSupplier= */ EmptyRunfilesSupplier.INSTANCE, |
| /* progressMessage= */ new ProgressMessage( |
| /* prefix= */ "Building", |
| /* output= */ outputs.output(), |
| /* sourceFiles= */ sourceFiles, |
| /* sourceJars= */ sourceJars, |
| /* plugins= */ plugins), |
| /* mandatoryInputs= */ mandatoryInputs, |
| /* transitiveInputs= */ classpathEntries, |
| /* directJars= */ directJars, |
| /* outputs= */ allOutputs(), |
| /* executionInfo= */ executionInfo, |
| /* extraActionInfoSupplier= */ extraActionInfoSupplier, |
| /* executableLine= */ executableLine.build(), |
| /* flagLine= */ buildParamFileContents(internedJcopts, stripOutputPaths), |
| /* configuration= */ ruleContext.getConfiguration(), |
| /* dependencyArtifacts= */ compileTimeDependencyArtifacts, |
| /* outputDepsProto= */ outputs.depsProto(), |
| /* classpathMode= */ classpathMode); |
| } |
| |
| private ImmutableSet<Artifact> allOutputs() { |
| ImmutableSet.Builder<Artifact> result = |
| ImmutableSet.<Artifact>builder() |
| .add(outputs.output()) |
| .addAll(additionalOutputs); |
| Stream.of(outputs.depsProto(), outputs.nativeHeader(), genSourceOutput, manifestOutput) |
| .filter(Objects::nonNull) |
| .forEachOrdered(result::add); |
| return result.build(); |
| } |
| |
| private CustomCommandLine buildParamFileContents( |
| ImmutableList<String> javacOpts, boolean stripOutputPaths) { |
| |
| CustomCommandLine.Builder result = CustomCommandLine.builder(); |
| if (stripOutputPaths) { |
| result.stripOutputPaths(JavaCompilationHelper.outputBase(outputs.output())); |
| } |
| |
| result.addExecPath("--output", outputs.output()); |
| result.addExecPath("--native_header_output", outputs.nativeHeader()); |
| result.addExecPath("--generated_sources_output", genSourceOutput); |
| result.addExecPath("--output_manifest_proto", manifestOutput); |
| if (compressJar) { |
| result.add("--compress_jar"); |
| } |
| result.addExecPath("--output_deps_proto", outputs.depsProto()); |
| result.addExecPaths("--bootclasspath", bootClassPath.bootclasspath()); |
| if (bootClassPath.systemPath().isPresent()) { |
| result.addPath("--system", bootClassPath.systemPath().get()); |
| } |
| result.addExecPaths("--sourcepath", sourcePathEntries); |
| result.addExecPaths("--processorpath", plugins.processorClasspath()); |
| result.addAll("--processors", plugins.processorClasses()); |
| result.addAll( |
| "--builtin_processors", |
| Sets.intersection(plugins.processorClasses().toSet(), builtinProcessorNames)); |
| result.addExecPaths("--source_jars", ImmutableList.copyOf(sourceJars)); |
| result.addExecPaths("--sources", sourceFiles); |
| if (!javacOpts.isEmpty()) { |
| if (stripOutputPaths) { |
| // Use textual search/replace to remove configuration prefixes from javacopts. This should |
| // ideally be constructed directly from artifact exec paths. In this case, targets with |
| // make variables like $(execpath ...)" stringify those paths before we get to this class. |
| // TODO(https://github.com/bazelbuild/bazel/issues/6526): provide direct path stripping |
| // support for make variable expansion. |
| PathStripper.StringStripper coptStripper = |
| new PathStripper.StringStripper( |
| ruleContext.getConfiguration().getDirectories().getRelativeOutputPath()); |
| result.addAll( |
| "--javacopts", |
| javacOpts.stream().map(coptStripper::strip).collect(Collectors.toList())); |
| } else { |
| result.addAll("--javacopts", javacOpts); |
| } |
| // terminate --javacopts with `--` to support javac flags that start with `--` |
| result.add("--"); |
| } |
| if (targetLabel != null) { |
| result.add("--target_label"); |
| if (targetLabel.getRepository().isMain()) { |
| result.addLabel(targetLabel); |
| } else { |
| // @-prefixed strings will be assumed to be filenames and expanded by |
| // {@link JavaLibraryBuildRequest}, so add an extra &at; to escape it. |
| result.addPrefixedLabel("@", targetLabel); |
| } |
| } |
| result.add("--injecting_rule_kind", injectingRuleKind); |
| // strict_java_deps controls whether the mapping from jars to targets is |
| // written out and whether we try to minimize the compile-time classpath. |
| if (strictJavaDeps != StrictDepsMode.OFF) { |
| result.add("--strict_java_deps", strictJavaDeps.toString()); |
| result.addExecPaths("--direct_dependencies", directJars); |
| } |
| result.add("--experimental_fix_deps_tool", fixDepsTool); |
| |
| // Chose what artifact to pass to JavaBuilder, as input to jacoco instrumentation processor. |
| if (coverageArtifact != null) { |
| result.add("--post_processor"); |
| result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, coverageArtifact); |
| result.addPath(ruleContext.getCoverageMetadataDirectory().getExecPath()); |
| result.add("-*Test"); |
| result.add("-*TestCase"); |
| } |
| return result.build(); |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setAdditionalOutputs(ImmutableSet<Artifact> outputs) { |
| this.additionalOutputs = outputs; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setSourceFiles(ImmutableSet<Artifact> sourceFiles) { |
| this.sourceFiles = sourceFiles; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setSourceJars(ImmutableList<Artifact> sourceJars) { |
| checkState(this.sourceJars.isEmpty()); |
| this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null"); |
| return this; |
| } |
| |
| /** Sets the strictness of Java dependency checking, see {@link StrictDepsMode}. */ |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setStrictJavaDeps(StrictDepsMode strictDeps) { |
| strictJavaDeps = strictDeps; |
| return this; |
| } |
| |
| /** Sets the tool with which to fix dependency errors. */ |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setFixDepsTool(String depsTool) { |
| fixDepsTool = depsTool; |
| return this; |
| } |
| |
| /** Accumulates the given jar artifacts as being provided by direct dependencies. */ |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setDirectJars(NestedSet<Artifact> directJars) { |
| this.directJars = checkNotNull(directJars, "directJars must not be null"); |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setCompileTimeDependencyArtifacts( |
| NestedSet<Artifact> dependencyArtifacts) { |
| checkNotNull(compileTimeDependencyArtifacts, "dependencyArtifacts must not be null"); |
| this.compileTimeDependencyArtifacts = dependencyArtifacts; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setJavacOpts(ImmutableList<String> copts) { |
| this.javacOpts = Preconditions.checkNotNull(copts); |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setJavacExecutionInfo( |
| ImmutableMap<String, String> executionInfo) { |
| this.executionInfo = executionInfo; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setCompressJar(boolean compressJar) { |
| this.compressJar = compressJar; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setClasspathEntries(NestedSet<Artifact> classpathEntries) { |
| this.classpathEntries = classpathEntries; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setBootClassPath(BootClassPathInfo bootClassPath) { |
| this.bootClassPath = bootClassPath; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setSourcePathEntries(ImmutableList<Artifact> sourcePathEntries) { |
| this.sourcePathEntries = Preconditions.checkNotNull(sourcePathEntries); |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setPlugins(JavaPluginData plugins) { |
| checkNotNull(plugins, "plugins must not be null"); |
| checkState(this.plugins.isEmpty()); |
| this.plugins = plugins; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setBuiltinProcessorNames( |
| ImmutableSet<String> builtinProcessorNames) { |
| this.builtinProcessorNames = |
| checkNotNull(builtinProcessorNames, "builtinProcessorNames must not be null"); |
| return this; |
| } |
| |
| public void setExtraData(NestedSet<Artifact> extraData) { |
| checkNotNull(extraData, "extraData must not be null"); |
| checkState(this.extraData.isEmpty()); |
| this.extraData = extraData; |
| } |
| |
| /** Sets the tools jars. */ |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setToolsJars(NestedSet<Artifact> toolsJars) { |
| checkNotNull(toolsJars, "toolsJars must not be null"); |
| this.toolsJars = toolsJars; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setJavaBuilder(JavaToolchainTool javaBuilder) { |
| this.javaBuilder = javaBuilder; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setCoverageArtifact(Artifact coverageArtifact) { |
| this.coverageArtifact = coverageArtifact; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setTargetLabel(Label targetLabel) { |
| this.targetLabel = targetLabel; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setInjectingRuleKind(@Nullable String injectingRuleKind) { |
| this.injectingRuleKind = injectingRuleKind; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public JavaCompileActionBuilder setAdditionalInputs(ImmutableList<Artifact> additionalInputs) { |
| checkNotNull(additionalInputs, "additionalInputs must not be null"); |
| this.additionalInputs = additionalInputs; |
| return this; |
| } |
| |
| public void setGenSourceOutput(Artifact genSourceOutput) { |
| this.genSourceOutput = genSourceOutput; |
| } |
| |
| public void setOutputs(JavaCompileOutputs<Artifact> outputs) { |
| this.outputs = outputs; |
| } |
| |
| public void setClasspathMode(JavaClasspathMode classpathMode) { |
| this.classpathMode = classpathMode; |
| } |
| |
| public void setManifestOutput(Artifact manifestOutput) { |
| this.manifestOutput = manifestOutput; |
| } |
| } |