diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java b/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java index cfe3e2a771af43..2507ce03ee28db 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java @@ -13,6 +13,9 @@ // limitations under the License. package com.google.devtools.build.lib.rules.java; +import static com.google.common.collect.Iterables.getOnlyElement; + +import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; @@ -24,7 +27,8 @@ import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import com.google.devtools.build.lib.starlarkbuildapi.FileApi; import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi; -import javax.annotation.Nullable; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.Optional; import net.starlark.java.annot.Param; import net.starlark.java.annot.ParamType; import net.starlark.java.annot.StarlarkBuiltin; @@ -66,8 +70,13 @@ private Provider() { allowedTypes = { @ParamType(type = FileApi.class), @ParamType(type = NoneType.class), + @ParamType(type = Sequence.class), }, - defaultValue = "None"), + defaultValue = "None", + doc = + "The inputs to javac's --system flag, either a directory or a listing of files," + + " which must contain at least 'release', 'lib/modules', and" + + " 'lib/jrt-fs.jar'"), }, selfCall = true, useStarlarkThread = true) @@ -77,10 +86,13 @@ public BootClassPathInfo bootClassPathInfo( Object systemOrNone, StarlarkThread thread) throws EvalException { + NestedSet systemInputs = getSystemInputs(systemOrNone); + Optional systemPath = getSystemPath(systemInputs); return new BootClassPathInfo( getBootClassPath(bootClassPathList), getAuxiliary(auxiliaryList), - getSystem(systemOrNone), + systemInputs, + systemPath, thread.getCallerLocation()); } @@ -96,32 +108,65 @@ private static NestedSet getAuxiliary(Sequence auxiliaryList) Order.STABLE_ORDER, Sequence.cast(auxiliaryList, Artifact.class, "auxiliary")); } - private static Artifact getSystem(Object systemOrNone) throws EvalException { + private static NestedSet getSystemInputs(Object systemOrNone) throws EvalException { if (systemOrNone == Starlark.NONE) { - return null; + return NestedSetBuilder.emptySet(Order.STABLE_ORDER); } if (systemOrNone instanceof Artifact) { - return (Artifact) systemOrNone; + return NestedSetBuilder.create(Order.STABLE_ORDER, (Artifact) systemOrNone); + } + if (systemOrNone instanceof Sequence) { + return NestedSetBuilder.wrap( + Order.STABLE_ORDER, Sequence.cast(systemOrNone, Artifact.class, "system")); + } + throw Starlark.errorf( + "for system, got %s, want File, sequence, or None", Starlark.type(systemOrNone)); + } + + private static Optional getSystemPath(NestedSet systemInputs) + throws EvalException { + ImmutableList inputs = systemInputs.toList(); + if (inputs.isEmpty()) { + return Optional.empty(); + } + if (inputs.size() == 1) { + Artifact input = getOnlyElement(inputs); + if (!input.isTreeArtifact()) { + throw Starlark.errorf("for system, %s is not a directory", input.getExecPathString()); + } + return Optional.of(input.getExecPath()); } - throw Starlark.errorf("for system, got %s, want File or None", Starlark.type(systemOrNone)); + Optional input = + inputs.stream() + .map(Artifact::getExecPath) + .filter(p -> p.getBaseName().equals("release")) + .map(PathFragment::getParentDirectory) + .findAny(); + if (!input.isPresent()) { + throw Starlark.errorf("for system, expected inputs to contain 'release'"); + } + return input; } } private final NestedSet bootclasspath; private final NestedSet auxiliary; - @Nullable private final Artifact system; + private final NestedSet systemInputs; + private final Optional systemPath; @VisibleForSerialization @AutoCodec.Instantiator public BootClassPathInfo( NestedSet bootclasspath, NestedSet auxiliary, - Artifact system, + NestedSet systemInputs, + Optional systemPath, Location creationLocation) { super(creationLocation); this.bootclasspath = bootclasspath; this.auxiliary = auxiliary; - this.system = system; + this.systemInputs = systemInputs; + this.systemPath = systemPath; } @Override @@ -131,14 +176,19 @@ public Provider getProvider() { public static BootClassPathInfo create(NestedSet bootclasspath) { return new BootClassPathInfo( - bootclasspath, NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), null, null); + bootclasspath, + NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), + NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), + Optional.empty(), + null); } public static BootClassPathInfo empty() { return new BootClassPathInfo( NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), - null, + NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), + Optional.empty(), null); } @@ -156,9 +206,13 @@ public NestedSet auxiliary() { } /** An argument to the javac >= 9 {@code --system} flag. */ - @Nullable - public Artifact system() { - return system; + public Optional systemPath() { + return systemPath; + } + + /** Contents of the directory that is passed to the javac >= 9 {@code --system} flag. */ + public NestedSet systemInputs() { + return systemInputs; } public boolean isEmpty() { diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileActionBuilder.java index 11de6810b7c83d..cc34c32de6f14d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileActionBuilder.java @@ -45,7 +45,9 @@ import com.google.devtools.build.lib.rules.java.JavaPluginInfo.JavaPluginData; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.util.StringCanonicalizer; +import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Collections; +import java.util.Optional; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -80,7 +82,7 @@ static class JavaCompileExtraActionInfoSupplier private final NestedSet bootclasspathEntries; /** An argument to the javac >= 9 {@code --system} flag. */ - @Nullable private final Artifact system; + @Nullable private final Optional system; /** The list of classpath entries to search for annotation processors. */ private final NestedSet processorPath; @@ -101,7 +103,7 @@ static class JavaCompileExtraActionInfoSupplier Artifact outputJar, NestedSet classpathEntries, NestedSet bootclasspathEntries, - @Nullable Artifact system, + Optional system, NestedSet processorPath, NestedSet processorNames, ImmutableList sourceJars, @@ -130,8 +132,8 @@ public void extend(ExtraActionInfo.Builder builder, ImmutableList argume .addAllProcessor(processorNames.toList()) .addAllProcessorpath(Artifact.toExecPaths(processorPath.toList())) .setOutputjar(outputJar.getExecPathString()); - if (system != null) { - info.setSystem(system.getExecPathString()); + if (system.isPresent()) { + info.setSystem(system.get().toString()); } info.addAllArgument(arguments); builder.setExtension(JavaCompileInfo.javaCompileInfo, info.build()); @@ -215,17 +217,18 @@ public JavaCompileAction build() { .addTransitive(toolchain.getJavaRuntime().javaBaseInputs()) .addTransitive(bootClassPath.bootclasspath()) .addAll(sourcePathEntries) - .addAll(additionalInputs); - Stream.of(coverageArtifact, bootClassPath.system()) - .filter(x -> x != null) - .forEachOrdered(mandatoryInputs::add); + .addAll(additionalInputs) + .addTransitive(bootClassPath.systemInputs()); + if (coverageArtifact != null) { + mandatoryInputs.add(coverageArtifact); + } JavaCompileExtraActionInfoSupplier extraActionInfoSupplier = new JavaCompileExtraActionInfoSupplier( outputs.output(), classpathEntries, bootClassPath.bootclasspath(), - bootClassPath.system(), + bootClassPath.systemPath(), plugins.processorClasspath(), plugins.processorClasses(), sourceJars, @@ -303,7 +306,9 @@ private CustomCommandLine buildParamFileContents(ImmutableList javacOpts } result.addExecPath("--output_deps_proto", outputs.depsProto()); result.addExecPaths("--bootclasspath", bootClassPath.bootclasspath()); - result.addExecPath("--system", bootClassPath.system()); + if (bootClassPath.systemPath().isPresent()) { + result.addPath("--system", bootClassPath.systemPath().get()); + } result.addExecPaths("--sourcepath", sourcePathEntries); result.addExecPaths("--processorpath", plugins.processorClasspath()); result.addAll("--processors", plugins.processorClasses());