Skip to content

Commit f687ebe

Browse files
committed
Bring in a gradle plugin
Many thanks to @renatoathaydes for https://github.com/renatoathaydes/wasm-on-jvm which is the basis of the code. I've mangled it somewhat, any issues are obviously mine.
1 parent bcb505f commit f687ebe

File tree

24 files changed

+525
-22
lines changed

24 files changed

+525
-22
lines changed

annotations/gradle.lockfile

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath,spotles
2020
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2121
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2222
org.jetbrains.kotlin:kotlin-reflect:1.5.31=kotlinCompilerClasspath,spotless-1972456287
23-
org.jetbrains.kotlin:kotlin-reflect:1.6.20=compileClasspath
23+
org.jetbrains.kotlin:kotlin-reflect:1.5.32=compileClasspath
2424
org.jetbrains.kotlin:kotlin-script-runtime:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
2525
org.jetbrains.kotlin:kotlin-scripting-common:1.5.31=kotlinCompilerPluginClasspathMain
2626
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.5.31=kotlinCompilerPluginClasspathMain
2727
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.31=kotlinCompilerPluginClasspathMain
2828
org.jetbrains.kotlin:kotlin-scripting-jvm:1.5.31=kotlinCompilerPluginClasspathMain
2929
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
30-
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20=compileClasspath
30+
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.32=compileClasspath
3131
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.0=kotlinCompilerPluginClasspathMain
3232
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31=spotless-1972456287
3333
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0=kotlinCompilerPluginClasspathMain
3434
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31=spotless-1972456287
3535
org.jetbrains.kotlin:kotlin-stdlib:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
36-
org.jetbrains.kotlin:kotlin-stdlib:1.6.20=compileClasspath
36+
org.jetbrains.kotlin:kotlin-stdlib:1.5.32=compileClasspath
3737
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=kotlinCompilerPluginClasspathMain
3838
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=kotlinCompilerPluginClasspathMain
3939
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.1.0=spotless-1972456287

buildSrc/build.gradle.kts

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ dependencies {
2626

2727
kotlin {
2828
jvmToolchain {
29-
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(11))
29+
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(8))
3030
}
31-
target { JavaLanguageVersion.of(11) }
3231
}

buildSrc/src/main/kotlin/asmble.java-conventions.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencyLocking {
2020
lockAllConfigurations()
2121
}
2222

23-
var kotlinRequire="1.6.+"
23+
var kotlinRequire="1.5.+"
2424
var junitJupiterRequire="5.+"
2525

2626
dependencies {

cli/gradle.lockfile

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath,spotles
2020
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2121
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2222
org.jetbrains.kotlin:kotlin-reflect:1.5.31=kotlinCompilerClasspath,spotless-1972456287
23-
org.jetbrains.kotlin:kotlin-reflect:1.6.20=compileClasspath,runtimeClasspath
23+
org.jetbrains.kotlin:kotlin-reflect:1.5.32=compileClasspath,runtimeClasspath
2424
org.jetbrains.kotlin:kotlin-script-runtime:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
2525
org.jetbrains.kotlin:kotlin-scripting-common:1.5.31=kotlinCompilerPluginClasspathMain
2626
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.5.31=kotlinCompilerPluginClasspathMain
2727
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.31=kotlinCompilerPluginClasspathMain
2828
org.jetbrains.kotlin:kotlin-scripting-jvm:1.5.31=kotlinCompilerPluginClasspathMain
2929
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
30-
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20=compileClasspath,runtimeClasspath
30+
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.32=compileClasspath,runtimeClasspath
3131
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.0=kotlinCompilerPluginClasspathMain
3232
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31=spotless-1972456287
3333
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0=kotlinCompilerPluginClasspathMain
3434
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31=spotless-1972456287
3535
org.jetbrains.kotlin:kotlin-stdlib:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,spotless-1972456287
36-
org.jetbrains.kotlin:kotlin-stdlib:1.6.20=compileClasspath,runtimeClasspath
36+
org.jetbrains.kotlin:kotlin-stdlib:1.5.32=compileClasspath,runtimeClasspath
3737
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=kotlinCompilerPluginClasspathMain
3838
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=kotlinCompilerPluginClasspathMain
3939
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.1.0=spotless-1972456287

compiler/gradle.lockfile

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath,spotles
2323
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2424
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.31=kotlinCompilerClasspath,spotless-1972456287
2525
org.jetbrains.kotlin:kotlin-reflect:1.5.31=kotlinCompilerClasspath,spotless-1972456287
26-
org.jetbrains.kotlin:kotlin-reflect:1.6.20=compileClasspath,testCompileClasspath,testRuntimeClasspath
26+
org.jetbrains.kotlin:kotlin-reflect:1.5.32=compileClasspath,testCompileClasspath,testRuntimeClasspath
2727
org.jetbrains.kotlin:kotlin-script-runtime:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,spotless-1972456287
2828
org.jetbrains.kotlin:kotlin-scripting-common:1.5.31=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
2929
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.5.31=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
3030
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.31=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
3131
org.jetbrains.kotlin:kotlin-scripting-jvm:1.5.31=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
3232
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,spotless-1972456287
33-
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20=compileClasspath,testCompileClasspath,testRuntimeClasspath
33+
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.32=compileClasspath,testCompileClasspath,testRuntimeClasspath
3434
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
3535
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31=spotless-1972456287
3636
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
3737
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31=spotless-1972456287
3838
org.jetbrains.kotlin:kotlin-stdlib:1.5.31=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,spotless-1972456287
39-
org.jetbrains.kotlin:kotlin-stdlib:1.6.20=compileClasspath,testCompileClasspath,testRuntimeClasspath
40-
org.jetbrains.kotlin:kotlin-test-junit:1.6.20=testCompileClasspath,testRuntimeClasspath
41-
org.jetbrains.kotlin:kotlin-test:1.6.20=testCompileClasspath,testRuntimeClasspath
39+
org.jetbrains.kotlin:kotlin-stdlib:1.5.32=compileClasspath,testCompileClasspath,testRuntimeClasspath
40+
org.jetbrains.kotlin:kotlin-test-junit:1.5.32=testCompileClasspath,testRuntimeClasspath
41+
org.jetbrains.kotlin:kotlin-test:1.5.32=testCompileClasspath,testRuntimeClasspath
4242
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
4343
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
4444
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.1.0=spotless-1972456287

compiler/src/test/kotlin/eu/aylett/asmble/TestBase.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package eu.aylett.asmble
22

33
import eu.aylett.asmble.util.Logger
44

5-
abstract class TestBase : Logger by TestBase.logger {
5+
abstract class TestBase : Logger by logger {
66
companion object {
77
val logger = Logger.Print(Logger.Level.INFO)
88
}

gradle-plugin/LICENSE

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Copyright 2019 Renato Athaydes
2+
3+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4+
following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
7+
following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10+
following disclaimer in the documentation and/or other materials provided with the distribution.
11+
12+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
13+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
15+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
17+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
18+
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

gradle-plugin/README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# WASM-on-JVM Gradle Plugin
2+
3+
This is a [Gradle Plugin](https://docs.gradle.org/current/userguide/plugins.html) that lets Java developers
4+
use [Web Assembly](https://webassembly.org/) (WASM) in their project with minimal friction,
5+
whether it's WASM text files [written by hand](https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html),
6+
or binaries compiled with [WASM compilers](https://github.com/appcypher/awesome-wasm-langs).
7+
8+
Compilation from WASM to JVM bytecode is performed by the [cretz/asmble](https://github.com/cretz/asmble) project.
9+
10+
## How to use
11+
12+
Apply this plugin to your Gradle project:
13+
14+
```groovy
15+
plugins {
16+
id 'com.athaydes.wasm'
17+
}
18+
```
19+
20+
Add `.wasm` (binary) or `wast` (text) WASM files to the `src/main/wasm` directory.
21+
22+
Compile them with `gradle compileWasm`.
23+
24+
Use the generated classes in your Java code.
25+
26+
Compile everything and run it!
27+
28+
## Configuration
29+
30+
Optionally, you can configure how the WASM Plugin behaves:
31+
32+
```groovy
33+
wasm {
34+
// where to find wasm files
35+
// (this value is the default, so omit if you don't want to change it)
36+
sourceDir = file('src/main/wasm')
37+
38+
// this is used as the top-level package name for this project.
39+
packageName = 'org.mypkg'
40+
41+
// mappings between WASM file names and desired Java class names.
42+
// By default, WASM files are compiled into Java classes with the same name as the files,
43+
// without their extension.
44+
classNameByFile = [
45+
'add.wasm': 'WasmAdder'
46+
]
47+
}
48+
```
49+
50+
## Examples
51+
52+
See the [hello-world](examples/hello-world) example to get started real quick!
53+
54+
And the [C-to-WASM-to-JVM](examples/configured-c-to-wasm) example for more advanced usage, such as
55+
compiling C files to WASM, then using them from Java or other JVM languages, and running it with a single,
56+
fat jar.

gradle-plugin/build.gradle.kts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
plugins {
2+
id("asmble.java-conventions")
3+
`java-gradle-plugin`
4+
id("asmble.publish-conventions")
5+
}
6+
7+
dependencies {
8+
implementation(project(":compiler"))
9+
testImplementation(project(":annotations"))
10+
}
11+
12+
gradlePlugin {
13+
plugins {
14+
create("asmbleGradlePlugin") {
15+
id = "eu.aylett.asmble.gradle-plugin"
16+
implementationClass = "com.athaydes.wasm.WasmGradlePlugin"
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Compile C to WASM to JVM bytecode Example
2+
3+
This example illustrates two things:
4+
5+
* how to compile C code to WASM via Gradle.
6+
* how to configure the build to customize package name and class names,
7+
regardless of the WASM file name and location.
8+
9+
See the [build.gradle](build.gradle) file for details.
10+
11+
## Requirements
12+
13+
This example requires that you have [LLVM](http://llvm.org/) installed as it will compile C code to WASM using
14+
the `clang` command.
15+
16+
## Compiling
17+
18+
The `compileC` Gradle task invokes the LLVM compiler to compile C files in
19+
[src/main/c](src/main/c) to WASM binaries.
20+
21+
The WASM files are saved at `build/compiled-wasm`.
22+
23+
After that, the `compileWasm` task, added by this plugin, compiles the WASM files to Java class
24+
files, putting them in the standard location `build/classes/wasm/main`.
25+
26+
This directory is automatically added to the `implementation` dependencies of the Gradle project when
27+
this plugin is applied to it, so the classes are "visible" to Java (and other JVM languages) source code.
28+
29+
To compile everything, run:
30+
31+
```bash
32+
gradle compileJava
33+
```
34+
35+
The `compileWasm` task is run automatically.
36+
37+
## Running
38+
39+
To run the `Main` class, which uses compiled C code, the easiest way is to create a fat jar first:
40+
41+
```bash
42+
gradle fatJar
43+
```
44+
45+
Then, simply run it with:
46+
47+
```bash
48+
java -jar build/libs/wasm-example-1.0-SNAPSHOT.jar 2 3
49+
```
50+
51+
Which should print `5` (showing that the C code is adding `2` and `3` as expected).
52+
53+
<hr/>
54+
55+
Notice that because this example does not have any external dependencies, using the jar produced by the `jar`
56+
task would still work as well, so a fat jar is not necessary.
57+
58+
You can even run it by simply specifying where the class files are (as in the [hello-world](../hello-world) example)
59+
and giving the qualified name of the main class:
60+
61+
```bash
62+
java -cp build/classes/java/main:build/classes/wasm/main com.athaydes.wasm.c.Main 2 3
63+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
buildscript {
2+
repositories {
3+
maven {
4+
url "https://plugins.gradle.org/m2/"
5+
}
6+
flatDir { dir '../../wasm-gradle-plugin/build/libs' }
7+
}
8+
dependencies {
9+
classpath "gradle.plugin.com.athaydes.wasm:wasm-gradle-plugin:1.0"
10+
11+
// classpath name: "wasm-gradle-plugin-1.0-SNAPSHOT"
12+
// classpath 'com.github.cretz.asmble:asmble-compiler:0.3.0'
13+
}
14+
}
15+
16+
apply plugin: "java"
17+
apply plugin: "com.athaydes.wasm"
18+
19+
group 'com.athaydes.wasm'
20+
version '1.0-SNAPSHOT'
21+
22+
repositories {
23+
jcenter()
24+
}
25+
26+
final wasmDir = file( "$buildDir/compiled-wasm" )
27+
28+
wasm {
29+
// where to find wasm files
30+
sourceDir = wasmDir
31+
32+
// this is used as the top-level package name for this project.
33+
packageName = 'com.athaydes.wasm.c'
34+
35+
// By default, class names are the same as the file names, so for `add.wasm` we would end up with
36+
// a class named `add`, which is not very good Java-style.
37+
// If the file name had a dash, as in `add-int`, then we wouldn't even be able to use it from Java source code!
38+
// But renaming all your wasm files would be a bummer, so this plugin allows you to provide a mapping between
39+
// file names and class names, as shown below.
40+
// The resulting class is called (packageName + '.' + name), so in this case, the class will be called
41+
// com.athaydes.wasm.c.Adder
42+
classNameByFile = [
43+
'add.wasm': 'Adder'
44+
]
45+
}
46+
47+
task compileC( description: 'Compiles C sources into WASM binaries' ) {
48+
inputs.dir( 'src/main/c' )
49+
outputs.dir( wasmDir )
50+
compileWasm.dependsOn( 'compileC' )
51+
52+
doFirst { wasmDir.mkdirs() }
53+
54+
doLast {
55+
exec {
56+
// See https://dassur.ma/things/c-to-webassembly/ for details about this
57+
commandLine 'clang', '--target=wasm32', '-nostdlib',
58+
'-Wl,--no-entry', '-Wl,--export-all', '-o', "$wasmDir/add.wasm",
59+
'src/main/c/add.c'
60+
}
61+
}
62+
}
63+
64+
task fatJar( type: Jar, description: 'Creates a fat, runnable jar for this app' ) {
65+
manifest {
66+
attributes 'Main-Class': 'com.athaydes.wasm.c.Main'
67+
}
68+
archiveBaseName = 'wasm-example'
69+
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree( it ) } }
70+
with jar
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
3+
<script type="module">
4+
async function init() {
5+
const { instance } = await WebAssembly.instantiateStreaming(
6+
fetch("build/compiled-wasm/add.wasm")
7+
);
8+
console.log(instance.exports.add(4, 1));
9+
}
10+
init();
11+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Filename: add.c
2+
int add(int a, int b) {
3+
return a + b;
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.athaydes.wasm.c;
2+
3+
public class Main {
4+
public static void main( String[] args ) {
5+
if ( args.length != 2 ) {
6+
throw new RuntimeException( "Please provide two integer values to be added." );
7+
}
8+
int a = Integer.parseInt( args[ 0 ] );
9+
int b = Integer.parseInt( args[ 1 ] );
10+
11+
System.out.println( new Adder( 131072 ).add( a, b ) );
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Hello World Example
2+
3+
Simplest possible example using the Gradle WASM plugin.
4+
5+
There are only 2 source files:
6+
7+
* [src/main/java/Hello.java](src/main/java/Hello.java)
8+
* [src/main/wasm/HelloWasm.wast](src/main/wasm/HelloWasm.wast)
9+
10+
To compile, run:
11+
12+
```bash
13+
gradle compileJava
14+
```
15+
16+
The `compileWasm` task is run automatically.
17+
18+
To run the `Hello` class, which uses the WASM-derived `HelloWasm` class, run:
19+
20+
```bash
21+
java -cp build/classes/java/main:build/classes/wasm/main Hello
22+
```

0 commit comments

Comments
 (0)