Code Monkey home page Code Monkey logo

sbt-jni's Introduction

CI Maven Central Snapshots

sbt version group id plugin version
0.13.x ch.jodersky 1.2.6
1.x ch.jodersky 1.4.1
1.x com.github.sbt Maven Central

SBT-JNI

A suite of sbt plugins for simplifying creation and distribution of JNI programs.

Setup

Add sbt-jni as a dependency to project/plugins.sbt:

addSbtPlugin("com.github.sbt" % "sbt-jni" % "<latest version>")

where <latest version> refers to the version indicated by the badge above.

Note: We changed the organization from ch.jodersky to com.github.sbt

Motivation

Java Native Interface (JNI), is a framework that enables programs written in a JVM language to interact with native code and vice-versa. Such programs can be divided into two logical parts: the JVM part, consisting of sources that will be compiled to bytecode (e.g. Scala or Java), and the native part, consisting of sources that will be compiled to machine-native code (e.g. C, C++ or assembly).

Using native code can be beneficial in some situations: it can, for example, provide raw performance boosts or enable otherwise infeasable features such as interaction with peripherals. However, it also adds a few layers of complexities, most notably:

  • Compilation: the project is divided into two parts, each of which require separate compilation.
  • Portability: native binaries only run on the platform on which they were compiled.
  • Distribution: native binaries must be made available and packaged for every supported platform.

The second point, portability, is inherent to JNI and thus unavoidable. However the first and last points can be greatly simplified with the help of build tools.

Plugin Summary

Plugin Description
JniJavah Adds support for generating headers from classfiles that have @native methods.
JniLoad Makes @nativeLoader annotation available, that injects code to transparently load native libraries.
JniNative Adds sbt wrapper tasks around native build tools to ease building and integrating native libraries.
JniPackage Packages native libraries into multi-platform fat jars. No more manual library installation!

Note that most plugins are enabled in projects by default. Disabling their functionality can be achieved by adding disablePlugins(<plugin>) to the corresponding project definition (for example, should you wish to disable packaging of native libraries).

Plugin Details

JniJavah

Enabled Source
automatic, for all projects JniJavah.scala

This plugin wraps the JDK javah command 1.

Run sbt-javah to generate C header files with prototypes for any methods marked as native. E.g. the following scala class

package org.example
class Adder(val base: Int) {
  @native def plus(term: Int): Int // implemented in a native library
}

will yield this prototype

/*
 * Class:     org_example_Adder
 * Method:    plus
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_org_example_Adder_plus
  (JNIEnv *, jobject, jint);

The header output directory can be configured

javah / target := <dir> // defaults to target/native/include

Note that native methods declared both in Scala and Java are supported. Whereas Scala uses the @native annotation, Java uses the native keyword.

JniLoad

Enabled Source
automatic, for all projects JniLoad.scala

This plugin enables loading native libraries in a safe and transparent manner to the developer (no more explicit, static System.load("library") calls required). It does so by providing a class annotation which injects native loading code to all its annottees. Furthermore, in case a native library is not available on the current java.library.path, the code injected by the annotation will fall back to loading native libraries packaged according to the rules of JniPackage.

Usage example (Scala 2.x):

import com.github.sbt.jni.nativeLoader

// By adding this annotation, there is no need to call
// System.load("adder0") before accessing native methods.
@nativeLoader("adder0")
class Adder(val base: Int) {
  @native def plus(term: Int): Int // implemented in libadder0.so
}

// The application feels like a pure Scala app.
object Main extends App {
  (new Adder(0)).plus(1)
}

Note: this plugin is just a shorthand for adding sbt-jni-core (the project in core/) and the scala-macros-paradise (on Scala <= 2.13) projects as provided dependencies.

See the annotation's implementation for details about the injected code.

Usage example (Scala 3.x / Scala 2.x):

Scala 3 has no macro annotations support. As a solution we don't need this to be a macro function anymore. As the result, this option requires to have an explicit dependency on the sbt-jni-core library.

Note that if you want to run or test the project from sbt and have ThisBuild / turbo := true, you have to change the classLoaderLayeringStrategy to ClassLoaderLayeringStrategy.Flat, otherwise you will get UnsatisfiedLinkError, like java.lang.UnsatisfiedLinkError: 'int simple.Adder.plus(int)'.

This plugin behavior is configurable via:

// set to `Provided` by default, `Compile` is needed to use syntax (`extends NativeLoader`)
sbtJniCoreScope := Compile
// to make the code below work the core project should be included as a dependency via
// sbtJniCoreScope := Compile
import com.github.sbt.jni.syntax.NativeLoader

// By adding this annotation, there is no need to call
// System.load("adder0") before accessing native methods.
class Adder(val base: Int) extends NativeLoader("adder0"):
  @native def plus(term: Int): Int // implemented in libadder0.so

// The application feels like a pure Scala app.
@main def main: Unit = (new Adder(0)).plus(1)

JniNative

Enabled Source
manual JniNative.scala

JniNative adds the capability of building native code (compiling and linking) to sbt, by interfacing with commonly used build tools.

Since this plugin is basically a command-line wrapper, native build tools must follow certain calling conventions to be compatible. The supported build tools are currently:

An initial, compatible build template can be obtained by running sbt nativeInit <tool>. Once the native build tool initialised, projects are built by calling the sbt nativeCompile task.

Source and output directories are configurable:

nativeCompile / sourceDirectory := sourceDirectory.value / "native"
nativeCompile / target := target.value / "native" / nativePlatform.value

Some JNI projects may produce more than a single output. If that's an intended behavior it's possible to include all of the produced binaries into the native package:

nativeMultipleOutputs := true

CMake

A regular CMake native project definition usually looks this following way:

lazy val native = project
  // sourceDirectory = <project_root>/native/src
  .settings(nativeCompile / sourceDirectory := sourceDirectory.value)
  .enablePlugins(JniNative)

Source directory is set to sourceDirectory.value since the CMake project structure is of the following shape:

├── src/
│   ├── CMakeLists.txt
│   ├── lib.cpp

By default, CMake build is launched the following flags:

  • -DCMAKE_BUILD_TYPE=Release
  • -DSBT:BOOLEAN=true

It is possible to configure CMake by overriding the nativeBuildTool setting:

// default 
nativeBuildTool := CMake.make(Seq("-DCMAKE_BUILD_TYPE=Release", "-DSBT:BOOLEAN=true"))
// debug mode
nativeBuildTool := CMake.make(Seq("-DCMAKE_BUILD_TYPE=Debug", "-DSBT:BOOLEAN=true"))
// no flags passed
nativeBuildTool := CMake.make(Nil)

Cargo

A regular Cargo native project definition usually looks this following way:

lazy val native = project
  // baseDirectory = <project_root>/native
  .settings(nativeCompile / sourceDirectory := baseDirectory.value)
  .enablePlugins(JniNative)

Source directory is set to baseDirectory.value since the Cargo project structure is of the following shape:

├── Cargo.toml
├── src/
│   ├── lib.rs

By default, Cargo build is launched with the --release flag. It is possible to configure Cargo profile by overriding the nativeBuildTool setting:

// default
nativeBuildTool := Cargo.make(Seq("--release"))
// extra flags passed
nativeBuildTool := Cargo.make(Seq("--release", "--ignore-rust-version"))
// no flags passed, debug mode
nativeBuildTool := Cargo.make(Nil)

Meson

A regular Meson native project definition usually looks this following way:

lazy val native = project
  // baseDirectory = <project_root>/native
  .settings(nativeCompile / sourceDirectory := baseDirectory.value)
  .enablePlugins(JniNative)

Source directory is set to baseDirectory.value since the Meson project structure is of the following shape:

├── meson.build
├── meson.options
├── src/
│   ├── library.c

By default, Meson build is launched with the --buildtype=release flag. It is possible to configure Meson by overriding the nativeBuildTool setting:

// default
nativeBuildTool := Meson.make(Seq("--buildtype=release"))
// extra flags passed
nativeBuildTool := Meson.make(Seq("--buildtype=release", "--fatal-meson-warnings"))
// no flags passed, debug mode
nativeBuildTool := Meson.make(Nil)

JniPackage

Enabled Source
automatic, when JniNative enabled JniPackage.scala

This plugin packages native libraries produced by JniNative in a way that they can be transparently loaded with JniLoad. It uses the notion of a native "platform", defined as the architecture-kernel values returned by uname -sm. A native binary of a given platform is assumed to be executable on any machines of the same platform.

Canonical Use

Keep in mind that sbt-jni is a suite of plugins, there are many other use cases. This is a just a description of the most common one.

  1. Define separate sub-projects for JVM and native sources. In myproject/build.sbt:

    lazy val core = (project in file("myproject-core")) // regular scala code with @native methods
      .dependsOn(native % Runtime) // remove this if `core` is a library, leave choice to end-user
    
    lazy val native = (project in file("myproject-native")) // native code and build script
      .enablePlugins(JniNative) // JniNative needs to be explicitly enabled

    Note that separate projects are not strictly required. They are strongly recommended nevertheless, as a portability-convenience tradeoff: programs written in a JVM language are expected to run anywhere without recompilation, but including native libraries in jars limits this portability to only platforms of the packaged libraries. Having a separate native project enables the users to easily swap out the native library with their own implementation.

  2. Initialize the native build tool from a template:

    Run sbt "nativeInit cmake <libname>"

  3. Implement core project:

    This step is identical to building a regular scala project, with the addition that some classes will also contain @native methods.

  4. Generate native headers:

    Run sbt javah

  5. Implement native headers:

    The function prototypes in the header files must be implemented in native code (such as C, C++) and built into a shared library. Run sbt nativeCompile to call the native build tool and build a shared library.

  6. Build/run/test:

    At this point, the project can be tested and run as any standard sbt project. For example, you can publish your project as a library (sbt publish), run it (sbt core/run) or simply run unit tests (sbt test). Packaging and recompiling of the native library will happen transparently.

  7. Develop:

    The usual iterative development process. Nothing speial needs to be done, except in case any @native methods are added/removed or their signature changed, then sbt javah needs to be run again.

Examples

The plugins' unit tests offer some simple examples. They can be run individually through these steps:

  1. Publish the core library locally sbt publishLocal.
  2. Change to the test's directory and run sbt -Dplugin.version=<version>.
  3. Follow the instructions in the test file (only enter the lines that start with ">" into sbt).

Real-world use-cases of sbt-jni include:

Requirements and Dependencies

  • projects using JniLoad must use Scala versions 2.11, 2.12, 2.13 or 3.2
    • projects using JniLoad with Scala 3 should use it with the sbtJniCoreScope := Compile SBT key set
  • only POSIX platforms are supported (actually, any platform that has the uname command available)

The goal of sbt-jni is to be the least intrusive possible. No transitive dependencies are added to projects using any plugin (some dependencies are added to the provided configuration, however these do not affect any downstream projects).

Building

Both the core (former macros) library (sbt-jni-core) and the sbt plugins (sbt-jni) are published. Cross-building happens on a per-project basis:

  • sbt-jni-core is built against Scala 2.11, 2.12, 2.13, and 3.2
  • sbt-jni is built against Scala 2.12 (the Scala version that sbt 1.x uses)

The differing Scala versions make it necessary to always cross-compile and cross-publish this project, i.e. append a "+" before every task.

Run sbt +publishLocal to build and use this plugin locally.

Copying

This project is released under the terms of the 3-clause BSD license. See LICENSE for details.

javah is released under the terms of the MIT license since it uses Glavo's gjavah. See LICENSE for details.

Footnotes

  1. Glavo's gjavah is actually used, since javah has been removed from the JDK since version 1.10. If something goes wrong, please open an issue to help us improve it.

sbt-jni's People

Contributors

4lex1v avatar ankurdave avatar atry avatar berkeleybarry avatar dependabot[bot] avatar duhemm avatar glavo avatar jodersky avatar kammoh avatar masseguillaume avatar michaelmior avatar mihnea-s avatar pomadchin avatar sammyne avatar scala-steward avatar sideeffffect avatar xuwei-k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sbt-jni's Issues

got error running nativeInit, but no error message printed

$ sbt nativeInit cmake
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /Users/jeanluo/test/sbt_jni/project
[info] Loading settings from build.sbt ...
[info] Set current project to sbt_jni (in build file:/Users/jeanluo/test/sbt_jni/)
[error] Total time: 0 s, completed Nov 14, 2017 11:32:15 AM

macOs: 10.12.6
sbt_jni: 1.3.0
sbt: 1.0.2

builds.sbt
import Dependencies._

lazy val root = (project in file("."))
.enablePlugins(JniNative)
.settings(
inThisBuild(List(
organization := "com.example",
scalaVersion := "2.12.3",
version := "0.1.0-SNAPSHOT"
)),
name := "sbt_jni_test",
libraryDependencies += scalaTest % Test
)
~

Using sbt-jni with multiple c++ libraries

I am researching the use of the sbt-jni plugin to integrate a number of c++ libraries into my scala project.
I have a basic project setup and working nicely, but would now like to add another c++ library in the cleanest possible way.
I am new to CMake and relatively new with SBT, so I apologise if this question seems obvious.
If you could point me in a direction I would really appreciate it.
Thanks, Peter

javah failed with ClassCastException

See the following stack trace that happens under sbt 1.4.5

[error] java.lang.ClassCastException: class sbt.internal.inc.MappedVirtualFile cannot be cast to class java.io.File (sbt.internal.inc.MappedVirtualFile is in unnamed module of loader sbt.internal.MetaBuildLoader @5bf0d49; java.io.File is in module java.base of loader 'bootstrap')
[error]         at scala.collection.TraversableLike.$anonfun$flatMap$1(TraversableLike.scala:292)
[error]         at scala.collection.immutable.HashSet$HashSet1.foreach(HashSet.scala:330)
[error]         at scala.collection.immutable.HashSet$HashTrieSet.foreach(HashSet.scala:1104)
[error]         at scala.collection.TraversableLike.flatMap(TraversableLike.scala:292)
[error]         at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:289)
[error]         at scala.collection.AbstractTraversable.flatMap(Traversable.scala:108)
[error]         at ch.jodersky.sbt.jni.plugins.JniJavah$.$anonfun$mainSettings$1(JniJavah.scala:38)
[error]         at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error]         at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error]         at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error]         at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error]         at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error]         at sbt.Execute.work(Execute.scala:291)
[error]         at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error]         at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error]         at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error]         at java.base/java.lang.Thread.run(Thread.java:834)
[error] (corev2bench / javah / javahClasses) java.lang.ClassCastException: class sbt.internal.inc.MappedVirtualFile cannot be cast to class java.io.File (sbt.internal.inc.MappedVirtualFile is in unnamed module of loader sbt.internal.MetaBuildLoader @5bf0d49; java.io.File is in module java.base of loader 'bootstrap')

SBT JNI Classes not available after publishing jar(s)

SBT JNI Classes not available after publishing jar(s) even though the following is set,

sbtJniCoreScope := Compile,

Any solutions for this except for using assembly plugin ?

Exception in thread "main" java.lang.NoClassDefFoundError: com/github/sbt/jni/syntax/NativeLoader
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	at org.a.b.c..version(Test.scala)
	at org.a.b.c.Main.main(Main.java:6)
Caused by: java.lang.ClassNotFoundException: com.github.sbt.jni.syntax.NativeLoader
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	... 26 more

Cannot find native library on classpath

For reference, here is my setup https://github.com/nlhkh/scala-jni-sample

The compilation seems to work, but there are 2 things I do not understand

  • The output native library has the scala problem name, whereas at runtime, the program looks for the library with name specified by nativeLoader.
  • How can I specify the classpath in SBT?

As the result, the program cannot run because it cannot find the native library implementation.

Could you help me point out how I can fix this?

[error] (run-main-8) java.lang.UnsatisfiedLinkError: Native library libDlibWrapper.dylib (/native/x86_64-darwin/libDlibWrapper.dylib) cannot be found on the classpath.
[error] java.lang.UnsatisfiedLinkError: Native library libDlibWrapper.dylib (/native/x86_64-darwin/libDlibWrapper.dylib) cannot be found on the classpath.

Add support for Windows

The command uname -sm only works for Unix-based OSes. Currently I have to manually set

nativePlatform := "win32-x86_64"

failed to bind cmake project

Environment

  • ubuntu 20.04
  • cmake 3.16.3
  • scala 2.12.15

Problem

I have make native project based on CMake as here. But it failed to build with sbt core/run, whose logs go as

(native / nativeCompile) No files were created during compilation, something went wrong with the CMake configuration.

Really appreciate if someone could help me out~

native library naming.

Java native interface (jni) allows calling native libraries

  1. Windows --> .dll,
  2. Linux --> .so and
  3. Mac -->.dylib.

I use the following code to load native library.

  static {
    System.loadLibrary("mypl");
    }

If I generate native library in windows os the native library's name comes mypl.dll

But , in Linux os the native library's name comes libmypl.so and in mac it comes libmypl.dylib

Will this code System.loadLibrary("mypl"); load native library correcty after creating JPackage in linux and mac ?

sbt javah cannot find Scala library

When running sbt javah I got the following error: Error: Class scala.Product could not be found.

The native code:

  @native
  def calculate(input: InputData): CalculationResult

My build.sbt:

name := "test"

version := "1.0"

scalaVersion := "2.10.6"

disablePlugins(JniLoad)

project/plugins.sbt:

addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC1")

Looks like there when javah was run, it's not looking at scalalibrary in the classpath. Is there anything needs to be added to the build.sbt?

Running 'javah' gives a ProviderNotFoundException

When running sbt javah I will sometimes get a java.nio.file.ProviderNotFoundException. If I generate a new project with the same build.sbt file and copy it and my .scala files over, the issue does not persist. Once the exception shows up, I haven't yet found a way to make it disappear again.

The contents of my build.sbt are as follows:

scalaVersion := "2.12.6"
scalacOptions := Seq("-Xsource:2.11")
resolvers ++= Seq(
  Resolver.sonatypeRepo("snapshots"),
  Resolver.sonatypeRepo("releases")
)
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.0" % "test"
enablePlugins(JniNative)

The stack trace when running sbt javah is as follows.

[info] Compiling 1 Scala source to /home/user/jnitest/target/classes ...
[info] Headers will be generated to /home/user/jnitest/src/native/include
[error] stack trace is suppressed; run last javah for the full output
[error] (javah) java.nio.file.ProviderNotFoundException: Provider not found
[error] Total time: 8 s, completed Sep 25, 2020, 4:01:07 PM
sbt:jnitest> last javah
[info] Headers will be generated to /home/user/jnitest/src/native/include
[error] java.nio.file.ProviderNotFoundException: Provider not found
[error]         at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:423)
[error]         at ch.jodersky.sbt.jni.javah.Utils.classPathRoot(Utils.java:99)
[error]         at ch.jodersky.sbt.jni.javah.ClassPath.<init>(ClassPath.java:16)
[error]         at ch.jodersky.sbt.jni.javah.JavahTask.addClassPath(JavahTask.java:54)
[error]         at ch.jodersky.sbt.jni.plugins.JniJavah$.$anonfun$mainSettings$9(JniJavah.scala:65)
[error]         at ch.jodersky.sbt.jni.plugins.JniJavah$.$anonfun$mainSettings$9$adapted(JniJavah.scala:65)
[error]         at scala.collection.immutable.List.foreach(List.scala:392)
[error]         at ch.jodersky.sbt.jni.plugins.JniJavah$.$anonfun$mainSettings$4(JniJavah.scala:65)
[error]         at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error]         at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error]         at sbt.std.Transform$$anon$4.work(Transform.scala:67)
[error]         at sbt.Execute.$anonfun$submit$2(Execute.scala:281)
[error]         at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:19)
[error]         at sbt.Execute.work(Execute.scala:290)
[error]         at sbt.Execute.$anonfun$submit$1(Execute.scala:281)
[error]         at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:178)
[error]         at sbt.CompletionService$$anon$2.call(CompletionService.scala:37)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error]         at java.base/java.lang.Thread.run(Thread.java:834)
[error] (javah) java.nio.file.ProviderNotFoundException: Provider not found

Do you have any ideas what this could be caused by? I have tried running sbt clean to no avail. I guess it's something I'm doing wrong, but for the life of me I cannot figure out what that thing is. Performing a reboot of my PC does not fix the issue.

Packaging for multiple platforms

Is there currently a way to package the native library for multiple platforms?

For example, I have some JNI bindings that compile on both Linux and Mac and I want to create and publish separate packages for each platform so someone can later add to his/her SBT dependencies the package matching their platform.

Thank you!

Default cmake build isn't release

cmake ideally wants you to pass the build type as an argument when calling it. Currently a release type isn't passed, so it uses whatever cmake does by default.

I'm not sure what that default behavior is in cmake, but for my case passing doing the following manually:

cmake -DCMAKE_BUILD_TYPE=Debug" 

Made by executable 500% faster.

Ideally this would be a config option? But probably a good idea to make default behavior RELEASE.

Change groupId to com.github.sbt

Change it to com.github.sbt. I think it makes sense to rename all packages to align with this groupId to cause less confusion. But I'm happy to listen to other opinions (i.e. we can only change the groupId but to keep the current internal packages structure).

Unresolved dependency: What does "<latest version>" refer to?

Simply adding

 addSbtPlugin("ch.jodersky" % "sbt-jni" % "<latest version>")

to project/plugins.sbt results in:

sbt.ResolveException: unresolved dependency: ch.jodersky#sbt-jni;<latest version>: not found

What does "<latest version>" refer to? Was searching around, but I can find it (only this, but that does not seem to work either: version in ThisBuild := ("git describe --always --dirty --match v[0-9].*" !!).tail.trim)

Or is this actually supposed to work and it is an issue with the Maven repository? Because I can't see it here.

Mac M1 nativeCompile not working

I cloned https://github.com/sbt/sbt-jni/tree/main/plugin/src/sbt-test/sbt-jni/simple and tried to run the test and it failed to run nativeCompile with the following log, despite the fact that that directory does exist.

[info] Building library with native build tool CMake
[error] java.io.IOException: Cannot run program "cmake" (in directory "/Users/nkohen/dev/sbt-jni/plugin/src/sbt-test/sbt-jni/simple/native/target/native/arm64-darwin/build"): error=2, No such file or directory
[error] 	at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1128)
[error] 	at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1071)
[error] 	at scala.sys.process.ProcessBuilderImpl$Simple.run(ProcessBuilderImpl.scala:75)
[error] 	at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.lineStream(ProcessBuilderImpl.scala:147)
[error] 	at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.lineStream(ProcessBuilderImpl.scala:113)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.cmakeVersion$lzycompute(CMake.scala:26)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.cmakeVersion(CMake.scala:25)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.configure(CMake.scala:45)
[error] 	at com.github.sbt.jni.build.ConfigureMakeInstall$Instance.library(ConfigureMakeInstall.scala:31)
[error] 	at com.github.sbt.jni.build.ConfigureMakeInstall$Instance.library$(ConfigureMakeInstall.scala:26)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.library(CMake.scala:17)
[error] 	at com.github.sbt.jni.plugins.JniNative$.$anonfun$settings$14(JniNative.scala:115)
[error] 	at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] 	at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] 	at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] 	at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] 	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] 	at sbt.Execute.work(Execute.scala:291)
[error] 	at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] 	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] 	at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] 	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error] 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error] 	at java.base/java.lang.Thread.run(Thread.java:834)
[error] Caused by: java.io.IOException: error=2, No such file or directory
[error] 	at java.base/java.lang.ProcessImpl.forkAndExec(Native Method)
[error] 	at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:340)
[error] 	at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:271)
[error] 	at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1107)
[error] 	at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1071)
[error] 	at scala.sys.process.ProcessBuilderImpl$Simple.run(ProcessBuilderImpl.scala:75)
[error] 	at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.lineStream(ProcessBuilderImpl.scala:147)
[error] 	at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.lineStream(ProcessBuilderImpl.scala:113)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.cmakeVersion$lzycompute(CMake.scala:26)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.cmakeVersion(CMake.scala:25)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.configure(CMake.scala:45)
[error] 	at com.github.sbt.jni.build.ConfigureMakeInstall$Instance.library(ConfigureMakeInstall.scala:31)
[error] 	at com.github.sbt.jni.build.ConfigureMakeInstall$Instance.library$(ConfigureMakeInstall.scala:26)
[error] 	at com.github.sbt.jni.build.CMake$$anon$1.library(CMake.scala:17)
[error] 	at com.github.sbt.jni.plugins.JniNative$.$anonfun$settings$14(JniNative.scala:115)
[error] 	at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] 	at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] 	at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] 	at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] 	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] 	at sbt.Execute.work(Execute.scala:291)
[error] 	at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] 	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] 	at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] 	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error] 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error] 	at java.base/java.lang.Thread.run(Thread.java:834)
[error] (native / nativeCompile) java.io.IOException: Cannot run program "cmake" (in directory "/Users/nkohen/dev/sbt-jni/plugin/src/sbt-test/sbt-jni/simple/native/target/native/arm64-darwin/build"): error=2, No such file or directory

Is m1 mac not yet supported or is something else the matter?

Plugin is looking for ch.jodersky#sbt-jni-macros_2.10;1.0.0-RC1 for Scala 2.10

After performing publishLocal, the plugin is published locally for both 2.10 and 2.11 under 2 different directory: sbt-jni and sbt-jni-macros_2.11.

However when I add the plugin for Scala 2.10 project:
addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC1")

it is actually looking for ch.jodersky#sbt-jni-macros_2.10;1.0.0-RC1. I wonder on the extra -macros on what it is looking for.

sbt-jni ignores sbt "-java-home" option

On my, and multiple other system I encountered, both Java 7 and 8 are installed. Our project depends on Java 8. To compile the project with sbt the "-java-home" command line option can be used like so:

sbt -java-home /usr/lib/jvm/java-8-openjdk-amd64/ compile

Unfortunately this does not work with nativeCompile:

sbt -java-home /usr/lib/jvm/java-8-openjdk-amd64/ nativeCompile
[info] Loading global plugins from /home/user/.sbt/0.13/plugins
[info] Loading project definition from /home/user/P/project
[info] Set current project to Leo III (in build file:/home/user/P/)
[error] CMake Error at /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:108 (message):
[error] Could NOT find JNI (missing: JAVA_AWT_LIBRARY JAVA_JVM_LIBRARY
[error] JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)
[error] Call Stack (most recent call first):
[error] /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:315 (_FPHSA_FAILURE_MESSAGE)
[error] /usr/share/cmake-2.8/Modules/FindJNI.cmake:251 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
[error] CMakeLists.txt:21 (find_package)
[error]
[error]
java.lang.RuntimeException: Building native library failed. Exit code: 1
at scala.sys.package$.error(package.scala:27)
at ch.jodersky.sbt.jni.build.ConfigureMakeInstall$Instance$class.library(ConfigureMakeInstall.scala:34)
at ch.jodersky.sbt.jni.build.CMake$$anon$1.library(CMake.scala:16)
at ch.jodersky.sbt.jni.plugins.JniNative$$anonfun$settings$7.apply(JniNative.scala:118)
at ch.jodersky.sbt.jni.plugins.JniNative$$anonfun$settings$7.apply(JniNative.scala:109)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
at sbt.std.Transform$$anon$4.work(System.scala:63)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
at sbt.Execute.work(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
error Building native library failed. Exit code: 1
[error] Total time: 0 s, completed Oct 5, 2016 10:52:13 AM

Using update-alternatives to set the Java version works fine, but requires root privileges and is therefore not always a feasible alternative.

sbt-javah mangles names of @native methods in objects incorrectly

object Foo {
  @native def bar()
}

should generate

JNIEXPORT void JNICALL Java_Foo_00024_bar
  (JNIEnv *, jobject);

but instead it generates

JNIEXPORT void JNICALL Java_Foo___bar
  (JNIEnv *, jobject);

... at least on recent JVMs. This seems to be already fixed in gjavah, but for some reason $ is special-cased in here https://github.com/jodersky/sbt-jni/blob/fb2c301d550cee5491780b40b6e87b7e3fc73163/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Utils.java#L55-L56

Scala 3 support

This issue is probably a bit obvious, but some one had to create it :D

In terms of API it means that macro annotations would have to be replaced (with a macro function call ?).

Library cannot be found on the classpath

I am using sbt-jni to add a number of c++ libraries to my scala project.
I have configured my build.sbt project with

lazy val projectCore = (project in file(".")).
  settings(target in javah := sourceDirectory.value / "native" / "include").
  dependsOn(nativeLibrary % Runtime)

lazy val nativeLibrary = (project in file("src/native/nativeLibrary")).
  settings(sourceDirectory in nativeCompile := baseDirectoryh.value).
  enablePlugins(JniNative)

My folder structure is like this:

src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   └── scala
│   ├── native
│   │   ├── nativeLibrary
│   │    |   ├── nativelibrarycode.cpp
│   │    |   ├── CMakeLists.txt
│   │   ├── include
│   │    |   ├── JNI_generated_headers.h
│   │   └── otherIncludes
│   │    |   ├── other_headers_needed.h

My Cmake file is like this:

################################################################
# A minimal CMake file that is compatible with sbt-jni         #
#                                                              #
# All settings required by sbt-jni have been marked so, please #
# add/modify/remove settings to build your specific library.   #
################################################################

cmake_minimum_required(VERSION 2.8.0)

# Define project and related variables
# (required by sbt-jni) please use semantic versioning
#
project (nativeLibrary)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 0)
set(PROJECT_VERSION_PATCH 0)

# Setup JNI
find_package(JNI REQUIRED)
if (JNI_FOUND)
    message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
endif()

# Include directories
include_directories(.)
include_directories(../include)
include_directories(../otherIncludes)
include_directories(${JNI_INCLUDE_DIRS})

# Sources
file(GLOB LIB_SRC
  "*.c"
  "*.cc"
  "*.cpp"
)

# Setup installation targets
# (required by sbt-jni) major version should always be appended to library name
#
set (LIB_NAME ${PROJECT_NAME}${PROJECT_VERSION_MAJOR})
add_library(${LIB_NAME} SHARED ${LIB_SRC})
install(TARGETS ${LIB_NAME} LIBRARY DESTINATION .)

When preparing to build the project, I open an sbt console and run

project nativeLibrary
nativeCompile

The nativeLibrary is built as libnativeLibrary0.so into the path
src/native/nativeLibrary/target/native/x86_64-linux/bin

then in the sbt console I run:

project projectCore
console

I import the scala class in question and try to create a new nativeLibrary object.
It fails with the error:

java.lang.UnsatisfiedLinkError: Native library libnativeLibrary0.so (/native/x86_64-linux/libnativeCompile0.so) cannot be found on the classpath.

I am new to sbt and don't know how to wire up my paths correctly.
Any advice is very appreciated.
Thanks, Peter

Cargo workspace

Hello, could you tell me how to configure sbt-jni with a cargo workspace and not a simple cargo lib, i did not find any example about this

javah requires JVM 11

when invoking sbt javah (plugin version 1.4.1) on Java 8 I'm getting below error:

[error] stack trace is suppressed; run last javah for the full output
[error] (javah) java.lang.UnsupportedClassVersionError: ch/jodersky/sbt/jni/javah/JavahTask has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

The same on Java 11 works. Is it possible to compile plugin for 1.8 target?

Expose BuildTools parameters

There is a usecase that's been revealed in terms of #139 that sometimes it may be useful passing extra arguments to the underlying build tool (cmake / cargo).

This can be achieved by exposing extra parameters that users may set in the build file.

Cargo unexpected argument

Hello! Faced the problem that when using the cargo build system with Rust, it is not possible to use this configuration if the path contains spaces (cargo build /home/user/Desktop/Code Workspace/...)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.