diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 99d5cec..cfed82f 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -4,47 +4,36 @@ on: [ push, pull_request, workflow_dispatch ] jobs: build-bld-project: - runs-on: ubuntu-latest - strategy: matrix: - java-version: [ 17, 20 ] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Download the examples dependencies + - name: Download dependencies [examples] working-directory: examples - run: | - chmod +x bld - ./bld download + run: ./bld download - - name: Run examples tests + - name: Run tests [examples] working-directory: examples run: ./bld compile test - - name: Build examples documentation - working-directory: examples - run : | - ./bld javadoc - ./bld dokka-html - ./bld dokka-gfm - ./bld dokka-jekyll - - - name: Grant execute permission for bld - run: chmod +x bld - - - name: Download the dependencies + - name: Download dependencies run: ./bld download - name: Run tests - run: ./bld compile pmd test + run: ./bld compile test \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 2120d4c..e864e62 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -3,7 +3,7 @@ name: javadocs-pages on: # Runs on pushes targeting the default branch push: - branches: ["main"] + branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -30,14 +30,14 @@ jobs: steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: 17 - name: Build Javadocs @@ -47,11 +47,11 @@ jobs: uses: actions/configure-pages@v3 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: # Upload generated Javadocs repository - path: 'build/javadoc/' + path: "build/javadoc/" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..d91f848 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml index ade80da..4446c15 100644 --- a/.idea/copyright/Apache_License.xml +++ b/.idea/copyright/Apache_License.xml @@ -1,6 +1,6 @@ - - + \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..81220b4 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index ad697f0..f9b1d1d 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b386dc0..593e427 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/bld.xml b/examples/.idea/libraries/bld.xml index 0b615c1..153a060 100644 --- a/examples/.idea/libraries/bld.xml +++ b/examples/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/examples/.idea/libraries/compile.xml b/examples/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/examples/.idea/libraries/compile.xml +++ b/examples/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/runtime.xml b/examples/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/examples/.idea/libraries/runtime.xml +++ b/examples/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/test.xml b/examples/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/examples/.idea/libraries/test.xml +++ b/examples/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/misc.xml b/examples/.idea/misc.xml index 78b0f96..70c470c 100644 --- a/examples/.idea/misc.xml +++ b/examples/.idea/misc.xml @@ -1,4 +1,3 @@ - @@ -24,4 +23,7 @@ + + \ No newline at end of file diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json index 9e8368e..ba429d0 100644 --- a/examples/.vscode/settings.json +++ b/examples/.vscode/settings.json @@ -9,7 +9,7 @@ ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.9.0.jar", + "${HOME}/.bld/dist/bld-2.2.1.jar", "lib/**/*.jar" ] } diff --git a/examples/README.md b/examples/README.md index 1dbc9fb..b22a8f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,3 @@ - ## Compile the Kotlin Example ```console @@ -17,11 +16,6 @@ ./bld test ``` -## Build the documentation with [Dokka](https://github.com/Kotlin/dokka) +## Requirements -```console -./bld javadoc -./bld dokka-html -./bld dokka-gfm -./bld dokka-jekyll -``` +- A Kotlin compiler must be [installed](https://github.com/rife2/bld-kotlin?tab=readme-ov-file#kotlin-compiler-requirement). diff --git a/examples/lib/bld/bld-wrapper.jar b/examples/lib/bld/bld-wrapper.jar index e637135..06cf59e 100644 Binary files a/examples/lib/bld/bld-wrapper.jar and b/examples/lib/bld/bld-wrapper.jar differ diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index 142e058..1f1009d 100644 --- a/examples/lib/bld/bld-wrapper.properties +++ b/examples/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extensions=com.uwyn.rife2:bld-kotlin:0.9.1 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.0 +bld.version=2.2.1 diff --git a/examples/src/bld/java/com/example/ExampleBuild.java b/examples/src/bld/java/com/example/ExampleBuild.java index 83ae793..170c5b5 100644 --- a/examples/src/bld/java/com/example/ExampleBuild.java +++ b/examples/src/bld/java/com/example/ExampleBuild.java @@ -3,14 +3,10 @@ package com.example; import rife.bld.BuildCommand; import rife.bld.Project; import rife.bld.extension.CompileKotlinOperation; -import rife.bld.extension.dokka.DokkaOperation; -import rife.bld.extension.dokka.LoggingLevel; -import rife.bld.extension.dokka.OutputFormat; -import rife.bld.operations.exceptions.ExitStatusException; +import rife.bld.extension.kotlin.CompileOptions; +import rife.bld.extension.kotlin.JvmOptions; import java.io.File; -import java.io.IOException; -import java.nio.file.Path; import java.util.List; import java.util.logging.ConsoleHandler; import java.util.logging.Level; @@ -34,13 +30,14 @@ public class ExampleBuild extends Project { repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES); - final var kotlin = version(1, 9, 22); + final var kotlin = version(2, 1, 20); scope(compile) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)); scope(test) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 2))); // Include the Kotlin source directory when creating or publishing sources Java Archives jarSourcesOperation().sourceDirectories(new File(srcMainDirectory(), "kotlin")); @@ -48,73 +45,27 @@ public class ExampleBuild extends Project { public static void main(String[] args) { // Enable detailed logging for the Kotlin extension - // var level = Level.ALL; - // var logger = Logger.getLogger("rife.bld.extension"); - // var consoleHandler = new ConsoleHandler(); + var level = Level.ALL; + var logger = Logger.getLogger("rife.bld.extension"); + var consoleHandler = new ConsoleHandler(); - // consoleHandler.setLevel(level); - // logger.addHandler(consoleHandler); - // logger.setLevel(level); - // logger.setUseParentHandlers(false); + consoleHandler.setLevel(level); + logger.addHandler(consoleHandler); + logger.setLevel(level); + logger.setUseParentHandlers(false); new ExampleBuild().start(args); } @BuildCommand(summary = "Compiles the Kotlin project") @Override - public void compile() throws IOException { + public void compile() throws Exception { // The source code located in src/main/kotlin and src/test/kotlin will be compiled new CompileKotlinOperation() +// .kotlinHome("path/to/kotlin") +// .kotlinc("path/to/kotlinc") + .compileOptions(new CompileOptions().verbose(true)) .fromProject(this) .execute(); - -// var op = new CompileKotlinOperation().fromProject(this); -// op.compileOptions().verbose(true); -// op.execute(); - } - - @BuildCommand(value = "dokka-gfm", summary = "Generates documentation in GitHub flavored markdown format") - public void dokkaGfm() throws ExitStatusException, IOException, InterruptedException { - new DokkaOperation() - .fromProject(this) - .loggingLevel(LoggingLevel.INFO) - // Create build/dokka/gfm - .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "gfm").toFile()) - .outputFormat(OutputFormat.MARKDOWN) - .execute(); - } - - @BuildCommand(value = "dokka-html", summary = "Generates documentation in HTML format") - public void dokkaHtml() throws ExitStatusException, IOException, InterruptedException { - new DokkaOperation() - .fromProject(this) - .loggingLevel(LoggingLevel.INFO) - // Create build/dokka/html - .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "html").toFile()) - .outputFormat(OutputFormat.HTML) - .execute(); - } - - @BuildCommand(value = "dokka-jekyll", summary = "Generates documentation in Jekyll flavored markdown format") - public void dokkaJekyll() throws ExitStatusException, IOException, InterruptedException { - new DokkaOperation() - .fromProject(this) - .loggingLevel(LoggingLevel.INFO) - // Create build/dokka/jekyll - .outputDir(Path.of(buildDirectory().getAbsolutePath(), "dokka", "jekkyl").toFile()) - .outputFormat(OutputFormat.JEKYLL) - .execute(); - } - - @BuildCommand(summary = "Generates Javadoc for the project") - @Override - public void javadoc() throws ExitStatusException, IOException, InterruptedException { - new DokkaOperation() - .fromProject(this) - .loggingLevel(LoggingLevel.INFO) - // Create build/javadoc - .outputDir(new File(buildDirectory(), "javadoc")) - .outputFormat(OutputFormat.JAVADOC) - .execute(); } } diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index e637135..d7879eb 100644 Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 69f1830..d29a55f 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-pmd=com.uwyn.rife2:bld-pmd:0.9.7 -bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_RELEASES bld.downloadLocation= +bld.javaOptions= +bld.javacOptions= +bld.extension-exec=com.uwyn.rife2:bld-exec:1.0.5 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2 +bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.0 +bld.version=2.2.1 diff --git a/scripts/checkcliargs.sh b/scripts/checkcliargs.sh new file mode 100755 index 0000000..94313ab --- /dev/null +++ b/scripts/checkcliargs.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +new=/tmp/checkcliargs-new +old=/tmp/checkcliargs-old + +kotlinc -h 2>$new +~/.sdkman/candidates/kotlin/2.1.10/bin/kotlinc -h 2>$old + +code --diff --wait $old $new + +rm -rf $new $old diff --git a/scripts/cliargs.sh b/scripts/cliargs.sh new file mode 100755 index 0000000..bb433c2 --- /dev/null +++ b/scripts/cliargs.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +kotlinc -h 2> >(grep "^ ") | + sed -e "s/^ //" -e "s/ .*//" -e "s/<.*//" -e '/-help/d' -e '/^-version/d' -e '/^$/d' | + sort >"src/test/resources/kotlinc-args.txt" diff --git a/src/bld/java/rife/bld/extension/CompileKotlinOperationBuild.java b/src/bld/java/rife/bld/extension/CompileKotlinOperationBuild.java index 89cfc4a..53e84c7 100644 --- a/src/bld/java/rife/bld/extension/CompileKotlinOperationBuild.java +++ b/src/bld/java/rife/bld/extension/CompileKotlinOperationBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import rife.bld.publish.PublishLicense; import rife.bld.publish.PublishScm; import java.util.List; +import java.util.Locale; import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; @@ -33,30 +34,21 @@ public class CompileKotlinOperationBuild extends Project { public CompileKotlinOperationBuild() { pkg = "rife.bld.extension"; name = "bld-kotlin"; - version = version(0, 9, 1); + version = version(1, 1, 0, "SNAPSHOT"); javaRelease = 17; + downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES); - var dokka = version(1, 9, 10); - var kotlin = version(1, 9, 22); + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS); + 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.dokka", "dokka-cli", dokka)) - .include(dependency("org.jetbrains.dokka", "dokka-base", dokka)) - .include(dependency("org.jetbrains.dokka", "analysis-kotlin-descriptors", dokka)) - .include(dependency("org.jetbrains.dokka", "javadoc-plugin", dokka)) - .include(dependency("org.jetbrains.dokka", "gfm-plugin", dokka)) - .include(dependency("org.jetbrains.dokka", "jekyll-plugin", dokka)) - .include(dependency("com.uwyn.rife2", "bld", version(1, 9, 0))); + .include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1))); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))) - .include(dependency("org.assertj", "assertj-core", version(3, 25, 3))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); javadocOperation() .javadocOptions() @@ -66,28 +58,26 @@ public class CompileKotlinOperationBuild extends Project { publishOperation() .repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .repository(repository("github")) .info() .groupId("com.uwyn.rife2") .artifactId("bld-kotlin") .description("bld Kotlin Extension") .url("https://github.com/rife2/bld-kotlin") - .developer( - new PublishDeveloper() - .id("ethauvin") - .name("Erik C. Thauvin") - .email("erik@thauvin.net") - .url("https://erik.thauvin.net/") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") ) - .license( - new PublishLicense() - .name("The Apache License, Version 2.0") - .url("http://www.apache.org/licenses/LICENSE-2.0.txt") + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt") ) - .scm( - new PublishScm() - .connection("scm:git:https://github.com/rife2/bld-kotlin.git") - .developerConnection("scm:git:git@github.com:rife2/bld-kotlin.git") - .url("https://github.com/rife2/bld-kotlin") + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/bld-kotlin.git") + .developerConnection("scm:git:git@github.com:rife2/bld-kotlin.git") + .url("https://github.com/rife2/bld-kotlin") ) .signKey(property("sign.key")) .signPassphrase(property("sign.passphrase")); @@ -98,11 +88,23 @@ public class CompileKotlinOperationBuild extends Project { } @BuildCommand(summary = "Runs PMD analysis") - public void pmd() { + public void pmd() throws Exception { new PmdOperation() .fromProject(this) .failOnViolation(true) .ruleSets("config/pmd.xml") .execute(); } + + @Override + public void test() throws Exception { + var os = System.getProperty("os.name"); + if (os != null && os.toLowerCase(Locale.US).contains("linux")) { + new ExecOperation() + .fromProject(this) + .command("scripts/cliargs.sh") + .execute(); + } + super.test(); + } } diff --git a/src/main/java/rife/bld/extension/CompileKotlinOperation.java b/src/main/java/rife/bld/extension/CompileKotlinOperation.java index 1465991..5932298 100644 --- a/src/main/java/rife/bld/extension/CompileKotlinOperation.java +++ b/src/main/java/rife/bld/extension/CompileKotlinOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,21 @@ 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; +import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.AbstractOperation; +import rife.bld.operations.exceptions.ExitStatusException; 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; import java.util.logging.Logger; -import java.util.regex.Pattern; /** * Compiles main and test Kotlin sources in the relevant build directories. @@ -35,13 +39,13 @@ import java.util.regex.Pattern; * @since 1.0 */ public class CompileKotlinOperation extends AbstractOperation { - /** - * The Kotlin file (.kt) pattern. - */ - public static final Pattern KOTLIN_FILE_PATTERN = Pattern.compile("^.*\\.kt$"); private static final Logger LOGGER = Logger.getLogger(CompileKotlinOperation.class.getName()); + private static final String OS_NAME = + System.getProperty("os.name") != null ? System.getProperty("os.name").toLowerCase(Locale.US) : null; + private static final String KOTLINC_EXECUTABLE = "kotlinc" + (isWindows() ? ".bat" : ""); private final Collection 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<>(); @@ -49,52 +53,188 @@ public class CompileKotlinOperation extends AbstractOperation testSourceFiles_ = new ArrayList<>(); private File buildMainDirectory_; private File buildTestDirectory_; - private CompileKotlinOptions compileOptions_ = new CompileKotlinOptions(); + private CompileOptions compileOptions_ = new CompileOptions(); + private File kotlinHome_; + private File kotlinc_; private BaseProject project_; + private File workDir_; + + private static String findKotlincInDir(String directory) { + var kotlinc = new File(directory, KOTLINC_EXECUTABLE); + + if (isExecutable(kotlinc)) { + return kotlinc.getAbsolutePath(); + } + + // Check bin subdirectory if it exists + var binDir = new File(directory, "bin"); + if (binDir.isDirectory()) { + kotlinc = new File(binDir, KOTLINC_EXECUTABLE); + if (isExecutable(kotlinc)) { + return kotlinc.getAbsolutePath(); + } + } + + return null; + } /** - * Returns the list of JARs contained in a given directory. + * Locates the Kotlin compiler (kotlinc) executable. * - * @param directory the directory - * @param regex the regular expression to match - * @return the list of JARs + * @return The path to the kotlinc executable, or {@code kotlinc}/{@code kotlinc.bat} if not found. + * @since 1.1.0 */ - public static List getJarList(File directory, String regex) { - var jars = new ArrayList(); + public static String findKotlincPath() { + return findKotlincPath(false); + } - 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()); - } + /** + * Locates the Kotlin compiler (kotlinc) executable. + * + * @param isSilent do not log the path to the kotlinc executable, if {@code true} + * @return The path to the kotlinc executable, or {@code kotlinc}/{@code kotlinc.bat} if not found. + * @since 1.1.0 + */ + public static String findKotlincPath(boolean isSilent) { + String kotlincPath; + + // Check KOTLIN_HOME environment variable first + var kotlinHome = System.getenv("KOTLIN_HOME"); + if (kotlinHome != null && !kotlinHome.isEmpty()) { + kotlincPath = findKotlincInDir(kotlinHome); + if (kotlincPath != null) { + logKotlincPath(kotlincPath, isSilent, "KOTLIN_HOME"); + return kotlincPath; + } + } + + // Check PATH environment variable + var pathEnv = System.getenv("PATH"); + if (pathEnv != null && !pathEnv.isEmpty()) { + var pathDirs = pathEnv.split(File.pathSeparator); + for (var dir : pathDirs) { + kotlincPath = findKotlincInDir(dir); + if (kotlincPath != null) { + logKotlincPath(kotlincPath, isSilent, "PATH"); + return kotlincPath; } } } - return jars; + // Common installation paths (e.g., SDKMAN!, IntelliJ IDEA, etc.) + var commonPaths = new LinkedHashMap(); + + if (isLinux()) { + var userHome = System.getProperty("user.home"); + if (userHome != null) { + commonPaths.put(userHome + "/.sdkman/candidates/kotlin/current/bin", "SDKMAN!"); + } + commonPaths.put("/snap/bin", "Kotlin (Snap)"); + commonPaths.put("/usr/bin", null); + commonPaths.put("/usr/share", null); + commonPaths.put("/usr/local/bin", null); + commonPaths.put("/usr/local/kotlin/bin", null); + commonPaths.put("/usr/share/kotlin/bin/", null); + commonPaths.put("/opt/kotlin/bin", null); + if (userHome != null) { + commonPaths.put(userHome + "/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/plugins/Kotlin/kotlinc/bin", + "IntelliJ IDEA Ultimate"); + commonPaths.put(userHome + "/.local/share/JetBrains/Toolbox/apps/intellij-idea-community-edition/plugins/Kotlin/kotlinc/bin", + "IntelliJ IDEA Community Edition"); + commonPaths.put(userHome + "/.local/share/JetBrains/Toolbox/apps/android-studio/plugins/Kotlin/kotlinc/bin", + "Android Studio"); + } + commonPaths.put("/snap/intellij-idea-ultimate/current/commons/plugins/Kotlin/kotlinc/bin", + "IntelliJ IDEA Ultimate (Snap)"); + commonPaths.put("/snap/intellij-idea-community/current/commons/plugins/Kotlin/kotlinc/bin", + "IntelliJ IDEA Community Edition (Snap)"); + commonPaths.put("/snap/android-studio/current/android-studio/commons/plugins/Kotlin/kotlinc/bin", + "Android Studio (Snap)"); + } else if (isWindows()) { + commonPaths.put("C:\\tools\\kotlinc\\bin", null); + var localAppData = System.getenv("LOCALAPPDATA"); + if (localAppData != null) { + commonPaths.put(localAppData + "\\Programs\\IntelliJ IDEA Ultimate\\plugins\\Kotlin\\kotlinc\\bin", + "IntelliJ IDEA Ultimate"); + commonPaths.put(localAppData + "\\Programs\\IntelliJ IDEA Community Edition\\plugins\\Kotlin\\kotlinc\\bin", + "IntelliJ IDEA Community Edition"); + commonPaths.put(localAppData + "\\Programs\\Android Studio\\plugins\\Kotlin\\kotlinc\\bin", + "Android Studio"); + } + var programFiles = System.getenv("ProgramFiles"); + if (programFiles != null) { + commonPaths.put(programFiles + "\\Kotlin\\bin", null); + } + } else if (isMacOS()) { + var userHome = System.getProperty("user.home"); + if (userHome != null) { + commonPaths.put(userHome + "/.sdkman/candidates/kotlin/current/bin", "SDKMAN!"); + } + commonPaths.put("/opt/homebrew/bin", "Homebrew"); + commonPaths.put("/usr/local/bin", null); + commonPaths.put("/Applications/IntelliJ IDEA.app/Contents/plugins/Kotlin/kotlinc/bin/", + "IntelliJ IDEA"); + commonPaths.put("/Applications/IntelliJ IDEA Community Edition.app/Contents/plugins/Kotlin/kotlinc/bin/", + "IntelliJ IDEA Community Edition"); + commonPaths.put("/Applications/Android Studio.app/Contents/plugins/Kotlin/kotlinc/bin", + "Android Studio"); + } + + for (var path : commonPaths.keySet()) { + kotlincPath = findKotlincInDir(path); + if (kotlincPath != null) { + logKotlincPath(kotlincPath, isSilent, commonPaths.get(path)); + return kotlincPath; + } + } + + // Try 'which' or 'where' commands (less reliable but sometimes works) + try { + Process process; + if (isWindows()) { + process = Runtime.getRuntime().exec("where kotlinc"); + } else { + process = Runtime.getRuntime().exec("which kotlinc"); + } + + try (var scanner = new Scanner(process.getInputStream())) { + if (scanner.hasNextLine()) { + kotlincPath = scanner.nextLine().trim(); + if (isExecutable(new File(kotlincPath))) { + logKotlincPath(kotlincPath, isSilent); + return kotlincPath; + } + } + } + } catch (Exception ignored) { + // Ignore exceptions from which/where, as they might not be available + } + + return KOTLINC_EXECUTABLE; + } + + private static boolean isExecutable(File file) { + return file != null && file.exists() && file.isFile() && file.canExecute(); } /** - * Returns the list of Kotlin source file {{@code .kt}} contained in a given directory. + * Determines if the operating system is Linux. * - * @param directory the directory - * @return the list of Kotlin files + * @return true if the operating system is Linux, false otherwise. + * @since 1.1.0 */ - public static Collection getKotlinFileList(File directory) { - if (directory == null) { - return Collections.emptyList(); - } else if (!directory.exists()) { - if (LOGGER.isLoggable(Level.WARNING)) { - LOGGER.warning("Directory not found: " + directory.getAbsolutePath()); - } - return Collections.emptyList(); - } else { - return FileUtils.getFileList(directory, KOTLIN_FILE_PATTERN, null).stream().map((file) -> - new File(directory, file)).toList(); - } + public static boolean isLinux() { + return OS_NAME != null && (OS_NAME.contains("linux") || OS_NAME.contains("unix")); // Consider Unix-like systems as well. + } + + /** + * Determines if the current operating system is macOS. + * + * @return true if the OS is macOS, false otherwise. + * @since 1.1.0 + */ + public static boolean isMacOS() { + return OS_NAME != null && (OS_NAME.contains("mac") || OS_NAME.contains("darwin") || OS_NAME.contains("osx")); } /** @@ -107,6 +247,40 @@ public class CompileKotlinOperation extends AbstractOperation classpath) { @@ -170,30 +385,30 @@ public class CompileKotlinOperation extends AbstractOperation compileMainClasspath() { return compileMainClasspath_; } /** - * Retrieves the list of compilation options for the compiler. + * Retrieves the compilation options for the compiler. * - * @return the compile kotlin options + * @return the compilation options */ - public CompileKotlinOptions compileOptions() { + public CompileOptions compileOptions() { return compileOptions_; } /** - * Provides a list of compilation options to pass to the Kotlin compiler. + * Provides the compilation options to pass to the Kotlin compiler. * * @param options the compiler options * @return this operation instance */ - public CompileKotlinOperation compileOptions(CompileKotlinOptions options) { + public CompileKotlinOperation compileOptions(CompileOptions options) { compileOptions_ = options; return this; } @@ -205,14 +420,13 @@ public class CompileKotlinOperation extends AbstractOperation classpath) { @@ -221,9 +435,9 @@ public class CompileKotlinOperation extends AbstractOperation compileTestClasspath() { return compileTestClasspath_; @@ -234,10 +448,17 @@ public class CompileKotlinOperation extends AbstractOperation classpath, Collection sources, File destination, File friendPaths) - throws IOException { - if (sources.isEmpty() || destination == null) { - return; + throws ExitStatusException { + + var cp = new ArrayList(); + if (classpath != null && !classpath.isEmpty()) { + cp.addAll(classpath); } - var k2 = new K2JVMCompiler(); + if (sources.isEmpty()) { + if (!silent() && LOGGER.isLoggable(Level.WARNING)) { + LOGGER.warning("Nothing to compile."); + } + return; + } else if (destination == null) { + if (!silent() && LOGGER.isLoggable(Level.SEVERE)) { + LOGGER.severe("No destination specified."); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } + + var command = new ArrayList(); var args = new ArrayList(); + // kotlinc + if (kotlinc_ != null) { + command.add(kotlinc_.getAbsolutePath()); + } else if (kotlinHome_ != null) { + var kotlinc = findKotlincInDir(kotlinHome_.getAbsolutePath()); + if (kotlinc != null) { + command.add(kotlinc); + } else { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("Could not locate Kotlin compiler in: " + kotlinHome_); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } + } else { + command.add(findKotlincPath(silent())); + } + + // JVM options + if (!jvmOptions_.isEmpty()) { + jvmOptions_.forEach(s -> command.add("-J" + s)); + } + // classpath - args.add("-cp"); - args.add(FileUtils.joinPaths(classpath.stream().toList())); + if (compileOptions_ != null && !compileOptions_.classpath().isEmpty()) { + cp.addAll(compileOptions_.classpath().stream().map(this::cleanPath).toList()); + } + if (!cp.isEmpty()) { + args.add("-cp"); + args.add('"' + FileUtils.joinPaths(cp.stream().map(this::cleanPath).toList()) + '"'); + } + + // compile options + if (compileOptions_ != null && !compileOptions_.args().isEmpty()) { + args.addAll(compileOptions_.args()); + } // 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("kotlinc " + String.join(" ", args)); + LOGGER.fine(String.join(" ", command) + " " + argsLine); } - var exitCode = k2.exec(System.err, args.toArray(new String[0])); - if (exitCode.getCode() != 0) { - throw new IOException("Kotlin compilation failed."); + 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()); + } catch (IOException | InterruptedException e) { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe(e.getLocalizedMessage()); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); } } /** * Part of the {@link #execute execute} operation, builds the test sources. * - * @throws IOException if an error occurs + * @throws ExitStatusException if an error occurs */ @SuppressWarnings("PMD.SystemPrintln") - protected void executeBuildTestSources() - throws IOException { + protected void executeBuildTestSources() throws ExitStatusException { if (!silent()) { System.out.println("Compiling Kotlin test sources."); } @@ -348,28 +632,52 @@ public class CompileKotlinOperation extends AbstractOperation * Sets the following from the project: *
    + *
  • {@link #workDir() workDir} to the project's directory.
  • *
  • {@link #buildMainDirectory() buildMainDirectory}
  • *
  • {@link #buildTestDirectory() buildTestDirectory}
  • *
  • {@link #compileMainClasspath() compileMainClassPath}
  • *
  • {@link #compileTestClasspath() compilesTestClassPath}
  • - *
  • {@link #mainSourceFiles() mainSourceFiles} to the {@code kotlin} directory in - * {@link BaseProject#srcMainDirectory() srcMainDirectory}
  • - *
  • {@link #testSourceFiles() testSourceFile} to the {@code kotlin} directory in - * {@link BaseProject#srcTestDirectory() srcTestDirectory}
  • - *
  • {@link CompileKotlinOptions#jdkRelease jdkRelease} to {@link BaseProject#javaRelease() javaRelease}
  • - *
  • {@link CompileKotlinOptions#noStdLib(boolean) noStdLib} to {@code true}
  • + *
  • {@link #mainSourceDirectories() mainSourceDirectories} to the {@code kotlin} directory in + * {@link BaseProject#srcMainDirectory() srcMainDirectory}, if present.
  • + *
  • {@link #testSourceDirectories() testSourceDirectories} to the {@code kotlin} directory in + * {@link BaseProject#srcTestDirectory() srcTestDirectory}, if present.
  • + *
  • {@link CompileOptions#jdkRelease jdkRelease} to {@link BaseProject#javaRelease() javaRelease}
  • + *
  • {@link CompileOptions#noStdLib(boolean) noStdLib} to {@code true}
  • *
* * @param project the project to configure the compile operation from @@ -377,12 +685,22 @@ public class CompileKotlinOperation extends AbstractOperation jvmOptions) { + jvmOptions_.addAll(jvmOptions); return this; } /** - * Provides a list of main source directories that should be compiled. + * Pass an option directly to the Java Virtual Machine * - * @param directories a list of main source directories + * @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}. + * + * @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)); + } + + /** + * 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(Path dir) { + return kotlinHome(dir.toFile()); + } + + /** + * Retrieves the Kotlin home directory. + * + * @return the directory + */ + public File kotlinHome() { + return kotlinHome_; + } + + /** + * Retrieves the path to the Kotlin compiler ({@code kotlinc}) executable, if not in {@link #kotlinHome()}. + * + * @return the executable path + */ + public File kotlinc() { + return kotlinc_; + } + + /** + * Provides the path to the Kotlin compiler ({@code kotlinc}) executable, if not in {@link #kotlinHome()}. + * + * @param executable the executable path + * @return this operation instance + */ + public CompileKotlinOperation kotlinc(File executable) { + kotlinc_ = executable; + return this; + } + + /** + * Provides the path to the Kotlin compiler ({@code kotlinc}) executable, if not in {@link #kotlinHome()}. + * + * @param executable the executable path + * @return this operation instance + */ + public CompileKotlinOperation kotlinc(String executable) { + return kotlinc(new File(executable)); + } + + /** + * Provides the path to the Kotlin compiler ({@code kotlinc}) executable, if not in {@link #kotlinHome()}. + * + * @param executable the executable path + * @return this operation instance + */ + public CompileKotlinOperation kotlinc(Path executable) { + return kotlinc(executable.toFile()); + } + + /** + * Provides main source directories that should be compiled. + * + * @param directories one or more main source directories + * @return this operation instance + * @see #mainSourceDirectories(Collection) + */ + public CompileKotlinOperation mainSourceDirectories(File... directories) { + return mainSourceDirectories(List.of(directories)); + } + + /** + * Provides main source directories that should be compiled. + * + * @param directories one or more main source directories + * @return this operation instance + * @see #mainSourceDirectoriesPaths(Collection) + */ + public CompileKotlinOperation mainSourceDirectories(Path... directories) { + return mainSourceDirectoriesPaths(List.of(directories)); + } + + /** + * Provides main source directories that should be compiled. + * + * @param directories one or more main source directories + * @return this operation instance + * @see #mainSourceDirectoriesStrings(Collection) + */ + public CompileKotlinOperation mainSourceDirectories(String... directories) { + return mainSourceDirectoriesStrings(List.of(directories)); + } + + /** + * Provides the main source directories that should be compiled. + * + * @param directories the main source directories + * @return this operation instance + * @see #mainSourceDirectories(File...) + */ public CompileKotlinOperation mainSourceDirectories(Collection directories) { mainSourceDirectories_.addAll(directories); return this; } /** - * Retrieves the list of main source directories that should be compiled. + * Retrieves the main source directories that should be compiled. * - * @return the list of main source directories to compile + * @return the main source directories */ public Collection mainSourceDirectories() { return mainSourceDirectories_; } /** - * Provides main files that should be compiled. + * Provides the main source directories that should be compiled. * - * @param files one or more main files + * @param directories the main source directories * @return this operation instance + * @see #mainSourceDirectories(Path...) */ - public CompileKotlinOperation mainSourceFiles(File... files) { - mainSourceFiles_.addAll(Arrays.asList(files)); - return this; + public CompileKotlinOperation mainSourceDirectoriesPaths(Collection directories) { + return mainSourceDirectories(directories.stream().map(Path::toFile).toList()); } /** - * Provides a list of main files that should be compiled. + * Provides the main source directories that should be compiled. * - * @param files a list of main files + * @param directories the main source directories * @return this operation instance + * @see #mainSourceDirectories(String...) + */ + public CompileKotlinOperation mainSourceDirectoriesStrings(Collection directories) { + return mainSourceDirectories(directories.stream().map(File::new).toList()); + } + + /** + * Provides main source files that should be compiled. + * + * @param files one or more main source files + * @return this operation instance + * @see #mainSourceFiles(Collection) + */ + public CompileKotlinOperation mainSourceFiles(File... files) { + return mainSourceFiles(List.of(files)); + } + + /** + * Provides main source files that should be compiled. + * + * @param files one or more main source files + * @return this operation instance + * @see #mainSourceFilesStrings(Collection) + */ + public CompileKotlinOperation mainSourceFiles(String... files) { + return mainSourceFilesStrings(List.of(files)); + } + + /** + * Provides main source files that should be compiled. + * + * @param files one or more main source files + * @return this operation instance + * @see #mainSourceFilesPaths(Collection) + */ + public CompileKotlinOperation mainSourceFiles(Path... files) { + return mainSourceFilesPaths(List.of(files)); + } + + /** + * Provides the main source files that should be compiled. + * + * @param files the main source files + * @return this operation instance + * @see #mainSourceFiles(File...) */ public CompileKotlinOperation mainSourceFiles(Collection files) { mainSourceFiles_.addAll(files); @@ -445,14 +941,47 @@ public class CompileKotlinOperation extends AbstractOperation mainSourceFiles() { return mainSourceFiles_; } + /** + * Provides the main source files that should be compiled. + * + * @param files the main source files + * @return this operation instance + * @see #mainSourceFiles(Path...) + */ + public CompileKotlinOperation mainSourceFilesPaths(Collection files) { + return mainSourceFiles(files.stream().map(Path::toFile).toList()); + } + + /** + * Provides the main source files that should be compiled. + * + * @param files the main source files + * @return this operation instance + * @see #mainSourceFiles(String...) + */ + public CompileKotlinOperation mainSourceFilesStrings(Collection files) { + return mainSourceFiles(files.stream().map(File::new).toList()); + } + + /** + * Provides compiler plugins. + * + * @param directory the directory containing the plugin JARs + * @param plugins one or more plugins + * @return this class instance + */ + public CompileKotlinOperation plugins(String directory, CompilerPlugin... plugins) { + return plugins(new File(directory), plugins); + } + /** * Provides compiler plugins. * @@ -460,14 +989,22 @@ public class CompileKotlinOperation extends AbstractOperation plugins() { + return plugins_; } /** * Provides compiler plugins. * - * @param plugins a list of plugins + * @param plugins the compiler plugins * @return this class instance */ public CompileKotlinOperation plugins(Collection plugins) { @@ -482,9 +1019,9 @@ public class CompileKotlinOperation extends AbstractOperation jars, CompileKotlinPlugin... plugins) { - jars.forEach(jar -> { - for (var plugin : plugins) { - if (jar.getName().matches(plugin.label)) { - plugins_.add(jar.getAbsolutePath()); - break; - } - } - }); + public CompileKotlinOperation plugins(Path directory, CompilerPlugin... plugins) { + return plugins(directory.toFile(), plugins); + } + /** + * Provides compiler plugins located in the {@link #kotlinHome()} lib directory. + * + * @param plugins one or more plugins + * @return this class instance + * @see #plugins(File, CompilerPlugin...) + */ + public CompileKotlinOperation plugins(CompilerPlugin... plugins) { + var kotlinHome = kotlinHome_; + if (kotlinHome == null) { + kotlinHome = findKotlinHome(); + } + 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 (or discovered) to specify compiler plugins directly."); + } return this; } // Combine Kotlin sources private Collection sources(Collection files, Collection directories) { var sources = new ArrayList<>(files); - for (var directory : directories) { - sources.addAll(getKotlinFileList(directory)); - } - + sources.addAll(directories); return sources; } @@ -524,17 +1072,40 @@ public class CompileKotlinOperation extends AbstractOperation directories) { testSourceDirectories_.addAll(directories); @@ -542,30 +1113,75 @@ public class CompileKotlinOperation extends AbstractOperation testSourceDirectories() { return testSourceDirectories_; } /** - * Provides test files that should be compiled. + * Provides the test source directories that should be compiled. * - * @param files one or more test files + * @param directories the test source directories * @return this operation instance + * @see #testSourceDirectories(Path...) */ - public CompileKotlinOperation testSourceFiles(File... files) { - testSourceFiles_.addAll(Arrays.asList(files)); - return this; + public CompileKotlinOperation testSourceDirectoriesPaths(Collection directories) { + return testSourceDirectories(directories.stream().map(Path::toFile).toList()); } /** - * Provides a list of test files that should be compiled. + * Provides the test source directories that should be compiled. * - * @param files a list of test files + * @param directories the test source directories * @return this operation instance + * @see #testSourceDirectories(String...) + */ + public CompileKotlinOperation testSourceDirectoriesStrings(Collection directories) { + return testSourceDirectories(directories.stream().map(File::new).toList()); + } + + /** + * Provides test source files that should be compiled. + * + * @param files one or more test source files + * @return this operation instance + * @see #testSourceFiles(Collection) + */ + public CompileKotlinOperation testSourceFiles(File... files) { + return testSourceFiles(List.of(files)); + } + + /** + * Provides the test sources files that should be compiled. + * + * @param files one or more test source files + * @return this operation instance + * @see #testSourceFilesStrings(Collection) + */ + public CompileKotlinOperation testSourceFiles(String... files) { + return testSourceFilesStrings(List.of(files)); + } + + /** + * Provides the test sources files that should be compiled. + * + * @param files one or more test source files + * @return this operation instance + * @see #testSourceFilesPaths(Collection) + */ + public CompileKotlinOperation testSourceFiles(Path... files) { + return testSourceFilesPaths(List.of(files)); + } + + /** + * Provides the test source files that should be compiled. + * + * @param files the test source files + * @return this operation instance + * @see #testSourceFiles(File...) */ public CompileKotlinOperation testSourceFiles(Collection files) { testSourceFiles_.addAll(files); @@ -573,11 +1189,73 @@ public class CompileKotlinOperation extends AbstractOperation testSourceFiles() { return testSourceFiles_; } + + /** + * Provides the test source files that should be compiled. + * + * @param files the test source files + * @return this operation instance + * @see #testSourceFiles(Path...) + */ + public CompileKotlinOperation testSourceFilesPaths(Collection files) { + return testSourceFiles(files.stream().map(Path::toFile).toList()); + } + + /** + * Provides the test source files that should be compiled. + * + * @param files the test source files + * @return this operation instance + * @see #testSourceFiles(String...) + */ + public CompileKotlinOperation testSourceFilesStrings(Collection files) { + return testSourceFiles(files.stream().map(File::new).toList()); + } + + /** + * 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 + * @return this operation instance + */ + public CompileKotlinOperation workDir(Path dir) { + return workDir(dir.toFile()); + } + + /** + * 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)); + } } diff --git a/src/main/java/rife/bld/extension/CompileKotlinOptions.java b/src/main/java/rife/bld/extension/CompileKotlinOptions.java deleted file mode 100644 index 343d1ba..0000000 --- a/src/main/java/rife/bld/extension/CompileKotlinOptions.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import static rife.bld.extension.CompileKotlinOperation.isNotBlank; - -/** - * Configuration for the Kotlin compiler options. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public class CompileKotlinOptions { - private final List argFile_ = new ArrayList<>(); - private final List classpath_ = new ArrayList<>(); - private final List optIn_ = new ArrayList<>(); - private final List options_ = new ArrayList<>(); - private final List plugin_ = new ArrayList<>(); - private final List scriptTemplates_ = new ArrayList<>(); - private String apiVersion_; - private boolean includeRuntime_; - private boolean javaParameters_; - private String jdkHome_; - private String jdkRelease_; - private String jvmTarget_; - private String kotlinHome_; - private String languageVersion_; - private String moduleName_; - private boolean noJdk_; - private boolean noReflect_; - private boolean noStdLib_; - private boolean noWarn_; - private String path_; - private boolean progressive_; - private boolean verbose_; - private boolean wError_; - - /** - * Allow using declarations only from the specified version of Kotlin bundled libraries. - * - * @param version the api version - * @return this operation instance - */ - public CompileKotlinOptions apiVersion(String version) { - apiVersion_ = version; - return this; - } - - /** - * Read the compiler options from the given files. - *

- * Such a file can contain compiler options with values and paths to the source files. - * Options and paths should be separated by whitespaces. For example: - *

    - *
  • {@code -include-runtime -d hello.jar hello.kt}
  • - *
- * To pass values that contain whitespaces, surround them with single ({@code '}) or double ({@code "}) quotes. - * If a value contains quotation marks in it, escape them with a backslash (\). - *
    - *
  • {@code -include-runtime -d 'My folder'}
  • - *
- * If the files reside in locations different from the current directory, use relative paths. - * - * @param files one or more files - * @return this operation instance - */ - public CompileKotlinOptions argFile(String... files) { - argFile_.addAll(List.of(files)); - return this; - } - - /** - * Read the compiler options from the given files. - * - * @param files the list of files - * @return this operation instance - * @see #argFile(String...) - */ - public CompileKotlinOptions argFile(Collection files) { - argFile_.addAll(files); - return this; - } - - /** - * Returns the formatted arguments. - * - * @return the arguments - */ - public List args() { - var args = new ArrayList(); - - // api-isNotBlankversion - if (isNotBlank(apiVersion_)) { - args.add("-api-version"); - args.add(apiVersion_); - } - - // @argfile - if (!argFile_.isEmpty()) { - argFile_.forEach(f -> args.add("@" + f)); - } - - // classpath - if (!classpath_.isEmpty()) { - args.add("-classpath"); - args.add(String.join(File.pathSeparator, classpath_)); - } - - // java-parameters - if (javaParameters_) { - args.add("-java-parameters"); - } - - // jvm-target - if (isNotBlank(jvmTarget_)) { - args.add("-jvm-target"); - args.add(jvmTarget_); - } - - // include-runtime - if (includeRuntime_) { - args.add("-include-runtime"); - } - - // jdk-home - if (isNotBlank(jdkHome_)) { - args.add("-jdk-home"); - args.add(jdkHome_); - } - - // jdk-release - if (isNotBlank(jdkRelease_)) { - args.add("-Xjdk-release=" + jdkRelease_); - } - - // kotlin-home - if (isNotBlank(kotlinHome_)) { - args.add("-kotlin-home"); - args.add(kotlinHome_); - } - - // language-version - if (isNotBlank(languageVersion_)) { - args.add("-language-version"); - args.add(languageVersion_); - } - - // module-name - if (isNotBlank(moduleName_)) { - args.add("-module-name"); - args.add(moduleName_); - } - - // no-jdk - if (noJdk_) { - args.add("-no-jdk"); - } - - // no-reflect - if (noReflect_) { - args.add("-no-reflect"); - } - - // no-std-lib - if (noStdLib_) { - args.add("-no-stdlib"); - } - - // no-warn - if (noWarn_) { - args.add("-no-warn"); - } - - // opt-in - optIn_.stream().filter(CompileKotlinOperation::isNotBlank).forEach(o -> { - args.add("-opt-in"); - args.add(o); - }); - - // options - if (!options_.isEmpty()) { - args.addAll(options_); - } - - // path - if (isNotBlank(path_)) { - args.add("-d"); - args.add(path_); - } - - // plugin - plugin_.stream().filter(CompileKotlinOperation::isNotBlank).forEach(p -> { - args.add("-P"); - args.add("plugin:" + p); - }); - - // progressive - if (progressive_) { - args.add("-progressive"); - } - - // script-templates - if (!scriptTemplates_.isEmpty()) { - args.add("-script-templates"); - args.add(String.join(",", scriptTemplates_)); - } - - // verbose - if (verbose_) { - args.add("-verbose"); - } - - // Werror - if (wError_) { - args.add("-Werror"); - } - - return args; - } - - /** - * Search for class files in the specified paths. - *

- * The classpath can contain file and directory paths, ZIP, or JAR files. - * - * @param paths one pr more paths - * @return this operation instance - */ - public CompileKotlinOptions classpath(String... paths) { - classpath_.addAll(List.of(paths)); - return this; - } - - /** - * Search for class files in the specified paths. - *

- * The classpath can contain file and directory paths, ZIP, or JAR files. - * - * @param paths the list of paths - * @return this operation instance - */ - public CompileKotlinOptions classpath(Collection paths) { - classpath_.addAll(paths); - return this; - } - - /** - * Indicates whether the {@link #jdkRelease(String) jdkRelease} was set. - * - * @return {@code true} if the release was set; or {@code false} otherwise - */ - public boolean hasRelease() { - return jdkRelease_ != null; - } - - /** - * Include the Kotlin runtime into the resulting JAR file. Makes the resulting archive runnable on any Java-enabled - * environment. - * - * @param includeRuntime {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions includeRuntime(boolean includeRuntime) { - includeRuntime_ = includeRuntime; - return this; - } - - /** - * Generate metadata for Java 1.8 reflection on method parameters. - * - * @param javaParameters {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions javaParameters(boolean javaParameters) { - javaParameters_ = javaParameters; - return this; - } - - /** - * Use a custom JDK home directory to include into the classpath if it differs from the default {@code JAVA_HOME}. - * - * @param jdkHome the JDK home path - * @return this operation instance - */ - public CompileKotlinOptions jdkHome(String jdkHome) { - jdkHome_ = jdkHome; - return this; - } - - /** - * Specify the target version of the generated JVM bytecode. - *

- * Limit the API of the JDK in the classpath to the specified Java version. Automatically sets - * {@link #jvmTarget(String) JVM target} version. - *

- * Possible values are 1.8, 9, 10, ..., 21. The default value is 1.8. - * - * @param version the target version - * @return this operation instance - */ - public CompileKotlinOptions jdkRelease(String version) { - jdkRelease_ = version; - return this; - } - - /** - * Specify the target version of the generated JVM bytecode. - * - * @param version the target version - * @return this operation instance - * @see #jdkRelease(String) - */ - public CompileKotlinOptions jdkRelease(int version) { - jdkRelease_ = String.valueOf(version); - return this; - } - - /** - * Specify the target version of the generated JVM bytecode. - *

- * Possible values are 1.8, 9, 10, ..., 21. The default value is 1.8. - * - * @param target the target version - * @return this operation instance - */ - public CompileKotlinOptions jvmTarget(String target) { - jvmTarget_ = target; - return this; - } - - /** - * Specify the target version of the generated JVM bytecode. - * - * @param target the target version - * @return this operation instance - * @see #jvmTarget(String) - */ - public CompileKotlinOptions jvmTarget(int target) { - jvmTarget_ = String.valueOf(target); - return this; - } - - /** - * Specify a custom path to the Kotlin compiler used for the discovery of runtime libraries. - * - * @param path the Kotlin home path - * @return this operation instance - */ - public CompileKotlinOptions kotlinHome(String path) { - kotlinHome_ = path; - return this; - } - - /** - * Provide source compatibility with the specified version of Kotlin. - * - * @param version the language version - * @return this operation instance - */ - public CompileKotlinOptions languageVersion(String version) { - languageVersion_ = version; - return this; - } - - /** - * Set a custom name for the generated {@code .kotlin_module} file. - * - * @param name the module name - * @return this operation instance - */ - public CompileKotlinOptions moduleName(String name) { - moduleName_ = name; - return this; - } - - /** - * Don't automatically include the Java runtime into the classpath. - * - * @param noJdk {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions noJdk(boolean noJdk) { - noJdk_ = noJdk; - return this; - } - - /** - * Don't automatically include the Kotlin reflection ({@code kotlin-reflect.jar}) into the classpath. - * - * @param noReflect {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions noReflect(boolean noReflect) { - noReflect_ = noReflect; - return this; - } - - /** - * Don't automatically include the Kotlin/JVM stdlib ({@code kotlin-stdlib.jar}) and Kotlin reflection - * ({@code kotlin-reflect.jar}) into the classpath. - * - * @param noStdLib {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions noStdLib(boolean noStdLib) { - noStdLib_ = noStdLib; - return this; - } - - /** - * Suppress the compiler from displaying warnings during compilation. - * - * @param noWarn {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions noWarn(boolean noWarn) { - noWarn_ = noWarn; - return this; - } - - /** - * Enable usages of API that requires opt-in with a requirement annotation with the given fully qualified name. - * - * @param annotations one or more annotation names - * @return this operation instance - */ - public CompileKotlinOptions optIn(String... annotations) { - optIn_.addAll(List.of(annotations)); - return this; - } - - /** - * Enable usages of API that requires opt-in with a requirement annotation with the given fully qualified name. - * - * @param annotations list of annotation names - * @return this operation instance - */ - public CompileKotlinOptions optIn(Collection annotations) { - optIn_.addAll(annotations); - return this; - } - - /** - * Specify additional compiler options. - * - * @param options one or more compiler options - * @return this operation instance - */ - public CompileKotlinOptions options(String... options) { - options_.addAll(List.of(options)); - return this; - } - - /** - * Specify additional compiler options. - * - * @param options list of compiler options - * @return this operation instance - */ - public CompileKotlinOptions options(Collection options) { - options_.addAll(options); - return this; - } - - /** - * Place the generated class files into the specified location. - *

- * The location can be a directory, a ZIP, or a JAR file. - * - * @param path the location path - * @return this operation instance - */ - public CompileKotlinOptions path(File path) { - path_ = path.getAbsolutePath(); - return this; - } - - /** - * Place the generated class files into the specified location. - *

- * The location can be a directory, a ZIP, or a JAR file. - * - * @param path the location path - * @return this operation instance - */ - public CompileKotlinOptions path(String path) { - path_ = path; - return this; - } - - /** - * Pass an option to a plugin. - * - * @param id the plugin ID - * @param optionName the plugin option name - * @param value the plugin option value - * @return this operation instance - */ - public CompileKotlinOptions plugin(String id, String optionName, String value) { - plugin_.add(id + ':' + optionName + ':' + value); - return this; - } - - /** - * Allow using declarations only from the specified version of Kotlin bundled libraries. - * - * @param progressive {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions progressive(boolean progressive) { - progressive_ = progressive; - return this; - } - - /** - * Script definition template classes. - *

- * Use fully qualified class names. - * - * @param classNames one or more class names - * @return this operation instance - */ - public CompileKotlinOptions scriptTemplates(String... classNames) { - scriptTemplates_.addAll(List.of(classNames)); - return this; - } - - /** - * Script definition template classes. - *

- * Use fully qualified class names. - * - * @param classNames the list class names - * @return this operation instance - */ - public CompileKotlinOptions scriptTemplates(Collection classNames) { - scriptTemplates_.addAll(classNames); - return this; - } - - /** - * Enable verbose logging output which includes details of the compilation process. - * - * @param verbose {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions verbose(boolean verbose) { - verbose_ = verbose; - return this; - } - - /** - * Turn any warnings into a compilation error. - * - * @param wError {@code true} or {@code false} - * @return this operation instance - */ - public CompileKotlinOptions wError(boolean wError) { - wError_ = wError; - return this; - } -} diff --git a/src/main/java/rife/bld/extension/CompileKotlinPlugin.java b/src/main/java/rife/bld/extension/CompileKotlinPlugin.java deleted file mode 100644 index bddafbf..0000000 --- a/src/main/java/rife/bld/extension/CompileKotlinPlugin.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension; - -/** - * Defines the known Kotlin compiler plugins match (regex) strings. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum CompileKotlinPlugin { - ALL_OPEN("^allopen-compiler-plugin-.*$"), - ASSIGNMENT("^assignment-compiler-plugin-.*$"), - KOTLIN_IMPORTS_DUMPER("^kotlin-imports-dumper-compiler-plugin-.*$"), - KOTLIN_SERIALIZATION("^kotlin-serialization-compiler-plugin-.*$"), - KOTLINX_SERIALIZATION("^kotlinx-serialization-compiler-plugin-.*$"), - LOMBOK("^lombok-compiler-plugin-.*$"), - NOARG("^noarg-compiler-plugin-.*$"), - SAM_WITH_RECEIVER("^sam-with-receiver-compiler-plugin-.*$"); - - public final String label; - - CompileKotlinPlugin(String label) { - this.label = label; - } -} diff --git a/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java b/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java deleted file mode 100644 index dd6ff1a..0000000 --- a/src/main/java/rife/bld/extension/dokka/AnalysisPlatform.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -/** - * Dokka's analysis platforms. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum AnalysisPlatform { - JVM, JS, NATIVE, COMMON, ANDROID -} diff --git a/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java b/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java deleted file mode 100644 index dae65a4..0000000 --- a/src/main/java/rife/bld/extension/dokka/DocumentedVisibility.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -/** - * Dokka documented visibilities. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum DocumentedVisibility { - PUBLIC, PRIVATE, PROTECTED, INTERNAL, PACKAGE -} diff --git a/src/main/java/rife/bld/extension/dokka/DokkaOperation.java b/src/main/java/rife/bld/extension/dokka/DokkaOperation.java deleted file mode 100644 index 0b0936e..0000000 --- a/src/main/java/rife/bld/extension/dokka/DokkaOperation.java +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -import rife.bld.BaseProject; -import rife.bld.extension.CompileKotlinOperation; -import rife.bld.operations.AbstractProcessOperation; -import rife.tools.StringUtils; - -import java.io.File; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static rife.bld.extension.CompileKotlinOperation.isNotBlank; - -/** - * Builds documentation (javadoc, HTML, etc.) using Dokka. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -@SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") -public class DokkaOperation extends AbstractProcessOperation { - private final static String GFM_PLUGIN_REGEXP = - "^.*(dokka-base|analysis-kotlin-descriptors|gfm-plugin|freemarker).*\\.jar$"; - private final static String HTML_PLUGIN_REGEXP = - "^.*(dokka-base|analysis-kotlin-descriptors|kotlinx-html-jvm|freemarker).*\\.jar$"; - private final static String JAVADOC_PLUGIN_REGEXP = - "^.*(dokka-base|analysis-kotlin-descriptors|javadoc-plugin|kotlin-as-java-plugin|korte-jvm).*\\.jar$"; - private final static String JEKYLL_PLUGIN_REGEXP = - "^.*(dokka-base|analysis-kotlin-descriptors|jekyll-plugin|gfm-plugin|freemarker).*\\.jar$"; - private final Logger LOGGER = Logger.getLogger(DokkaOperation.class.getName()); - private final Map globalLinks_ = new ConcurrentHashMap<>(); - private final Collection globalPackageOptions_ = new ArrayList<>(); - private final Collection globalSrcLinks_ = new ArrayList<>(); - private final Collection includes_ = new ArrayList<>(); - private final Map pluginConfiguration_ = new ConcurrentHashMap<>(); - private final Collection pluginsClasspath_ = new ArrayList<>(); - private boolean delayTemplateSubstitution_; - private boolean failOnWarning_; - private LoggingLevel loggingLevel_; - private String moduleName_; - private String moduleVersion_; - private boolean noSuppressObviousFunctions_; - private boolean offlineMode_; - private File outputDir_; - private BaseProject project_; - private SourceSet sourceSet_; - private boolean suppressInheritedMembers_; - - /** - * Sets the delay substitution of some elements. - *

- * Used in incremental builds of multimodule projects. - * - * @param delayTemplateSubstitution the delay - * @return this operation instance - */ - public DokkaOperation delayTemplateSubstitution(Boolean delayTemplateSubstitution) { - delayTemplateSubstitution_ = delayTemplateSubstitution; - return this; - } - - /** - * Part of the {@link #execute execute} operation, constructs the command list to use for building the process. - * - * @since 1.5 - */ - @Override - protected List executeConstructProcessCommandList() { - if (project_ == null) { - throw new IllegalArgumentException("A project must be specified."); - } - - final List args = new ArrayList<>(); - - // java - args.add(javaTool()); - - var cli = CompileKotlinOperation.getJarList(project_.libBldDirectory(), "^.*dokka-cli.*\\.jar$"); - - if (cli.size() != 1) { - throw new RuntimeException("The dokka-cli JAR could not be found."); - } - - // -jar dokka-cli - args.add("-jar"); - args.add(cli.get(0)); - - // -pluginClasspath - if (!pluginsClasspath_.isEmpty()) { - args.add("-pluginsClasspath"); - args.add(String.join(";", pluginsClasspath_)); - } - - // -sourceSet - var sourceSetArgs = sourceSet_.args(); - if (sourceSetArgs.isEmpty()) { - throw new IllegalArgumentException("At least one sourceSet is required."); - } else { - args.add("-sourceSet"); - args.add(String.join(" ", sourceSet_.args())); - } - - // -outputDir - if (outputDir_ != null) { - if (!outputDir_.exists() && !outputDir_.mkdirs()) { - throw new RuntimeException("Could not create: " + outputDir_.getAbsolutePath()); - } - - args.add("-outputDir"); - args.add(outputDir_.getAbsolutePath()); - } - - // -delayTemplateSubstitution - if (delayTemplateSubstitution_) { - args.add("-delayTemplateSubstitution"); - args.add(String.valueOf(delayTemplateSubstitution_)); - } - - // -failOnWarning - if (failOnWarning_) { - args.add("-failOnWarning"); - args.add(String.valueOf(failOnWarning_)); - } - - // -globalLinks_ - if (!globalLinks_.isEmpty()) { - args.add("-globalLinks"); - var links = new ArrayList(); - globalLinks_.forEach((k, v) -> - links.add(String.format("%s^%s", k, v))); - args.add(String.join("^^", links)); - } - - // -globalPackageOptions - if (!globalPackageOptions_.isEmpty()) { - args.add("-globalPackageOptions"); - args.add(String.join(";", globalPackageOptions_)); - } - - // -globalSrcLinks - if (!globalSrcLinks_.isEmpty()) { - args.add("-globalSrcLinks_"); - args.add(String.join(";", globalSrcLinks_)); - } - - // -includes - if (!includes_.isEmpty()) { - args.add("-includes"); - args.add(String.join(";", includes_)); - } - - // -loggingLevel - if (loggingLevel_ != null) { - args.add("-loggingLevel"); - args.add(loggingLevel_.name().toLowerCase()); - } - - // -moduleName - if (isNotBlank(moduleName_)) { - args.add("-moduleName"); - args.add(moduleName_); - } - - // -moduleVersion - if (isNotBlank(moduleVersion_)) { - args.add("-moduleVersion"); - args.add(moduleVersion_); - } - - // -noSuppressObviousFunctions - if (noSuppressObviousFunctions_) { - args.add("-noSuppressObviousFunctions"); - args.add(String.valueOf(noSuppressObviousFunctions_)); - } - - // -offlineMode - if (offlineMode_) { - args.add("-offlineMode"); - args.add(String.valueOf(offlineMode_)); - } - - // -pluginConfiguration - if (!pluginConfiguration_.isEmpty()) { - args.add("-pluginConfiguration"); - var confs = new ArrayList(); - pluginConfiguration_.forEach((k, v) -> - confs.add(String.format("{%s}={%s}", StringUtils.encodeJson(k), StringUtils.encodeJson(v)))); - args.add(String.join("^^", confs)); - } - - // -suppressInheritedMembers - if (suppressInheritedMembers_) { - args.add("-suppressInheritedMembers"); - args.add(String.valueOf(suppressInheritedMembers_)); - } - - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine(String.join(" ", args)); - } - - return args; - } - - /** - * Configures the operation from a {@link BaseProject}. - *

- * Sets the {@link #sourceSet sourceSet}, {@link SourceSet#jdkVersion jdkVersion} and {@link #moduleName moduleName} - * from the project. - * - * @param project the project to configure the operation from - */ - @Override - public DokkaOperation fromProject(BaseProject project) { - project_ = project; - sourceSet_ = new SourceSet().src(new File(project.srcMainDirectory(), "kotlin").getAbsolutePath()); - if (project.javaRelease() != null) { - sourceSet_ = sourceSet_.jdkVersion(project.javaRelease()); - } - moduleName_ = project.name(); - return this; - } - - /** - * Sets whether to fail documentation generation if Dokka has emitted a warning or an error. - *

- * Whether to fail documentation generation if Dokka has emitted a warning or an error. The process waits until all - * errors and warnings have been emitted first. - *

- * This setting works well with {@link SourceSet#reportUndocumented} - * - * @param failOnWarning {@code true} or {@code false} - * @return this operation instance - */ - public DokkaOperation failOnWarning(Boolean failOnWarning) { - failOnWarning_ = failOnWarning; - return this; - } - - /** - * Set the global external documentation links. - * - * @param url the external documentation URL - * @param packageListUrl the external documentation package list URL - * @return this operation instance - */ - public DokkaOperation globalLinks(String url, String packageListUrl) { - globalLinks_.put(url, packageListUrl); - return this; - } - - /** - * Set the global external documentation links. - * - * @param globalLinks the map of global links - * @return this operation instance - * @see #globalSrcLink(String...) #globalSrcLink(String...)#globalSrcLink(String...) - */ - public DokkaOperation globalLinks(Map globalLinks) { - globalLinks_.putAll(globalLinks); - return this; - } - - /** - * Sets the global list of package configurations. - *

- * Using format: - *

    - *
  • matchingRegexp
  • - *
  • -deprecated
  • - *
  • -privateApi
  • - *
  • +warnUndocumented
  • - *
  • +suppress
  • - *
  • +visibility:PUBLIC
  • - *
  • ...
  • - *
- * - * @param options ome pr more package configurations - * @return this operation instance - */ - public DokkaOperation globalPackageOptions(String... options) { - globalPackageOptions_.addAll(Arrays.asList(options)); - return this; - } - - /** - * Sets the global list of package configurations. - *

- * Using format: - *

    - *
  • matchingRegexp
  • - *
  • -deprecated
  • - *
  • -privateApi
  • - *
  • +warnUndocumented
  • - *
  • +suppress
  • - *
  • +visibility:PUBLIC
  • - *
  • ...
  • - *
- * - * @param options the list of package configurations - * @return this operation instance - */ - public DokkaOperation globalPackageOptions(Collection options) { - globalPackageOptions_.addAll(options); - return this; - } - - /** - * Sets the global mapping between a source directory and a Web service for browsing the code. - * - * @param links one or more links mapping - * @return this operation instance - */ - public DokkaOperation globalSrcLink(String... links) { - globalSrcLinks_.addAll(Arrays.asList(links)); - return this; - } - - /** - * Sets the global mapping between a source directory and a Web service for browsing the code. - * - * @param links the links mapping - * @return this operation instance - */ - public DokkaOperation globalSrcLink(Collection links) { - globalSrcLinks_.addAll(links); - return this; - } - - /** - * Sets the Markdown files that contain module and package documentation. - *

- * The contents of specified files are parsed and embedded into documentation as module and package descriptions. - *

- * This can be configured on per-package basis. - * - * @param files one or more files - * @return this operation instance - */ - public DokkaOperation includes(String... files) { - includes_.addAll(Arrays.asList(files)); - return this; - } - - /** - * Sets the Markdown files that contain module and package documentation. - *

- * The contents of specified files are parsed and embedded into documentation as module and package descriptions. - *

- * This can be configured on per-package basis. - * - * @param files the list of files - * @return this operation instance - */ - public DokkaOperation includes(Collection files) { - includes_.addAll(files); - return this; - } - - /** - * Sets the logging level. - * - * @param loggingLevel the logging level - * @return this operation instance - */ - public DokkaOperation loggingLevel(LoggingLevel loggingLevel) { - loggingLevel_ = loggingLevel; - return this; - } - - /** - * Sets the name of the project/module. Default is {@code root}. - *

- * The display name used to refer to the module. It is used for the table of contents, navigation, logging, etc. - * - * @param moduleName the project/module name - * @return this operation instance - */ - public DokkaOperation moduleName(String moduleName) { - moduleName_ = moduleName; - return this; - } - - /** - * Set the documented version. - * - * @param version the version - * @return this operation instance - */ - public DokkaOperation moduleVersion(String version) { - moduleVersion_ = version; - return this; - } - - /** - * Sets whether to suppress obvious functions such as inherited from - * kotlin.Any and {@link java.lang.Object}. - *

- * A function is considered to be obvious if it is: - *

    - *
  • Inherited from kotlin.Any, - * Kotlin.Enum, {@link java.lang.Object} - * or {@link java.lang.Enum}, such as {@code equals}, {@code hashCode}, {@code toString}. - *
  • Synthetic (generated by the compiler) and does not have any documentation, such as - * {@code dataClass.componentN} or {@code dataClass.copy}. - *
- * - * @param noSuppressObviousFunctions {@code true} or {@code false} - * @return this operation instance - */ - public DokkaOperation noSuppressObviousFunctions(Boolean noSuppressObviousFunctions) { - noSuppressObviousFunctions_ = noSuppressObviousFunctions; - return this; - } - - /** - * Sets whether to resolve remote files/links over network. - *

- * This includes package-lists used for generating external documentation links. For example, to make classes from - * the standard library clickable. - *

- * Setting this to true can significantly speed up build times in certain cases, but can also worsen documentation - * quality and user experience. For example, by not resolving class/member links from your dependencies, including - * the standard library. - *

- * Note: You can cache fetched files locally and provide them to Dokka as local paths. - * - * @param offlineMode the offline mode - * @return this operation instance - * @see SourceSet#externalDocumentationLinks(String, String) - */ - public DokkaOperation offlineMode(Boolean offlineMode) { - offlineMode_ = offlineMode; - return this; - } - - /** - * Sets the output directory path, {@code ./dokka} by default. - *

- * The directory to where documentation is generated, regardless of output format. - * - * @param outputDir the output directory - * @return this operation instance - */ - public DokkaOperation outputDir(File outputDir) { - outputDir_ = outputDir; - return this; - } - - /** - * Sets the output directory path, {@code ./dokka} by default. - * - * @param outputDir the output directory - * @return this operation instance - */ - public DokkaOperation outputDir(String outputDir) { - outputDir_ = new File(outputDir); - return this; - } - - /** - * Sets the Dokka {@link OutputFormat output format}. - * - * @param format The {@link OutputFormat output format} - * @return this operation instance - */ - public DokkaOperation outputFormat(OutputFormat format) { - pluginsClasspath_.clear(); - if (format.equals(OutputFormat.JAVADOC)) { - pluginsClasspath_.addAll(CompileKotlinOperation.getJarList(project_.libBldDirectory(), - JAVADOC_PLUGIN_REGEXP)); - } else if (format.equals(OutputFormat.HTML)) { - pluginsClasspath_.addAll(CompileKotlinOperation.getJarList(project_.libBldDirectory(), - HTML_PLUGIN_REGEXP)); - } else if (format.equals(OutputFormat.MARKDOWN)) { - pluginsClasspath_.addAll(CompileKotlinOperation.getJarList(project_.libBldDirectory(), - GFM_PLUGIN_REGEXP)); - } else if (format.equals(OutputFormat.JEKYLL)) { - pluginsClasspath_.addAll(CompileKotlinOperation.getJarList(project_.libBldDirectory(), - JEKYLL_PLUGIN_REGEXP)); - } - return this; - } - - /** - * Sets the configuration for Dokka plugins. - * - * @param name The fully-qualified plugin name - * @param jsonConfiguration The plugin JSON configuration - * @return this operation instance - */ - public DokkaOperation pluginConfiguration(String name, String jsonConfiguration) { - pluginConfiguration_.put(name, jsonConfiguration); - return this; - } - - /** - * Sets the configuration for Dokka plugins. - * - * @param pluginConfiguration the map of configurations - * @return this operation instance - * @see #pluginConfiguration(String, String) - */ - public DokkaOperation pluginConfiguration(Map pluginConfiguration) { - pluginConfiguration_.putAll(pluginConfiguration); - return this; - } - - /** - * Sets the list of jars with Dokka plugins and their dependencies. - * - * @param jars one or more jars - * @return this operation instance - */ - public DokkaOperation pluginsClasspath(String... jars) { - pluginsClasspath_.addAll(Arrays.asList(jars)); - return this; - } - - /** - * Sets the list of jars with Dokka plugins and their dependencies. - * - * @param jars the list of jars - * @return this operation instance - */ - public DokkaOperation pluginsClasspath(Collection jars) { - pluginsClasspath_.addAll(jars); - return this; - } - - /** - * Clears the list of Dokka plugins. - * - * @param clear set to clear the list - * @return this operation instance - */ - public DokkaOperation pluginsClasspath(boolean clear) { - if (clear) { - pluginsClasspath_.clear(); - } - return this; - } - - /** - * Sets the configurations for a source set. - *

- * Individual and additional configuration of Kotlin source sets. - * - * @param sourceSet the source set configurations - * @return this operation instance - */ - public DokkaOperation sourceSet(SourceSet sourceSet) { - sourceSet_ = sourceSet; - return this; - } - - /** - * Sets whether to suppress inherited members that aren't explicitly overridden in a given class. - * - * @param suppressInheritedMembers {@code true} or {@code false} - * @return this operation instance - */ - public DokkaOperation suppressInheritedMembers(Boolean suppressInheritedMembers) { - suppressInheritedMembers_ = suppressInheritedMembers; - return this; - } -} diff --git a/src/main/java/rife/bld/extension/dokka/LoggingLevel.java b/src/main/java/rife/bld/extension/dokka/LoggingLevel.java deleted file mode 100644 index b493e11..0000000 --- a/src/main/java/rife/bld/extension/dokka/LoggingLevel.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -/** - * Dokka logging levels. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum LoggingLevel { - DEBUG, PROGRESS, INFO, WARN, ERROR -} diff --git a/src/main/java/rife/bld/extension/dokka/OutputFormat.java b/src/main/java/rife/bld/extension/dokka/OutputFormat.java deleted file mode 100644 index 0d4a2c0..0000000 --- a/src/main/java/rife/bld/extension/dokka/OutputFormat.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -/** - * Dokka output formats. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum OutputFormat { - JAVADOC, JEKYLL, HTML, MARKDOWN -} diff --git a/src/main/java/rife/bld/extension/dokka/SourceSet.java b/src/main/java/rife/bld/extension/dokka/SourceSet.java deleted file mode 100644 index 4a2dfb9..0000000 --- a/src/main/java/rife/bld/extension/dokka/SourceSet.java +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Configuration for a Dokka source set. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public class SourceSet { - private static final String SEMICOLON = ";"; - private final Collection classpath_ = new ArrayList<>(); - private final Map dependentSourceSets_ = new ConcurrentHashMap<>(); - private final Collection documentedVisibilities_ = new ArrayList<>(); - private final Map externalDocumentationLinks_ = new ConcurrentHashMap<>(); - private final Collection includes_ = new ArrayList<>(); - private final Collection perPackageOptions_ = new ArrayList<>(); - private final Collection samples_ = new ArrayList<>(); - private final Map srcLinks_ = new ConcurrentHashMap<>(); - private final Collection src_ = new ArrayList<>(); - private final Collection suppressedFiles_ = new ArrayList<>(); - private AnalysisPlatform analysisPlatform_; - private String apiVersion_; - private String displayName_; - private int jdkVersion_; - private String languageVersion_; - private boolean noJdkLink_; - private boolean noSkipEmptyPackages_; - private boolean noStdlibLink_; - private boolean reportUndocumented_; - private boolean skipDeprecated_; - private String sourceSetName_; - - /** - * Sets the platform used for setting up analysis. Default is {@link AnalysisPlatform#JVM JVM} - *

- * Platform to be used for setting up code analysis and {@code @sample} environment. - * - * @param analysisPlatform the analysis platform - * @return this operation instance - */ - public SourceSet analysisPlatform(AnalysisPlatform analysisPlatform) { - analysisPlatform_ = analysisPlatform; - return this; - } - - /** - * Sets the Kotlin API version used for setting up analysis and samples. - * - * @param apiVersion the api version - * @return this operation instance - */ - public SourceSet apiVersion(String apiVersion) { - apiVersion_ = apiVersion; - return this; - } - - /** - * Returns the formatted arguments. - * - * @return the arguments - */ - public List args() { - var args = new ArrayList(); - - // -analysisPlatform - if (analysisPlatform_ != null) { - args.add("-analysisPlatform"); - args.add(analysisPlatform_.name().toLowerCase()); - } - - // -apiVersion - if (apiVersion_ != null) { - args.add("-apiVersion"); - args.add(apiVersion_); - } - - // -classpath - if (!classpath_.isEmpty()) { - args.add("-classpath"); - args.add(String.join(SEMICOLON, classpath_)); - } - - // -dependentSourceSets - if (!dependentSourceSets_.isEmpty()) { - args.add("-dependentSourceSets"); - var deps = new ArrayList(); - dependentSourceSets_.forEach((k, v) -> deps.add(String.format("%s/%s", k, v))); - args.add(String.join(SEMICOLON, deps)); - } - - // -displayName - if (displayName_ != null) { - args.add("-displayName"); - args.add(displayName_); - } - - // -documentedVisibilities - if (!documentedVisibilities_.isEmpty()) { - args.add("-documentedVisibilities"); - var vis = new ArrayList(); - documentedVisibilities_.forEach(d -> vis.add(d.name().toLowerCase())); - args.add(String.join(SEMICOLON, vis)); - } - - // -externalDocumentationLinks - if (!externalDocumentationLinks_.isEmpty()) { - args.add("-externalDocumentationLinks"); - var links = new ArrayList(); - externalDocumentationLinks_.forEach((k, v) -> links.add(String.format("%s^%s", k, v))); - args.add(String.join("^^", links)); - } - - // -jdkVersion - if (jdkVersion_ > 0) { - args.add("-jdkVersion"); - args.add(String.valueOf(jdkVersion_)); - } - - // -includes - if (!includes_.isEmpty()) { - args.add("-includes"); - args.add(String.join(SEMICOLON, includes_)); - } - - // -languageVersion - if (languageVersion_ != null) { - args.add("-languageVersion"); - args.add(languageVersion_); - } - - // -noJdkLink - if (noJdkLink_) { - args.add("-noJdkLink"); - args.add(String.valueOf(noJdkLink_)); - } - - // -noSkipEmptyPackages - if (noSkipEmptyPackages_) { - args.add("-noSkipEmptyPackages"); - args.add(String.valueOf(noSkipEmptyPackages_)); - } - - // -noStdlibLink - if (noStdlibLink_) { - args.add("-noStdlibLink"); - args.add(String.valueOf(noStdlibLink_)); - } - - // -reportUndocumented - if (reportUndocumented_) { - args.add("-reportUndocumented"); - args.add(String.valueOf(reportUndocumented_)); - } - - // -perPackageOptions - if (!perPackageOptions_.isEmpty()) { - args.add("-perPackageOptions"); - args.add(String.join(SEMICOLON, perPackageOptions_)); - } - - // -samples - if (!samples_.isEmpty()) { - args.add("-samples"); - args.add(String.join(SEMICOLON, samples_)); - } - - // -skipDeprecated - if (skipDeprecated_) { - args.add("-skipDeprecated"); - args.add(String.valueOf(skipDeprecated_)); - } - - // -src - if (!src_.isEmpty()) { - args.add("-src"); - args.add(String.join(SEMICOLON, src_)); - } - - // -srcLink - if (!srcLinks_.isEmpty()) { - args.add("-srcLink"); - var links = new ArrayList(); - srcLinks_.forEach((k, v) -> links.add(String.format("%s=%s", k, v))); - args.add(String.join(SEMICOLON, links)); - } - - // -sourceSetName - if (sourceSetName_ != null) { - args.add("-sourceSetName"); - args.add(sourceSetName_); - } - - // -suppressedFiles - if (!suppressedFiles_.isEmpty()) { - args.add("-suppressedFiles"); - args.add(String.join(SEMICOLON, suppressedFiles_)); - } - - return args; - } - - /** - * Sets classpath for analysis and interactive samples. - *

- * This is useful if some types that come from dependencies are not resolved/picked up automatically. - *

- * This option accepts both {@code .jar} and {@code .klib} files. - * - * @param classpath one or more classpath - * @return this operation instance - */ - public SourceSet classpath(String... classpath) { - classpath_.addAll(Arrays.asList(classpath)); - return this; - } - - /** - * Sets classpath for analysis and interactive samples. - *

- * This is useful if some types that come from dependencies are not resolved/picked up automatically. - *

- * This option accepts both {@code .jar} and {@code .klib} files. - * - * @param classpath the list of classpath - * @return this operation instance - */ - public SourceSet classpath(Collection classpath) { - classpath_.addAll(classpath); - return this; - } - - /** - * Sets the names of dependent source sets. - * - * @param moduleName the module name - * @param sourceSetName the source set name - * @return this operation instance - */ - public SourceSet dependentSourceSets(String moduleName, String sourceSetName) { - dependentSourceSets_.put(moduleName, sourceSetName); - return this; - } - - /** - * Sets the names of dependent source sets. - * - * @param dependentSourceSets the map of dependent source set names - * @return this operation instance - * @see #dependentSourceSets(String, String) - */ - public SourceSet dependentSourceSets(Map dependentSourceSets) { - dependentSourceSets_.putAll(dependentSourceSets); - return this; - } - - /** - * Sets the display name of the source set, used both internally and externally. - *

- * The name is used both externally (for example, the source set name is visible to documentation readers) and - * internally (for example, for logging messages of {@link #reportUndocumented reportUndocumented}). - *

- * The platform name can be used if you don't have a better alternative. - * - * @param displayName the display name - * @return this operation instance - */ - public SourceSet displayName(String displayName) { - displayName_ = displayName; - return this; - } - - /** - * Sets visibilities to be documented. - *

- * This can be used if you want to document protected/internal/private declarations, as well as if you want to - * exclude public declarations and only document internal API. - *

- * This can be configured on per-package basis. - * - * @param visibilities one or more visibilities - * @return this operation instance - */ - public SourceSet documentedVisibilities(DocumentedVisibility... visibilities) { - documentedVisibilities_.addAll(Arrays.asList(visibilities)); - return this; - } - - /** - * Sets the external documentation links. - *

- * A set of parameters for external documentation links that is applied only for this source set. - * - * @param url the external documentation URL - * @param packageListUrl the external documentation package list URL - * @return this operation instance - */ - public SourceSet externalDocumentationLinks(String url, String packageListUrl) { - externalDocumentationLinks_.put(url, packageListUrl); - return this; - } - - /** - * Sets the external documentation links. - *

- * A set of parameters for external documentation links that is applied only for this source set. - * - * @param externalDocumentationLinks the map of external documentation links - * @return this operation instance - * @see #externalDocumentationLinks(String, String) - */ - public SourceSet externalDocumentationLinks(Map externalDocumentationLinks) { - externalDocumentationLinks_.putAll(externalDocumentationLinks); - return this; - } - - /** - * Sets the Markdown files that contain module and package documentation. - *

- * A list of Markdown files that contain module and package documentation. - *

- * The contents of the specified files are parsed and embedded into documentation as module and package - * descriptions. - * - * @param files one or more files - * @return this operation instance - */ - public SourceSet includes(String... files) { - includes_.addAll(Arrays.asList(files)); - return this; - } - - /** - * Sets the Markdown files that contain module and package documentation. - *

- * A list of Markdown files that contain module and package documentation. - *

- * The contents of the specified files are parsed and embedded into documentation as module and package - * descriptions. - * - * @param files the list of files - * @return this operation instance - */ - public SourceSet includes(Collection files) { - includes_.addAll(files); - return this; - } - - /** - * Sets the version of JDK to use for linking to JDK Javadocs. - *

- * The JDK version to use when generating external documentation links for Java types. - *

- * For example, if you use {@link java.util.UUID} in some public declaration signature, and this option is set to 8, - * Dokka generates an external documentation link to JDK 8 Javadocs for it. - * - * @param jdkVersion the JDK version - * @return this operation instance - */ - public SourceSet jdkVersion(int jdkVersion) { - jdkVersion_ = jdkVersion; - return this; - } - - /** - * Sets the language version used for setting up analysis and samples. - * - * @param languageVersion the language version - * @return this operation instance - */ - public SourceSet languageVersion(String languageVersion) { - languageVersion_ = languageVersion; - return this; - } - - /** - * Sets whether to generate links to JDK Javadocs. - *

- * Whether to generate external documentation links to JDK's Javadocs. - *

- * The version of JDK Javadocs is determined by the {@link #jdkVersion jdkVersion} option. - *

- * Note: Links are generated when noJdkLink is set to false. - * - * @param noJdkLink {@code true} or {@code false} - * @return this operation instance - */ - public SourceSet noJdkLink(Boolean noJdkLink) { - noJdkLink_ = noJdkLink; - return this; - } - - /** - * Sets whether to create pages for empty packages. - *

- * Whether to skip packages that contain no visible declarations after various filters have been applied. - * - * @param noSkipEmptyPackages {@code true} or {@code false} - * @return this operation instance - */ - public SourceSet noSkipEmptyPackages(boolean noSkipEmptyPackages) { - noSkipEmptyPackages_ = noSkipEmptyPackages; - return this; - } - - /** - * Sets whether to generate links to Standard library. - *

- * Whether to generate external documentation links that lead to the API reference documentation of Kotlin's - * standard library. - *

- * Note: Links are generated when noStdLibLink is set to {@code false}. - * - * @param noStdlibLink {@code true} or {@code false} - * @return this operation instance - */ - public SourceSet noStdlibLink(Boolean noStdlibLink) { - noStdlibLink_ = noStdlibLink; - return this; - } - - /** - * Set the list of package source set configuration. - *

- * A set of parameters specific to matched packages within this source set. - *

- * Using format: - *

    - *
  • matchingRegexp
  • - *
  • -deprecated
  • - *
  • -privateApi
  • - *
  • +warnUndocumented
  • - *
  • +suppress
  • - *
  • +visibility:PUBLIC
  • - *
  • ...
  • - *
- * - * @param perPackageOptions the list of per package options - * @return this operation instance - */ - public SourceSet perPackageOptions(Collection perPackageOptions) { - perPackageOptions_.addAll(perPackageOptions); - return this; - } - - /** - * Set the list of package source set configuration. - *

- * A set of parameters specific to matched packages within this source set. - *

- * Using format: - *

    - *
  • matchingRegexp
  • - *
  • -deprecated
  • - *
  • -privateApi
  • - *
  • +warnUndocumented
  • - *
  • +suppress
  • - *
  • +visibility:PUBLIC
  • - *
  • ...
  • - *
- * - * @param perPackageOptions the list of per package options - * @return this operation instance - */ - public SourceSet perPackageOptions(String... perPackageOptions) { - perPackageOptions_.addAll(List.of(perPackageOptions)); - return this; - } - - /** - * Sets whether to report undocumented declarations. - *

- * Whether to emit warnings about visible undocumented declarations, that is declarations without KDocs after they - * have been filtered by documentedVisibilities and other filters. - *

- * This setting works well with {@link DokkaOperation#failOnWarning}. - *

- * This can be configured on per-package basis. - * - * @param reportUndocumented {@code true} or {@code false} - * @return this operation instance - */ - public SourceSet reportUndocumented(Boolean reportUndocumented) { - reportUndocumented_ = reportUndocumented; - return this; - } - - /** - * Set the list of directories or files that contain sample functions. - *

- * A list of directories or files that contain sample functions which are referenced via the {@code @sample} KDoc - * tag. - * - * @param samples the list of samples - * @return this operation instance - */ - public SourceSet samples(Collection samples) { - samples_.addAll(samples); - return this; - } - - /** - * Set the list of directories or files that contain sample functions. - *

- * A list of directories or files that contain sample functions which are referenced via the {@code @sample} KDoc - * tag. - * - * @param samples nne or more samples - * @return this operation instance - */ - public SourceSet samples(String... samples) { - samples_.addAll(List.of(samples)); - return this; - } - - /** - * Sets whether to skip deprecated declarations. - *

- * Whether to document declarations annotated with {@code @Deprecated}. - *

- * This can be configured on per-package basis. - * - * @param skipDeprecated {@code true} or {@code false} - * @return this operation instance - */ - public SourceSet skipDeprecated(boolean skipDeprecated) { - skipDeprecated_ = skipDeprecated; - return this; - } - - /** - * Sets the name of the source set. Default is {@code main}. - * - * @param sourceSetName the source set name. - * @return this operation instance - */ - public SourceSet sourceSetName(String sourceSetName) { - sourceSetName_ = sourceSetName; - return this; - } - - /** - * Sets the source code roots to be analyzed and documented. - *

- * The source code roots to be analyzed and documented. Acceptable inputs are directories and individual - * {@code .kt} / {@code .java} files. - * - * @param src the list of source code roots - * @return this operation instance - */ - public SourceSet src(Collection src) { - src_.addAll(src); - return this; - } - - /** - * Sets the source code roots to be analyzed and documented. - *

- * The source code roots to be analyzed and documented. Acceptable inputs are directories and individual - * {@code .kt} / {@code .java} files. - * - * @param src pne ore moe source code roots - * @return this operation instance - */ - public SourceSet src(String... src) { - src_.addAll(List.of(src)); - return this; - } - - /** - * Sets the mapping between a source directory and a Web service for browsing the code. - * - * @param srcPath the source path - * @param remotePath the remote path - * @param lineSuffix the line suffix - * @return this operation instance - */ - public SourceSet srcLink(String srcPath, String remotePath, String lineSuffix) { - srcLinks_.put(srcPath, remotePath + lineSuffix); - return this; - } - - /** - * Sets the paths to files to be suppressed. - *

- * The files to be suppressed when generating documentation. - * - * @param suppressedFiles the list of suppressed files - * @return this operation instance - */ - public SourceSet suppressedFiles(Collection suppressedFiles) { - suppressedFiles_.addAll(suppressedFiles); - return this; - } - - /** - * Sets the paths to files to be suppressed. - *

- * The files to be suppressed when generating documentation. - * - * @param suppressedFiles one or moe suppressed files - * @return this operation instance - */ - public SourceSet suppressedFiles(String... suppressedFiles) { - suppressedFiles_.addAll(Arrays.asList(suppressedFiles)); - return this; - } -} diff --git a/src/main/java/rife/bld/extension/kotlin/CompileOptions.java b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java new file mode 100644 index 0000000..e43adc8 --- /dev/null +++ b/src/main/java/rife/bld/extension/kotlin/CompileOptions.java @@ -0,0 +1,1087 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * 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 + * + * https://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 rife.bld.extension.kotlin; + +import rife.bld.extension.CompileKotlinOperation; +import rife.bld.operations.AbstractToolProviderOperation; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static rife.bld.extension.CompileKotlinOperation.isNotBlank; + +/** + * Configuration for the Kotlin compiler options. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class CompileOptions { + private static final Logger LOGGER = Logger.getLogger(CompileOptions.class.getName()); + private final Collection advancedOptions_ = new ArrayList<>(); + private final Collection argFile_ = new ArrayList<>(); + private final Collection classpath_ = new ArrayList<>(); + private final Collection optIn_ = new ArrayList<>(); + private final Collection options_ = new ArrayList<>(); + private final Collection plugin_ = new ArrayList<>(); + private final Collection scriptTemplates_ = new ArrayList<>(); + private String apiVersion_; + private String expression_; + private boolean includeRuntime_; + private boolean javaParameters_; + private File jdkHome_; + private String jdkRelease_; + private String jvmTarget_; + private File kotlinHome_; + private String languageVersion_; + private String moduleName_; + private boolean noJdk_; + private boolean noReflect_; + private boolean noStdLib_; + private boolean noWarn_; + private File path_; + private boolean progressive_; + private boolean verbose_; + private boolean wError_; + private boolean wExtra_; + + /** + * Specify advanced compiler options. + * + * @param options one or more advanced options + * @return this operation instance + */ + public CompileOptions advancedOptions(String... options) { + return advancedOptions(List.of(options)); + } + + /** + * Specify advanced compiler options. + * + * @param options the compiler options + * @return this operation instance + */ + public CompileOptions advancedOptions(Collection options) { + advancedOptions_.addAll(options); + return this; + } + + /** + * Retrieves advanced compiler options. + * + * @return the advanced compiler options + */ + public Collection advancedOptions() { + return advancedOptions_; + } + + /** + * Retrieves the version of Kotlin bundled libraries. + * + * @return the API version + */ + public String apiVersion() { + return apiVersion_; + } + + /** + * Allow using declarations only from the specified version of Kotlin bundled libraries. + * + * @param version the API version + * @return this operation instance + */ + public CompileOptions apiVersion(String version) { + apiVersion_ = version; + return this; + } + + /** + * Allow using declarations only from the specified version of Kotlin bundled libraries. + * + * @param version the API version + * @return this operation instance + */ + public CompileOptions apiVersion(int version) { + return apiVersion(String.valueOf(version)); + } + + /** + * Read the compiler options from the given files. + *

+ * Such a file can contain compiler options with values and paths to the source files. + * Options and paths should be separated by whitespaces. For example: + *

    + *
  • {@code -include-runtime -d hello.jar hello.kt}
  • + *
+ * To pass values that contain whitespaces, surround them with single ({@code '}) or double ({@code "}) quotes. + * If a value contains quotation marks in it, escape them with a backslash (\). + *
    + *
  • {@code -include-runtime -d 'My folder'}
  • + *
+ * If the files reside in locations different from the current directory, use relative paths. + * + * @param files one or more files + * @return this operation instance + * @see #argFileStrings(Collection) + */ + public CompileOptions argFile(String... files) { + return argFileStrings(List.of(files)); + } + + /** + * Read the compiler options from the given files. + * + * @param files the compiler options files + * @return this operation instance + * @see #argFile(File...) + */ + public CompileOptions argFile(Collection files) { + argFile_.addAll(files); + return this; + } + + /** + * Read the compiler options from the given files. + *

+ * Such a file can contain compiler options with values and paths to the source files. + * Options and paths should be separated by whitespaces. For example: + *

    + *
  • {@code -include-runtime -d hello.jar hello.kt}
  • + *
+ * To pass values that contain whitespaces, surround them with single ({@code '}) or double ({@code "}) quotes. + * If a value contains quotation marks in it, escape them with a backslash (\). + *
    + *
  • {@code -include-runtime -d 'My folder'}
  • + *
+ * If the files reside in locations different from the current directory, use relative paths. + * + * @param files one or more files + * @return this operation instance + * @see #argFile(Collection) + */ + public CompileOptions argFile(File... files) { + return argFile(List.of(files)); + } + + /** + * Read the compiler options from the given files. + *

+ * Such a file can contain compiler options with values and paths to the source files. + * Options and paths should be separated by whitespaces. For example: + *

    + *
  • {@code -include-runtime -d hello.jar hello.kt}
  • + *
+ * To pass values that contain whitespaces, surround them with single ({@code '}) or double ({@code "}) quotes. + * If a value contains quotation marks in it, escape them with a backslash (\). + *
    + *
  • {@code -include-runtime -d 'My folder'}
  • + *
+ * If the files reside in locations different from the current directory, use relative paths. + * + * @param files one or more files + * @return this operation instance + * @see #argFilePaths(Collection) + */ + public CompileOptions argFile(Path... files) { + return argFilePaths(List.of(files)); + } + + /** + * Retrieves the files containing compiler options. + * + * @return the compiler options files + */ + public Collection argFile() { + return argFile_; + } + + /** + * Read the compiler options from the given files. + * + * @param files the compiler options files + * @return this operation instance + * @see #argFile(Path...) + */ + public CompileOptions argFilePaths(Collection files) { + return argFile(files.stream().map(Path::toFile).toList()); + } + + /** + * Read the compiler options from the given files. + * + * @param files the compiler options files + * @return this operation instance + * @see #argFile(String...) + */ + public CompileOptions argFileStrings(Collection files) { + return argFile(files.stream().map(File::new).toList()); + } + + /** + * Returns the formatted arguments. + * + * @return the arguments + */ + public List args() { + var args = new ArrayList(); + + // api-version + if (isNotBlank(apiVersion_)) { + args.add("-api-version"); + args.add(apiVersion_); + } + + // @argfile + if (!argFile_.isEmpty()) { + argFile_.forEach(f -> { + if (f.exists()) { + try { + try (var reader = Files.newBufferedReader(f.toPath(), Charset.defaultCharset())) { + var tokenizer = new AbstractToolProviderOperation.CommandLineTokenizer(reader); + String token; + while ((token = tokenizer.nextToken()) != null) { + args.add(token); + } + } + } catch (IOException e) { + if (LOGGER.isLoggable(Level.WARNING)) { + LOGGER.log(Level.WARNING, "Could not read: " + f.getAbsolutePath(), e); + } + } + } else { + if (LOGGER.isLoggable(Level.WARNING)) { + LOGGER.warning("File not found: " + f.getAbsolutePath()); + } + } + }); + } + + // expression + if (isNotBlank(expression_)) { + args.add("-expression"); + args.add(expression_); + } + + // java-parameters + if (javaParameters_) { + args.add("-java-parameters"); + } + + // jvm-target + if (isNotBlank(jvmTarget_)) { + args.add("-jvm-target"); + args.add(jvmTarget_); + } + + // include-runtime + if (includeRuntime_) { + args.add("-include-runtime"); + } + + // jdk-home + if (jdkHome_ != null) { + args.add("-jdk-home"); + args.add(jdkHome_.getAbsolutePath()); + } + + // jdk-release + if (isNotBlank(jdkRelease_)) { + args.add("-Xjdk-release=" + jdkRelease_); + } + + // kotlin-home + if (kotlinHome_ != null) { + args.add("-kotlin-home"); + args.add(kotlinHome_.getAbsolutePath()); + } + + // language-version + if (isNotBlank(languageVersion_)) { + args.add("-language-version"); + args.add(languageVersion_); + } + + // module-name + if (isNotBlank(moduleName_)) { + args.add("-module-name"); + args.add(moduleName_); + } + + // no-jdk + if (noJdk_) { + args.add("-no-jdk"); + } + + // no-reflect + if (noReflect_) { + args.add("-no-reflect"); + } + + // no-std-lib + if (noStdLib_) { + args.add("-no-stdlib"); + } + + // no-warn + if (noWarn_) { + args.add("-nowarn"); + } + + // opt-in + optIn_.stream().filter(CompileKotlinOperation::isNotBlank).forEach(o -> { + args.add("-opt-in"); + args.add(o); + }); + + // options + if (!options_.isEmpty()) { + args.addAll(options_); + } + + // path + if (path_ != null) { + args.add("-d"); + args.add(path_.getAbsolutePath()); + } + + // plugin + plugin_.stream().filter(CompileKotlinOperation::isNotBlank).forEach(p -> { + args.add("-P"); + args.add("plugin:" + p); + }); + + // progressive + if (progressive_) { + args.add("-progressive"); + } + + // script-templates + if (!scriptTemplates_.isEmpty()) { + args.add("-script-templates"); + args.add(String.join(",", scriptTemplates_)); + } + + // verbose + if (verbose_) { + args.add("-verbose"); + } + + // Werror + if (wError_) { + args.add("-Werror"); + } + + // Wextra + if (wExtra_) { + args.add("-Wextra"); + } + + // advanced options (X) + if (!advancedOptions_.isEmpty()) { + advancedOptions_.forEach(it -> { + if (it.startsWith("-X")) { + args.add(it); + } else { + args.add("-X" + it); + } + }); + } + + return args; + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths one pr more paths + * @return this operation instance + * @see #classpathStrings(Collection) + */ + public CompileOptions classpath(String... paths) { + return classpathStrings(List.of(paths)); + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths one or more path + * @return this operation instance + * @see #classpath(Collection) + */ + public CompileOptions classpath(File... paths) { + return classpath(List.of(paths)); + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths one or more path + * @return this operation instance + * @see #classpathPaths(Collection) + */ + public CompileOptions classpath(Path... paths) { + return classpathPaths(List.of(paths)); + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths the search paths + * @return this operation instance + * @see #classpath(File...) + */ + public CompileOptions classpath(Collection paths) { + classpath_.addAll(paths); + return this; + } + + /** + * Retrieves the class files classpath. + * + * @return the class files classpath + */ + public Collection classpath() { + return classpath_; + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths one pr more paths + * @return this operation instance + * @see #classpath(Path...) + */ + public CompileOptions classpathPaths(Collection paths) { + return classpath(paths.stream().map(Path::toFile).toList()); + } + + /** + * Search for class files in the specified paths. + *

+ * The classpath can contain file and directory paths, ZIP, or JAR files. + * + * @param paths one pr more paths + * @return this operation instance + * @see #classpath(String...) + */ + public CompileOptions classpathStrings(Collection paths) { + return classpath(paths.stream().map(File::new).toList()); + } + + /** + * Retrieves the string to evaluate as a Kotlin script. + * + * @return the expression + */ + public String expression() { + return expression_; + } + + /** + * Evaluate the given string as a Kotlin script. + * + * @param expression the expression + * @return this operation instance + */ + public CompileOptions expression(String expression) { + expression_ = expression; + return this; + } + + /** + * Indicates whether the {@link #jdkRelease(String) jdkRelease} was set. + * + * @return {@code true} if the release was set; or {@code false} otherwise + */ + public boolean hasRelease() { + return jdkRelease_ != null; + } + + /** + * Include the Kotlin runtime into the resulting JAR file. Makes the resulting archive runnable on any Java-enabled + * environment. + * + * @param includeRuntime {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions includeRuntime(boolean includeRuntime) { + includeRuntime_ = includeRuntime; + return this; + } + + /** + * Indicates whether the {@link #includeRuntime(boolean)} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isIncludeRuntime() { + return includeRuntime_; + } + + /** + * Indicates whether {@link #javaParameters(boolean)} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isJavaParameters() { + return javaParameters_; + } + + /** + * Indicates whether {@link #noJdk(boolean) noJdk} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isNoJdk() { + return noJdk_; + } + + /** + * Indicates whether {@link #noReflect(boolean) noRflect} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isNoReflect() { + return noReflect_; + } + + /** + * Indicates whether {@link #noStdLib(boolean) noStdLib} +was set. + * + * @return {@code true} or {@code false} + */ + public boolean isNoStdLib() { + return noStdLib_; + } + + /** + * Indicates whether {@link #noWarn(boolean) noWarn} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isNoWarn() { + return noWarn_; + } + + /** + * Indicates whether {@link #progressive(boolean) progressive} was set. + * + * @return {@code true} or {@code false} + */ + public boolean isProgressive() { + return progressive_; + } + + /** + * Indicates whether {@link #verbose(boolean)} was set. + * + * @return {@code true} if verbose was set; or {@code false} otherwise + */ + public boolean isVerbose() { + return verbose_; + } + + /** + * Indicates whether warnings are turned into a compilation error. + * + * @return {@code true} or {@code false} + */ + public boolean isWError() { + return wError_; + } + + /** + * Indicates whether additional declaration, expression, and type compiler checks emit warnings. + * + * @return {@code true} or {@code false} + */ + public boolean isWExtra() { + return wExtra_; + } + + /** + * Generate metadata for Java 1.8 reflection on method parameters. + * + * @param javaParameters {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions javaParameters(boolean javaParameters) { + javaParameters_ = javaParameters; + return this; + } + + /** + * Use a custom JDK home directory to include into the classpath if it differs from the default {@code JAVA_HOME}. + * + * @param jdkHome the JDK home path + * @return this operation instance + */ + public CompileOptions jdkHome(File jdkHome) { + jdkHome_ = jdkHome; + return this; + } + + /** + * Use a custom JDK home directory to include into the classpath if it differs from the default {@code JAVA_HOME}. + * + * @param jdkHome the JDK home path + * @return this operation instance + */ + public CompileOptions jdkHome(String jdkHome) { + return jdkHome(new File(jdkHome)); + } + + /** + * Use a custom JDK home directory to include into the classpath if it differs from the default {@code JAVA_HOME}. + * + * @param jdkHome the JDK home path + * @return this operation instance + */ + public CompileOptions jdkHome(Path jdkHome) { + return jdkHome(jdkHome.toFile()); + } + + /** + * Retrieves the custom JDK home directory. + * + * @return the JDK home path. + */ + public File jdkHome() { + return jdkHome_; + } + + /** + * Return the specified JDK API version. + * + * @return the API version + */ + public String jdkRelease() { + return jdkRelease_; + } + + /** + * Compile against the specified JDK API version. + *

+ * Limit the API of the JDK in the classpath to the specified Java version. Automatically sets + * {@link #jvmTarget(String) JVM target} version. + *

+ * Possible values are 1.8, 9, 10, ..., 23. The default value is 1.8. + * + * @param version the target version + * @return this operation instance + */ + public CompileOptions jdkRelease(String version) { + jdkRelease_ = version; + return this; + } + + /** + * Compile against the specified JDK API version. + *

+ * Limit the API of the JDK in the classpath to the specified Java version. Automatically sets + * {@link #jvmTarget(String) JVM target} version. + * + * @param version the target version + * @return this operation instance + * @see #jdkRelease(String) + */ + public CompileOptions jdkRelease(int version) { + return jdkRelease(String.valueOf(version)); + } + + /** + * Specify the target version of the generated JVM bytecode. + * + * @param target the target version + * @return this operation instance + * @see #jvmTarget(String) + */ + public CompileOptions jvmTarget(int target) { + return jvmTarget(String.valueOf(target)); + } + + /** + * Specify the target version of the generated JVM bytecode. + *

+ * Possible values are 1.8, 9, 10, ..., 23. The default value is 1.8. + * + * @param target the target version + * @return this operation instance + */ + public CompileOptions jvmTarget(String target) { + jvmTarget_ = target; + return this; + } + + /** + * Retrieves the target version of the generated JVM bytecode. + * + * @return the target version + */ + public String jvmTarget() { + return jvmTarget_; + } + + /** + * Specify a custom path to the Kotlin compiler used for the discovery of runtime libraries. + * + * @param path the Kotlin home path + * @return this operation instance + */ + public CompileOptions kotlinHome(File path) { + kotlinHome_ = path; + return this; + } + + /** + * Retrieves the custom path of the Kotlin compiler. + * + * @return the Kotlin home path + */ + public File kotlinHome() { + return kotlinHome_; + } + + /** + * Specify a custom path to the Kotlin compiler used for the discovery of runtime libraries. + * + * @param path the Kotlin home path + * @return this operation instance + */ + public CompileOptions kotlinHome(Path path) { + return kotlinHome(path.toFile()); + } + + /** + * Specify a custom path to the Kotlin compiler used for the discovery of runtime libraries. + * + * @param path the Kotlin home path + * @return this operation instance + */ + public CompileOptions kotlinHome(String path) { + return kotlinHome(new File(path)); + } + + /** + * Provide source compatibility with the specified version of Kotlin. + * + * @param version the language version + * @return this operation instance + */ + public CompileOptions languageVersion(String version) { + languageVersion_ = version; + return this; + } + + /** + * Retrieves the {@link #languageVersion(String) language version}. + * + * @return the language version + */ + public String languageVersion() { + return languageVersion_; + } + + /** + * Set a custom name for the generated {@code .kotlin_module} file. + * + * @param name the module name + * @return this operation instance + */ + public CompileOptions moduleName(String name) { + moduleName_ = name; + return this; + } + + /** + * Retrieves the {@link #moduleName(String) module name}. + * + * @return the module name + */ + public String moduleName() { + return moduleName_; + } + + /** + * Don't automatically include the Java runtime into the classpath. + * + * @param noJdk {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions noJdk(boolean noJdk) { + noJdk_ = noJdk; + return this; + } + + /** + * Don't automatically include the Kotlin reflection ({@code kotlin-reflect.jar}) into the classpath. + * + * @param noReflect {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions noReflect(boolean noReflect) { + noReflect_ = noReflect; + return this; + } + + /** + * Don't automatically include the Kotlin/JVM stdlib ({@code kotlin-stdlib.jar}) and Kotlin reflection + * ({@code kotlin-reflect.jar}) into the classpath. + * + * @param noStdLib {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions noStdLib(boolean noStdLib) { + noStdLib_ = noStdLib; + return this; + } + + /** + * Suppress the compiler from displaying warnings during compilation. + * + * @param noWarn {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions noWarn(boolean noWarn) { + noWarn_ = noWarn; + return this; + } + + /** + * Enable usages of API that requires opt-in with a requirement annotation with the given fully qualified name. + * + * @param annotations one or more annotation names + * @return this operation instance + */ + public CompileOptions optIn(String... annotations) { + return optIn(List.of(annotations)); + } + + /** + * Retrieves the opt-in fully qualified names. + * + * @return the fully qualified names + */ + public Collection optIn() { + return optIn_; + } + + /** + * Enable usages of API that requires opt-in with a requirement annotation with the given fully qualified name. + * + * @param annotations the annotation names + * @return this operation instance + */ + public CompileOptions optIn(Collection annotations) { + optIn_.addAll(annotations); + return this; + } + + /** + * Specify additional compiler options. + * + * @param options one or more compiler options + * @return this operation instance + */ + public CompileOptions options(String... options) { + return options(List.of(options)); + } + + /** + * Retrieves additional compiler options. + * + * @return the compiler options + */ + public Collection options() { + return options_; + } + + /** + * Specify additional compiler options. + * + * @param options the compiler options + * @return this operation instance + */ + public CompileOptions options(Collection options) { + options_.addAll(options); + return this; + } + + /** + * Place the generated class files into the specified location. + *

+ * The location can be a directory, a ZIP, or a JAR file. + * + * @param path the location path + * @return this operation instance + */ + public CompileOptions path(File path) { + path_ = path; + return this; + } + + /** + * Retrieves the location to place generated class files into. + * + * @return the location path. + */ + public File path() { + return path_; + } + + /** + * Place the generated class files into the specified location. + *

+ * The location can be a directory, a ZIP, or a JAR file. + * + * @param path the location path + * @return this operation instance + */ + public CompileOptions path(Path path) { + return path(path.toFile()); + } + + /** + * Place the generated class files into the specified location. + *

+ * The location can be a directory, a ZIP, or a JAR file. + * + * @param path the location path + * @return this operation instance + */ + public CompileOptions path(String path) { + return path(new File(path)); + } + + /** + * Pass an option to a plugin. + * + * @param id the plugin ID + * @param optionName the plugin option name + * @param value the plugin option value + * @return this operation instance + */ + public CompileOptions plugin(String id, String optionName, String value) { + plugin_.add(id + ':' + optionName + ':' + value); + return this; + } + + /** + * Retrieves the plugin options. + * + * @return the plugin options. + */ + public Collection plugin() { + return plugin_; + } + + /** + * Allow using declarations only from the specified version of Kotlin bundled libraries. + * + * @param progressive {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions progressive(boolean progressive) { + progressive_ = progressive; + return this; + } + + /** + * Script definition template classes. + *

+ * Use fully qualified class names. + * + * @param classNames one or more class names + * @return this operation instance + */ + public CompileOptions scriptTemplates(String... classNames) { + return scriptTemplates(List.of(classNames)); + } + + /** + * Retrieves the script templates. + * + * @return the script templates. + */ + public Collection scriptTemplates() { + return scriptTemplates_; + } + + /** + * Script definition template classes. + *

+ * Use fully qualified class names. + * + * @param classNames the class names + * @return this operation instance + */ + public CompileOptions scriptTemplates(Collection classNames) { + scriptTemplates_.addAll(classNames); + return this; + } + + /** + * Enable verbose logging output which includes details of the compilation process. + * + * @param verbose {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions verbose(boolean verbose) { + verbose_ = verbose; + return this; + } + + /** + * Turn any warnings into a compilation error. + * + * @param wError {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions wError(boolean wError) { + wError_ = wError; + return this; + } + + /** + * Enable additional declaration, expression, and type compiler checks that emit warnings if {@code true}. + * + * @param wExtra {@code true} or {@code false} + * @return this operation instance + */ + public CompileOptions wExtra(boolean wExtra) { + wExtra_ = wExtra; + return this; + } +} diff --git a/src/main/java/rife/bld/extension/kotlin/CompilerPlugin.java b/src/main/java/rife/bld/extension/kotlin/CompilerPlugin.java new file mode 100644 index 0000000..f1fd584 --- /dev/null +++ b/src/main/java/rife/bld/extension/kotlin/CompilerPlugin.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * 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 + * + * https://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 rife.bld.extension.kotlin; + +/** + * @author Erik C. Thauvin + * Defines the known Kotlin compiler plugin JARs. + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public enum CompilerPlugin { + ALL_OPEN("allopen-compiler-plugin.jar"), + ASSIGNMENT("assignment-compiler-plugin.jar"), + COMPOSE("compose-compiler-plugin.jar"), + KOTLIN_IMPORTS_DUMPER("kotlin-imports-dumper-compiler-plugin.jar"), + KOTLINX_SERIALIZATION("kotlinx-serialization-compiler-plugin.jar"), + KOTLIN_SERIALIZATION("kotlin-serialization-compiler-plugin.jar"), + LOMBOK("lombok-compiler-plugin.jar"), + NOARG("noarg-compiler-plugin.jar"), + POWER_ASSERT("power-assert-compiler-plugin.jar"), + SAM_WITH_RECEIVER("sam-with-receiver-compiler-plugin.jar"); + + public final String jar; + + CompilerPlugin(String jar) { + this.jar = jar; + } +} diff --git a/src/main/java/rife/bld/extension/kotlin/JvmOptions.java b/src/main/java/rife/bld/extension/kotlin/JvmOptions.java new file mode 100644 index 0000000..7e2ec62 --- /dev/null +++ b/src/main/java/rife/bld/extension/kotlin/JvmOptions.java @@ -0,0 +1,90 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * 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 + * + * https://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 rife.bld.extension.kotlin; + +import rife.tools.StringUtils; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Java Virtual Machine options. + * + * @author Erik C. Thauvin + * @since 1.1.0 + */ +@SuppressWarnings("PMD.LooseCoupling") +public class JvmOptions extends ArrayList { + /** + * Keyword to enable native access for all code on the class path. + */ + public final static String ALL_UNNAMED = "ALL-UNNAMED"; + + @Serial + private static final long serialVersionUID = 1L; + + /** + * Modules that are permitted to perform restricted native operations. + * The module name can also be {@link #ALL_UNNAMED}. + * + * @param modules the module names + * @return this list of options + */ + public JvmOptions enableNativeAccess(String... modules) { + return enableNativeAccess(List.of(modules)); + } + + /** + * Modules that are permitted to perform restricted native operations. + * The module name can also be {@link #ALL_UNNAMED}. + * + * @param modules the module names + * @return this list of options + */ + public JvmOptions enableNativeAccess(Collection modules) { + add("--enable-native-access=" + StringUtils.join(modules, ",")); + return this; + } + + /** + * Controls what action the Java runtime takes when native access is not enabled for a module. + * + * @param access the access mode + * @return this list of options + */ + public JvmOptions illegalNativeAccess(NativeAccess access) { + add("--illegal-native-access=" + access.mode); + return this; + } + + /** + * Illegal native access modes. + */ + public enum NativeAccess { + ALLOW("allow"), + DENY("deny"), + WARN("warn"); + + public final String mode; + + NativeAccess(String mode) { + this.mode = mode; + } + } +} diff --git a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java index f64e6d3..4e0182e 100644 --- a/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java +++ b/src/test/java/rife/bld/extension/CompileKotlinOperationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,22 @@ package rife.bld.extension; +import org.assertj.core.api.AutoCloseableSoftAssertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import rife.bld.BaseProject; import rife.bld.blueprints.BaseProjectBlueprint; +import rife.bld.extension.kotlin.CompileOptions; +import rife.bld.extension.kotlin.CompilerPlugin; +import rife.bld.extension.kotlin.JvmOptions; 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.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.logging.ConsoleHandler; import java.util.logging.Level; @@ -34,7 +39,11 @@ import java.util.logging.Logger; import static org.assertj.core.api.Assertions.assertThat; +@SuppressWarnings("PMD.AvoidDuplicateLiterals") class CompileKotlinOperationTest { + private static final String FILE_1 = "file1"; + private static final String FILE_2 = "file2"; + @BeforeAll static void beforeAll() { var level = Level.ALL; @@ -47,7 +56,98 @@ class CompileKotlinOperationTest { } @Test - void testExecute() throws IOException { + void testBuildMainDirectory() { + var foo = new File("foo"); + var bar = new File("bar"); + + var op = new CompileKotlinOperation().buildMainDirectory(foo); + assertThat(op.buildMainDirectory()).as("as file").isEqualTo(foo); + + op = op.buildMainDirectory(bar.toPath()); + assertThat(op.buildMainDirectory()).as("as path").isEqualTo(bar); + + op = new CompileKotlinOperation().buildMainDirectory("foo"); + assertThat(op.buildMainDirectory()).as("as string").isEqualTo(foo); + } + + @Test + void testBuildTestDirectory() { + var foo = new File("foo"); + var bar = new File("bar"); + + var op = new CompileKotlinOperation().buildTestDirectory(foo); + assertThat(op.buildTestDirectory()).as("as file").isEqualTo(foo); + + op = op.buildTestDirectory(bar.toPath()); + assertThat(op.buildTestDirectory()).as("as path").isEqualTo(bar); + + op = new CompileKotlinOperation().buildTestDirectory("foo"); + assertThat(op.buildTestDirectory()).as("as string").isEqualTo(foo); + } + + @Test + void testCollections() { + var op = new CompileKotlinOperation() + .fromProject(new BaseProjectBlueprint(new File("examples"), "com.example", "Example", "Example")) + .kotlinHome("/kotlin_home") + .kotlinc("kotlinc") + .workDir("work_dir") + .compileMainClasspath("path1", "path2") + .compileOptions(new CompileOptions().jdkRelease("17").verbose(true)) + .mainSourceDirectories("dir1", "dir2") + .mainSourceDirectories(List.of(new File("dir3"), new File("dir4"))) + .mainSourceFiles("file1", "file2") + .mainSourceFiles(List.of(new File("file3"), new File("file4"))) + .mainSourceFiles(new File("file5"), new File("file6")) + .testSourceDirectories("tdir1", "tdir2") + .testSourceDirectories(List.of(new File("tdir3"), new File("tdir4"))) + .testSourceFiles("tfile1", "tfile2") + .testSourceFiles(List.of(new File("tfile3"), new File("tfile4"))) + .testSourceFiles(new File("tfile5"), new File("tfile6")) + .plugins("plugin1", "plugin2") + .plugins(CompilerPlugin.KOTLIN_SERIALIZATION, CompilerPlugin.ASSIGNMENT, CompilerPlugin.COMPOSE) + .plugins(new File("lib/compile"), CompilerPlugin.LOMBOK, CompilerPlugin.POWER_ASSERT) + .plugins(Path.of("lib/compile"), CompilerPlugin.NOARG, CompilerPlugin.ALL_OPEN, + CompilerPlugin.KOTLIN_IMPORTS_DUMPER) + .plugins("lib/compile", CompilerPlugin.KOTLINX_SERIALIZATION, CompilerPlugin.SAM_WITH_RECEIVER) + .plugins(List.of("plugin3", "plugin4")); + + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(op.kotlinHome().getName()).as("kotlin_home").isEqualTo("kotlin_home"); + softly.assertThat(op.kotlinc().getName()).as("kotlinc").isEqualTo("kotlinc"); + softly.assertThat(op.workDir().getName()).as("work_dir").isEqualTo("work_dir"); + softly.assertThat(op.compileMainClasspath()).as("compileMainClassPath") + .containsAll(List.of("path1", "path2")); + softly.assertThat(op.compileOptions().hasRelease()).as("hasRelease").isTrue(); + softly.assertThat(op.compileOptions().isVerbose()).as("isVerbose").isTrue(); + softly.assertThat(op.mainSourceDirectories()).as("mainSourceDirectories").containsExactly( + Path.of("examples", "src", "main", "kotlin").toFile(), new File("dir1"), + new File("dir2"), new File("dir3"), new File("dir4")); + softly.assertThat(op.testSourceDirectories()).as("testSourceDirectories").containsOnly( + Path.of("examples", "src", "test", "kotlin").toFile(), new File("tdir1"), + new File("tdir2"), new File("tdir3"), new File("tdir4")); + softly.assertThat(op.mainSourceFiles()).as("mainSourceFiles").containsOnly( + new File("file1"), new File("file2"), new File("file3"), + new File("file4"), new File("file5"), new File("file6")); + softly.assertThat(op.testSourceFiles()).as("testSourceFiles").containsOnly( + new File("tfile1"), new File("tfile2"), new File("tfile3"), + new File("tfile4"), new File("tfile5"), new File("tfile6")); + softly.assertThat(op.plugins()).as("plugins").contains("plugin1", "plugin2", "plugin3", "plugin4", + new File("/kotlin_home/lib/kotlin-serialization-compiler-plugin.jar").getAbsolutePath(), + new File("/kotlin_home/lib/assignment-compiler-plugin.jar").getAbsolutePath(), + new File("/kotlin_home/lib/compose-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "lombok-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "power-assert-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "noarg-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "allopen-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "kotlin-imports-dumper-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "kotlinx-serialization-compiler-plugin.jar").getAbsolutePath(), + new File("lib/compile", "sam-with-receiver-compiler-plugin.jar").getAbsolutePath()); + } + } + + @Test + void testExecute() throws Exception { var tmpDir = Files.createTempDirectory("bld-kotlin").toFile(); try { @@ -55,8 +155,10 @@ class CompileKotlinOperationTest { var mainDir = new File(buildDir, "main"); var testDir = new File(buildDir, "test"); - assertThat(mainDir.mkdirs()).isTrue(); - assertThat(testDir.mkdirs()).isTrue(); + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(mainDir.mkdirs()).as("make mainDir").isTrue(); + softly.assertThat(testDir.mkdirs()).as("make testDir").isTrue(); + } var compileJars = new ArrayList(); for (var f : Objects.requireNonNull(new File("examples/lib/compile").listFiles())) { @@ -69,8 +171,7 @@ class CompileKotlinOperationTest { } var op = new CompileKotlinOperation() - .fromProject(new BaseProjectBlueprint(new File("examples"), "com.example", - "Example")) + .fromProject(new BaseProjectBlueprint(new File("examples"), "com.example", "Example", "Example")) .buildMainDirectory(mainDir) .buildTestDirectory(testDir) .compileMainClasspath(compileJars) @@ -79,26 +180,268 @@ class CompileKotlinOperationTest { .compileTestClasspath(mainDir.getAbsolutePath()); op.compileOptions().verbose(true); - op.compileOptions().jdkRelease("17"); + op.compileOptions().argFile("src/test/resources/argfile.txt", "src/test/resources/argfile2.txt"); + + 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", "-no-stdlib", "-verbose"); - assertThat(args).isEqualTo(matches); + var matches = List.of("-Xjdk-release=17", "-no-reflect", "-progressive", "-include-runtime", "-no-stdlib", + "-verbose"); + assertThat(args).as(args + " == " + matches).isEqualTo(matches); op.execute(); - assertThat(tmpDir).isNotEmptyDirectory(); - assertThat(mainDir).isNotEmptyDirectory(); - assertThat(testDir).isNotEmptyDirectory(); + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(tmpDir).as("tmpDir").isNotEmptyDirectory(); + softly.assertThat(mainDir).as("mainDir").isNotEmptyDirectory(); + softly.assertThat(testDir).as("testDir").isNotEmptyDirectory(); + } var mainOut = Path.of(mainDir.getAbsolutePath(), "com", "example").toFile(); - assertThat(new File(mainOut, "Example.class")).exists(); - assertThat(new File(mainOut, "Example$Companion.class")).exists(); + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(new File(mainOut, "Example.class")).as("Example.class").exists(); + softly.assertThat(new File(mainOut, "Example$Companion.class")) + .as("ExampleCompanion.class").exists(); + } var testOut = Path.of(testDir.getAbsolutePath(), "com", "example").toFile(); - assertThat(new File(testOut, "ExampleTest.class")).exists(); + assertThat(new File(testOut, "ExampleTest.class")).as("ExampleTest.class").exists(); } finally { FileUtils.deleteDirectory(tmpDir); } } + + @Test + void testFindKotlincPath() { + assertThat(CompileKotlinOperation.findKotlincPath()).doesNotStartWith("kotlinc"); + } + + @Test + void testFromProject() { + var examples = new File("examples"); + var op = new CompileKotlinOperation().fromProject( + new BaseProjectBlueprint(examples, "com.example", "examples", "examples")); + assertThat(op.mainSourceDirectories()).containsExactly(new File(examples, "src/main/kotlin")); + assertThat(op.testSourceDirectories()).containsExactly(new File(examples, "src/test/kotlin")); + } + + @Test + void testFromProjectNoKotlin() { + var op = new CompileKotlinOperation().fromProject( + new BaseProjectBlueprint(new File("foo"), "org.example", "foo", "foo")); + assertThat(op.mainSourceDirectories()).isEmpty(); + assertThat(op.testSourceDirectories()).isEmpty(); + } + + @Test + void testIsOS() { + var osName = System.getProperty("os.name"); + if (osName != null) { + var os = osName.toLowerCase(Locale.US); + if (os.contains("win")) { + assertThat(CompileKotlinOperation.isWindows()).isTrue(); + } else if (os.contains("linux") || os.contains("unix")) { + assertThat(CompileKotlinOperation.isLinux()).isTrue(); + } else if (os.contains("mac") || os.contains("darwin")) { + assertThat(CompileKotlinOperation.isMacOS()).isTrue(); + } + } + } + + @Test + void testKotlinHome() { + var foo = new File("foo"); + var bar = new File("bar"); + + var op = new CompileKotlinOperation().kotlinHome(foo); + assertThat(op.kotlinHome()).as("as file").isEqualTo(foo); + + op = op.kotlinHome(bar.toPath()); + assertThat(op.kotlinHome()).as("as path").isEqualTo(bar); + + op = new CompileKotlinOperation().kotlinHome("foo"); + assertThat(op.kotlinHome()).as("as string").isEqualTo(foo); + } + + @Test + void testKotlinc() { + var foo = new File("foo"); + var bar = new File("bar"); + + var op = new CompileKotlinOperation().kotlinc(foo); + assertThat(op.kotlinc()).as("as file").isEqualTo(foo); + + op = op.kotlinc(bar.toPath()); + assertThat(op.kotlinc()).as("as path").isEqualTo(bar); + + op = new CompileKotlinOperation().kotlinc("foo"); + assertThat(op.kotlinc()).as("as string").isEqualTo(foo); + } + + @Test + void testMainSourceDirectories() { + var op = new CompileKotlinOperation(); + + op.mainSourceDirectories(List.of(new File(FILE_1), new File(FILE_2))); + assertThat(op.mainSourceDirectories()).as("List(File...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + + op.mainSourceDirectories(new File(FILE_1), new File(FILE_2)); + assertThat(op.mainSourceDirectories()).as("File...").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + + op.mainSourceDirectories(FILE_1, FILE_2); + assertThat(op.mainSourceDirectories()).as("String...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + + op = op.mainSourceDirectories(Path.of(FILE_1), Path.of(FILE_2)); + assertThat(op.mainSourceDirectories()).as("Path...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + + op.mainSourceDirectoriesPaths(List.of(new File(FILE_1).toPath(), new File(FILE_2).toPath())); + assertThat(op.mainSourceDirectories()).as("List(Path...)") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + + op.mainSourceDirectoriesStrings(List.of(FILE_1, FILE_2)); + assertThat(op.mainSourceDirectories()).as("List(String...)") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceDirectories().clear(); + } + + @Test + void testMainSourceFiles() { + var op = new CompileKotlinOperation(); + + op.mainSourceFiles(List.of(new File(FILE_1), new File(FILE_2))); + assertThat(op.mainSourceFiles()).as("List(File...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + + op.mainSourceFiles(new File(FILE_1), new File(FILE_2)); + assertThat(op.mainSourceFiles()).as("File...").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + + op.mainSourceFiles(FILE_1, FILE_2); + assertThat(op.mainSourceFiles()).as("String...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + + op = op.mainSourceFiles(Path.of(FILE_1), Path.of(FILE_2)); + assertThat(op.mainSourceFiles()).as("Path...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + + op.mainSourceFilesPaths(List.of(new File(FILE_1).toPath(), new File(FILE_2).toPath())); + assertThat(op.mainSourceFiles()).as("List(Path...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + + op.mainSourceFilesStrings(List.of(FILE_1, FILE_2)); + assertThat(op.mainSourceFiles()).as("List(String...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.mainSourceFiles().clear(); + } + + @Test + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + void testPlugins() { + var op = new CompileKotlinOperation() + .fromProject(new BaseProject()) + .plugins(CompilerPlugin.ALL_OPEN, + CompilerPlugin.ASSIGNMENT, + CompilerPlugin.COMPOSE, + CompilerPlugin.KOTLIN_IMPORTS_DUMPER, + CompilerPlugin.KOTLINX_SERIALIZATION, + CompilerPlugin.KOTLIN_SERIALIZATION, + CompilerPlugin.LOMBOK, + CompilerPlugin.NOARG, + CompilerPlugin.POWER_ASSERT, + CompilerPlugin.SAM_WITH_RECEIVER); + + try (var softly = new AutoCloseableSoftAssertions()) { + for (var p : op.plugins()) { + softly.assertThat(new File(p)).as(p).exists(); + } + } + } + + @Test + void testTestSourceDirectories() { + var op = new CompileKotlinOperation(); + + op.testSourceDirectories(List.of(new File(FILE_1), new File(FILE_2))); + assertThat(op.testSourceDirectories()).as("List(File...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + + op.testSourceDirectories(new File(FILE_1), new File(FILE_2)); + assertThat(op.testSourceDirectories()).as("File...").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + + op.testSourceDirectories(FILE_1, FILE_2); + assertThat(op.testSourceDirectories()).as("String...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + + op = op.testSourceDirectories(Path.of(FILE_1), Path.of(FILE_2)); + assertThat(op.testSourceDirectories()).as("Path...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + + op.testSourceDirectoriesPaths(List.of(new File(FILE_1).toPath(), new File(FILE_2).toPath())); + assertThat(op.testSourceDirectories()).as("List(Path...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + + op.testSourceDirectoriesStrings(List.of(FILE_1, FILE_2)); + assertThat(op.testSourceDirectories()).as("List(String...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceDirectories().clear(); + } + + @Test + void testTestSourceFiles() { + var op = new CompileKotlinOperation(); + + op.testSourceFiles(List.of(new File(FILE_1), new File(FILE_2))); + assertThat(op.testSourceFiles()).as("List(File...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + + op.testSourceFiles(new File(FILE_1), new File(FILE_2)); + assertThat(op.testSourceFiles()).as("File...").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + + op.testSourceFiles(FILE_1, FILE_2); + assertThat(op.testSourceFiles()).as("String...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + + op = op.testSourceFiles(Path.of(FILE_1), Path.of(FILE_2)); + assertThat(op.testSourceFiles()).as("Path...") + .containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + + op.testSourceFilesPaths(List.of(new File(FILE_1).toPath(), new File(FILE_2).toPath())); + assertThat(op.testSourceFiles()).as("List(Path...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + + op.testSourceFilesStrings(List.of(FILE_1, FILE_2)); + assertThat(op.testSourceFiles()).as("List(String...)").containsExactly(new File(FILE_1), new File(FILE_2)); + op.testSourceFiles().clear(); + } + + @Test + void testWorkDir() { + var foo = new File("foo"); + var bar = new File("bar"); + + var op = new CompileKotlinOperation().workDir(foo); + assertThat(op.workDir()).as("as file").isEqualTo(foo); + + op = op.workDir(bar.toPath()); + assertThat(op.workDir()).as("as path").isEqualTo(bar); + + op = new CompileKotlinOperation().workDir("foo"); + assertThat(op.workDir()).as("as string").isEqualTo(foo); + } } diff --git a/src/test/java/rife/bld/extension/CompileKotlinOptionsTest.java b/src/test/java/rife/bld/extension/CompileKotlinOptionsTest.java deleted file mode 100644 index f19663d..0000000 --- a/src/test/java/rife/bld/extension/CompileKotlinOptionsTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension; - -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.util.List; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; - -@SuppressWarnings("PMD.AvoidDuplicateLiterals") -class CompileKotlinOptionsTest { - @Test - void argsCollectionTest() { - var args = new CompileKotlinOptions() - .argFile(List.of("arg1.txt", "arg2.txt")) - .classpath(List.of("path1", "path2")) - .noStdLib(false) - .optIn(List.of("opt1", "opt2")) - .options(List.of("-foo", "-bar")) - .scriptTemplates(List.of("temp1", "temp2")) - .args(); - var matches = List.of( - "@arg1.txt", "@arg2.txt", - "-classpath", "path1:path2", - "-opt-in", "opt1", - "-opt-in", "opt2", - "-foo", - "-bar", - "-script-templates", "temp1,temp2"); - - assertThat(args).hasSize(matches.size()); - - IntStream.range(0, args.size()).forEach(i -> assertThat(args.get(i)).isEqualTo(matches.get(i))); - - - } - - @Test - void argsTest() { - var args = new CompileKotlinOptions() - .apiVersion("11") - .argFile("file.txt", "file2.txt") - .classpath("path1", "path2") - .javaParameters(true) - .jvmTarget("11") - .includeRuntime(true) - .jdkHome("path") - .jdkRelease("11") - .kotlinHome("path") - .languageVersion("1.0") - .moduleName("module") - .noJdk(true) - .noReflect(true) - .noWarn(true) - .optIn("opt1", "opt2") - .options("-foo", "-bar") - .path("path") - .plugin("id", "name", "value") - .progressive(true) - .scriptTemplates("name", "name2") - .verbose(true) - .wError(true) - .args(); - - var matches = List.of( - "-api-version", "11", - "@file.txt", "@file2.txt", - "-classpath", "path1" + File.pathSeparator + "path2", - "-java-parameters", - "-jvm-target", "11", - "-include-runtime", - "-jdk-home", "path", - "-Xjdk-release=11", - "-kotlin-home", "path", - "-language-version", "1.0", - "-module-name", "module", - "-no-jdk", - "-no-reflect", - "-no-warn", - "-opt-in", "opt1", - "-opt-in", "opt2", - "-foo", - "-bar", - "-d", "path", - "-P", "plugin:id:name:value", - "-progressive", - "-script-templates", "name,name2", - "-verbose", - "-Werror"); - - assertThat(args).hasSize(matches.size()); - - IntStream.range(0, args.size()).forEach(i -> assertThat(args.get(i)).isEqualTo(matches.get(i))); - } -} diff --git a/src/test/java/rife/bld/extension/dokka/DokkaOperationTest.java b/src/test/java/rife/bld/extension/dokka/DokkaOperationTest.java deleted file mode 100644 index 637b1f2..0000000 --- a/src/test/java/rife/bld/extension/dokka/DokkaOperationTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -import org.junit.jupiter.api.Test; -import rife.bld.blueprints.BaseProjectBlueprint; - -import java.io.File; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; - -class DokkaOperationTest { - @Test - @SuppressWarnings({"ExtractMethodRecommender", "PMD.AvoidDuplicateLiterals"}) - void executeConstructProcessCommandListTest() { - var examples = new File("examples"); - var args = new DokkaOperation() - .fromProject(new BaseProjectBlueprint(examples, "com.example", "Example")) - .globalLinks("s", "link") - .globalLinks(Map.of("s2", "link2")) - .globalPackageOptions("option1", "option2") - .globalPackageOptions(List.of("option3", "option4")) - .globalSrcLink("link1", "link2") - .globalSrcLink(List.of("link3", "link4")) - .includes("file1", "file2") - .pluginConfiguration("name", "\"json\"") - .pluginConfiguration(Map.of("\"name2\"", "json2")) - .pluginsClasspath("path1", "path2") - .pluginsClasspath(List.of("path3", "path4")) - .delayTemplateSubstitution(true) - .failOnWarning(true) - .loggingLevel(LoggingLevel.DEBUG) - .moduleName("name") - .moduleVersion("1.0") - .noSuppressObviousFunctions(true) - .offlineMode(true) - .outputDir(new File(examples, "build")) - .outputFormat(OutputFormat.JAVADOC) - .suppressInheritedMembers(true) - .executeConstructProcessCommandList(); - - var path = examples.getAbsolutePath(); - var matches = List.of("java", - "-jar", path + "/lib/bld/dokka-cli-1.9.10.jar", - "-pluginsClasspath", path + "/lib/bld/dokka-base-1.9.10.jar;" + - path + "/lib/bld/analysis-kotlin-descriptors-1.9.10.jar;" + - path + "/lib/bld/javadoc-plugin-1.9.10.jar;" + - path + "/lib/bld/korte-jvm-2.7.0.jar;" + - path + "/lib/bld/kotlin-as-java-plugin-1.9.10.jar;path1;path2;path3;path4", - "-sourceSet", "-src " + path + "/src/main/kotlin", - "-outputDir", path + "/build", - "-delayTemplateSubstitution", "true", - "-failOnWarning", "true", - "-globalLinks", "s^link^^s2^link2", - "-globalPackageOptions", "option1;option2;option3;option4", - "-globalSrcLinks_", "link1;link2;link3;link4", - "-includes", "file1;file2", - "-loggingLevel", "debug", - "-moduleName", "name", - "-moduleVersion", "1.0", - "-noSuppressObviousFunctions", "true", - "-offlineMode", "true", - "-pluginConfiguration", "{name}={\\\"json\\\"}^^{\\\"name2\\\"}={json2}", - "-suppressInheritedMembers", "true"); - - assertThat(args).hasSize(matches.size()); - - IntStream.range(0, args.size()).forEach(i -> { - if (args.get(i).contains(".jar;")) { - var jars = args.get(i).split(";"); - Arrays.stream(jars).forEach(jar -> assertThat(matches.get(i)).contains(jar)); - } else { - assertThat(args.get(i)).isEqualTo(matches.get(i)); - } - }); - } -} diff --git a/src/test/java/rife/bld/extension/dokka/SourceSetTest.java b/src/test/java/rife/bld/extension/dokka/SourceSetTest.java deleted file mode 100644 index 4edefe0..0000000 --- a/src/test/java/rife/bld/extension/dokka/SourceSetTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * 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 - * - * https://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 rife.bld.extension.dokka; - -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; - -class SourceSetTest { - @Test - void sourceSetCollectionsTest() { - var args = new SourceSet() - .classpath(List.of("path1", "path2")) - .dependentSourceSets(Map.of("set1", "set2", "set3", "set4")) - .externalDocumentationLinks(Map.of("link1", "link2", "link3", "link4")) - .perPackageOptions(List.of("option1", "option2")) - .samples(List.of("samples1", "samples1")) - .suppressedFiles(List.of("sup1", "sup2")) - .args(); - - var matches = List.of( - "-classpath", "path1;path2", - "-dependentSourceSets", "set1/set2;set3/set4", - "-externalDocumentationLinks", "link3^link4^^link1^link2", - "-perPackageOptions", "option1;option2", - "-samples", "samples1;samples1", - "-suppressedFiles", "sup1;sup2" - ); - - assertThat(args).hasSize(matches.size()); - - IntStream.range(0, args.size()).forEach(i -> assertThat(args.get(i)).isEqualTo(matches.get(i))); - } - - @Test - @SuppressWarnings("PMD.AvoidDuplicateLiterals") - void sourceSetTest() { - var args = new SourceSet() - .classpath("classpath1", "classpath2") - .dependentSourceSets("moduleName", "sourceSetName") - .documentedVisibilities(DocumentedVisibility.PACKAGE, DocumentedVisibility.PRIVATE) - .externalDocumentationLinks("url1", "packageListUrl1") - .externalDocumentationLinks("url2", "packageListUrl2") - .includes("includes1", "includes2") - .perPackageOptions("options1", "options2") - .samples("samples1", "sample2") - .srcLink("path1", "remote1", "#suffix1") - .srcLink("path2", "remote2", "#suffix2") - .src("src1", "src2") - .suppressedFiles("sup1", "sup2") - .analysisPlatform(AnalysisPlatform.JVM) - .apiVersion("1.0") - .displayName("name") - .jdkVersion(18) - .languageVersion("2.0") - .noJdkLink(true) - .noSkipEmptyPackages(true) - .noStdlibLink(true) - .reportUndocumented(true) - .skipDeprecated(true) - .sourceSetName("setName") - .args(); - - var matches = List.of( - "-analysisPlatform", "jvm", - "-apiVersion", "1.0", - "-classpath", "classpath1;classpath2", - "-dependentSourceSets", "moduleName/sourceSetName", - "-displayName", "name", - "-documentedVisibilities", "package;private", - "-externalDocumentationLinks", "url1^packageListUrl1^^url2^packageListUrl2", - "-jdkVersion", "18", - "-includes", "includes1;includes2", - "-languageVersion", "2.0", - "-noJdkLink", "true", - "-noSkipEmptyPackages", "true", - "-noStdlibLink", "true", - "-reportUndocumented", "true", - "-perPackageOptions", "options1;options2", - "-samples", "samples1;sample2", - "-skipDeprecated", "true", - "-src", "src1;src2", - "-srcLink", "path1=remote1#suffix1;path2=remote2#suffix2", - "-sourceSetName", "setName", - "-suppressedFiles", "sup1;sup2"); - - assertThat(args).hasSize(matches.size()); - - IntStream.range(0, args.size()).forEach(i -> assertThat(args.get(i)).isEqualTo(matches.get(i))); - } -} diff --git a/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java new file mode 100644 index 0000000..c6e6965 --- /dev/null +++ b/src/test/java/rife/bld/extension/kotlin/CompileOptionsTest.java @@ -0,0 +1,393 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * 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 + * + * https://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 rife.bld.extension.kotlin; + +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +class CompileOptionsTest { + /** + * Returns the local path of the given file names. + * + * @param fileNames The file names + * @return the local path + */ + private String localPath(String... fileNames) { + return Arrays.stream(fileNames).map(it -> new File(it).getAbsolutePath()) + .collect(Collectors.joining(File.pathSeparator)); + } + + @Test + @SuppressWarnings("PMD.UnitTestShouldIncludeAssert") + void testArgs() { + var options = new CompileOptions() + .apiVersion("11") + .javaParameters(true) + .jvmTarget("11") + .includeRuntime(true) + .jdkHome(new File("path")) + .jdkRelease("11") + .kotlinHome(new File("path")) + .languageVersion("1.0") + .moduleName("module") + .noJdk(true) + .noReflect(true) + .noWarn(true) + .optIn("opt1", "opt2") + .options("-foo", "-bar") + .path("path") + .plugin("id", "name", "value") + .progressive(true) + .scriptTemplates("name", "name2") + .verbose(true) + .wError(true) + .wExtra(true); + + var matches = List.of( + "-api-version", "11", + "-java-parameters", + "-jvm-target", "11", + "-include-runtime", + "-jdk-home", localPath("path"), + "-Xjdk-release=11", + "-kotlin-home", localPath("path"), + "-language-version", "1.0", + "-module-name", "module", + "-no-jdk", + "-no-reflect", + "-nowarn", + "-opt-in", "opt1", + "-opt-in", "opt2", + "-foo", + "-bar", + "-d", localPath("path"), + "-P", "plugin:id:name:value", + "-progressive", + "-script-templates", "name,name2", + "-verbose", + "-Werror", + "-Wextra"); + + var args = new ArrayList>(); + args.add(options.args()); + args.add(options.apiVersion(11).jvmTarget(11).args()); + + try (var softly = new AutoCloseableSoftAssertions()) { + for (var a : args) { + IntStream.range(0, a.size()).forEach(i -> softly.assertThat(a.get(i)) + .as(a.get(i) + " == " + matches.get(i)).isEqualTo(matches.get(i))); + } + } + } + + @Test + void testArgsCollections() { + var advanceOptions = List.of("-Xoption1", "option=2"); + var argFile = List.of(new File("arg1.txt"), new File("arg2.txt")); + var classpath = List.of(new File("path1"), new File("path2")); + var optIn = List.of("opt1", "opt2"); + var options = List.of("-foo", "-bar"); + var plugin = List.of("id:name:value", "id2:name2:value2"); + var scriptTemplates = List.of("temp1", "temp2"); + + var op = new CompileOptions() + .advancedOptions(advanceOptions) + .argFile(argFile) + .classpath(classpath) + .noStdLib(false) + .optIn(optIn) + .options(options) + .scriptTemplates(scriptTemplates); + + plugin.forEach(it -> { + var p = it.split(":"); + op.plugin(p[0], p[1], p[2]); + }); + + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(op.advancedOptions()).as("advancedOptions") + .hasSize(advanceOptions.size()).containsAll(advanceOptions); + softly.assertThat(op.argFile()).as("argFile") + .hasSize(argFile.size()).containsAll(argFile); + softly.assertThat(op.classpath()).as("classpath") + .hasSize(classpath.size()).containsAll(classpath); + softly.assertThat(op.optIn()).as("optIn") + .hasSize(optIn.size()).containsAll(optIn); + softly.assertThat(op.options()).as("options") + .hasSize(options.size()).containsAll(options); + softly.assertThat(op.plugin()).as("plugin") + .hasSize(plugin.size()).containsAll(plugin); + softly.assertThat(op.scriptTemplates()).as("scriptTemplates") + .hasSize(scriptTemplates.size()).containsAll(scriptTemplates); + } + + var matches = List.of( + '@' + localPath("arg1.txt"), '@' + localPath("arg2.txt"), + "-classpath", localPath("path1", "path2"), + "-Joption1", "-Joption2", + "-opt-in", "opt1", + "-opt-in", "opt2", + "-foo", "-bar", + "-script-templates", + "temp1,temp2", + "-Xoption1", "-Xoption=2", + "-P", "plugin:id:name:value", + "-P", "plugin:id2:name2:value2"); + + try (var softly = new AutoCloseableSoftAssertions()) { + var args = op.args(); + for (var arg : args) { + var found = false; + for (var match : matches) { + if (match.equals(arg)) { + found = true; + break; + } + } + softly.assertThat(found).as(arg + " not found.").isTrue(); + } + } + } + + @Test + void testArgsFile() { + var foo = new File("foo.txt"); + var bar = new File("bar.txt"); + var options = new CompileOptions(); + + options = options.argFile(foo); + assertThat(options.argFile()).contains(foo); + options.argFile().clear(); + assertThat(options.argFile()).isEmpty(); + + options.argFile(foo, bar); + assertThat(options.argFile()).contains(foo, bar); + options.argFile().clear(); + assertThat(options.argFile()).isEmpty(); + + options = options.argFile(foo.toPath(), bar.toPath()); + assertThat(options.argFile()).contains(foo, bar); + options.argFile().clear(); + assertThat(options.argFile()).isEmpty(); + + options = options.argFile(foo.getAbsolutePath(), bar.getAbsolutePath()); + assertThat(options.argFile()).contains(new File(foo.getAbsolutePath()), new File(bar.getAbsolutePath())); + } + + @Test + @EnabledOnOs(OS.LINUX) + void testCheckAllParams() throws IOException { + var args = Files.readAllLines(Paths.get("src", "test", "resources", "kotlinc-args.txt")); + + assertThat(args).isNotEmpty(); + + var params = new CompileOptions() + .advancedOptions("Xoption") + .apiVersion("11") + .expression("expression") + .includeRuntime(true) + .javaParameters(true) + .jdkHome("jdkhome") + .jvmTarget(12) + .kotlinHome("kotlin") + .languageVersion("1.0") + .moduleName("moduleName") + .noJdk(true) + .noReflect(true) + .noStdLib(true) + .noWarn(true) + .optIn("annotation") + .options("option") + .path(new File("path")) + .plugin("id", "option", "value") + .progressive(true) + .scriptTemplates("template") + .verbose(true) + .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; + for (var a : params.args()) { + if (a.startsWith(p)) { + found = true; + break; + } + } + softly.assertThat(found).as(p + " not found.").isTrue(); + } + } + } + + @Test + void testClasspath() { + var foo = new File("foo.txt"); + var bar = new File("bar.txt"); + var options = new CompileOptions(); + + options = options.classpath(foo); + assertThat(options.classpath()).as("File").containsExactly(foo); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + + options.classpath(foo, bar); + assertThat(options.classpath()).as("File...").containsExactly(foo, bar); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + + options.classpath(List.of(foo, bar)); + assertThat(options.classpath()).as("List(File...)").containsExactly(foo, bar); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + + options = options.classpath(foo.toPath(), bar.toPath()); + assertThat(options.classpath()).as("Path...").containsExactly(foo, bar); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + + options = options.classpathPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(options.classpath()).as("List(Path...)").containsExactly(foo, bar); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + + options.classpath(foo.getAbsolutePath(), bar.getAbsolutePath()); + assertThat(options.classpath()).as("String...") + .containsExactly(new File(foo.getAbsolutePath()), new File(bar.getAbsolutePath())); + options.classpath().clear(); + assertThat(options.argFile()).isEmpty(); + + options.classpathStrings(List.of(foo.getAbsolutePath(), bar.getAbsolutePath())); + assertThat(options.classpath()).as("List(String...)") + .containsExactly(new File(foo.getAbsolutePath()), new File(bar.getAbsolutePath())); + } + + @Test + void testJdkHome() { + var foo = new File("foo.txt"); + var options = new CompileOptions(); + + options.jdkHome(foo); + assertThat(options.jdkHome()).isEqualTo(foo); + + options = options.jdkHome(foo.toPath()); + assertThat(options.jdkHome()).isEqualTo(foo); + + options.jdkHome(foo.getAbsolutePath()); + assertThat(options.jdkHome().getAbsolutePath()).isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testKotlinHome() { + var foo = new File("foo.txt"); + var options = new CompileOptions(); + + options.kotlinHome(foo); + assertThat(options.kotlinHome()).isEqualTo(foo); + + options = options.kotlinHome(foo.toPath()); + assertThat(options.kotlinHome()).isEqualTo(foo); + + options.kotlinHome(foo.getAbsolutePath()); + assertThat(options.kotlinHome().getAbsolutePath()).isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testOptions() { + var options = new CompileOptions() + .advancedOptions("xopt1", "xopt2") + .apiVersion("11") + .argFile(Path.of("args.txt")) + .classpath("classpath") + .expression("expression") + .includeRuntime(true) + .javaParameters(true) + .jdkHome("jdk-home") + .jdkRelease(22) + .jvmTarget("9") + .kotlinHome("kotlin-home") + .languageVersion("1.0") + .moduleName("module") + .noJdk(true) + .noReflect(true) + .noStdLib(true) + .noWarn(true) + .optIn("opt1", "opt2") + .options("-foo", "-bar") + .path(Path.of("path")) + .plugin("id", "name", "value") + .progressive(true) + .scriptTemplates("name", "name2") + .verbose(true) + .wError(true) + .wExtra(true); + + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(options.advancedOptions()).containsExactly("xopt1", "xopt2"); + softly.assertThat(options.apiVersion()).isEqualTo("11"); + softly.assertThat(options.argFile()).containsExactly(new File("args.txt")); + softly.assertThat(options.classpath()).containsExactly(new File("classpath")); + softly.assertThat(options.expression()).isEqualTo("expression"); + softly.assertThat(options.isIncludeRuntime()).isTrue(); + softly.assertThat(options.isJavaParameters()).isTrue(); + softly.assertThat(options.isNoJdk()).isTrue(); + softly.assertThat(options.isNoReflect()).isTrue(); + softly.assertThat(options.isNoStdLib()).isTrue(); + softly.assertThat(options.isNoWarn()).isTrue(); + softly.assertThat(options.isProgressive()).isTrue(); + softly.assertThat(options.isVerbose()).isTrue(); + softly.assertThat(options.jdkHome()).isEqualTo(new File("jdk-home")); + softly.assertThat(options.jdkRelease()).isEqualTo("22"); + softly.assertThat(options.jvmTarget()).isEqualTo("9"); + softly.assertThat(options.kotlinHome()).isEqualTo(new File("kotlin-home")); + softly.assertThat(options.languageVersion()).isEqualTo("1.0"); + softly.assertThat(options.moduleName()).isEqualTo("module"); + softly.assertThat(options.optIn()).containsExactly("opt1", "opt2"); + softly.assertThat(options.options()).containsExactly("-foo", "-bar"); + softly.assertThat(options.path()).isEqualTo(new File("path")); + softly.assertThat(options.plugin()).containsExactly("id:name:value"); + softly.assertThat(options.scriptTemplates()).containsExactly("name", "name2"); + softly.assertThat(options.isWError()).isTrue(); + softly.assertThat(options.isWExtra()).isTrue(); + } + } +} diff --git a/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java b/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java new file mode 100644 index 0000000..6f8474f --- /dev/null +++ b/src/test/java/rife/bld/extension/kotlin/JvmOptionsTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * 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 + * + * https://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 rife.bld.extension.kotlin; + +import org.junit.jupiter.api.Test; +import rife.bld.extension.CompileKotlinOperation; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +class JvmOptionsTest { + @Test + 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"); + + op = new CompileKotlinOperation().jvmOptions(new JvmOptions().enableNativeAccess("m1", "m2")); + assertThat(op.jvmOptions()).as("m1,m2").containsExactly("--enable-native-access=m1,m2"); + } + + @Test + void testEnableNativeAccess() { + var options = new JvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + assertThat(options).as(JvmOptions.ALL_UNNAMED).containsExactly("--enable-native-access=ALL-UNNAMED"); + + options = new JvmOptions().enableNativeAccess("m1"); + assertThat(options).as("m1").containsExactly("--enable-native-access=m1"); + + options = new JvmOptions().enableNativeAccess("m1", "m2"); + assertThat(options).as("m1,m2").containsExactly("--enable-native-access=m1,m2"); + } + + @Test + void testIllegalNativeAccess() { + var options = new JvmOptions().illegalNativeAccess(JvmOptions.NativeAccess.ALLOW); + assertThat(options).as("ALLOW").containsExactly("--illegal-native-access=allow"); + + options = new JvmOptions().illegalNativeAccess(JvmOptions.NativeAccess.DENY); + assertThat(options).as("DENY").containsExactly("--illegal-native-access=deny"); + + options = new JvmOptions().illegalNativeAccess(JvmOptions.NativeAccess.WARN); + assertThat(options).as("WARN").containsExactly("--illegal-native-access=warn"); + } + + @Test + void testJvmOptions() { + var op = new CompileKotlinOperation().jvmOptions("option1", "option2"); + assertThat(op.jvmOptions()).as("option1,option2").containsExactly("option1", "option2"); + + op = new CompileKotlinOperation().jvmOptions(List.of("option1", "option2")); + assertThat(op.jvmOptions()).as("List.of(option1,option2)").containsExactly("option1", "option2"); + + 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"); + + 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"); + } +} diff --git a/src/test/resources/argfile.txt b/src/test/resources/argfile.txt new file mode 100644 index 0000000..d128d62 --- /dev/null +++ b/src/test/resources/argfile.txt @@ -0,0 +1,3 @@ +-Xjdk-release=17 -no-reflect + +-progressive diff --git a/src/test/resources/argfile2.txt b/src/test/resources/argfile2.txt new file mode 100644 index 0000000..93f9181 --- /dev/null +++ b/src/test/resources/argfile2.txt @@ -0,0 +1 @@ +-include-runtime \ No newline at end of file diff --git a/src/test/resources/kotlinc-args.txt b/src/test/resources/kotlinc-args.txt new file mode 100644 index 0000000..e764231 --- /dev/null +++ b/src/test/resources/kotlinc-args.txt @@ -0,0 +1,26 @@ +@ +-api-version +-classpath +-d +-expression +-include-runtime +-J +-java-parameters +-jdk-home +-jvm-target +-kotlin-home +-language-version +-module-name +-no-jdk +-no-reflect +-no-stdlib +-nowarn +-opt-in +-P +-progressive +-script +-script-templates +-verbose +-Werror +-Wextra +-X