Skip to content

Commit

Permalink
Add guide on embedding C in Java using GraalWasm
Browse files Browse the repository at this point in the history
  • Loading branch information
jirkamarsik committed Oct 8, 2024
1 parent 5550bed commit 84e6d42
Show file tree
Hide file tree
Showing 11 changed files with 843 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/graalwasm-embed-c-code-guide.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Test Embed C in Java Using GraalWasm Guide
on:
push:
paths:
- 'graalwasm/graalwasm-embed-c-code-guide/**'
- '.github/workflows/graalwasm-embed-c-code-guide.yml'
pull_request:
paths:
- 'graalwasm/graalwasm-embed-c-code-guide/**'
- '.github/workflows/graalwasm-embed-c-code-guide.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
run:
name: 'graalwasm-embed-c-code-guide'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
java-version: '23.0.0'
distribution: 'graalvm'
github-token: ${{ secrets.GITHUB_TOKEN }}
cache: 'maven'
native-image-job-reports: 'true'
- uses: mymindstorm/setup-emsdk@v14
- name: Build, test, and run 'graalwasm-embed-c-code-guide'
run: |
cd graalwasm/graalwasm-embed-c-code-guide
./mvnw --no-transfer-progress package
./mvnw --no-transfer-progress exec:java
4 changes: 4 additions & 0 deletions graalwasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ This directory contains demo applications and guides for [GraalWasm](https://www
- [Minimal Java application that embeds GraalWasm](graalwasm-starter/)
- [Embed Photon image processing library with GraalWasm in Micronaut](graalwasm-micronaut-photon/)
- [Embed Photon image processing library with GraalWasm in Spring Boot](graalwasm-spring-boot-photon/)

## Guides

- [Embed C in Java Using GraalWasm](graalwasm-embed-c-code-guide/)
2 changes: 2 additions & 0 deletions graalwasm/graalwasm-embed-c-code-guide/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore maven build output directory
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
185 changes: 185 additions & 0 deletions graalwasm/graalwasm-embed-c-code-guide/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Embed C in Java Using GraalWasm

The example below demonstrates how to compile a C function to WebAssembly and run it embedded in a Java application.

## 1. Getting Started

To complete this guide, you need the following:
- [GraalVM JDK](https://www.graalvm.org/downloads/)
- [Emscripten compiler frontend](https://emscripten.org/docs/tools_reference/emcc.html)
- [Maven](https://maven.apache.org/)

## 2. Setting up the Maven Project

To follow this guide, generate the application from the [Maven Quickstart Archetype](https://maven.apache.org/archetypes/maven-archetype-quickstart/):

```shell
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DgroupId=com.example -DartifactId=demo -DinteractiveMode=false
cd demo
```

### 2.1. Adding the Polyglot API and GraalWasm Dependencies

The GraalVM SDK Polyglot API can be easily added as a Maven dependency to your Java project.
The GraalWasm artifact should be on the Java module or class path too.

Add the following set of dependencies to the `<dependencies>` section to your project's _pom.xml_:

- To add the Polyglot API:
```xml
<!-- <dependencies> -->
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>24.1.0</version>
</dependency>
<!-- </dependencies> -->
```
- To add GraalWasm:
```xml
<!-- <dependencies> -->
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>wasm</artifactId>
<version>24.1.0</version>
<type>pom</type>
</dependency>
<!-- </dependencies> -->
```

## 3. Setting Up C Code

Next, we will write a C function and compile it to a WebAssembly module.

### 3.1 Writing C Code

Put the following C program in _src/main/c/floyd.c_:

```c
#include <stdio.h>

void floyd() {
int number = 1;
int rows = 10;
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= i; j++) {
printf("%d ", number);
++number;
}
printf(".\n");
}
}

int main() {
floyd();
return 0;
}
```

Note that `floyd` is defined as a separate function and can be exported.

### 3.2 Compiling C Code to WebAssembly

Compile the C code using the most recent version of the [Emscripten compiler frontend](https://emscripten.org/docs/tools_reference/emcc.html):

```shell
mkdir -p target/classes/com/example
emcc --no-entry -s EXPORTED_FUNCTIONS=_floyd -o target/classes/com/example/floyd.wasm src/main/c/floyd.c
```

> The exported functions must be prefixed by `_`. If you reference that function in the Java code, the exported name should not contain the underscore.
It produces a standalone file _floyd.wasm_ in _target/classes/com/example_, which is where Maven puts resource files for the `com.example` package.
This lets us load the file as a resource from the `com.example.App` class.

#### Using Maven to Compile C Code

We can automate the C compilation and make it a part of the Maven build process by adding the following plugin configuration to the `<build>` section of our _pom.xml_ file.

```xml
<!-- <build> -->
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>emcc</executable>
<commandlineArgs>--no-entry -s EXPORTED_FUNCTIONS=_floyd -o target/classes/com/example/floyd.wasm src/main/c/floyd.c</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<!-- </build> -->
```

This binds the `exec-maven-plugin:exec` goal to the `generate-resources` phase of the build lifecycle.
The `exec` goal runs `emcc` with the same command line arguments as above, ensuring that the generated WebAssembly module file is included as a resource file in the final JAR.

### 4. Using the WebAssembly Module from Java

Now you can embed this WebAssembly function in a Java application. Put the following in _src/main/java/com/example/App.java_:

```java
package com.example;

import java.io.IOException;
import java.net.URL;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;

public class App {
public static void main(String[] args) throws IOException {
// Find the WebAssembly module resource
URL wasmFile = App.class.getResource("floyd.wasm");

// Setup context
Context.Builder contextBuilder = Context.newBuilder("wasm").option("wasm.Builtins", "wasi_snapshot_preview1");
Source.Builder sourceBuilder = Source.newBuilder("wasm", wasmFile).name("example");
Source source = sourceBuilder.build();
Context context = contextBuilder.build();

// Evaluate the WebAssembly module
context.eval(source);

// Execute the floyd function
context.getBindings("wasm").getMember("example").getMember("_initialize").executeVoid();
Value mainFunction = context.getBindings("wasm").getMember("example").getMember("floyd");
mainFunction.execute();
context.close();
}
}
```

### 5. Building and Testing the Application

Compile and run this Java application with Maven:

```shell
mvw package
mvn exec:java -Dexec.mainClass=com.example.App
```

The expected output should contain the first 10 lines of [Floyd's triangle](https://en.wikipedia.org/wiki/Floyd%27s_triangle), printed using the C function:

```
1 .
2 3 .
4 5 6 .
7 8 9 10 .
11 12 13 14 15 .
16 17 18 19 20 21 .
22 23 24 25 26 27 28 .
29 30 31 32 33 34 35 36 .
37 38 39 40 41 42 43 44 45 .
46 47 48 49 50 51 52 53 54 55 .
```
Loading

0 comments on commit 84e6d42

Please sign in to comment.