-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Cargo (Rust) #42
Changes from all commits
0577d79
e5b1f12
e3ca373
2e645d6
1c02c65
3c503d4
cd9e062
0103d08
d71e190
70f8b33
75b3143
3e7cec7
5e56a7a
e0a8270
f1dca96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "{{project}}" | ||
version = "0.1.0" | ||
authors = ["John Doe <[email protected]>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
jni = "0.19" | ||
|
||
[lib] | ||
crate_type = ["cdylib"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ trait BuildTool { | |
|
||
baseDirectory.mkdir() | ||
val out = baseDirectory.toPath().resolve(name) | ||
Files.createDirectories(out.getParent) | ||
Files.write(out, replaced.getBytes) | ||
out.toFile() | ||
} | ||
|
@@ -77,3 +78,10 @@ trait BuildTool { | |
def getInstance(baseDirectory: File, buildDirectory: File, logger: Logger): Instance | ||
|
||
} | ||
|
||
object BuildTool { | ||
lazy val buildTools: Map[String, BuildTool] = Map( | ||
CMake.name.toLowerCase -> CMake, | ||
Cargo.release.name.toLowerCase -> Cargo.release | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, what do you about making the releaseFlag configurable? i.e. having a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is!
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sideeffffect 💯 niice! |
||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package ch.jodersky.sbt.jni.build | ||
|
||
import sbt._ | ||
|
||
import java.io.File | ||
import scala.sys.process._ | ||
|
||
class Cargo(protected val release: Boolean = true) extends BuildTool { | ||
|
||
def name: String = "Cargo" | ||
|
||
def detect(baseDirectory: File): Boolean = | ||
baseDirectory.list().contains("Cargo.toml") | ||
|
||
protected def templateMappings: List[(String, String)] = List( | ||
"/ch/jodersky/sbt/jni/templates/Cargo.toml" -> "Cargo.toml" | ||
) | ||
|
||
def getInstance(baseDirectory: File, buildDirectory: File, logger: sbt.Logger): Instance = | ||
new Instance(baseDirectory, logger) | ||
|
||
class Instance(protected val baseDirectory: File, protected val logger: sbt.Logger) extends super.Instance { | ||
// IntelliJ friendly logger, IntelliJ doesn't start tests if a line is printed as "error", which Cargo does for regular output | ||
protected val log: ProcessLogger = new ProcessLogger { | ||
def out(s: => String): Unit = logger.info(s) | ||
def err(s: => String): Unit = logger.warn(s) | ||
def buffer[T](f: => T): T = f | ||
} | ||
|
||
def clean(): Unit = | ||
Process("cargo clean", baseDirectory) ! log | ||
|
||
def library(targetDirectory: File): File = { | ||
val releaseFlag = if (release) "--release " else "" | ||
val ev = | ||
Process( | ||
s"cargo build $releaseFlag--target-dir ${targetDirectory.getAbsolutePath}", | ||
baseDirectory | ||
) ! log | ||
if (ev != 0) sys.error(s"Building native library failed. Exit code: $ev") | ||
|
||
val subdir = if (release) "release" else "debug" | ||
val products: List[File] = | ||
(targetDirectory / subdir * ("*.so" | "*.dylib")).get.filter(_.isFile).toList | ||
|
||
// only one produced library is expected | ||
products match { | ||
case Nil => | ||
sys.error( | ||
s"No files were created during compilation, " + | ||
s"something went wrong with the $name configuration." | ||
) | ||
case head :: Nil => | ||
head | ||
case head :: _ => | ||
logger.warn( | ||
"More than one file was created during compilation, " + | ||
s"only the first one (${head.getAbsolutePath}) will be used." | ||
) | ||
head | ||
} | ||
} | ||
} | ||
} | ||
|
||
object Cargo { | ||
|
||
/** | ||
* If `release` is `true`, `cargo build` will run with the `--release` flag. | ||
*/ | ||
def make(release: Boolean = true): BuildTool = new Cargo(release) | ||
|
||
/** | ||
* Cargo build tool, with the `--release` flag. | ||
*/ | ||
lazy val release: BuildTool = make() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Very basic Rust/Cargo test. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
ivyLoggingLevel := UpdateLogging.Quiet | ||
|
||
lazy val root = (project in file(".")).aggregate(core, native) | ||
|
||
lazy val core = project | ||
.settings(libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.9" % Test) | ||
.dependsOn(native % Runtime) | ||
|
||
lazy val native = project | ||
.settings(nativeCompile / sourceDirectory := baseDirectory.value) // `baseDirectory`, not `sourceDirectory` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ha, what a trick, indeed, that's due to the Cargo project structure (a comment for myself):
I'll add it into docs later. |
||
.enablePlugins(JniNative) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package simplecargo | ||
|
||
import ch.jodersky.jni.nativeLoader | ||
|
||
@nativeLoader("adder") | ||
class Adder(val base: Int) { | ||
@native def plus(term: Int): Int // implemented in libadder.so | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package simplecargo | ||
|
||
object Main { | ||
|
||
def main(args: Array[String]): Unit = { | ||
println("hello") | ||
val adder = new Adder(1) | ||
val sum = adder.plus(2) | ||
println(s"sum: $sum") | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package simplecargo | ||
|
||
import org.scalatest.flatspec._ | ||
|
||
class SimpleSpec extends AnyFlatSpec { | ||
|
||
"Calling native methods in tests" should "work" in { | ||
assert(new Adder(12).plus(34) == 46) | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// This is the interface to the JVM that we'll call the majority of our | ||
// methods on. | ||
use jni::JNIEnv; | ||
|
||
// These objects are what you should use as arguments to your native | ||
// function. They carry extra lifetime information to prevent them escaping | ||
// this context and getting used after being GC'd. | ||
use jni::objects::JObject; | ||
|
||
use jni::sys::jint; | ||
|
||
// This keeps Rust from "mangling" the name and making it unique for this | ||
// crate. | ||
#[no_mangle] | ||
pub extern "system" fn Java_simplecargo_Adder_plus( | ||
env: JNIEnv, | ||
object: JObject, | ||
term: jint, | ||
) -> jint { | ||
let base = env.get_field(object, "base", "I").unwrap().i().unwrap(); | ||
println!("Printing from rust library. base: {}", base); | ||
println!("Printing from rust library. term: {}", term); | ||
base + term | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import sbt._ | ||
import sbt.Keys._ | ||
|
||
object ScriptedHelper extends AutoPlugin { | ||
|
||
override def requires = empty | ||
override def trigger = allRequirements | ||
|
||
override def projectSettings = Seq( | ||
scalacOptions ++= Seq("-feature", "-deprecation"), | ||
crossScalaVersions := Seq("2.13.6", "2.12.14"), | ||
scalaVersion := crossScalaVersions.value.head | ||
) | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version=1.5.5 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ivyLoggingLevel := UpdateLogging.Quiet | ||
|
||
addSbtPlugin("com.github.sbt" % "sbt-jni" % System.getProperty("plugin.version")) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
> nativeInit cargo adder | ||
> +test | ||
> +core/run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is necessary, so that the possibly deeply nested structure of directories is created before trying creating a new file.