Bazel rulesets for building Scala Native applications.
This project provides two main rules:
scala_native_library— compiles Scala sources to class files, tasty files, and NIR (Native Intermediate Representation).scala_native_binary— links NIR files into a native executable using a hermetic C++ toolchain (clang/lld).scala_native_test— a test rule for running Scala Native JUnit tests.
- Bazel 9+ (bzlmod only, no WORKSPACE support)
- Scala 3.8.1
- Scala Native 0.5.10
- Linux, macOS, Windows (x86_64, aarch64)
module(name="my_scala_native_app")
bazel_dep(name="protobuf", version="33.4")
bazel_dep(name="rules_scala", version="7.2.2")
bazel_dep(name="rules_scala_native", version="0.1.0")
bazel_dep(name="llvm", version="0.5.4")
# Register hermetic C++ toolchain (clang/lld) for cross-compilationregister_toolchains(
"@llvm//toolchain:all",
)
# Configure Scalascala_config=use_extension(
"@rules_scala//scala/extensions:config.bzl",
"scala_config",
)
scala_config.settings(scala_version="3.8.1")
use_repo(scala_config, "rules_scala_config")
scala_deps=use_extension(
"@rules_scala//scala/extensions:deps.bzl",
"scala_deps",
)
scala_deps.scala()
use_repo(scala_deps, "rules_scala_toolchains")
register_toolchains(
"@rules_scala_toolchains//...:all",
)common --tool_java_runtime_version=remotejdk_17
common --java_runtime_version=remotejdk_17
common --@protobuf//bazel/toolchains:prefer_prebuilt_protoc
// HelloNative.scalapackageexamples.native@main
defsayHello(name: String):Unit=
println(s"Hello from Scala Native to $name")// NativeTest.scalapackageexamples.nativeimportorg.junit.Testimportorg.junit.Assert.*classNativeTest:@TestdeftestMath():Unit=
assertEquals("Basic arithmetic should work", 4, 2+2)load("@rules_scala_native//scala_native:scala_native_library.bzl", "scala_native_library")
load("@rules_scala_native//scala_native:scala_native_binary.bzl", "scala_native_binary")
load("@rules_scala_native//scala_native:scala_native_test.bzl", "scala_native_test")
scala_native_library(
name="hello_native",
srcs= ["HelloNative.scala"],
)
scala_native_binary(
name="hello_native_bin",
main_class="examples.native.sayHello",
deps= [":hello_native"],
)
scala_native_test(
name="hello_native_test",
srcs= ["NativeTest.scala"],
deps= [":hello_native"],
suites= ["examples.native.NativeTest"], # test suites must be explicitly listed
)Warning
The test rule requires the suites to be listed explicitly. This is a known issue and is being worked on.
$ bazel run //:hello_native_bin -- "the World!"It should print this output:
Hello from Scala Native to the World
Hello from a C function!
To run tests:
$ bazel test //:hello_native_testCross-compilation is as easy as passing the --platforms flag to Bazel:
bazel build //... --platforms=@llvm//platforms:linux_aarch64rules_scala_native uses a hermetic C++ toolchain based on LLVM/Clang to cross-compile Scala Native code.
| Platform | Architectures | C Library / Toolchain | Build | Execution |
|---|---|---|---|---|
| Linux | aarch64 | glibc (2.28—2.42), musl | ✅ Succeeded | ❓ Not tested |
| Linux | x86_64 | glibc (2.28—2.42), musl | ✅ Succeeded | ✅ Succeeded |
| macOS | aarch64 | Native Apple SDK | ✅ Succeeded | ✅ Succeeded |
| macOS | x86_64 | Native Apple SDK | ✅ Succeeded | ❓ Not tested |
| Windows | aarch64 | - | ✅ Succeeded | ❓ Not tested |
| Windows | x86_64 | - | ✅ Succeeded | ❓ Not tested |
There is no plan to support WebAssembly targets.
Note
Cross-compilation has been verified from a Linux x86_64 host to a total of 72 targets, including multiple architectures, musl, and glibc versions 2.28 through 2.42.
rules_scala_native makes it easy to interoperate with other native languages by leveraging Bazel's dependency system. Since scala_native_binary links against the hermetic C++ toolchain, it can seamlessly link with static libraries produced by cc_library, rust_static_library, zig_static_library, and more.
You can link directly with C code using cc_library.
C Code:
// math_utils.cintadd(inta, intb) {
returna+b;
}Scala Definition:
@extern
objectMathUtils:defadd(a: CInt, b: CInt):CInt= externBUILD.bazel:
cc_library(
name="math_utils",
srcs= ["math_utils.c"],
)
scala_native_binary(
name="app",
deps= [":math_utils", ...],
...
)Interoperate with Rust by using rules_rust to create a rust_static_library with extern "C" functions.
Rust Code:
#[no_mangle]pubextern"C"fnrust_add(a:i32,b:i32) -> i32{
a + b
}Scala Definition:
@extern
objectRustLib:@name("rust_add")
defadd(a: CInt, b: CInt):CInt= extern
BUILD.bazel:
rust_static_library(
name="rust_lib",
srcs= ["lib.rs"],
)
scala_native_binary(
name="app",
deps= [":rust_lib", ...],
...
)Similarly, you can use rules_zig to link with Zig code.
Zig Code:
exportfnzig_add(a: i32, b: i32) i32 {
returna+b;
}Scala Definition:
@extern
objectZigLib:@name("zig_add")
defadd(a: CInt, b: CInt):CInt= externBUILD.bazel:
zig_static_library(
name="zig_lib",
main="lib.zig",
)
scala_native_binary(
name="app",
deps= [":zig_lib", ...],
...
)C++ interop is supported by wrapping C++ functionality in extern "C" blocks to ensure stable ABI linkage.
C++ Code:
// greeter.cpp
#include<iostream>
#include<string>classGreeter {
public:Greeter(const std::string& name) : name_(name) {}
voidgreet() { std::cout << "Hello, " << name_ << "!" << std::endl; }
private:
std::string name_;
};
extern"C" {
void* greeter_new(constchar* name) { returnnewGreeter(name); }
voidgreeter_greet(void* g) { static_cast<Greeter*>(g)->greet(); }
voidgreeter_delete(void* g) { deletestatic_cast<Greeter*>(g); }
}Scala Definition:
@extern
objectCppGreeter:defgreeter_new(name: CString):Ptr[Byte] = extern
defgreeter_greet(greeter: Ptr[Byte]):Unit= extern
defgreeter_delete(greeter: Ptr[Byte]):Unit= externBUILD.bazel:
cc_library(
name="cpp_greeter",
srcs= ["greeter.cpp"],
)
scala_native_binary(
name="app",
deps= [":cpp_greeter", ...],
...
)```mermaid
graph LR
Scala_Sources[scala sources] --> scalac
nscplugin --> scalac
scalac --> class_files[".class files"]
scalac --> nir_files[".nir files"]
class_files --> scala_native_linker[Scala native linker]
nir_files --> scala_native_linker
scala_native_linker --> LLVM
LLVM --> native_binary[native binary]
| Component | Description |
|---|---|
scala_native_toolchain | Manages Scala Native dependencies (nscplugin, runtime libs, linker) |
scala_native_library | Injects the nscplugin compiler plugin for NIR generation |
scala_native_test | Runs Scala Native JUnit tests |
NativeLinker | Bridges the Scala Native build API with Bazel's action graph |
This project depends on:
rules_scala— for Scala compilation infrastructurerules_cc— for C++ toolchain accessllvm— for hermetic clang/lld
- Only Scala 3.8.1 is supported
- Only Scala Native 0.5.10 is supported
- Windows and WebAssembly support is currently work-in-progress.
- rules_scala issue #1409 — Support for Scala Native
- rules_scala PR #1809 — Original proof of concept
- Scala Native — Scala Native project
See LICENSE.txt.