{"id":19248976,"url":"https://github.com/sbt/sbt-jni","last_synced_at":"2025-04-05T01:03:08.046Z","repository":{"id":40338996,"uuid":"47514336","full_name":"sbt/sbt-jni","owner":"sbt","description":"SBT Plugin to ease working with JNI","archived":false,"fork":false,"pushed_at":"2025-03-17T23:41:39.000Z","size":510,"stargazers_count":129,"open_issues_count":4,"forks_count":27,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-29T00:03:56.583Z","etag":null,"topics":["hacktoberfest","jni","jvm","sbt","sbt-plugin","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sbt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-12-06T21:22:17.000Z","updated_at":"2025-03-17T23:41:42.000Z","dependencies_parsed_at":"2023-02-14T11:31:02.201Z","dependency_job_id":"8611a81a-d1ca-4894-a0c4-9e3952bb527e","html_url":"https://github.com/sbt/sbt-jni","commit_stats":null,"previous_names":["jodersky/sbt-jni"],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbt%2Fsbt-jni","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbt%2Fsbt-jni/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbt%2Fsbt-jni/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbt%2Fsbt-jni/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sbt","download_url":"https://codeload.github.com/sbt/sbt-jni/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247271514,"owners_count":20911587,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","jni","jvm","sbt","sbt-plugin","scala"],"created_at":"2024-11-09T18:11:50.038Z","updated_at":"2025-04-05T01:03:08.015Z","avatar_url":"https://github.com/sbt.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/sbt/sbt-jni/workflows/CI/badge.svg)](https://github.com/sbt/sbt-jni/actions) [![Maven Central](https://img.shields.io/maven-central/v/com.github.sbt/sbt-jni-core_3)](https://search.maven.org/search?q=g:com.github.sbt%20AND%20a:sbt-jni) [![Snapshots](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.github.sbt/sbt-jni-core_3.svg)](https://oss.sonatype.org/content/repositories/snapshots/com/github/sbt/sbt-jni_2.12_1.0/)\n\n| sbt version | group id | plugin version |\n|-------------|----------------|----------------|\n| 0.13.x      | ch.jodersky    | [1.2.6](https://scala.jfrog.io/artifactory/sbt-plugin-releases/ch.jodersky/sbt-jni/scala_2.10/sbt_0.13/1.2.6/) |\n| 1.x         | ch.jodersky    | [1.4.1](https://scala.jfrog.io/artifactory/sbt-plugin-releases/ch.jodersky/sbt-jni/scala_2.12/sbt_1.0/1.4.1/) |\n| 1.x         | com.github.sbt | [![Maven Central](https://img.shields.io/maven-central/v/com.github.sbt/sbt-jni-core_3)](https://search.maven.org/search?q=g:com.github.sbt%20AND%20a:sbt-jni) |\n\n# SBT-JNI\n\nA suite of sbt plugins for simplifying creation and distribution of JNI programs.\n\n## Setup\n\nAdd sbt-jni as a dependency to `project/plugins.sbt`:\n```scala\naddSbtPlugin(\"com.github.sbt\" % \"sbt-jni\" % \"\u003clatest version\u003e\")\n```\n\nwhere `\u003clatest version\u003e` refers to the version indicated by the badge above.\n\n*Note: We changed the organization from `ch.jodersky` to `com.github.sbt`*\n\n## Motivation\nJava 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).\n\nUsing 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:\n\n- Compilation: the project is divided into two parts, each of which require separate compilation.\n- Portability: native binaries only run on the platform on which they were compiled.\n- Distribution: native binaries must be made available and packaged for every supported platform.\n\nThe 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.\n\n## Plugin Summary\n\n| Plugin     | Description                                                                                            |\n|------------|--------------------------------------------------------------------------------------------------------|\n| JniJavah   | Adds support for generating headers from classfiles that have `@native` methods.                       |\n| JniLoad    | Makes `@nativeLoader` annotation available, that injects code to transparently load native libraries.  |\n| JniNative  | Adds sbt wrapper tasks around native build tools to ease building and integrating native libraries.    |\n| JniPackage | Packages native libraries into multi-platform fat jars. No more manual library installation!     |\n\nNote that most plugins are enabled in projects by default. Disabling their functionality can be achieved by adding `disablePlugins(\u003cplugin\u003e)` to the corresponding project definition (for example, should you wish to disable packaging of native libraries).\n\n## Plugin Details\n\n### JniJavah\n\n| Enabled                        | Source        |\n|--------------------------------|---------------|\n| automatic, for all projects    | [JniJavah.scala](plugin/src/main/scala/com/github/sbt/jni/plugins/JniJavah.scala)|\n\nThis plugin wraps the JDK `javah` command [^1].\n\n[^1]: Glavo's [gjavah](https://github.com/Glavo/gjavah) is actually used, since `javah` has been\nremoved from the JDK since version 1.10. If something goes wrong, please open an issue to help us improve it.\n\nRun `sbt-javah` to generate C header files with prototypes for any methods marked as native.\nE.g. the following scala class\n```scala\npackage org.example\nclass Adder(val base: Int) {\n  @native def plus(term: Int): Int // implemented in a native library\n}\n```\nwill yield this prototype\n```c\n/*\n * Class:     org_example_Adder\n * Method:    plus\n * Signature: (I)I\n */\nJNIEXPORT jint JNICALL Java_org_example_Adder_plus\n  (JNIEnv *, jobject, jint);\n\n```\n\nThe header output directory can be configured\n```\njavah / target := \u003cdir\u003e // defaults to target/native/include\n```\n\nNote that native methods declared both in Scala and Java are supported. Whereas Scala uses the `@native` annotation, Java uses the\n`native` keyword.\n\n### JniLoad\n| Enabled                        | Source        |\n|--------------------------------|---------------|\n| automatic, for all projects    | [JniLoad.scala](plugin/src/main/scala/com/github/sbt/jni/plugins/JniLoad.scala) |\n\nThis 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`.\n\n#### Usage example (Scala 2.x):\n```scala\nimport com.github.sbt.jni.nativeLoader\n\n// By adding this annotation, there is no need to call\n// System.load(\"adder0\") before accessing native methods.\n@nativeLoader(\"adder0\")\nclass Adder(val base: Int) {\n  @native def plus(term: Int): Int // implemented in libadder0.so\n}\n\n// The application feels like a pure Scala app.\nobject Main extends App {\n  (new Adder(0)).plus(1)\n}\n```\n\nNote: this plugin is just a shorthand for adding `sbt-jni-core` (the project in `core/`) and the scala-macros-paradise (on Scala \u003c= 2.13) projects as provided dependencies.\n\nSee the [annotation's implementation](core/src/main/scala/com/github/sbt/jni/annotations.scala) for details about the injected code.\n\n#### Usage example (Scala 3.x / Scala 2.x):\n\nScala 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](./core) library.\n\nNote 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)'`.\n\nThis plugin behavior is configurable via:\n\n```sbt\n// set to `Provided` by default, `Compile` is needed to use syntax (`extends NativeLoader`)\nsbtJniCoreScope := Compile\n```\n\n```scala\n// to make the code below work the core project should be included as a dependency via\n// sbtJniCoreScope := Compile\nimport com.github.sbt.jni.syntax.NativeLoader\n\n// By adding this annotation, there is no need to call\n// System.load(\"adder0\") before accessing native methods.\nclass Adder(val base: Int) extends NativeLoader(\"adder0\"):\n  @native def plus(term: Int): Int // implemented in libadder0.so\n\n// The application feels like a pure Scala app.\n@main def main: Unit = (new Adder(0)).plus(1)\n```\n\n### JniNative\n| Enabled                        | Source        |\n|--------------------------------|---------------|\n| manual                         | [JniNative.scala](plugin/src/main/scala/com/github/sbt/jni/plugins/JniNative.scala) |\n\nJniNative adds the capability of building native code (compiling and linking) to sbt, by interfacing with commonly used build tools.\n\nSince 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:\n\n- [CMake](#cmake)\n- [Cargo (Rust)](#cargo)\n\nAn initial, compatible build template can be obtained by running `sbt nativeInit \u003ctool\u003e`. Once the native build tool initialised, projects are built by calling the `sbt nativeCompile` task.\n\nSource and output directories are configurable:\n```scala\nnativeCompile / sourceDirectory := sourceDirectory.value / \"native\"\nnativeCompile / target := target.value / \"native\" / nativePlatform.value\n```\n\nSome JNI projects may produce more than a single output. If that's an intended behavior it's possible to include all of the produced \nbinaries into the native package:\n```scala\nnativeMultipleOutputs := true\n```\n\n#### CMake\n\nA regular `CMake` native project definition usually looks this following way:\n\n```scala\nlazy val native = project\n  // sourceDirectory = \u003cproject_root\u003e/native/src\n  .settings(nativeCompile / sourceDirectory := sourceDirectory.value)\n  .enablePlugins(JniNative)\n```\n\nSource directory is set to `sourceDirectory.value` since the `CMake` project structure is of the following shape:\n\n```\n├── src/\n│   ├── CMakeLists.txt\n│   ├── lib.cpp\n```\n\nBy default, `CMake` build is launched the following flags:\n\n* `-DCMAKE_BUILD_TYPE=Release` \n* `-DSBT:BOOLEAN=true`\n\nIt is possible to configure `CMake` by overriding the `nativeBuildTool` setting:\n\n```scala\n// default \nnativeBuildTool := CMake.make(Seq(\"-DCMAKE_BUILD_TYPE=Release\", \"-DSBT:BOOLEAN=true\"))\n// debug mode\nnativeBuildTool := CMake.make(Seq(\"-DCMAKE_BUILD_TYPE=Debug\", \"-DSBT:BOOLEAN=true\"))\n// no flags passed\nnativeBuildTool := CMake.make(Nil)\n```\n\n#### Cargo\n\nA regular `Cargo` native project definition usually looks this following way:\n\n```scala\nlazy val native = project\n  // baseDirectory = \u003cproject_root\u003e/native\n  .settings(nativeCompile / sourceDirectory := baseDirectory.value)\n  .enablePlugins(JniNative)\n```\n\nSource directory is set to `baseDirectory.value` since the `Cargo` project structure is of the following shape:\n\n```\n├── Cargo.toml\n├── src/\n│   ├── lib.rs\n\n```\n\nBy default, `Cargo` build is launched with the `--release` flag. It is possible to configure `Cargo` profile by overriding the `nativeBuildTool` setting:\n\n```scala\n// default\nnativeBuildTool := Cargo.make(Seq(\"--release\"))\n// extra flags passed\nnativeBuildTool := Cargo.make(Seq(\"--release\", \"--ignore-rust-version\"))\n// no flags passed, debug mode\nnativeBuildTool := Cargo.make(Nil)\n```\n\n#### Meson\n\nA regular `Meson` native project definition usually looks this following way:\n\n```scala\nlazy val native = project\n  // baseDirectory = \u003cproject_root\u003e/native\n  .settings(nativeCompile / sourceDirectory := baseDirectory.value)\n  .enablePlugins(JniNative)\n```\n\nSource directory is set to `baseDirectory.value` since the `Meson` project structure is of the following shape:\n\n```\n├── meson.build\n├── meson.options\n├── src/\n│   ├── library.c\n\n```\n\nBy default, `Meson` build is launched with the `--buildtype=release` flag. It is possible to configure `Meson` by overriding the `nativeBuildTool` setting:\n\n```scala\n// default\nnativeBuildTool := Meson.make(Seq(\"--buildtype=release\"))\n// extra flags passed\nnativeBuildTool := Meson.make(Seq(\"--buildtype=release\", \"--fatal-meson-warnings\"))\n// no flags passed, debug mode\nnativeBuildTool := Meson.make(Nil)\n```\n\n### JniPackage\n| Enabled                        | Source        |\n|--------------------------------|---------------|\n| automatic, when JniNative enabled | [JniPackage.scala](plugin/src/main/scala/com/github/sbt/jni/plugins/JniPackage.scala) |\n\nThis 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.\n\n## Canonical Use\n\n*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.*\n\n1. Define separate sub-projects for JVM and native sources. In `myproject/build.sbt`:\n\n   ```scala\n   lazy val core = (project in file(\"myproject-core\")) // regular scala code with @native methods\n     .dependsOn(native % Runtime) // remove this if `core` is a library, leave choice to end-user\n\n   lazy val native = (project in file(\"myproject-native\")) // native code and build script\n     .enablePlugins(JniNative) // JniNative needs to be explicitly enabled\n   ```\n   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.\n\n2. Initialize the native build tool from a template:\n\n   Run `sbt \"nativeInit cmake \u003clibname\u003e\"`\n\n3. Implement core project:\n\n   This step is identical to building a regular scala project, with the addition that some classes will also contain `@native` methods.\n\n4. Generate native headers:\n\n   Run `sbt javah`\n\n5. Implement native headers:\n\n   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.\n\n6. Build/run/test:\n\n   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.\n\n7. Develop:\n\n   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.\n\n## Examples\nThe [plugins' unit tests](plugin/src/sbt-test/sbt-jni) offer some simple examples. They can be run individually through these steps:\n\n1. Publish the core library locally `sbt publishLocal`.\n2. Change to the test's directory and run `sbt -Dplugin.version=\u003cversion\u003e`.\n3. Follow the instructions in the `test` file (only enter the lines that start with \"\u003e\" into sbt).\n\nReal-world use-cases of sbt-jni include:\n\n- [serial communication library for scala](https://github.com/jodersky/akka-serial)\n- [PDAL Java bindings](https://github.com/PDAL/java)\n\n## Requirements and Dependencies\n\n- projects using `JniLoad` must use Scala versions 2.11, 2.12, 2.13 or 3.2\n  - projects using `JniLoad` with Scala 3 should use it with the `sbtJniCoreScope := Compile` SBT key set\n- only POSIX platforms are supported (actually, any platform that has the `uname` command available)\n\nThe 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).\n\n## Building\nBoth the core (former macros) library (`sbt-jni-core`) and the sbt plugins (`sbt-jni`) are published. Cross-building happens on a per-project basis:\n\n- sbt-jni-core is built against Scala 2.11, 2.12, 2.13, and 3.2\n- sbt-jni is built against Scala 2.12 (the Scala version that sbt 1.x uses)\n\nThe differing Scala versions make it necessary to always cross-compile and cross-publish this project, i.e. append a \"+\" before every task.\n\nRun `sbt +publishLocal` to build and use this plugin locally.\n\n## Copying\nThis project is released under the terms of the 3-clause BSD license. See [LICENSE](./LICENSE) for details.\n\n`javah` is released under the terms of the MIT license since it uses Glavo's [gjavah](https://github.com/Glavo/gjavah). See [LICENSE](./plugin/src/main/java/com/github/sbt/jni/javah/LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbt%2Fsbt-jni","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsbt%2Fsbt-jni","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbt%2Fsbt-jni/lists"}