Skip to content

Redline maven plugin - #131

Open
andreaTP wants to merge 16 commits into
bytecodealliance:mainfrom
andreaTP:redline-maven-plugin
Open

Redline maven plugin#131
andreaTP wants to merge 16 commits into
bytecodealliance:mainfrom
andreaTP:redline-maven-plugin

Conversation

@andreaTP

Copy link
Copy Markdown
Contributor

No description provided.

@andreaTP
andreaTP force-pushed the redline-maven-plugin branch 4 times, most recently from b659f62 to 7b5388f Compare July 29, 2026 20:20
@andreaTP

Copy link
Copy Markdown
Contributor Author

on hold, not having jffi requires too much Java versions contorsionism.

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 <redlineTargets> 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.
- Add <redline>true</redline> 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
@andreaTP
andreaTP force-pushed the redline-maven-plugin branch from 7b5388f to 66ffb2e Compare August 4, 2026 17:40
- Add Rust toolchain + cranelift_bridge.wasm build to ci.yaml
- Remove -Predline from release.yaml and redline.yaml
- Plugin depends directly on redline-build-time-compiler (no reflection)
- Bridge uses exec:java with GeneratorMain to break the cycle
- Generated code uses NativeMachineFactoryProvider SPI (no Panama import)
- Remove redundant targetResourceFolder from Config
- Validate target triples before compilation in RedlineGenerator
- E2E tests: JFFI (all JDKs) + Panama (JDK 25+ only)
- Restore javadoc comments on Mojo fields
@andreaTP
andreaTP force-pushed the redline-maven-plugin branch from 401e2a6 to f58545f Compare August 10, 2026 10:18

@andreaTP andreaTP left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few comments

}

@Test
public void nativeCodeIsAvailable() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is machine dependent

"var host = RedlineTarget.detectHost().orElse(null);"));

initBody.addStatement(
StaticJavaParser.parseStatement(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no parseStatement + string interpolation, we should use plain JavaParser to build the desired code, is more maintainable in the longer run

<dependencies>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javaparser as a dependency of the build time compiler looks a little strange, but maybe worth if it keeps complexity low.

</dependency>
<dependency>
<groupId>run.endive</groupId>
<artifactId>runtime</artifactId>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't runtime a transitive dependency of redline?

<dependencies>
<dependency>
<groupId>run.endive</groupId>
<artifactId>redline-api-experimental</artifactId>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to explicitly add the api module? isn't it transitive from runner?

<configuration>
<name>endive.test.AddModule</name>
<wasmFile>src/test/resources/add.wat.wasm</wasmFile>
<interpreterFallback>WARN</interpreterFallback>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

un-necessary

}

@Test
public void nativeCodeIsAvailable() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is machine dependent

<version>0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comments as above for the transitive deps

Replace Rust toolchain + make all in CI with inlay-maven-plugin
fetching the pre-built wasm from ghcr.io/bytecodealliance/endive-cranelift-bridge.
No Rust needed in any CI workflow except the new wasm-publish.yaml.

- Bridge POM: inlay:fetch before exec:java GeneratorMain
- Root POM: add inlay-maven-plugin to pluginManagement
- CI/redline/release workflows: remove Rust toolchain steps
- New wasm-publish.yaml: build + publish wasm to GHCR on wasm-build changes
- wkg.lock: pins wasm digest for reproducible builds
The it module is unconditional (builds on all JDKs) but
redline-runner-experimental requires JDK 25+. The Panama E2E
test project declares its own dependency via @project.version@.
The it module is unconditional but redline-runner-experimental
requires JDK 25+. Move it to a java25 profile so it only resolves
on JDK 25+ where the Panama E2E test actually runs.
Matches the redlineExperimental flag naming — both are user-facing
plugin parameters for the experimental redline feature.
Replace parseStatement + string concatenation with direct AST node
construction, matching the idiom already used by build-time-compiler's
Generator (which has zero parse calls in its codegen).

Each generator method now carries a comment showing the code it emits.
Generated output is unchanged.
- Drop redline-api and runtime from the E2E POMs; both come
  transitively from the runner, leaving a single consumer dependency
- Drop interpreterFallback=WARN; the trivial add module never needs it
  and FAIL (the default) is the stricter check
- Guard nativeCodeIsAvailable with an assumption on detectHost(), so it
  skips rather than fails on platforms Redline does not target
Two bugs that would have made the publish automation a no-op or
produced an unpullable artifact:

- It pushed :latest while the bridge POM consumes :999.0.0-SNAPSHOT, so
  republishing would never update what the build actually reads. The tag
  now lives in one env var, noted as needing to match the POM.
- It pushed from the repo root, so the image title annotation became
  "redline/cranelift_bridge.wasm" and the OCI client tried to write into
  a directory that does not exist on pull. Push from redline/ instead.

Also emit the wkg.lock refresh command in the job summary, since a
publish invalidates the pinned digest until the lock file is updated.
Removing " -Predline" also consumed the following newline in three
places, concatenating commands: "versions:set ... git add ." would run
git as a Maven lifecycle phase, and "clean deploy" stopped being its own
command. One -Predline also survived, referencing a profile this PR
deletes. The release workflow could not have succeeded.
detectHost() used isAarch64 as its only discriminator, so every other
architecture fell through to the x86_64 variant. On Linux/riscv64 (or
ppc64le, s390x, 32-bit ARM) it returned LINUX_X86_64, the
.x86_64-linux.native resource loaded, and the runner handed x86-64
machine code to the CPU — a JVM crash rather than a fallback to the
build-time compiler.

Recognise x86_64/amd64/x64 explicitly and return empty otherwise, which
routes those platforms to the compiled-bytecode path. Adds the first
tests for RedlineTarget, using the endive.redline.os.* overrides.
Correctness:
- discover() caught ServiceConfigurationError around the loop body, but
  ServiceLoader raises it from the iterator, so a for-each let it escape.
  Wrap next() instead, and also catch LinkageError: the Panama runner is
  compiled for 25 and fails to link on older JDKs, which is exactly the
  case the catch was meant to tolerate.
- NativeCodeSerializer allocated byte[count][] straight from the file, so
  a corrupt count became OutOfMemoryError before truncation could be
  detected. Reject negative counts and lengths and collect incrementally.
- A corrupt blob threw UncheckedIOException from a static initializer,
  which bricks the class for the life of the JVM. Load into a local and
  leave CODE null instead, so builder() degrades to compiled bytecode.

DX:
- Generate nativeProvider(), non-empty exactly when builder() takes the
  native path, and have builder() use it as the single source of truth.
  This makes the choice observable, and gives modules with imported
  memories the provider they must create those imports with.
- Assert in both E2Es that the native path actually engages. Every other
  assertion there passes when it silently does not, because the fallback
  produces identical results.

Cleanup: drop the unused Cli.execute overload, put expected before
actual in the E2E assertEquals calls, and rename the generator's
interpreterBuilder helper to compiledBuilder, which is what it emits.
Since redline builds unconditionally and no longer needs -Predline or a
Rust toolchain, ci.yaml runs the identical command on a superset of the
platforms: it covers ubuntu-latest/25 and macos-latest/25 (exactly the
redline matrix), pins the same testsuite ref and sets the same
MAVEN_OPTS.

Note this leaves redline covered only by jobs with continue-on-error,
so redline failures no longer turn the build red on their own.
@andreaTP
andreaTP marked this pull request as ready for review August 24, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant