This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 55ed20f14e90 CAMEL-25717: camel-jbang - The docs of camel test init
list its own options, not those of camel init (#27997)
55ed20f14e90 is described below
commit 55ed20f14e902a400dc5048691f4f592481ffbbf
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 17 10:08:11 2026 +0200
CAMEL-25717: camel-jbang - The docs of camel test init list its own
options, not those of camel init (#27997)
PrepareCamelJBangCommandsMojo found the source of a parent class (and of a
mixin type) by its simple name, so camel test init, whose parent is the Init of
Citrus, was documented with the options of camel init. A type that the class
imports by name is now only taken from a source of that type, or else (also
when that source cannot be parsed) its options are read from the class on the
classpath of the project, by reflection. A type that is not imported by name is
looked up as before, as [...]
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../jbang-commands/camel-jbang-test-init.adoc | 9 +-
.../camel-jbang-plugin-commands-metadata.json | 2 +-
.../packaging/PrepareCamelJBangCommandsMojo.java | 137 ++++++++---
.../PrepareCamelJBangCommandsMojoTest.java | 270 +++++++++++++++++++++
4 files changed, 377 insertions(+), 41 deletions(-)
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-init.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-init.adoc
index b8f803053c14..197f4499bcf8 100644
---
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-init.adoc
+++
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-init.adoc
@@ -21,14 +21,7 @@ camel test init [options]
[cols="2,5,1,2",options="header"]
|===
| Option | Description | Default | Type
-| `--clean-dir,--clean-directory` | Whether to clean directory first (deletes
all files in directory) | | boolean
-| `--dir,--directory` | Directory relative path where the new Camel
integration will be saved | . | String
-| `--force` | Overwrite a file that exists already (by default the command
fails instead) | | boolean
-| `--from-kamelet` | To be used when extending an existing Kamelet | | String
-| `--kamelets-version` | Apache Camel Kamelets version (auto-detected from
classpath if not set) | | String
-| `--list` | List available templates | | boolean
-| `--pipe` | When creating a yaml file should it be created as a Pipe CR | |
boolean
-| `--repo,--repos` | Additional maven repositories (Use commas to separate
multiple repositories) | | String
+| `--directory` | Directory where the files will be created | . | String
| `-h,--help` | Display the help and sub-commands | | boolean
|===
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json
b/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json
index 36d3c46b9e14..173749a94911 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json
+++
b/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json
@@ -1,5 +1,5 @@
{
"commands": [
- { "name": "test", "fullName": "test", "description": "Manage tests for
Camel applications", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.test.TestCommand", "plugin": "test",
"options": [ { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ {
"name": "init", "fullName": "test init", "description": "Creates a new Citrus
test", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.test.T [...]
+ { "name": "test", "fullName": "test", "description": "Manage tests for
Camel applications", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.test.TestCommand", "plugin": "test",
"options": [ { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ {
"name": "init", "fullName": "test init", "description": "Creates a new Citrus
test", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.test.T [...]
]
}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java
index 6cd3b9952277..9e46cd905846 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java
@@ -18,6 +18,8 @@ package org.apache.camel.maven.packaging;
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -48,6 +50,7 @@ import org.jboss.forge.roaster.Roaster;
import org.jboss.forge.roaster.model.JavaType;
import org.jboss.forge.roaster.model.source.AnnotationSource;
import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.Import;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.JavaSource;
@@ -485,26 +488,7 @@ public class PrepareCamelJBangCommandsMojo extends
AbstractGeneratorMojo {
// First, parse options from parent class (if any)
String superType = clazz.getSuperType();
if (superType != null && !superType.equals("java.lang.Object") &&
!superType.equals("Object")) {
- // Try to find and parse the parent class
- String parentClassName = superType;
- if (parentClassName.contains(".")) {
- parentClassName =
parentClassName.substring(parentClassName.lastIndexOf('.') + 1);
- }
-
- // Search for parent class file
- File parentFile = findClassFile(baseDir, parentClassName);
- if (parentFile != null && parentFile.exists()) {
- try {
- JavaType<?> parsedParent = Roaster.parse(parentFile);
- if (parsedParent instanceof JavaClassSource parentClazz) {
- parseOptionsFromClassHierarchy(parentClazz, options,
parentFile.getParentFile());
- } else {
- getLog().debug("Skipping options from non-class parent
type: " + parentClassName);
- }
- } catch (Exception e) {
- getLog().debug("Could not parse parent class: " +
parentClassName + " - " + e.getMessage());
- }
- }
+ parseOptionsFromType(clazz, superType, options, baseDir, "parent");
}
// Then parse options from this class (will override parent options
with same name)
@@ -534,24 +518,113 @@ public class PrepareCamelJBangCommandsMojo extends
AbstractGeneratorMojo {
// Also parse options from @CommandLine.Mixin fields
for (FieldSource<JavaClassSource> field : clazz.getFields()) {
if (hasMixinAnnotation(field)) {
- String mixinTypeName = field.getType().getName();
- File mixinFile = findClassFile(baseDir, mixinTypeName);
- if (mixinFile != null && mixinFile.exists()) {
- try {
- JavaType<?> parsedMixin = Roaster.parse(mixinFile);
- if (parsedMixin instanceof JavaClassSource mixinClazz)
{
- parseOptionsFromClassHierarchy(mixinClazz,
options, mixinFile.getParentFile());
- } else {
- getLog().debug("Skipping options from non-class
mixin type: " + mixinTypeName);
- }
- } catch (Exception e) {
- getLog().debug("Could not parse mixin class: " +
mixinTypeName + " - " + e.getMessage());
+ parseOptionsFromType(clazz, field.getType().getName(),
options, baseDir, "mixin");
+ }
+ }
+ }
+
+ /**
+ * Parses the options of a parent class or a mixin type of a class: from
its source when it is one of the command
+ * classes, or else from its class on the classpath of the project, such
as the class of a library that a plugin
+ * command extends. The source is found by the simple name of the type, so
for a type that the class imports by name
+ * it is only taken when it is of that type, as a class of a library may
have the simple name of a command of
+ * camel-jbang-core. (The package that Roaster resolves for a type that is
not imported by name is not certain: with
+ * an import on demand of a class, it takes a class of the same package
for a nested class of the imported one.)
+ */
+ private void parseOptionsFromType(JavaClassSource clazz, String type,
List<OptionInfo> options, File baseDir, String kind) {
+ String simpleName = type.substring(type.lastIndexOf('.') + 1);
+ String imported = importedByName(clazz, simpleName);
+ File file = findClassFile(baseDir, simpleName);
+ if (file != null && file.exists()) {
+ try {
+ JavaType<?> parsed = Roaster.parse(file);
+ if (imported == null ||
imported.equals(parsed.getQualifiedName())) {
+ if (parsed instanceof JavaClassSource source) {
+ parseOptionsFromClassHierarchy(source, options,
file.getParentFile());
+ } else {
+ getLog().debug("Skipping options from non-class " +
kind + " type: " + simpleName);
}
+ return;
+ }
+ } catch (Exception e) {
+ getLog().debug("Could not parse " + kind + " class: " +
simpleName + " - " + e.getMessage());
+ if (imported == null) {
+ return;
+ }
+ }
+ }
+ if (imported != null) {
+ parseOptionsFromClass(imported, options);
+ }
+ }
+
+ /**
+ * The qualified name of the type with the simple name that the class
imports by name, or null when it does not.
+ */
+ private static String importedByName(JavaClassSource clazz, String
simpleName) {
+ for (Import imported : clazz.getImports()) {
+ if (!imported.isWildcard() && !imported.isStatic() &&
imported.getSimpleName().equals(simpleName)) {
+ return imported.getQualifiedName();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the options of a class that is not one of the command classes
from its picocli annotations (and those of
+ * its parent classes and mixins), with the classpath of the project.
picocli is not a dependency of this plugin, so
+ * the annotations are read by reflection.
+ */
+ @SuppressWarnings("unchecked")
+ private void parseOptionsFromClass(String className, List<OptionInfo>
options) {
+ try {
+ ClassLoader loader = getProjectClassLoader();
+ Class<? extends Annotation> optionType
+ = (Class<? extends Annotation>)
loader.loadClass("picocli.CommandLine$Option");
+ Class<? extends Annotation> mixinType
+ = (Class<? extends Annotation>)
loader.loadClass("picocli.CommandLine$Mixin");
+ parseOptionsFromClass(loader.loadClass(className), optionType,
mixinType, options);
+ } catch (Exception | LinkageError e) {
+ getLog().debug("Could not read the options of class: " + className
+ " - " + e.getMessage());
+ }
+ }
+
+ private void parseOptionsFromClass(
+ Class<?> clazz, Class<? extends Annotation> optionType, Class<?
extends Annotation> mixinType,
+ List<OptionInfo> options)
+ throws ReflectiveOperationException {
+ if (clazz.getSuperclass() != null && clazz.getSuperclass() !=
Object.class) {
+ parseOptionsFromClass(clazz.getSuperclass(), optionType,
mixinType, options);
+ }
+ for (Field field : clazz.getDeclaredFields()) {
+ Annotation optionAnn = field.getAnnotation(optionType);
+ if (optionAnn != null) {
+ OptionInfo option = new OptionInfo();
+ option.names = String.join(",", (String[])
attribute(optionAnn, "names"));
+ option.description = sanitizeDescription(String.join(" ",
(String[]) attribute(optionAnn, "description")));
+ String defaultValue = (String) attribute(optionAnn,
"defaultValue");
+ // how picocli marks an option without a default value
+ if (!"__no_default_value__".equals(defaultValue)) {
+ option.defaultValue = sanitizeDescription(defaultValue);
}
+ option.required = (Boolean) attribute(optionAnn, "required");
+ option.hidden = (Boolean) attribute(optionAnn, "hidden");
+ String paramLabel = (String) attribute(optionAnn,
"paramLabel");
+ option.paramLabel = paramLabel.isEmpty() ? null : paramLabel;
+ option.javaType = field.getType().getTypeName();
+ option.deprecated =
field.isAnnotationPresent(Deprecated.class);
+ options.removeIf(o -> o.names.equals(option.names));
+ options.add(option);
+ } else if (field.isAnnotationPresent(mixinType)) {
+ parseOptionsFromClass(field.getType(), optionType, mixinType,
options);
}
}
}
+ private static Object attribute(Annotation annotation, String name) throws
ReflectiveOperationException {
+ return annotation.annotationType().getMethod(name).invoke(annotation);
+ }
+
private File findClassFile(File dir, String className) {
// Search in current directory
File file = new File(dir, className + ".java");
diff --git
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java
index 582e47054fa1..553683627b87 100644
---
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java
+++
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java
@@ -17,11 +17,20 @@
package org.apache.camel.maven.packaging;
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.stream.Stream;
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import org.apache.camel.tooling.model.JBangCommandModel.JBangCommand;
+import org.apache.camel.tooling.model.JBangCommandModel.JBangCommandOption;
+import org.apache.camel.tooling.model.JsonMapper;
import org.apache.maven.model.Build;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugin.logging.SystemStreamLog;
@@ -37,6 +46,21 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class PrepareCamelJBangCommandsMojoTest {
+ // the command Init of camel-jbang-core
+ private static final String CORE_INIT = """
+ package org.apache.camel.dsl.jbang.core.commands;
+
+ import picocli.CommandLine.Command;
+ import picocli.CommandLine.Option;
+
+ @Command(name = "init", description = "Creates a new Camel
integration")
+ public class Init {
+
+ @Option(names = "--from-kamelet", description = "To be used
when extending an existing Kamelet")
+ String fromKamelet;
+ }
+ """;
+
@TempDir
Path tempDir;
@@ -204,6 +228,252 @@ class PrepareCamelJBangCommandsMojoTest {
.isEmpty();
}
+ /**
+ * CAMEL-25717: a plugin command whose parent class is of a library does
not get the options of the command of
+ * camel-jbang-core with the simple name of that class, as camel test init
(whose parent is the Init of Citrus) got
+ * the options of camel init.
+ */
+ @Test
+ void
testPluginCommandDoesNotGetTheOptionsOfTheCoreCommandWithTheNameOfItsParent()
throws Exception {
+ JBangCommand init = generatePluginWithLibraryParent(CORE_INIT);
+
+
assertThat(init.getOptions()).extracting(JBangCommandOption::getNames).doesNotContain("--from-kamelet");
+ }
+
+ /**
+ * CAMEL-25717: a plugin command whose parent class is of a library gets
the options of that class, and of its
+ * parent classes, from the classpath of the project.
+ */
+ @Test
+ void testPluginCommandGetsTheOptionsOfTheLibraryClassItExtends() throws
Exception {
+ compileLibrary();
+
+ JBangCommand init = generatePluginWithLibraryParent(CORE_INIT);
+
+
assertThat(init.getOptions()).extracting(JBangCommandOption::getNames).containsExactly("--directory",
"-h,--help");
+ JBangCommandOption directory = init.getOptions().get(0);
+ assertThat(directory.getDescription()).isEqualTo("Directory where the
files will be created");
+ assertThat(directory.getDefaultValue()).isEqualTo(".");
+ assertThat(directory.getJavaType()).isEqualTo("java.lang.String");
+ }
+
+ /**
+ * A source with the simple name of the library class that cannot be
parsed does not keep the options of the library
+ * class from the classpath of the project.
+ */
+ @Test
+ void
testPluginCommandGetsTheOptionsOfTheLibraryClassWhenASourceWithItsNameCannotBeParsed()
throws Exception {
+ compileLibrary();
+
+ JBangCommand init = generatePluginWithLibraryParent("this is not
java");
+
+
assertThat(init.getOptions()).extracting(JBangCommandOption::getNames).containsExactly("--directory",
"-h,--help");
+ }
+
+ /**
+ * A parent class of the same package is found when the class has a static
import on demand, for which Roaster
+ * resolves the parent class as a nested class of the imported one (as
camel export, whose ExportBaseCommand has
+ * {@code import static ...CamelJBangConstants.*}).
+ */
+ @Test
+ void testParentOfTheSamePackageWithAStaticImportOnDemand() throws
Exception {
+ Path commands = tempDir.resolve("commands");
+ write(commands.resolve("BaseCommand.java"), """
+ package org.apache.camel.dsl.jbang.core.commands;
+
+ import picocli.CommandLine.Option;
+
+ public abstract class BaseCommand {
+
+ @Option(names = "--verbose", description = "Verbose
output")
+ boolean verbose;
+ }
+ """);
+ write(commands.resolve("Export.java"), """
+ package org.apache.camel.dsl.jbang.core.commands;
+
+ import picocli.CommandLine.Command;
+ import picocli.CommandLine.Option;
+
+ import static
org.apache.camel.dsl.jbang.core.common.CamelJBangConstants.*;
+
+ @Command(name = "export", description = "Export to other
runtimes")
+ public class Export extends BaseCommand {
+
+ @Option(names = "--dir", description = "Directory where
the project will be exported")
+ String dir;
+ }
+ """);
+ write(commands.resolve("CamelJBangMain.java"), """
+ package org.apache.camel.dsl.jbang.core.commands;
+
+ import picocli.CommandLine;
+ import picocli.CommandLine.Command;
+
+ @Command(name = "camel")
+ public class CamelJBangMain {
+ public static void main(String[] args) {
+ CommandLine commandLine;
+ commandLine = new CommandLine(new CamelJBangMain())
+ .addSubcommand("export", new CommandLine(new
Export()));
+ }
+ }
+ """);
+
+ PrepareCamelJBangCommandsMojo mojo = new
PrepareCamelJBangCommandsMojo(null, null);
+ mojo.commandsDir = commands.toFile();
+ mojo.outFolder = tempDir.resolve("generated-resources").toFile();
+ mojo.project =
projectWithOutputDir(tempDir.resolve("classes").toFile());
+
+ mojo.execute();
+
+ String json = Files.readString(
+
tempDir.resolve("generated-resources").resolve(PrepareCamelJBangCommandsMojo.CORE_METADATA_FILE));
+ JBangCommand export =
JsonMapper.generateJBangCommandModel(json).getCommands().get(0);
+
assertThat(export.getOptions()).extracting(JBangCommandOption::getNames).containsExactly("--dir",
"--verbose");
+ }
+
+ /**
+ * A plugin as camel-jbang-plugin-test: camel test init, whose parent
class Init is of a library, while
+ * camel-jbang-core has a command class Init too (with the given source).
Returns the generated test init command.
+ */
+ private JBangCommand generatePluginWithLibraryParent(String coreInit)
throws Exception {
+ Path core = tempDir.resolve("core/commands");
+ write(core.resolve("Init.java"), coreInit);
+ Path plugin = tempDir.resolve("plugin/commands");
+ write(plugin.resolve("test/TestPlugin.java"), """
+ package org.apache.camel.dsl.jbang.core.commands.test;
+
+ import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+ import org.apache.camel.dsl.jbang.core.common.CamelJBangPlugin;
+ import org.apache.camel.dsl.jbang.core.common.Plugin;
+ import picocli.CommandLine;
+
+ @CamelJBangPlugin(name = "camel-jbang-plugin-test",
firstVersion = "4.14.0")
+ public class TestPlugin implements Plugin {
+
+ @Override
+ public void customize(CommandLine commandLine,
CamelJBangMain main) {
+ var cmd = new CommandLine(new TestCommand(main))
+ .addSubcommand("init", new CommandLine(new
TestInit(main)));
+
+ commandLine.addSubcommand("test", cmd);
+ }
+ }
+ """);
+ write(plugin.resolve("test/TestCommand.java"), """
+ package org.apache.camel.dsl.jbang.core.commands.test;
+
+ import picocli.CommandLine.Command;
+
+ @Command(name = "test", description = "Manage tests")
+ public class TestCommand {
+ }
+ """);
+ write(plugin.resolve("test/TestInit.java"), """
+ package org.apache.camel.dsl.jbang.core.commands.test;
+
+ import org.example.cli.Init;
+ import picocli.CommandLine.Command;
+
+ @Command(name = "init", description = "Creates a new test")
+ public class TestInit extends Init {
+ }
+ """);
+
+ PrepareCamelJBangCommandsMojo mojo = new
PrepareCamelJBangCommandsMojo(null, null);
+ mojo.commandsDir = plugin.toFile();
+ mojo.coreCommandsDir = core.toFile();
+ mojo.outFolder = tempDir.resolve("generated-resources").toFile();
+ mojo.project =
projectWithOutputDir(tempDir.resolve("classes").toFile());
+
+ mojo.execute();
+
+ String json = Files.readString(
+
tempDir.resolve("generated-resources").resolve(PrepareCamelJBangCommandsMojo.PLUGIN_METADATA_FILE));
+ JBangCommand test =
JsonMapper.generateJBangCommandModel(json).getCommands().get(0);
+
assertThat(test.getSubcommands()).extracting(JBangCommand::getFullName).containsExactly("test
init");
+ return test.getSubcommands().get(0);
+ }
+
+ /**
+ * Compiles the library of the command classes into the classes of the
project. picocli is not a dependency of this
+ * plugin, so the library has the annotations of picocli that the plugin
reads.
+ */
+ private void compileLibrary() throws IOException {
+ Path sources = tempDir.resolve("library");
+ write(sources.resolve("picocli/CommandLine.java"), """
+ package picocli;
+
+ import java.lang.annotation.ElementType;
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.RetentionPolicy;
+ import java.lang.annotation.Target;
+
+ public class CommandLine {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ public @interface Option {
+ String[] names();
+
+ String[] description() default {};
+
+ String defaultValue() default "__no_default_value__";
+
+ boolean required() default false;
+
+ boolean hidden() default false;
+
+ String paramLabel() default "";
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ public @interface Mixin {
+ }
+ }
+ """);
+ write(sources.resolve("org/example/cli/LibraryCommand.java"), """
+ package org.example.cli;
+
+ import picocli.CommandLine;
+
+ public abstract class LibraryCommand {
+
+ @CommandLine.Option(names = { "-h", "--help" },
description = "Display the help and sub-commands")
+ boolean help;
+ }
+ """);
+ write(sources.resolve("org/example/cli/Init.java"), """
+ package org.example.cli;
+
+ import picocli.CommandLine;
+
+ public class Init extends LibraryCommand {
+
+ @CommandLine.Option(names = "--directory", description =
"Directory where the files will be created",
+ defaultValue = ".")
+ String directory;
+ }
+ """);
+ Path classes = tempDir.resolve("classes");
+ Files.createDirectories(classes);
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ try (StandardJavaFileManager fileManager =
compiler.getStandardFileManager(null, null, null);
+ Stream<Path> files = Files.walk(sources)) {
+ List<File> javaFiles = files.filter(f ->
f.toString().endsWith(".java")).map(Path::toFile).toList();
+ Boolean compiled = compiler.getTask(null, fileManager, null,
List.of("-d", classes.toString()), null,
+ fileManager.getJavaFileObjectsFromFiles(javaFiles)).call();
+ assertThat(compiled).isTrue();
+ }
+ }
+
+ private static void write(Path file, String content) throws IOException {
+ Files.createDirectories(file.getParent());
+ Files.writeString(file, content);
+ }
+
private static MavenProject projectWithOutputDir(File outputDir) {
outputDir.mkdirs();
Build build = new Build();