From 125e9f7327127b66250825f51ef9a3b30e1a326e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 23 Mar 2025 02:18:31 -0700 Subject: [PATCH 1/2] Move jvmOptions to main compile operation --- .../bld/java/com/example/ExampleBuild.java | 15 +++++--- .../bld/extension/CompileKotlinOperation.java | 36 ++++++++++++++++++ .../bld/extension/kotlin/CompileOptions.java | 36 ------------------ .../extension/CompileKotlinOperationTest.java | 8 +++- .../extension/kotlin/CompileOptionsTest.java | 5 --- .../bld/extension/kotlin/JvmOptionsTest.java | 37 +++++++------------ 6 files changed, 65 insertions(+), 72 deletions(-) diff --git a/examples/src/bld/java/com/example/ExampleBuild.java b/examples/src/bld/java/com/example/ExampleBuild.java index cd44399..87cc83a 100644 --- a/examples/src/bld/java/com/example/ExampleBuild.java +++ b/examples/src/bld/java/com/example/ExampleBuild.java @@ -60,14 +60,17 @@ public class ExampleBuild extends Project { @BuildCommand(summary = "Compiles the Kotlin project") @Override public void compile() throws Exception { - var options = new CompileOptions().verbose(true); - options.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); // The source code located in src/main/kotlin and src/test/kotlin will be compiled - new CompileKotlinOperation() + var op = new CompileKotlinOperation() // .kotlinHome("path/to/kotlin") // .kotlinc("path/to/kotlinc") - .compileOptions(options) - .fromProject(this) - .execute(); + .compileOptions(new CompileOptions().verbose(true)) + .fromProject(this); + + if (!CompileKotlinOperation.isWindows()) { + op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + } + + op.execute(); } } diff --git a/src/main/java/rife/bld/extension/CompileKotlinOperation.java b/src/main/java/rife/bld/extension/CompileKotlinOperation.java index 64aabff..c093bdd 100644 --- a/src/main/java/rife/bld/extension/CompileKotlinOperation.java +++ b/src/main/java/rife/bld/extension/CompileKotlinOperation.java @@ -19,6 +19,7 @@ package rife.bld.extension; import rife.bld.BaseProject; import rife.bld.extension.kotlin.CompileOptions; import rife.bld.extension.kotlin.CompilerPlugin; +import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.AbstractOperation; import rife.bld.operations.exceptions.ExitStatusException; import rife.tools.FileUtils; @@ -43,6 +44,7 @@ public class CompileKotlinOperation extends AbstractOperation compileMainClasspath_ = new ArrayList<>(); private final Collection compileTestClasspath_ = new ArrayList<>(); + private final JvmOptions jvmOptions_ = new JvmOptions(); private final Collection mainSourceDirectories_ = new ArrayList<>(); private final Collection mainSourceFiles_ = new ArrayList<>(); private final Collection plugins_ = new ArrayList<>(); @@ -479,6 +481,10 @@ public class CompileKotlinOperation extends AbstractOperation command.add("-J" + s)); + } // classpath if (classpath != null && !classpath.isEmpty()) { @@ -607,6 +613,36 @@ public class CompileKotlinOperation extends AbstractOperation jvmOptions) { + jvmOptions_.addAll(jvmOptions); + return this; + } + + /** + * Pass an option directly to the Java Virtual Machine + * + * @param jvmOptions one or more JVM option + * @return this operation instance + */ + public CompileKotlinOperation jvmOptions(String... jvmOptions) { + return jvmOptions(List.of(jvmOptions)); + } + /** * Provides the Kotlin home directory, if it differs from the default {@code KOTLIN_HOME}. * diff --git a/src/main/java/rife/bld/extension/kotlin/CompileOptions.java b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java index 30f0a4e..6c9382c 100644 --- a/src/main/java/rife/bld/extension/kotlin/CompileOptions.java +++ b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java @@ -37,7 +37,6 @@ public class CompileOptions { private final Collection advancedOptions_ = new ArrayList<>(); private final Collection argFile_ = new ArrayList<>(); private final Collection classpath_ = new ArrayList<>(); - private final JvmOptions jvmOptions_ = new JvmOptions(); private final Collection optIn_ = new ArrayList<>(); private final Collection options_ = new ArrayList<>(); private final Collection plugin_ = new ArrayList<>(); @@ -293,11 +292,6 @@ public class CompileOptions { args.add("-Xjdk-release=" + jdkRelease_); } - // JVM options - if (!jvmOptions_.isEmpty()) { - jvmOptions_.forEach(s -> args.add("-J" + s)); - } - // kotlin-home if (kotlinHome_ != null) { args.add("-kotlin-home"); @@ -704,36 +698,6 @@ public class CompileOptions { return jdkRelease(String.valueOf(version)); } - /** - * Retrieves the Java Virtual Machine options. - * - * @return the JVM options - */ - public JvmOptions jvmOptions() { - return jvmOptions_; - } - - /** - * Pass an option directly to the Java Virtual Machine - * - * @param jvmOptions the JVM options - * @return this operation instance - */ - public CompileOptions jvmOptions(Collection jvmOptions) { - jvmOptions_.addAll(jvmOptions); - return this; - } - - /** - * Pass an option directly to the Java Virtual Machine - * - * @param jvmOptions one or more JVM option - * @return this operation instance - */ - public CompileOptions jvmOptions(String... jvmOptions) { - return jvmOptions(List.of(jvmOptions)); - } - /** * Specify the target version of the generated JVM bytecode. * diff --git a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java index 0c33df0..e077341 100644 --- a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java +++ b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java @@ -182,10 +182,14 @@ class CompileKotlinOperationTest { op.compileOptions().verbose(true); op.compileOptions().jdkRelease("17"); - op.compileOptions().jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + + if (!CompileKotlinOperation.isWindows()) { + op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + assertThat(op.jvmOptions()).containsExactly("--enable-native-access=ALL-UNNAMED"); + } var args = op.compileOptions().args(); - var matches = List.of("-Xjdk-release=17", "-J--enable-native-access=ALL-UNNAMED", "-no-stdlib", "-verbose"); + var matches = List.of("-Xjdk-release=17", "-no-stdlib", "-verbose"); assertThat(args).as(args + " == " + matches).isEqualTo(matches); op.execute(); diff --git a/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java index cdabf36..eaf2f44 100644 --- a/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java +++ b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java @@ -117,7 +117,6 @@ class CompileOptionsTest { var advanceOptions = List.of("Xoption1", "Xoption2"); var argFile = List.of(new File("arg1.txt"), new File("arg2.txt")); var classpath = List.of(new File("path1"), new File("path2")); - var jvmOptions = List.of("option1", "option2"); var optIn = List.of("opt1", "opt2"); var options = List.of("-foo", "-bar"); var plugin = List.of("id:name:value", "id2:name2:value2"); @@ -127,7 +126,6 @@ class CompileOptionsTest { .advancedOptions(advanceOptions) .argFile(argFile) .classpath(classpath) - .jvmOptions(jvmOptions) .noStdLib(false) .optIn(optIn) .options(options) @@ -145,8 +143,6 @@ class CompileOptionsTest { .hasSize(argFile.size()).containsAll(argFile); softly.assertThat(op.classpath()).as("classpath") .hasSize(classpath.size()).containsAll(classpath); - softly.assertThat(op.jvmOptions()).as("jvmOptions") - .hasSize(jvmOptions.size()).containsAll(jvmOptions); softly.assertThat(op.optIn()).as("optIn") .hasSize(optIn.size()).containsAll(optIn); softly.assertThat(op.options()).as("options") @@ -216,7 +212,6 @@ class CompileOptionsTest { .argFile("file") .classpath("classpath") .expression("expression") - .jvmOptions("option") .includeRuntime(true) .javaParameters(true) .jdkHome("jdkhome") diff --git a/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java b/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java index 0dcfbef..6f8474f 100644 --- a/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java +++ b/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java @@ -17,6 +17,7 @@ package rife.bld.extension.kotlin; import org.junit.jupiter.api.Test; +import rife.bld.extension.CompileKotlinOperation; import java.util.List; @@ -25,15 +26,12 @@ import static org.assertj.core.api.Assertions.assertThat; @SuppressWarnings("PMD.AvoidDuplicateLiterals") class JvmOptionsTest { @Test - void testCompileOptions() { - var compileOptions = new CompileOptions().jvmOptions(new JvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED)); - assertThat(compileOptions.jvmOptions()).as(JvmOptions.ALL_UNNAMED) - .containsExactly("--enable-native-access=ALL-UNNAMED"); - assertThat(compileOptions.args()).as("args()").containsExactly("-J--enable-native-access=ALL-UNNAMED"); + void testop() { + var op = new CompileKotlinOperation().jvmOptions(new JvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED)); + assertThat(op.jvmOptions()).as(JvmOptions.ALL_UNNAMED).containsExactly("--enable-native-access=ALL-UNNAMED"); - compileOptions = new CompileOptions().jvmOptions(new JvmOptions().enableNativeAccess("m1", "m2")); - assertThat(compileOptions.jvmOptions()).as("m1,m2").containsExactly("--enable-native-access=m1,m2"); - assertThat(compileOptions.args()).as("args(m1,m2)").containsExactly("-J--enable-native-access=m1,m2"); + op = new CompileKotlinOperation().jvmOptions(new JvmOptions().enableNativeAccess("m1", "m2")); + assertThat(op.jvmOptions()).as("m1,m2").containsExactly("--enable-native-access=m1,m2"); } @Test @@ -62,26 +60,19 @@ class JvmOptionsTest { @Test void testJvmOptions() { - var compileOptions = new CompileOptions().jvmOptions("option1", "option2"); - assertThat(compileOptions.jvmOptions()).as("option1,option2").containsExactly("option1", "option2"); - assertThat(compileOptions.args()).as("args()").containsExactly("-Joption1", "-Joption2"); + var op = new CompileKotlinOperation().jvmOptions("option1", "option2"); + assertThat(op.jvmOptions()).as("option1,option2").containsExactly("option1", "option2"); - compileOptions = new CompileOptions().jvmOptions(List.of("option1", "option2")); - assertThat(compileOptions.jvmOptions()).as("List.of(option1,option2)").containsExactly("option1", "option2"); - assertThat(compileOptions.args()).as("args(list)").containsExactly("-Joption1", "-Joption2"); + op = new CompileKotlinOperation().jvmOptions(List.of("option1", "option2")); + assertThat(op.jvmOptions()).as("List.of(option1,option2)").containsExactly("option1", "option2"); - compileOptions = compileOptions.jvmOptions(new JvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED)); - assertThat(compileOptions.jvmOptions()).as("List.of(option1,option2,ALL_UNNAMED)") + op = op.jvmOptions(new JvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED)); + assertThat(op.jvmOptions()).as("List.of(option1,option2,ALL_UNNAMED)") .containsExactly("option1", "option2", "--enable-native-access=ALL-UNNAMED"); - assertThat(compileOptions.args()).as("args(option1,option2,ALL_UNNAMED)") - .containsExactly("-Joption1", "-Joption2", "-J--enable-native-access=ALL-UNNAMED"); - compileOptions = compileOptions.jvmOptions(new JvmOptions().illegalNativeAccess(JvmOptions.NativeAccess.ALLOW)); - assertThat(compileOptions.jvmOptions()).as("allow") + op = op.jvmOptions(new JvmOptions().illegalNativeAccess(JvmOptions.NativeAccess.ALLOW)); + assertThat(op.jvmOptions()).as("allow") .containsExactly("option1", "option2", "--enable-native-access=ALL-UNNAMED", "--illegal-native-access=allow"); - assertThat(compileOptions.args()).as("args(option1,option2,ALL_UNNAMED,allow)") - .containsExactly("-Joption1", "-Joption2", "-J--enable-native-access=ALL-UNNAMED", - "-J--illegal-native-access=allow"); } } From b0d6a3ac3b63c769c4bd9be1036302056306ccaf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 23 Mar 2025 02:30:17 -0700 Subject: [PATCH 2/2] Pass arguments to kotlinc via @argfile --- .../bld/extension/CompileKotlinOperation.java | 73 ++++++++++++++----- .../bld/extension/kotlin/CompileOptions.java | 8 -- .../extension/CompileKotlinOperationTest.java | 4 +- .../extension/kotlin/CompileOptionsTest.java | 7 +- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/main/java/rife/bld/extension/CompileKotlinOperation.java b/src/main/java/rife/bld/extension/CompileKotlinOperation.java index c093bdd..8f7372e 100644 --- a/src/main/java/rife/bld/extension/CompileKotlinOperation.java +++ b/src/main/java/rife/bld/extension/CompileKotlinOperation.java @@ -26,6 +26,7 @@ import rife.tools.FileUtils; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.logging.Level; @@ -319,6 +320,17 @@ public class CompileKotlinOperation extends AbstractOperation classpath, Collection sources, File destination, File friendPaths) throws ExitStatusException { + + var cp = new ArrayList(); + if (classpath != null && !classpath.isEmpty()) { + cp.addAll(classpath); + } + if (sources.isEmpty()) { if (!silent() && LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("Nothing to compile."); @@ -470,60 +488,75 @@ public class CompileKotlinOperation extends AbstractOperation(); var args = new ArrayList(); // kotlinc if (kotlinc_ != null) { - args.add(kotlinc_.getAbsolutePath()); + command.add(kotlinc_.getAbsolutePath()); } else if (kotlinHome_ != null) { - args.add(Objects.requireNonNullElseGet(findKotlincInDir(kotlinHome_.getAbsolutePath()), + command.add(Objects.requireNonNullElseGet(findKotlincInDir(kotlinHome_.getAbsolutePath()), CompileKotlinOperation::findKotlincPath)); } else { - args.add(findKotlincPath(silent())); + command.add(findKotlincPath(silent())); } + // JVM options if (!jvmOptions_.isEmpty()) { jvmOptions_.forEach(s -> command.add("-J" + s)); } + // compiler options + if (compileOptions_ != null) { + args.addAll(compileOptions_.args()); + cp.addAll(compileOptions_.classpath().stream().map(this::cleanPath).toList()); + } + // classpath - if (classpath != null && !classpath.isEmpty()) { + if (!cp.isEmpty()) { args.add("-cp"); - args.add(FileUtils.joinPaths(classpath.stream().toList())); + args.add('"' + FileUtils.joinPaths(cp.stream().map(this::cleanPath).toList()) + '"'); } // destination args.add("-d"); - args.add(destination.getAbsolutePath()); + args.add('"' + cleanPath(destination) + '"'); // friend-path if (friendPaths != null && friendPaths.exists()) { - args.add("-Xfriend-paths=" + friendPaths.getAbsolutePath()); - } - - // options - if (compileOptions_ != null) { - args.addAll(compileOptions_.args()); + args.add("-Xfriend-paths=\"" + cleanPath(friendPaths) + '"'); } // plugins if (!plugins_.isEmpty()) { - plugins_.forEach(p -> args.add("-Xplugin=" + p)); + plugins_.forEach(p -> args.add("-Xplugin=\"" + cleanPath(p) + '"')); } // sources - sources.forEach(f -> args.add(f.getAbsolutePath())); + sources.forEach(f -> args.add('"' + cleanPath(f) + '"')); + var argsLine = String.join(" ", args); + + // log the command line if (LOGGER.isLoggable(Level.FINE) && !silent()) { - LOGGER.fine(String.join(" ", args)); + LOGGER.fine(String.join(" ", command) + " " + argsLine); } - var pb = new ProcessBuilder(); - pb.inheritIO(); - pb.command(args); - pb.directory(workDir_); - try { + // create and write the @argfile + var argsFile = File.createTempFile("bld-kotlinc-", ".args"); + argsFile.deleteOnExit(); + + Files.write(argsFile.toPath(), argsLine.getBytes()); + + command.add("@" + argsFile.getAbsolutePath()); + + // run the command + var pb = new ProcessBuilder(); + pb.inheritIO(); + pb.command(command); + pb.directory(workDir_); + var proc = pb.start(); proc.waitFor(); ExitStatusException.throwOnFailure(proc.exitValue()); diff --git a/src/main/java/rife/bld/extension/kotlin/CompileOptions.java b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java index 6c9382c..3e0e8bf 100644 --- a/src/main/java/rife/bld/extension/kotlin/CompileOptions.java +++ b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java @@ -23,7 +23,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; import static rife.bld.extension.CompileKotlinOperation.isNotBlank; @@ -111,7 +110,6 @@ public class CompileOptions { return this; } - /** * Allow using declarations only from the specified version of Kotlin bundled libraries. * @@ -253,12 +251,6 @@ public class CompileOptions { argFile_.forEach(f -> args.add("@" + f.getAbsolutePath())); } - // classpath - if (!classpath_.isEmpty()) { - args.add("-classpath"); - args.add(classpath_.stream().map(File::getAbsolutePath).collect(Collectors.joining(File.pathSeparator))); - } - // expression if (isNotBlank(expression_)) { args.add("-expression"); diff --git a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java index e077341..fc8aab2 100644 --- a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java +++ b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java @@ -224,8 +224,8 @@ class CompileKotlinOperationTest { var examples = new File("examples"); var op = new CompileKotlinOperation().fromProject( new BaseProjectBlueprint(examples, "com.example", "examples", "examples")); - assertThat(op.mainSourceDirectories()).contains(new File(examples, "src/main/kotlin")); - assertThat(op.testSourceDirectories()).contains(new File(examples, "src/test/kotlin")); + assertThat(op.mainSourceDirectories()).containsExactly(new File(examples, "src/main/kotlin")); + assertThat(op.testSourceDirectories()).containsExactly(new File(examples, "src/test/kotlin")); } @Test diff --git a/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java index eaf2f44..02f9a59 100644 --- a/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java +++ b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java @@ -51,7 +51,6 @@ class CompileOptionsTest { var options = new CompileOptions() .apiVersion("11") .argFile(new File("file.txt"), new File("file2.txt")) - .classpath(new File("path1"), new File("path2")) .javaParameters(true) .jvmTarget("11") .includeRuntime(true) @@ -76,7 +75,6 @@ class CompileOptionsTest { var matches = List.of( "-api-version", "11", "@" + localPath("file.txt"), "@" + localPath("file2.txt"), - "-classpath", localPath("path1", "path2"), "-java-parameters", "-jvm-target", "11", "-include-runtime", @@ -210,7 +208,6 @@ class CompileOptionsTest { .advancedOptions("Xoption") .apiVersion("11") .argFile("file") - .classpath("classpath") .expression("expression") .includeRuntime(true) .javaParameters(true) @@ -233,6 +230,10 @@ class CompileOptionsTest { .wError(true) .wExtra(true); + var skipArgs = List.of("-J", "-classpath"); + assertThat(args).as(skipArgs + " not found.").containsAll(skipArgs); + args.removeAll(skipArgs); + try (var softly = new AutoCloseableSoftAssertions()) { for (var p : args) { var found = false;