Skip to content

Repository files navigation

rules_scala_native

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.

Requirements

  • Bazel 9+ (bzlmod only, no WORKSPACE support)
  • Scala 3.8.1
  • Scala Native 0.5.10
  • Linux, macOS, Windows (x86_64, aarch64)

Quick Start

1. Configure MODULE.bazel

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",
)

2. Configure .bazelrc

common --tool_java_runtime_version=remotejdk_17
common --java_runtime_version=remotejdk_17
common --@protobuf//bazel/toolchains:prefer_prebuilt_protoc

3. Write Scala Native code

// 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)

4. Define build targets

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.

5. Build and run

$ 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_test

Cross-Compilation Support

Cross-compilation is as easy as passing the --platforms flag to Bazel:

 bazel build //... --platforms=@llvm//platforms:linux_aarch64

rules_scala_native uses a hermetic C++ toolchain based on LLVM/Clang to cross-compile Scala Native code.

Support Matrix

PlatformArchitecturesC Library / ToolchainBuildExecution
Linuxaarch64glibc (2.28—2.42), musl✅ Succeeded❓ Not tested
Linuxx86_64glibc (2.28—2.42), musl✅ Succeeded✅ Succeeded
macOSaarch64Native Apple SDK✅ Succeeded✅ Succeeded
macOSx86_64Native Apple SDK✅ Succeeded❓ Not tested
Windowsaarch64-✅ Succeeded❓ Not tested
Windowsx86_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.

Interoperability with Other Languages

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.

C Interop

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= extern

BUILD.bazel:

cc_library(
name="math_utils",
srcs= ["math_utils.c"],
)
scala_native_binary(
name="app",
deps= [":math_utils", ...],
...
)

Rust Interop

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", ...],
...
)

Zig Interop

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= extern

BUILD.bazel:

zig_static_library(
name="zig_lib",
main="lib.zig",
)
scala_native_binary(
name="app",
deps= [":zig_lib", ...],
...
)

C++ Interop

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= extern

BUILD.bazel:

cc_library(
name="cpp_greeter",
srcs= ["greeter.cpp"],
)
scala_native_binary(
name="app",
deps= [":cpp_greeter", ...],
...
)

Architecture

Build Pipeline

```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]

Key Components

ComponentDescription
scala_native_toolchainManages Scala Native dependencies (nscplugin, runtime libs, linker)
scala_native_libraryInjects the nscplugin compiler plugin for NIR generation
scala_native_testRuns Scala Native JUnit tests
NativeLinkerBridges the Scala Native build API with Bazel's action graph

Dependencies

This project depends on:

  • rules_scala — for Scala compilation infrastructure
  • rules_cc — for C++ toolchain access
  • llvm — for hermetic clang/lld

Current Limitations

  • Only Scala 3.8.1 is supported
  • Only Scala Native 0.5.10 is supported
  • Windows and WebAssembly support is currently work-in-progress.

Related

License

See LICENSE.txt.

About

Scala Native Rules for Bazel

Resources

Code of conduct

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages