mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-24 16:14:48 +01:00
commit
d3e16a2bb2
10 changed files with 157 additions and 27 deletions
28
.github/workflows/java.yml
vendored
28
.github/workflows/java.yml
vendored
|
@ -23,26 +23,39 @@ jobs:
|
|||
filename: codemp.dll
|
||||
- runner: macos-14
|
||||
target: darwin-arm64
|
||||
filename: codemp.dylib
|
||||
filename: libcodemp.dylib
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: cargo build --release --features=java
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codemp-java-${{ matrix.platform.target }}
|
||||
path: target/release/${{ matrix.platform.filename }}
|
||||
|
||||
publish:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist/java/artifacts
|
||||
pattern: codemp-java-*
|
||||
merge-multiple: true
|
||||
- run: tree
|
||||
working-directory: dist/java
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
- uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: "8.10" # Quotes required to prevent YAML converting to number
|
||||
- run: gradle build
|
||||
gradle-version: "8.10"
|
||||
working-directory: dist/java
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codemp-java-${{ matrix.platform.target }}
|
||||
path: dist/java/build/libs
|
||||
- run: gradle publish
|
||||
working-directory: dist/java
|
||||
env:
|
||||
|
@ -50,3 +63,4 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_CENTRAL_GPG_SECRET_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_CENTRAL_GPG_PASSWORD }}
|
||||
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ dist/java/.gradle/
|
|||
dist/java/.project
|
||||
dist/java/.settings/
|
||||
dist/java/bin/
|
||||
dist/java/artifacts/
|
||||
|
||||
# intellij insists on creating the wrapper every time even if it's not strictly necessary
|
||||
dist/java/gradle/
|
||||
|
|
16
dist/README.md
vendored
16
dist/README.md
vendored
|
@ -33,14 +33,20 @@ Thus, we also provide pre-made Java glue code, wrapping all native calls and def
|
|||
|
||||
The Java bindings have no known major quirk. However, here are a list of facts that are useful to know when developing with these:
|
||||
|
||||
* Memory management is entirely delegated to the JVM's garbage collector.
|
||||
* A more elegant solution than `Object.finalize()`, who is deprecated in newer Java versions, may be coming eventually.
|
||||
* Memory management is entirely delegated to the JVM's garbage collector using the `Cleaner` API.
|
||||
* Because of this, we require Java 11 as minimum version: `Cleaner` was added in version 9. This should not be a problem, as IDEs tend to run on recent versions, but if there is actual demand for it we may add a Java 8-friendly version using `Object.finalize()` (which is deprecated in modern JDKs).
|
||||
* Exceptions coming from the native side have generally been made checked to imitate Rust's philosophy with `Result`.
|
||||
* `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
|
||||
|
||||
### Using
|
||||
`codemp` **will be available soon** as an artifact on [Maven Central](https://mvnrepository.com)
|
||||
`codemp` is available on [Maven Central](https://central.sonatype.com/artifact/mp.code/codemp), with each officially supported OS as an archive classifier.
|
||||
|
||||
### Building
|
||||
This is a [Gradle](https://gradle.org/) project: building requires having both Gradle and Cargo installed, as well as the JDK (any non-abandoned version).
|
||||
Once you have all the requirements, building is as simple as running `gradle build`: the output is going to be a JAR under `build/libs`, which you can import into your classpath with your IDE of choice.
|
||||
> [!NOTE]
|
||||
> The following instructions assume `dist/java` as current working directory.
|
||||
|
||||
This is a [Gradle](https://gradle.org/) project, so you must have install `gradle` (as well as JDK 11 or higher) in order to build it.
|
||||
- You can build a JAR without bundling the native library with `gradle build`.
|
||||
- Otherwise, you can compile the project for your current OS and create a JAR that bundles the resulting binary with `gradle nativeBuild`; do note that this second way of building also requires Cargo and the relevant Rust toolchain.
|
||||
|
||||
In both cases, the output is going to be a JAR under `build/libs`, which you can import into your classpath with your IDE of choice.
|
||||
|
|
114
dist/java/build.gradle
vendored
114
dist/java/build.gradle
vendored
|
@ -1,14 +1,104 @@
|
|||
plugins {
|
||||
id 'java-library'
|
||||
id "com.vanniktech.maven.publish" version "0.29.0"
|
||||
id "com.vanniktech.maven.publish.base" version "0.30.0"
|
||||
id 'com.google.osdetector' version '1.7.3'
|
||||
}
|
||||
|
||||
group = 'mp.code'
|
||||
version = '0.7.3'
|
||||
|
||||
tasks.register('windowsJar', Jar) {
|
||||
outputs.upToDateWhen { false }
|
||||
archiveClassifier = 'windows-x86_64'
|
||||
from sourceSets.main.runtimeClasspath
|
||||
from('artifacts') {
|
||||
include('*.dll')
|
||||
into('natives/')
|
||||
}
|
||||
doFirst {
|
||||
if(!(new File('artifacts/codemp.dll').exists())) {
|
||||
throw new GradleException("The required file does not exist!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('macosJar', Jar) {
|
||||
outputs.upToDateWhen { false }
|
||||
archiveClassifier = 'osx-aarch_64'
|
||||
from sourceSets.main.runtimeClasspath
|
||||
from('artifacts') {
|
||||
include('*.dylib')
|
||||
into('natives/')
|
||||
}
|
||||
doFirst {
|
||||
if(!(new File('artifacts/libcodemp.dylib').exists())) {
|
||||
throw new GradleException("The required file does not exist!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('linuxJar', Jar) {
|
||||
outputs.upToDateWhen { false }
|
||||
archiveClassifier = 'linux-x86_64'
|
||||
from sourceSets.main.runtimeClasspath
|
||||
from('artifacts') {
|
||||
include('*.so')
|
||||
into('natives/')
|
||||
}
|
||||
doFirst {
|
||||
if(!(new File('artifacts/libcodemp.so').exists())) {
|
||||
throw new GradleException("The required file does not exist! Maybe you need to `cargo build` the main library first?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('multiplatformJar', Jar) {
|
||||
outputs.upToDateWhen { false }
|
||||
archiveClassifier = 'all'
|
||||
from sourceSets.main.runtimeClasspath
|
||||
from('artifacts') {
|
||||
include('*')
|
||||
into('natives/')
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
windowsJar {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
extendsFrom implementation, runtimeOnly
|
||||
}
|
||||
linuxJar {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
extendsFrom implementation, runtimeOnly
|
||||
}
|
||||
macosJar {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
extendsFrom implementation, runtimeOnly
|
||||
}
|
||||
multiplatformJar {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
extendsFrom implementation, runtimeOnly
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives sourcesJar
|
||||
archives javadocJar
|
||||
windowsJar(windowsJar)
|
||||
macosJar(macosJar)
|
||||
linuxJar(linuxJar)
|
||||
multiplatformJar(multiplatformJar)
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -29,17 +119,17 @@ tasks.register('cargoBuild', Exec) {
|
|||
commandLine 'cargo', 'build', '--release', '--features=java'
|
||||
}
|
||||
|
||||
jar.archiveClassifier = osdetector.classifier
|
||||
|
||||
def rustDir = projectDir.toPath()
|
||||
.parent
|
||||
.parent
|
||||
.resolve('target')
|
||||
.resolve('release')
|
||||
.toFile()
|
||||
processResources {
|
||||
|
||||
tasks.register('nativeBuild', Jar) {
|
||||
archiveClassifier = osdetector.classifier
|
||||
dependsOn cargoBuild
|
||||
outputs.upToDateWhen { false } // no caching
|
||||
from sourceSets.main.runtimeClasspath
|
||||
from(rustDir) {
|
||||
include('*.dll')
|
||||
include('*.so')
|
||||
|
@ -48,6 +138,20 @@ processResources {
|
|||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact jar
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
artifact windowsJar
|
||||
artifact linuxJar
|
||||
artifact macosJar
|
||||
artifact multiplatformJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import com.vanniktech.maven.publish.SonatypeHost
|
||||
mavenPublishing {
|
||||
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, true)
|
||||
|
|
3
dist/java/src/mp/code/BufferController.java
vendored
3
dist/java/src/mp/code/BufferController.java
vendored
|
@ -70,6 +70,7 @@ public final class BufferController {
|
|||
|
||||
/**
|
||||
* Tries to send a {@link TextChange} update.
|
||||
* @param change the update to send
|
||||
* @throws ControllerException if the controller was stopped
|
||||
*/
|
||||
public void send(TextChange change) throws ControllerException {
|
||||
|
@ -81,6 +82,8 @@ public final class BufferController {
|
|||
/**
|
||||
* Registers a callback to be invoked whenever a {@link BufferUpdate} occurs.
|
||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||
* @see Extensions#drive(boolean)
|
||||
*/
|
||||
public void callback(Consumer<BufferController> cb) {
|
||||
|
|
9
dist/java/src/mp/code/CursorController.java
vendored
9
dist/java/src/mp/code/CursorController.java
vendored
|
@ -43,14 +43,15 @@ public final class CursorController {
|
|||
return recv(this.ptr);
|
||||
}
|
||||
|
||||
private static native void send(long self, Selection cursor) throws ControllerException;
|
||||
private static native void send(long self, Selection selection) throws ControllerException;
|
||||
|
||||
/**
|
||||
* Tries to send a {@link Selection} update.
|
||||
* @param selection the update to send
|
||||
* @throws ControllerException if the controller was stopped
|
||||
*/
|
||||
public void send(Selection cursor) throws ControllerException {
|
||||
send(this.ptr, cursor);
|
||||
public void send(Selection selection) throws ControllerException {
|
||||
send(this.ptr, selection);
|
||||
}
|
||||
|
||||
private static native void callback(long self, Consumer<CursorController> cb);
|
||||
|
@ -58,6 +59,8 @@ public final class CursorController {
|
|||
/**
|
||||
* Registers a callback to be invoked whenever a {@link Cursor} update occurs.
|
||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||
* @see Extensions#drive(boolean)
|
||||
*/
|
||||
public void callback(Consumer<CursorController> cb) {
|
||||
|
|
2
dist/java/src/mp/code/Extensions.java
vendored
2
dist/java/src/mp/code/Extensions.java
vendored
|
@ -33,7 +33,7 @@ public final class Extensions {
|
|||
* <p>
|
||||
* You may alternatively call this with true, in a separate and dedicated Java thread;
|
||||
* it will remain active in the background and act as event loop. Assign it like this:
|
||||
* <p><code>new Thread(() -> Extensions.drive(true)).start();</code></p>
|
||||
* <p><code>new Thread(() -> Extensions.drive(true)).start();</code></p>
|
||||
* @param block true if it should use the current thread
|
||||
*/
|
||||
public static native void drive(boolean block);
|
||||
|
|
2
dist/java/src/mp/code/Workspace.java
vendored
2
dist/java/src/mp/code/Workspace.java
vendored
|
@ -196,6 +196,8 @@ public final class Workspace {
|
|||
/**
|
||||
* Registers a callback to be invoked whenever a new {@link Event} is ready to be received.
|
||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||
* @see Extensions#drive(boolean)
|
||||
*/
|
||||
public void callback(Consumer<Workspace> cb) {
|
||||
|
|
3
dist/java/src/mp/code/data/TextChange.java
vendored
3
dist/java/src/mp/code/data/TextChange.java
vendored
|
@ -3,9 +3,6 @@ package mp.code.data;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import mp.code.Extensions;
|
||||
|
||||
import java.util.OptionalLong;
|
||||
|
||||
/**
|
||||
* A data class holding information about a text change.
|
||||
|
|
|
@ -2,8 +2,8 @@ package mp.code.exceptions;
|
|||
|
||||
/**
|
||||
* An exception that may occur when a {@link mp.code.BufferController} or
|
||||
* a {@link mp.code.CursorController} perform an illegal operation.
|
||||
* It may also occur as a result of {@link mp.code.Workspace#event()}.
|
||||
* a {@link mp.code.CursorController} or {@link mp.code.Workspace} (in the
|
||||
* receiver part) perform an illegal operation.
|
||||
*/
|
||||
public abstract class ControllerException extends Exception {
|
||||
|
||||
|
|
Loading…
Reference in a new issue