BREAKING CHANGE: Kotlin must be installed. Location is deducted from KOTLIN_HOME, if set.

This commit is contained in:
Erik C. Thauvin 2024-07-12 02:13:13 -07:00
parent c84019de2c
commit 6fa3bdefdb
Signed by: erik
GPG key ID: 776702A6A2DA330E
8 changed files with 190 additions and 96 deletions

View file

@ -42,23 +42,7 @@ public class CompileKotlinOperationBuild extends Project {
repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS);
var kotlin = version(2, 0, 0);
scope(compile)
.include(dependency("org.jetbrains.kotlin", "kotlin-compiler", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-annotation-processing", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-scripting-compiler", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-reflect", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin))
.include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm", version(1, 9, 0, "RC")))
// Compiler Plugins
.include(dependency("org.jetbrains.kotlin", "kotlin-allopen-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-assignment-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-serialization-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-lombok-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-allopen-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-noarg-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-power-assert-compiler-plugin", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-sam-with-receiver-compiler-plugin", kotlin))
.include(dependency("com.uwyn.rife2", "bld", version(2, 0, 0, "SNAPSHOT")));
scope(test)
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 3)))

View file

@ -16,7 +16,6 @@
package rife.bld.extension;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import rife.bld.BaseProject;
import rife.bld.extension.kotlin.CompileOptions;
import rife.bld.extension.kotlin.CompilerPlugin;
@ -26,7 +25,7 @@ import rife.tools.FileUtils;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -52,32 +51,9 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
private File buildMainDirectory_;
private File buildTestDirectory_;
private CompileOptions compileOptions_ = new CompileOptions();
private File kotlinHome_;
private BaseProject project_;
/**
* Returns the list of Java archives contained in a given directory.
*
* @param directory the directory
* @param regex the regular expression to match
* @return the list of JARs
*/
public static List<String> getJarList(File directory, String regex) {
var jars = new ArrayList<String>();
if (directory.isDirectory()) {
var files = directory.listFiles();
if (files != null) {
for (var f : files) {
if (!f.getName().endsWith("-sources.jar") && (!f.getName().endsWith("-javadoc.jar")) &&
f.getName().matches(regex)) {
jars.add(f.getAbsolutePath());
}
}
}
}
return jars;
}
private File workDir_;
/**
* Determines if the given string is not blank.
@ -222,6 +198,18 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
LOGGER.severe("A project must be specified.");
}
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
} else if (!workDir_.isDirectory()) {
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
LOGGER.severe("Invalid working directory: " + workDir_.getAbsolutePath());
}
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
if (kotlinHome_ == null) {
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
LOGGER.severe("The KOTLIN_HOME environment variable is not set.");
}
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
executeCreateBuildDirectories();
@ -239,7 +227,7 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
* @throws ExitStatusException if an error occurs
*/
@SuppressWarnings("PMD.SystemPrintln")
protected void executeBuildMainSources() throws ExitStatusException {
protected void executeBuildMainSources() throws ExitStatusException, IOException, InterruptedException {
if (!silent()) {
System.out.println("Compiling Kotlin main sources.");
}
@ -262,47 +250,62 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
*/
protected void executeBuildSources(Collection<String> classpath, Collection<File> sources, File destination,
File friendPaths)
throws ExitStatusException {
throws ExitStatusException, InterruptedException, IOException {
if (sources.isEmpty() || destination == null) {
return;
}
var k2 = new K2JVMCompiler();
var args = new ArrayList<String>();
var kotlinc = Path.of(kotlinHome_.getAbsolutePath(), "bin", "kotlinc").toFile();
// classpath
args.add("-cp");
args.add(FileUtils.joinPaths(classpath.stream().toList()));
if (kotlinc.exists() && kotlinc.canExecute()) {
var args = new ArrayList<String>();
// destination
args.add("-d");
args.add(destination.getAbsolutePath());
// kotlinc
args.add(kotlinc.getAbsolutePath());
// friend-path
if (friendPaths != null && friendPaths.exists()) {
args.add("-Xfriend-paths=" + friendPaths.getAbsolutePath());
}
// classpath
args.add("-cp");
args.add(FileUtils.joinPaths(classpath.stream().toList()));
// options
if (compileOptions_ != null) {
args.addAll(compileOptions_.args());
}
// destination
args.add("-d");
args.add(destination.getAbsolutePath());
// plugins
if (!plugins_.isEmpty()) {
plugins_.forEach(p -> args.add("-Xplugin=" + p));
}
// friend-path
if (friendPaths != null && friendPaths.exists()) {
args.add("-Xfriend-paths=" + friendPaths.getAbsolutePath());
}
// sources
sources.forEach(f -> args.add(f.getAbsolutePath()));
// options
if (compileOptions_ != null) {
args.addAll(compileOptions_.args());
}
if (LOGGER.isLoggable(Level.FINE) && !silent()) {
LOGGER.fine("kotlinc " + String.join(" ", args));
}
// plugins
if (!plugins_.isEmpty()) {
plugins_.forEach(p -> args.add("-Xplugin=" + p));
}
var exitCode = k2.exec(System.err, args.toArray(String[]::new));
if (exitCode.getCode() != 0) {
throw new ExitStatusException(exitCode.getCode());
// sources
sources.forEach(f -> args.add(f.getAbsolutePath()));
if (LOGGER.isLoggable(Level.FINE) && !silent()) {
LOGGER.fine(String.join(" ", args));
}
var pb = new ProcessBuilder();
pb.inheritIO();
pb.command(args);
pb.directory(workDir_);
var proc = pb.start();
proc.waitFor();
ExitStatusException.throwOnFailure(proc.exitValue());
} else {
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
LOGGER.severe("The Kotlin compiler could not be found or executed: " + kotlinc.getAbsolutePath());
}
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
}
@ -312,7 +315,7 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
* @throws ExitStatusException if an error occurs
*/
@SuppressWarnings("PMD.SystemPrintln")
protected void executeBuildTestSources() throws ExitStatusException {
protected void executeBuildTestSources() throws ExitStatusException, IOException, InterruptedException {
if (!silent()) {
System.out.println("Compiling Kotlin test sources.");
}
@ -342,6 +345,8 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
* <p>
* Sets the following from the project:
* <ul>
* <li>{@link #kotlinHome()} to the {@code KOTLIN_HOME} environment variable, if set.</li>
* <li>{@link #workDir()} to the project's directory.</li>
* <li>{@link #buildMainDirectory() buildMainDirectory}</li>
* <li>{@link #buildTestDirectory() buildTestDirectory}</li>
* <li>{@link #compileMainClasspath() compileMainClassPath}</li>
@ -359,6 +364,14 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
*/
public CompileKotlinOperation fromProject(BaseProject project) {
project_ = project;
var env = System.getenv("KOTLIN_HOME");
if (env != null) {
kotlinHome_ = new File(env);
}
workDir_ = new File(project.workDirectory().getAbsolutePath());
var op = buildMainDirectory(project.buildMainDirectory())
.buildTestDirectory(project.buildTestDirectory())
.compileMainClasspath(project.compileMainClasspath())
@ -373,6 +386,36 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
return op;
}
/**
* Provides the Kotlin home directory, if it differs from the default {@code KOTLIN_HOME}.
*
* @param dir the directory
* @return this operation instance
*/
public CompileKotlinOperation kotlinHome(File dir) {
kotlinHome_ = dir;
return this;
}
/**
* Provides the Kotlin home directory, if it differs from the default {@code KOTLIN_HOME}.
*
* @param dir the directory path
* @return this operation instance
*/
public CompileKotlinOperation kotlinHome(String dir) {
return kotlinHome(new File(dir));
}
/**
* Returns the Kotlin home directory.
*
* @return the directory
*/
public File kotlinHome() {
return kotlinHome_;
}
/**
* Provides main source directories that should be compiled.
*
@ -497,20 +540,29 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
*/
public CompileKotlinOperation plugins(File directory, CompilerPlugin... plugins) {
for (var plugin : plugins) {
plugins_.addAll(getJarList(directory, plugin.regex));
plugins_.add(new File(directory, plugin.jar).getAbsolutePath());
}
return this;
}
/**
* Provides compiler plugins.
* <p>
* The {@link #kotlinHome()} should be set first.
*
* @param plugins one or more plugins
* @return this class instance
*/
public CompileKotlinOperation plugins(CompilerPlugin... plugins) {
for (var plugin : plugins) {
plugins_.addAll(getJarList(project_.libBldDirectory(), plugin.regex));
if (kotlinHome_ != null) {
var kotlinLib = new File(kotlinHome_, "lib");
for (var plugin : plugins) {
plugins(kotlinLib, plugin);
}
} else {
if (LOGGER.isLoggable(Level.WARNING) && !silent()) {
LOGGER.warning("The Kotlin home must be set to specify compiler plugins directly.");
}
}
return this;
}
@ -525,7 +577,7 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
public CompileKotlinOperation plugins(Collection<File> jars, CompilerPlugin... plugins) {
jars.forEach(jar -> {
for (var plugin : plugins) {
if (jar.getName().matches(plugin.regex)) {
if (jar.getName().matches(plugin.jar)) {
plugins_.add(jar.getAbsolutePath());
break;
}
@ -625,4 +677,34 @@ public class CompileKotlinOperation extends AbstractOperation<CompileKotlinOpera
public Collection<File> testSourceFiles() {
return testSourceFiles_;
}
/**
* Retrieves the working directory.
*
* @return the directory
*/
public File workDir() {
return workDir_;
}
/**
* Provides the working directory, if it differs from the project's directory.
*
* @param dir the directory
* @return this operation instance
*/
public CompileKotlinOperation workDir(File dir) {
workDir_ = dir;
return this;
}
/**
* Provides the working directory, if it differs from the project's directory.
*
* @param dir the directory path
* @return this operation instance
*/
public CompileKotlinOperation workDir(String dir) {
return workDir(new File(dir));
}
}

View file

@ -17,23 +17,24 @@
package rife.bld.extension.kotlin;
/**
* Defines the known Kotlin compiler plugins match (regex) strings.
* Defines the known Kotlin compiler plugin JARs.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public enum CompilerPlugin {
ALL_OPEN("^kotlin-allopen-compiler-plugin-.*$"),
ASSIGNMENT("^kotlin-assignment-compiler-plugin-.*$"),
KOTLIN_SERIALIZATION("^kotlin-serialization-compiler-plugin-.*$"),
LOMBOK("^kotlin-lombok-compiler-plugin-.*$"),
NOARG("^kotlin-noarg-compiler-plugin-.*$"),
POWER_ASSERT("^kotlin-power-assert-compiler-plugin-.*$"),
SAM_WITH_RECEIVER("^kotlin-sam-with-receiver-compiler-plugin-.*$");
ALL_OPEN("kotlin-allopen-compiler-plugin.jar"),
ASSIGNMENT("kotlin-assignment-compiler-plugin.jar"),
KOTLINX_SERIALIZATION("kotlinx-serialization-compiler-plugin.jar"),
KOTLIN_SERIALIZATION("kotlin-serialization-compiler-plugin.jar"),
LOMBOK("kotlin-lombok-compiler-plugin.jar"),
NOARG("kotlin-noarg-compiler-plugin.jar"),
POWER_ASSERT("kotlin-power-assert-compiler-plugin.jar"),
SAM_WITH_RECEIVER("kotlin-sam-with-receiver-compiler-plugin.jar");
public final String regex;
public final String jar;
CompilerPlugin(String regex) {
this.regex = regex;
CompilerPlugin(String jar) {
this.jar = jar;
}
}

View file

@ -53,6 +53,8 @@ class CompileKotlinOperationTest {
void testCollections() {
var op = new CompileKotlinOperation()
.fromProject(new Project())
.kotlinHome("/kotlin_home")
.workDir("work_dir")
.compileMainClasspath("path1", "path2")
.compileOptions(new CompileOptions().jdkRelease("17").verbose(true))
.mainSourceDirectories("dir1", "dir2")
@ -72,6 +74,9 @@ class CompileKotlinOperationTest {
.plugins(Arrays.stream(Objects.requireNonNull(new File("lib/compile").listFiles())).toList(),
CompilerPlugin.ALL_OPEN, CompilerPlugin.SAM_WITH_RECEIVER);
assertThat(op.kotlinHome().getName()).as("kotlin_home").isEqualTo("kotlin_home");
assertThat(op.workDir().getName()).as("work_dir").isEqualTo("work_dir");
assertThat(op.compileMainClasspath()).as("compileMainClassPath")
.containsAll(List.of("path1", "path2"));
assertThat(op.compileOptions().hasRelease()).as("hasRelease").isTrue();
@ -88,7 +93,9 @@ class CompileKotlinOperationTest {
assertThat(op.testSourceFiles()).as("testSourceFiles").containsOnly(
new File("tfile1"), new File("tfile2"), new File("tfile3"),
new File("tfile4"), new File("tfile5"), new File("tfile6"));
assertThat(op.plugins()).hasSize(10);
assertThat(op.plugins()).as("plugins").contains("/kotlin_home/lib/kotlin-serialization-compiler-plugin.jar",
"/kotlin_home/lib/kotlin-assignment-compiler-plugin.jar", "plugin2", "plugin3", "plugin4");
assertThat(op.plugins()).as("plugins size").hasSize(8);
}
@Test