From 942d416898b3db4e9a9a904e73655d2a446fea8a Mon Sep 17 00:00:00 2001 From: andreatp Date: Fri, 24 Jul 2026 12:47:22 +0100 Subject: [PATCH 1/4] Extend Maven plugin with redline native compilation support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture: - Bridge module uses exec:java with Generator.main() instead of the Maven plugin, breaking the circular dependency (plugin → redline → bridge → plugin) - New redline/build-time-compiler module with RedlineGenerator that handles native code generation and extends generated sources with builder()/safeBuilder()/loadNativeCode() methods - Maven plugin calls RedlineGenerator when is configured Config extended with: - redlineTargets: list of target triples for cross-compilation - targetResourceFolder: where .native files are written Generated module class gains (when redlineTargets configured): - loadNativeCode(): loads platform-specific native code from resources - builder(): automatic backend selection (native if platform + Java 25 supported, bytecode otherwise) - safeBuilder(): always uses bytecode compiler NativeMachineFactory.Builder gains toInstanceBuilder() to return a configured Instance.Builder for the generated builder() method. RedlineTarget gains fromTriple() for target triple lookup. --- bom/pom.xml | 5 + .../endive/build/time/compiler/Config.java | 45 ++++- .../endive/build/time/compiler/Generator.java | 31 ++++ compiler-maven-plugin/pom.xml | 4 + .../time/maven/EndiveCompilerGenMojo.java | 33 +++- pom.xml | 5 + .../api/internal/RedlineTarget.java | 9 + redline/bridge/pom.xml | 71 +++++++- redline/build-time-compiler/pom.xml | 38 ++++ .../experimental/build/RedlineGenerator.java | 172 ++++++++++++++++++ redline/pom.xml | 2 + 11 files changed, 403 insertions(+), 12 deletions(-) create mode 100644 redline/build-time-compiler/pom.xml create mode 100644 redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java diff --git a/bom/pom.xml b/bom/pom.xml index af3f71462..afe9217ed 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -78,6 +78,11 @@ redline-bridge-experimental ${project.version} + + run.endive + redline-build-time-compiler-experimental + ${project.version} + run.endive redline-compiler-experimental diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java index 1aba2b9b9..22a870d6e 100644 --- a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java +++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java @@ -1,6 +1,7 @@ package run.endive.build.time.compiler; import java.nio.file.Path; +import java.util.List; import java.util.Set; import java.util.StringJoiner; import run.endive.compiler.InterpreterFallback; @@ -46,6 +47,16 @@ public final class Config { */ private final String moduleInterface; + /** + * target triples for redline native compilation (empty = no native compilation) + */ + private final List redlineTargets; + + /** + * the target resource folder for native code files + */ + private final Path targetResourceFolder; + private Config( Path wasmFile, String name, @@ -54,7 +65,9 @@ private Config( Path targetWasmFolder, InterpreterFallback interpreterFallback, Set interpretedFunctions, - String moduleInterface) { + String moduleInterface, + List redlineTargets, + Path targetResourceFolder) { this.wasmFile = wasmFile; this.name = name; this.targetClassFolder = targetClassFolder; @@ -63,6 +76,8 @@ private Config( this.interpreterFallback = interpreterFallback; this.interpretedFunctions = interpretedFunctions; this.moduleInterface = moduleInterface; + this.redlineTargets = redlineTargets; + this.targetResourceFolder = targetResourceFolder; } public Path wasmFile() { @@ -97,6 +112,18 @@ public String moduleInterface() { return moduleInterface; } + public List redlineTargets() { + return redlineTargets; + } + + public Path targetResourceFolder() { + return targetResourceFolder; + } + + public boolean hasRedlineTargets() { + return redlineTargets != null && !redlineTargets.isEmpty(); + } + public static Builder builder() { return new Builder(); } @@ -126,6 +153,8 @@ public static final class Builder { private InterpreterFallback interpreterFallback = InterpreterFallback.FAIL; private Set interpretedFunctions; private String moduleInterface; + private List redlineTargets = List.of(); + private Path targetResourceFolder; private Builder() {} @@ -169,6 +198,16 @@ public Builder withModuleInterface(String moduleInterface) { return this; } + public Builder withRedlineTargets(List redlineTargets) { + this.redlineTargets = redlineTargets; + return this; + } + + public Builder withTargetResourceFolder(Path targetResourceFolder) { + this.targetResourceFolder = targetResourceFolder; + return this; + } + public Config build() { return new Config( wasmFile, @@ -178,7 +217,9 @@ public Config build() { targetWasmFolder, interpreterFallback, interpretedFunctions, - moduleInterface); + moduleInterface, + redlineTargets, + targetResourceFolder); } } } diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java index 1aacbf2cd..8e4b73092 100644 --- a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java +++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java @@ -56,6 +56,37 @@ public Generator(Config config) { this.config = config; } + public static void main(String[] args) throws IOException { + if (args.length < 5) { + throw new IllegalArgumentException( + "Usage: Generator " + + " " + + " [interpreterFallback] [moduleInterface]"); + } + var configBuilder = + Config.builder() + .withWasmFile(Path.of(args[0])) + .withName(args[1]) + .withTargetClassFolder(Path.of(args[2])) + .withTargetSourceFolder(Path.of(args[3])) + .withTargetWasmFolder(Path.of(args[4])); + if (args.length > 5 && !args[5].isEmpty()) { + configBuilder.withInterpreterFallback( + run.endive.compiler.InterpreterFallback.valueOf(args[5])); + } + if (args.length > 6 && !args[6].isEmpty()) { + configBuilder.withModuleInterface(args[6]); + } + var config = configBuilder.build(); + var generator = new Generator(config); + var interpreted = generator.generateResources(); + generator.generateMetaWasm(interpreted); + generator.generateSources(); + if (config.moduleInterface() != null && !config.moduleInterface().isEmpty()) { + generator.generateModuleInterface(config.moduleInterface()); + } + } + public Set generateResources() throws IOException { var module = Parser.parse(config.wasmFile()); var machineName = config.name() + "Machine"; diff --git a/compiler-maven-plugin/pom.xml b/compiler-maven-plugin/pom.xml index 223b8df00..a7ea02a6a 100644 --- a/compiler-maven-plugin/pom.xml +++ b/compiler-maven-plugin/pom.xml @@ -22,6 +22,10 @@ run.endive compiler + + run.endive + redline-build-time-compiler-experimental + org.apache.maven maven-core diff --git a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java index 5866a32ed..ee1c3a82b 100644 --- a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java +++ b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Set; import java.util.TreeSet; import org.apache.maven.model.Resource; @@ -14,6 +15,7 @@ import run.endive.build.time.compiler.Config; import run.endive.build.time.compiler.Generator; import run.endive.compiler.InterpreterFallback; +import run.endive.redline.experimental.build.RedlineGenerator; /** * This plugin generates an invokable library from the compiled Wasm @@ -77,6 +79,23 @@ public class EndiveCompilerGenMojo extends AbstractMojo { @Parameter(required = false) String moduleInterface; + /** + * Target triples for Redline native compilation. When set, the plugin + * cross-compiles the Wasm module to native code for each target using + * Cranelift and generates builder()/safeBuilder() methods in the module class. + * Example: x86_64-unknown-linux-gnu, aarch64-apple-darwin + */ + @Parameter(required = false) + List redlineTargets; + + /** + * The target resource folder for native code files (.native). + */ + @Parameter( + required = true, + defaultValue = "${project.build.directory}/generated-resources/endive-compiler") + private File targetResourceFolder; + /** * The current Maven project. */ @@ -87,7 +106,7 @@ public class EndiveCompilerGenMojo extends AbstractMojo { public void execute() throws MojoExecutionException { getLog().info("Compiling classes for " + name + " from " + wasmFile); - var config = + var configBuilder = Config.builder() .withWasmFile(wasmFile.toPath()) .withName(name) @@ -97,7 +116,11 @@ public void execute() throws MojoExecutionException { .withInterpreterFallback(interpreterFallback) .withInterpretedFunctions(interpretedFunctions) .withModuleInterface(moduleInterface) - .build(); + .withTargetResourceFolder(targetResourceFolder.toPath()); + if (redlineTargets != null && !redlineTargets.isEmpty()) { + configBuilder.withRedlineTargets(redlineTargets); + } + var config = configBuilder.build(); var generator = new Generator(config); @@ -106,6 +129,12 @@ public void execute() throws MojoExecutionException { generator.generateMetaWasm(finalInterpretedFunctions); generator.generateSources(); + if (config.hasRedlineTargets()) { + var redlineGenerator = new RedlineGenerator(config); + redlineGenerator.generateNativeCode(); + redlineGenerator.extendGeneratedSources(); + } + if (moduleInterface != null && !moduleInterface.isEmpty()) { generator.generateModuleInterface(moduleInterface); } diff --git a/pom.xml b/pom.xml index d526ae500..d5999c408 100644 --- a/pom.xml +++ b/pom.xml @@ -268,6 +268,11 @@ redline-bridge-experimental ${project.version} + + run.endive + redline-build-time-compiler-experimental + ${project.version} + run.endive redline-compiler-experimental diff --git a/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java b/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java index 933d2feef..dba0b0ab9 100644 --- a/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java +++ b/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java @@ -27,6 +27,15 @@ public String resourceSuffix() { return resourceSuffix; } + public static Optional fromTriple(String triple) { + for (RedlineTarget target : values()) { + if (target.triple.equals(triple)) { + return Optional.of(target); + } + } + return Optional.empty(); + } + public static Optional detectHost() { String osName = System.getProperty("endive.redline.os.name", System.getProperty("os.name", "")) diff --git a/redline/bridge/pom.xml b/redline/bridge/pom.xml index 412153b93..784cfdd8a 100644 --- a/redline/bridge/pom.xml +++ b/redline/bridge/pom.xml @@ -29,21 +29,76 @@ + - run.endive - endive-compiler-maven-plugin + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} - redline-bridge + add-generated-sources - compile + add-source + generate-sources - run.endive.redline.experimental.bridge.internal.Cranelift - ${project.basedir}/../cranelift_bridge.wasm - WARN - run.endive.redline.experimental.bridge.internal.CraneliftBridge + + ${project.build.directory}/generated-sources/endive-compiler + + + + + add-generated-resources + + add-resource + + generate-resources + + + + ${project.build.directory}/generated-resources/endive-compiler + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run.endive + build-time-compiler + ${project.version} + + + + + redline-bridge-compile + + java + + generate-sources + + run.endive.build.time.compiler.Generator + + ${project.basedir}/../cranelift_bridge.wasm + run.endive.redline.experimental.bridge.internal.Cranelift + ${project.build.directory}/generated-resources/endive-compiler + ${project.build.directory}/generated-sources/endive-compiler + ${project.build.directory}/generated-resources/endive-compiler + WARN + run.endive.redline.experimental.bridge.internal.CraneliftBridge + + true + false diff --git a/redline/build-time-compiler/pom.xml b/redline/build-time-compiler/pom.xml new file mode 100644 index 000000000..50ee68428 --- /dev/null +++ b/redline/build-time-compiler/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + run.endive + redline-parent-experimental + 999-SNAPSHOT + ../pom.xml + + redline-build-time-compiler-experimental + jar + Endive - Redline Build Time Compiler + Build-time native code generation via Cranelift + + + + com.github.javaparser + javaparser-core + + + run.endive + build-time-compiler + + + run.endive + redline-api-experimental + + + run.endive + redline-compiler-experimental + + + run.endive + wasm + + + diff --git a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java new file mode 100644 index 000000000..f16ed9840 --- /dev/null +++ b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java @@ -0,0 +1,172 @@ +package run.endive.redline.experimental.build; + +import static com.github.javaparser.StaticJavaParser.parseClassOrInterfaceType; +import static com.github.javaparser.StaticJavaParser.parseType; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.expr.FieldAccessExpr; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.stmt.ReturnStmt; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import run.endive.build.time.compiler.Config; +import run.endive.redline.experimental.api.NativeCodeSerializer; +import run.endive.redline.experimental.api.internal.RedlineTarget; +import run.endive.redline.experimental.compiler.internal.NativeCompiler; +import run.endive.wasm.Parser; + +public final class RedlineGenerator { + + private final Config config; + + public RedlineGenerator(Config config) { + this.config = config; + } + + public void generateNativeCode() throws IOException { + if (!config.hasRedlineTargets()) { + return; + } + var module = Parser.parse(config.wasmFile()); + var packagePath = config.getPackageName().replace('.', '/'); + var baseName = config.getBaseName(); + var resourceDir = config.targetResourceFolder().resolve(packagePath); + Files.createDirectories(resourceDir); + + for (String triple : config.redlineTargets()) { + byte[][] compiledCode = NativeCompiler.compileAll(triple, module); + + var target = + RedlineTarget.fromTriple(triple) + .orElseThrow( + () -> + new IllegalArgumentException( + "Unknown target triple: " + triple)); + var nativeFile = + resourceDir.resolve(baseName + "." + target.resourceSuffix() + ".native"); + + try (var out = new FileOutputStream(nativeFile.toFile())) { + NativeCodeSerializer.serialize(compiledCode, out); + } + } + } + + public void extendGeneratedSources() throws IOException { + if (!config.hasRedlineTargets()) { + return; + } + var packagePath = config.getPackageName().replace('.', '/'); + var baseName = config.getBaseName(); + var sourceFile = + config.targetSourceFolder().resolve(packagePath).resolve(baseName + ".java"); + + var cu = StaticJavaParser.parse(sourceFile); + var type = cu.getClassByName(baseName).orElseThrow(); + + cu.addImport("run.endive.redline.experimental.api.NativeCodeSerializer"); + cu.addImport("run.endive.redline.experimental.api.internal.RedlineTarget"); + cu.addImport("run.endive.redline.experimental.runner.NativeMachineFactory"); + cu.addImport("java.io.InputStream"); + cu.addImport("java.io.IOException"); + cu.addImport("java.io.UncheckedIOException"); + cu.addImport("run.endive.runtime.Instance"); + + generateNativeCodeHolderInnerClass(type, baseName); + generateLoadNativeCodeMethod(type); + generateBuilderMethod(type, baseName); + generateSafeBuilderMethod(type, baseName); + + Files.writeString(sourceFile, cu.toString()); + } + + private static void generateNativeCodeHolderInnerClass( + ClassOrInterfaceDeclaration type, String moduleName) { + var holderClass = + new ClassOrInterfaceDeclaration( + NodeList.nodeList( + new Modifier(Modifier.Keyword.PRIVATE), + new Modifier(Modifier.Keyword.STATIC)), + false, + "NativeCodeHolder"); + type.addMember(holderClass); + + holderClass.addField( + parseType("byte[][]"), "CODE", Modifier.Keyword.STATIC, Modifier.Keyword.FINAL); + + var initBody = holderClass.addStaticInitializer(); + + initBody.addStatement( + StaticJavaParser.parseStatement( + "var host = RedlineTarget.detectHost().orElse(null);")); + + initBody.addStatement( + StaticJavaParser.parseStatement( + "if (host == null || Runtime.version().feature() < 25) {\n" + + " CODE = null;\n" + + "} else {\n" + + " String resource = \"" + + moduleName + + ".\" + host.resourceSuffix() + \".native\";\n" + + " try (InputStream in = " + + moduleName + + ".class.getResourceAsStream(resource)) {\n" + + " CODE = (in == null) ? null" + + " : NativeCodeSerializer.deserialize(in);\n" + + " } catch (IOException e) {\n" + + " throw new UncheckedIOException(" + + "\"Failed to load native code\", e);\n" + + " }\n" + + "}")); + } + + private static void generateLoadNativeCodeMethod(ClassOrInterfaceDeclaration type) { + var method = + type.addMethod("loadNativeCode", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC) + .setType(parseType("byte[][]")); + method.createBody() + .addStatement( + new ReturnStmt( + new FieldAccessExpr(new NameExpr("NativeCodeHolder"), "CODE"))); + } + + private static void generateBuilderMethod(ClassOrInterfaceDeclaration type, String moduleName) { + var method = + type.addMethod("builder", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC) + .setType(parseClassOrInterfaceType("Instance.Builder")); + + var body = method.createBody(); + body.addStatement(StaticJavaParser.parseStatement("var module = load();")); + body.addStatement( + StaticJavaParser.parseStatement("byte[][] nativeCode = loadNativeCode();")); + body.addStatement( + StaticJavaParser.parseStatement( + "if (nativeCode != null) {\n" + + " return NativeMachineFactory.builder(module)" + + ".withPrecompiledCode(nativeCode)" + + ".toInstanceBuilder();\n" + + "}")); + body.addStatement( + StaticJavaParser.parseStatement( + "return Instance.builder(module).withMachineFactory(" + + moduleName + + "::create);")); + } + + private static void generateSafeBuilderMethod( + ClassOrInterfaceDeclaration type, String moduleName) { + var method = + type.addMethod("safeBuilder", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC) + .setType(parseClassOrInterfaceType("Instance.Builder")); + + method.createBody() + .addStatement( + StaticJavaParser.parseStatement( + "return Instance.builder(load()).withMachineFactory(" + + moduleName + + "::create);")); + } +} diff --git a/redline/pom.xml b/redline/pom.xml index 13f29061f..e8c0f3c7c 100644 --- a/redline/pom.xml +++ b/redline/pom.xml @@ -17,6 +17,7 @@ api bridge + build-time-compiler compiler runner-jffi runner-jffi-tests @@ -29,6 +30,7 @@ [25,) + it runner runner-tests From 38d1566f3b9c70ef51548abd13a22b2a8d981a82 Mon Sep 17 00:00:00 2001 From: andreatp Date: Fri, 24 Jul 2026 15:04:01 +0100 Subject: [PATCH 2/4] Add true flag and E2E integration test - Add true shortcut to compile for all supported platforms (instead of listing 6 target triples) - Add redline/it module with Maven Invoker integration test: - Compiles add.wat.wasm with native code for all platforms - Tests builder() (native), safeBuilder() (bytecode), loadNativeCode() - Verifies native and bytecode produce identical results - Add logging when redline native compilation runs --- .../endive/experimental/compiler/cli/Cli.java | 19 ++++- .../endive/build/time/compiler/Generator.java | 31 ------- .../build/time/compiler/GeneratorMain.java | 40 +++++++++ .../time/maven/EndiveCompilerGenMojo.java | 48 +++-------- pom.xml | 8 +- redline/bridge/pom.xml | 2 +- .../experimental/build/RedlineGenerator.java | 9 ++ redline/it/pom.xml | 68 ++++++++++++++++ .../it/src/it/redline-e2e/invoker.properties | 1 + redline/it/src/it/redline-e2e/pom.xml | 77 ++++++++++++++++++ .../test/java/endive/test/RedlineE2eTest.java | 53 ++++++++++++ .../src/test/resources/add.wat.wasm | Bin 0 -> 67 bytes redline/it/src/it/settings.xml | 35 ++++++++ 13 files changed, 314 insertions(+), 77 deletions(-) create mode 100644 build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java create mode 100644 redline/it/pom.xml create mode 100644 redline/it/src/it/redline-e2e/invoker.properties create mode 100644 redline/it/src/it/redline-e2e/pom.xml create mode 100644 redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java create mode 100644 redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm create mode 100644 redline/it/src/it/settings.xml diff --git a/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java b/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java index 9e4245caa..6a954e005 100644 --- a/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java +++ b/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java @@ -71,6 +71,14 @@ public String[] getVersion() { "The indexes of functions that should be interpreted, separated by commas") Set interpretedFunctions; + @CommandLine.Option( + order = 7, + names = "--module-interface", + description = + "Fully qualified class name for which to generate _ModuleExports and" + + " _ModuleImports wrappers") + String moduleInterface; + @Override public void run() { var config = @@ -82,6 +90,7 @@ public void run() { .withTargetWasmFolder(targetWasmFolder) .withInterpreterFallback(interpreterFallback) .withInterpretedFunctions(interpretedFunctions) + .withModuleInterface(moduleInterface) .build(); var generator = new Generator(config); @@ -90,13 +99,19 @@ public void run() { var interpretedFunctions = generator.generateResources(); generator.generateMetaWasm(interpretedFunctions); generator.generateSources(); + if (moduleInterface != null && !moduleInterface.isEmpty()) { + generator.generateModuleInterface(moduleInterface); + } } catch (IOException e) { throw new CommandLine.PicocliException("Failed to execute the command", e); } } + public static int execute(String[] args) { + return new CommandLine(new Cli()).execute(args); + } + public static void main(String[] args) { - int exitCode = new CommandLine(new Cli()).execute(args); - System.exit(exitCode); + System.exit(execute(args)); } } diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java index 8e4b73092..1aacbf2cd 100644 --- a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java +++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Generator.java @@ -56,37 +56,6 @@ public Generator(Config config) { this.config = config; } - public static void main(String[] args) throws IOException { - if (args.length < 5) { - throw new IllegalArgumentException( - "Usage: Generator " - + " " - + " [interpreterFallback] [moduleInterface]"); - } - var configBuilder = - Config.builder() - .withWasmFile(Path.of(args[0])) - .withName(args[1]) - .withTargetClassFolder(Path.of(args[2])) - .withTargetSourceFolder(Path.of(args[3])) - .withTargetWasmFolder(Path.of(args[4])); - if (args.length > 5 && !args[5].isEmpty()) { - configBuilder.withInterpreterFallback( - run.endive.compiler.InterpreterFallback.valueOf(args[5])); - } - if (args.length > 6 && !args[6].isEmpty()) { - configBuilder.withModuleInterface(args[6]); - } - var config = configBuilder.build(); - var generator = new Generator(config); - var interpreted = generator.generateResources(); - generator.generateMetaWasm(interpreted); - generator.generateSources(); - if (config.moduleInterface() != null && !config.moduleInterface().isEmpty()) { - generator.generateModuleInterface(config.moduleInterface()); - } - } - public Set generateResources() throws IOException { var module = Parser.parse(config.wasmFile()); var machineName = config.name() + "Machine"; diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java new file mode 100644 index 000000000..fa24c8d3d --- /dev/null +++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java @@ -0,0 +1,40 @@ +package run.endive.build.time.compiler; + +import java.io.IOException; +import java.nio.file.Path; +import run.endive.compiler.InterpreterFallback; + +public final class GeneratorMain { + + private GeneratorMain() {} + + public static void main(String[] args) throws IOException { + if (args.length < 5) { + throw new IllegalArgumentException( + "Usage: GeneratorMain " + + " " + + " [interpreterFallback] [moduleInterface]"); + } + var configBuilder = + Config.builder() + .withWasmFile(Path.of(args[0])) + .withName(args[1]) + .withTargetClassFolder(Path.of(args[2])) + .withTargetSourceFolder(Path.of(args[3])) + .withTargetWasmFolder(Path.of(args[4])); + if (args.length > 5 && !args[5].isEmpty()) { + configBuilder.withInterpreterFallback(InterpreterFallback.valueOf(args[5])); + } + if (args.length > 6 && !args[6].isEmpty()) { + configBuilder.withModuleInterface(args[6]); + } + var config = configBuilder.build(); + var generator = new Generator(config); + var interpreted = generator.generateResources(); + generator.generateMetaWasm(interpreted); + generator.generateSources(); + if (config.moduleInterface() != null && !config.moduleInterface().isEmpty()) { + generator.generateModuleInterface(config.moduleInterface()); + } + } +} diff --git a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java index ee1c3a82b..d1e8ad898 100644 --- a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java +++ b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java @@ -23,82 +23,55 @@ @Mojo(name = "compile", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true) public class EndiveCompilerGenMojo extends AbstractMojo { - /** - * the wasm module to be used - */ @Parameter(required = true) private File wasmFile; - /** - * the base name to be used for the generated classes - */ @Parameter(required = true) private String name; - /** - * the target folder to generate classes - */ @Parameter( required = true, defaultValue = "${project.build.directory}/generated-resources/endive-compiler") private File targetClassFolder; - /** - * the target source folder to generate the Machine implementation - */ @Parameter( required = true, defaultValue = "${project.build.directory}/generated-sources/endive-compiler") private File targetSourceFolder; - /** - * the target wasm folder to generate the stripped meta wasm module - */ @Parameter( required = true, defaultValue = "${project.build.directory}/generated-resources/endive-compiler") private File targetWasmFolder; - /** - * the action to take if the compiler needs to use the interpreter because a function is too big - */ @Parameter(required = true, defaultValue = "FAIL") InterpreterFallback interpreterFallback; - /** - * The indexes of functions that should be interpreted, separated by commas - */ @Parameter(required = false, defaultValue = "") Set interpretedFunctions; - /** - * Fully qualified name of the user's class that will use the compiled module. - * When set, the plugin generates _ModuleExports and _ModuleImports wrapper classes, - * eliminating the need for @WasmModuleInterface annotation and the annotation processor. - */ @Parameter(required = false) String moduleInterface; /** - * Target triples for Redline native compilation. When set, the plugin - * cross-compiles the Wasm module to native code for each target using - * Cranelift and generates builder()/safeBuilder() methods in the module class. - * Example: x86_64-unknown-linux-gnu, aarch64-apple-darwin + * Enable Redline native compilation (experimental) for all supported + * platforms (x86_64 and aarch64 on Linux, macOS, and Windows). */ - @Parameter(required = false) - List redlineTargets; + @Parameter(required = false, defaultValue = "false") + boolean redlineExperimental; /** - * The target resource folder for native code files (.native). + * Target triples for Redline native compilation. Overrides {@code redlineExperimental} + * for fine-grained control over which platforms to cross-compile for. */ + @Parameter(required = false) + List redlineTargets; + @Parameter( required = true, defaultValue = "${project.build.directory}/generated-resources/endive-compiler") private File targetResourceFolder; - /** - * The current Maven project. - */ @Parameter(property = "project", required = true, readonly = true) private MavenProject project; @@ -119,6 +92,8 @@ public void execute() throws MojoExecutionException { .withTargetResourceFolder(targetResourceFolder.toPath()); if (redlineTargets != null && !redlineTargets.isEmpty()) { configBuilder.withRedlineTargets(redlineTargets); + } else if (redlineExperimental) { + configBuilder.withRedlineTargets(RedlineGenerator.allTargets()); } var config = configBuilder.build(); @@ -130,6 +105,7 @@ public void execute() throws MojoExecutionException { generator.generateSources(); if (config.hasRedlineTargets()) { + getLog().info("Redline native compilation for targets: " + config.redlineTargets()); var redlineGenerator = new RedlineGenerator(config); redlineGenerator.generateNativeCode(); redlineGenerator.extendGeneratedSources(); diff --git a/pom.xml b/pom.xml index d5999c408..d6ac3bde2 100644 --- a/pom.xml +++ b/pom.xml @@ -1046,13 +1046,6 @@ - - redline - - redline - - - default-all-modules @@ -1078,6 +1071,7 @@ jmh log machine-tests + redline runtime runtime-tests test-gen-lib diff --git a/redline/bridge/pom.xml b/redline/bridge/pom.xml index 784cfdd8a..bbf6c3627 100644 --- a/redline/bridge/pom.xml +++ b/redline/bridge/pom.xml @@ -87,7 +87,7 @@ generate-sources - run.endive.build.time.compiler.Generator + run.endive.build.time.compiler.GeneratorMain ${project.basedir}/../cranelift_bridge.wasm run.endive.redline.experimental.bridge.internal.Cranelift diff --git a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java index f16ed9840..5c5e63f9e 100644 --- a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java +++ b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java @@ -13,6 +13,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import run.endive.build.time.compiler.Config; import run.endive.redline.experimental.api.NativeCodeSerializer; import run.endive.redline.experimental.api.internal.RedlineTarget; @@ -23,6 +26,12 @@ public final class RedlineGenerator { private final Config config; + public static List allTargets() { + return Arrays.stream(RedlineTarget.values()) + .map(RedlineTarget::triple) + .collect(Collectors.toList()); + } + public RedlineGenerator(Config config) { this.config = config; } diff --git a/redline/it/pom.xml b/redline/it/pom.xml new file mode 100644 index 000000000..0a98d80b6 --- /dev/null +++ b/redline/it/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + + run.endive + redline-parent-experimental + 999-SNAPSHOT + ../pom.xml + + redline-it-experimental + jar + Endive - Redline IT + Integration tests for the Redline native compiler + + + true + + + + + run.endive + endive-compiler-maven-plugin + + + run.endive + redline-runner-experimental + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + run.endive:endive-compiler-maven-plugin + run.endive:redline-runner-experimental + + + + + org.apache.maven.plugins + maven-invoker-plugin + + ${project.build.directory}/it + true + src/it/settings.xml + verify + true + ${skipTests} + true + invoker.properties + + + + integration-tests + + install + run + + + + + + + diff --git a/redline/it/src/it/redline-e2e/invoker.properties b/redline/it/src/it/redline-e2e/invoker.properties new file mode 100644 index 000000000..84099bc5a --- /dev/null +++ b/redline/it/src/it/redline-e2e/invoker.properties @@ -0,0 +1 @@ +invoker.goals=test diff --git a/redline/it/src/it/redline-e2e/pom.xml b/redline/it/src/it/redline-e2e/pom.xml new file mode 100644 index 000000000..34f210861 --- /dev/null +++ b/redline/it/src/it/redline-e2e/pom.xml @@ -0,0 +1,77 @@ + + + + 4.0.0 + run.endive + redline-e2e-it + 0.0-SNAPSHOT + jar + + + 25 + + + + + run.endive + redline-api-experimental + @project.version@ + + + run.endive + redline-runner-experimental + @project.version@ + + + run.endive + runtime + @project.version@ + + + + org.junit.jupiter + junit-jupiter-api + @junit.version@ + test + + + org.junit.jupiter + junit-jupiter-engine + @junit.version@ + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @maven-compiler-plugin.version@ + + ${maven.compiler.release} + + + + run.endive + endive-compiler-maven-plugin + @project.version@ + + + compile-i32 + + compile + + + endive.test.AddModule + src/test/resources/add.wat.wasm + WARN + true + + + + + + + + diff --git a/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java new file mode 100644 index 000000000..d1c06503b --- /dev/null +++ b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java @@ -0,0 +1,53 @@ +package endive.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class RedlineE2eTest { + + @Test + public void nativeBuilderProducesCorrectResults() { + try (var instance = AddModule.builder().build()) { + var add = instance.export("add"); + assertArrayEquals(new long[] {3}, add.apply(1, 2)); + assertArrayEquals(new long[] {0}, add.apply(0, 0)); + assertArrayEquals(new long[] {-1}, add.apply(0, -1)); + } + } + + @Test + public void safeBuilderProducesCorrectResults() { + try (var instance = AddModule.safeBuilder().build()) { + var add = instance.export("add"); + assertArrayEquals(new long[] {3}, add.apply(1, 2)); + assertArrayEquals(new long[] {0}, add.apply(0, 0)); + assertArrayEquals(new long[] {-1}, add.apply(0, -1)); + } + } + + @Test + public void nativeCodeIsAvailable() { + assertNotNull( + AddModule.loadNativeCode(), "Native code should be available on this platform"); + } + + @Test + public void bothBuildersProduceSameResults() { + try (var nativeInstance = AddModule.builder().build(); + var safeInstance = AddModule.safeBuilder().build()) { + var nativeAdd = nativeInstance.export("add"); + var safeAdd = safeInstance.export("add"); + + for (int a = -10; a <= 10; a++) { + for (int b = -10; b <= 10; b++) { + assertArrayEquals( + safeAdd.apply(a, b), + nativeAdd.apply(a, b), + "add(" + a + ", " + b + ") should match"); + } + } + } + } +} diff --git a/redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm b/redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ad1f2f7a3e5ef7d1729d54d08e504be1ef891ae5 GIT binary patch literal 67 zcmWm3u?>JQ3`N2J9E25EfHI?Dlr%`8VF0g=xb_t9L=w(vnPf5KPKn;7t>S8G+`EqD Oi8)x&e^!x+fa?Q1WC;TR literal 0 HcmV?d00001 diff --git a/redline/it/src/it/settings.xml b/redline/it/src/it/settings.xml new file mode 100644 index 000000000..2d90068bb --- /dev/null +++ b/redline/it/src/it/settings.xml @@ -0,0 +1,35 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + From 66ffb2ee681e2da10f193096477950c41fd385e7 Mon Sep 17 00:00:00 2001 From: andreatp Date: Tue, 4 Aug 2026 18:40:20 +0100 Subject: [PATCH 3/4] Use SPI for native backend discovery, restore redline profile gate - Restore redline profile in root POM so regular CI never enters redline/ - Replace direct NativeMachineFactory (Panama) usage in generated code with NativeMachineFactoryProvider.discover() (SPI) - Remove hardcoded JDK 25 version check from NativeCodeHolder - E2E integration test now works on any JDK via JFFI runner - Fix i32 sign-extension in E2E test assertions --- pom.xml | 8 +++++- .../experimental/build/RedlineGenerator.java | 11 ++++---- redline/it/pom.xml | 4 +-- redline/it/src/it/redline-e2e/pom.xml | 8 ++---- .../test/java/endive/test/RedlineE2eTest.java | 26 +++++++++++++++---- redline/pom.xml | 2 +- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index d6ac3bde2..d5999c408 100644 --- a/pom.xml +++ b/pom.xml @@ -1046,6 +1046,13 @@ + + redline + + redline + + + default-all-modules @@ -1071,7 +1078,6 @@ jmh log machine-tests - redline runtime runtime-tests test-gen-lib diff --git a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java index 5c5e63f9e..9ebad5534 100644 --- a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java +++ b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java @@ -77,8 +77,8 @@ public void extendGeneratedSources() throws IOException { var type = cu.getClassByName(baseName).orElseThrow(); cu.addImport("run.endive.redline.experimental.api.NativeCodeSerializer"); + cu.addImport("run.endive.redline.experimental.api.NativeMachineFactoryProvider"); cu.addImport("run.endive.redline.experimental.api.internal.RedlineTarget"); - cu.addImport("run.endive.redline.experimental.runner.NativeMachineFactory"); cu.addImport("java.io.InputStream"); cu.addImport("java.io.IOException"); cu.addImport("java.io.UncheckedIOException"); @@ -114,7 +114,7 @@ private static void generateNativeCodeHolderInnerClass( initBody.addStatement( StaticJavaParser.parseStatement( - "if (host == null || Runtime.version().feature() < 25) {\n" + "if (host == null) {\n" + " CODE = null;\n" + "} else {\n" + " String resource = \"" @@ -154,9 +154,10 @@ private static void generateBuilderMethod(ClassOrInterfaceDeclaration type, Stri body.addStatement( StaticJavaParser.parseStatement( "if (nativeCode != null) {\n" - + " return NativeMachineFactory.builder(module)" - + ".withPrecompiledCode(nativeCode)" - + ".toInstanceBuilder();\n" + + " var provider = NativeMachineFactoryProvider.discover();\n" + + " if (provider.isPresent()) {\n" + + " return provider.get().builder(module, nativeCode);\n" + + " }\n" + "}")); body.addStatement( StaticJavaParser.parseStatement( diff --git a/redline/it/pom.xml b/redline/it/pom.xml index 0a98d80b6..91c36dc9d 100644 --- a/redline/it/pom.xml +++ b/redline/it/pom.xml @@ -24,7 +24,7 @@ run.endive - redline-runner-experimental + redline-runner-jffi-experimental @@ -36,7 +36,7 @@ run.endive:endive-compiler-maven-plugin - run.endive:redline-runner-experimental + run.endive:redline-runner-jffi-experimental diff --git a/redline/it/src/it/redline-e2e/pom.xml b/redline/it/src/it/redline-e2e/pom.xml index 34f210861..09c52c649 100644 --- a/redline/it/src/it/redline-e2e/pom.xml +++ b/redline/it/src/it/redline-e2e/pom.xml @@ -7,10 +7,6 @@ 0.0-SNAPSHOT jar - - 25 - - run.endive @@ -19,7 +15,7 @@ run.endive - redline-runner-experimental + redline-runner-jffi-experimental @project.version@ @@ -49,7 +45,7 @@ maven-compiler-plugin @maven-compiler-plugin.version@ - ${maven.compiler.release} + 11 diff --git a/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java index d1c06503b..0233cafb1 100644 --- a/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java +++ b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java @@ -1,9 +1,12 @@ package endive.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import run.endive.redline.experimental.api.NativeMachineFactoryProvider; class RedlineE2eTest { @@ -13,7 +16,10 @@ public void nativeBuilderProducesCorrectResults() { var add = instance.export("add"); assertArrayEquals(new long[] {3}, add.apply(1, 2)); assertArrayEquals(new long[] {0}, add.apply(0, 0)); - assertArrayEquals(new long[] {-1}, add.apply(0, -1)); + assertEquals( + (int) add.apply(0, -1)[0], + -1, + "i32 add(0, -1) should be -1 when narrowed to int"); } } @@ -23,7 +29,10 @@ public void safeBuilderProducesCorrectResults() { var add = instance.export("add"); assertArrayEquals(new long[] {3}, add.apply(1, 2)); assertArrayEquals(new long[] {0}, add.apply(0, 0)); - assertArrayEquals(new long[] {-1}, add.apply(0, -1)); + assertEquals( + (int) add.apply(0, -1)[0], + -1, + "i32 add(0, -1) should be -1 when narrowed to int"); } } @@ -33,6 +42,13 @@ public void nativeCodeIsAvailable() { AddModule.loadNativeCode(), "Native code should be available on this platform"); } + @Test + public void spiDiscoversProvider() { + var provider = NativeMachineFactoryProvider.discover(); + assertTrue(provider.isPresent(), "Should discover a native provider"); + assertTrue(provider.get().priority() > 0, "Provider should have positive priority"); + } + @Test public void bothBuildersProduceSameResults() { try (var nativeInstance = AddModule.builder().build(); @@ -42,9 +58,9 @@ public void bothBuildersProduceSameResults() { for (int a = -10; a <= 10; a++) { for (int b = -10; b <= 10; b++) { - assertArrayEquals( - safeAdd.apply(a, b), - nativeAdd.apply(a, b), + assertEquals( + (int) safeAdd.apply(a, b)[0], + (int) nativeAdd.apply(a, b)[0], "add(" + a + ", " + b + ") should match"); } } diff --git a/redline/pom.xml b/redline/pom.xml index e8c0f3c7c..2c617995f 100644 --- a/redline/pom.xml +++ b/redline/pom.xml @@ -19,6 +19,7 @@ bridge build-time-compiler compiler + it runner-jffi runner-jffi-tests @@ -30,7 +31,6 @@ [25,) - it runner runner-tests From 835b2d91b245d0d27915da26db92c43112d41ce8 Mon Sep 17 00:00:00 2001 From: andreatp Date: Tue, 4 Aug 2026 19:47:49 +0100 Subject: [PATCH 4/4] Load RedlineGenerator reflectively so plugin compiles without -Predline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler-maven-plugin must build on regular CI (no -Predline). Replace the direct import of RedlineGenerator with reflection — the class is only needed at runtime when true is set. Users add redline-build-time-compiler-experimental as a plugin dependency in their POM to enable it. --- compiler-maven-plugin/pom.xml | 5 +-- .../time/maven/EndiveCompilerGenMojo.java | 38 ++++++++++++++++--- redline/it/src/it/redline-e2e/pom.xml | 7 ++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/compiler-maven-plugin/pom.xml b/compiler-maven-plugin/pom.xml index a7ea02a6a..303c703b7 100644 --- a/compiler-maven-plugin/pom.xml +++ b/compiler-maven-plugin/pom.xml @@ -22,10 +22,6 @@ run.endive compiler - - run.endive - redline-build-time-compiler-experimental - org.apache.maven maven-core @@ -66,4 +62,5 @@ + diff --git a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java index d1e8ad898..21d30b5c9 100644 --- a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java +++ b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java @@ -15,7 +15,6 @@ import run.endive.build.time.compiler.Config; import run.endive.build.time.compiler.Generator; import run.endive.compiler.InterpreterFallback; -import run.endive.redline.experimental.build.RedlineGenerator; /** * This plugin generates an invokable library from the compiled Wasm @@ -93,7 +92,7 @@ public void execute() throws MojoExecutionException { if (redlineTargets != null && !redlineTargets.isEmpty()) { configBuilder.withRedlineTargets(redlineTargets); } else if (redlineExperimental) { - configBuilder.withRedlineTargets(RedlineGenerator.allTargets()); + configBuilder.withRedlineTargets(redlineAllTargets()); } var config = configBuilder.build(); @@ -106,9 +105,7 @@ public void execute() throws MojoExecutionException { if (config.hasRedlineTargets()) { getLog().info("Redline native compilation for targets: " + config.redlineTargets()); - var redlineGenerator = new RedlineGenerator(config); - redlineGenerator.generateNativeCode(); - redlineGenerator.extendGeneratedSources(); + invokeRedlineGenerator(config); } if (moduleInterface != null && !moduleInterface.isEmpty()) { @@ -138,4 +135,35 @@ public void execute() throws MojoExecutionException { project.addResource(resource); project.addCompileSourceRoot(targetSourceFolder.getPath()); } + + @SuppressWarnings("unchecked") + private static List redlineAllTargets() throws MojoExecutionException { + try { + var clazz = Class.forName("run.endive.redline.experimental.build.RedlineGenerator"); + return (List) clazz.getMethod("allTargets").invoke(null); + } catch (ReflectiveOperationException e) { + throw new MojoExecutionException( + "Redline support requires redline-build-time-compiler on the classpath" + + " (build with -Predline)", + e); + } + } + + private static void invokeRedlineGenerator(Config config) throws IOException { + try { + var clazz = Class.forName("run.endive.redline.experimental.build.RedlineGenerator"); + var constructor = clazz.getConstructor(Config.class); + var generator = constructor.newInstance(config); + clazz.getMethod("generateNativeCode").invoke(generator); + clazz.getMethod("extendGeneratedSources").invoke(generator); + } catch (ReflectiveOperationException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } + throw new RuntimeException( + "Redline support requires redline-build-time-compiler on the classpath" + + " (build with -Predline)", + e); + } + } } diff --git a/redline/it/src/it/redline-e2e/pom.xml b/redline/it/src/it/redline-e2e/pom.xml index 09c52c649..123640cb7 100644 --- a/redline/it/src/it/redline-e2e/pom.xml +++ b/redline/it/src/it/redline-e2e/pom.xml @@ -52,6 +52,13 @@ run.endive endive-compiler-maven-plugin @project.version@ + + + run.endive + redline-build-time-compiler-experimental + @project.version@ + + compile-i32