Redline maven plugin - #131
Conversation
b659f62 to
7b5388f
Compare
|
on hold, not having jffi requires too much Java versions contorsionism. |
401e2a6 to
f58545f
Compare
| } | ||
|
|
||
| @Test | ||
| public void nativeCodeIsAvailable() { |
There was a problem hiding this comment.
this is machine dependent
| "var host = RedlineTarget.detectHost().orElse(null);")); | ||
|
|
||
| initBody.addStatement( | ||
| StaticJavaParser.parseStatement( |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
isn't runtime a transitive dependency of redline?
| <dependencies> | ||
| <dependency> | ||
| <groupId>run.endive</groupId> | ||
| <artifactId>redline-api-experimental</artifactId> |
There was a problem hiding this comment.
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> |
| } | ||
|
|
||
| @Test | ||
| public void nativeCodeIsAvailable() { |
There was a problem hiding this comment.
this is machine dependent
| <version>0.0-SNAPSHOT</version> | ||
| <packaging>jar</packaging> | ||
|
|
||
| <dependencies> |
There was a problem hiding this comment.
same comments as above for the transitive deps
andreaTP
left a comment
There was a problem hiding this comment.
This is finally ready, there might be a few rough edges still, but I want to stop growing it more.
Let's merge and fix on top if something is off.
04eee9f to
79db10f
Compare
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
- 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
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.
None of this was written down: that a Rust toolchain is not needed because inlay fetches a digest-pinned wasm from GHCR, how to build the bridge locally, that a stale local wasm silently shadows the pinned one, and that publishing does not refresh wkg.lock so the lock has to be updated in a follow-up commit.
The imageRef was a hardcoded 999.0.0-SNAPSHOT, which versions:set does
not rewrite, so a release would have shipped jars built from a mutable
OCI tag: once that tag moved, the release could no longer be rebuilt
from its git tag.
The tag is now the cranelift-bridge.version property, and the publish
workflow takes a version as a workflow_dispatch input, so an immutable
release tag can be published and adopted before cutting a release. The
workflow validates the input is semver up front, since an unparseable
tag otherwise only fails later when a consumer writes wkg.lock.
${project.version} cannot be reused here: the Maven version 999-SNAPSHOT
is not semver, which is what wkg.lock requires.
The adopt step was manual, so forgetting it would silently ship jars built against the mutable development snapshot, unreproducible as soon as that tag moved. The release now retags the digest already pinned in wkg.lock under the release version, points cranelift-bridge.version at it and re-pins the lock, all before the version-bump commit so both land in it. Retagging rather than rebuilding means the released wasm is byte-identical to the artifact CI tested and no Rust toolchain is needed in this pipeline. A guard before deploy refuses to publish if the property still resolves to a SNAPSHOT tag, and the snapshot value is restored afterwards so main is not left pinned to the release tag.
Without org.opencontainers.image.source the GHCR package is not linked to this repository, so GITHUB_TOKEN has no write access to it from Actions and the package does not appear under the repo. The package was first pushed by hand and carries no such annotation, so the link has to be granted once in the package settings; this keeps it in place for every subsequent publish.
79db10f to
8b95968
Compare
it, runner-jffi-tests and runner-tests publish nothing, but all three were being built during a release: the first two sat in the unconditional module list and runner-tests rode in via the java25 profile. Building them there only adds time and risk to the release. They now sit behind !release activation, mirroring how the root pom keeps its own test modules out of releases.
No description provided.