Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ CI currently runs:

- `bazel test //...`

That includes the dedicated regression tests under `tests/` plus the runnable examples that are test targets.

As the ruleset grows, this matrix can expand to include more Bazel versions, additional examples, and stricter validation.

## Setup
Expand Down Expand Up @@ -81,10 +83,20 @@ In practice:
- local Linux builds use a Linux Zig SDK
- remote execution would pick the Zig SDK that matches the remote exec platform

`zig_binary`, `zig_library`, and `zig_test` do not yet model Bazel target-platform driven cross-compilation. They invoke Zig without a `-target` flag, so outputs are currently host-native for the chosen exec-platform SDK.
`zig_binary`, `zig_library`, and `zig_test` currently pass an explicit Zig `-target` matching the selected exec-platform SDK. They do not yet model Bazel target-platform driven cross-compilation. Outputs are currently host-native for the chosen exec-platform SDK.

So for now, treat these rules as host-native builds with exec-platform-aware toolchain selection, not full Bazel cross-compilation rules yet.

## Examples

The repository includes runnable examples for common usage patterns:

- `examples/hello_world` — minimal `zig_binary`
- `examples/deps` — dependency composition with `zig_library` and `zig_binary`
- `examples/math_lib` — `zig_library` plus `zig_test`
- `examples/multi_package` — a binary depending on a library from another Bazel package
- `examples/bzlmod_consumer` — template files for consuming `rules_zig` from another module before BCR publication

## Rules

### `zig_binary` example
Expand Down
6 changes: 6 additions & 0 deletions examples/bzlmod_consumer/BUILD.bazel.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
load("@rules_zig//zig:defs.bzl", "zig_binary")

zig_binary(
name = "hello",
srcs = ["main.zig"],
)
14 changes: 14 additions & 0 deletions examples/bzlmod_consumer/MODULE.bazel.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module(name = "rules_zig_consumer", version = "0.1.0")

bazel_dep(name = "rules_zig", version = "0.1.0")
git_override(
module_name = "rules_zig",
remote = "https://github.com/darthfork/rules_zig.git",
commit = "<commit_sha>",
)

zig = use_extension("@rules_zig//zig:extensions.bzl", "zig")
zig.toolchain(zig_version = "0.13.0")
use_repo(zig, "zig_toolchains")

register_toolchains("@zig_toolchains//:all")
5 changes: 5 additions & 0 deletions examples/bzlmod_consumer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Bzlmod consumer example

This is a copy-paste skeleton for consuming `rules_zig` from another Bazel module before it is published to the Bazel Central Registry.

Use the `.example` files as templates in a separate repository.
5 changes: 5 additions & 0 deletions examples/bzlmod_consumer/main.zig.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const std = @import("std");

pub fn main() !void {
try std.io.getStdOut().writer().print("Hello from a rules_zig consumer\n", .{});
}
9 changes: 9 additions & 0 deletions examples/multi_package/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Multi-package example

This example shows a `zig_binary` in one Bazel package depending on a `zig_library` in another package.

Build it with:

```bash
bazel build //examples/multi_package/app:hello_multi_package
```
7 changes: 7 additions & 0 deletions examples/multi_package/app/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
load("//zig:defs.bzl", "zig_binary")

zig_binary(
name = "hello_multi_package",
srcs = ["main.zig"],
deps = ["//examples/multi_package/lib:greeting"],
)
6 changes: 6 additions & 0 deletions examples/multi_package/app/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const std = @import("std");
const greeting = @import("greeting");

pub fn main() !void {
try std.io.getStdOut().writer().print("{s}\n", .{greeting.text()});
}
8 changes: 8 additions & 0 deletions examples/multi_package/lib/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("//zig:defs.bzl", "zig_library")

zig_library(
name = "greeting",
srcs = ["greeting.zig"],
module_name = "greeting",
visibility = ["//examples/multi_package/app:__pkg__"],
)
3 changes: 3 additions & 0 deletions examples/multi_package/lib/greeting.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn text() []const u8 {
return "Hello from a Zig library in another Bazel package";
}
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Integration and regression tests for rules_zig.
12 changes: 12 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# rules_zig tests

This tree contains regression coverage for the ruleset itself.

- `integration/basic` verifies that `zig_binary`, `zig_library`, and `zig_test` all work under `bazel test`.
- `private` contains small Starlark helpers used only by the test suite.

Run everything with:

```bash
bazel test //...
```
30 changes: 30 additions & 0 deletions tests/integration/basic/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
load("//tests/private:smoke_test.bzl", "binary_smoke_test", "build_smoke_test")
load("//zig:defs.bzl", "zig_binary", "zig_library", "zig_test")

zig_library(
name = "math",
srcs = ["math.zig"],
)

zig_binary(
name = "calculator",
srcs = ["main.zig"],
)

zig_test(
name = "math_unit_test",
srcs = [
"math_test.zig",
"math.zig",
],
)

build_smoke_test(
name = "math_build_test",
target = ":math",
)

binary_smoke_test(
name = "calculator_smoke_test",
binary = ":calculator",
)
6 changes: 6 additions & 0 deletions tests/integration/basic/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const std = @import("std");

pub fn main() void {
const stdout = std.io.getStdOut().writer();
stdout.print("calculator smoke test\n", .{}) catch unreachable;
}
7 changes: 7 additions & 0 deletions tests/integration/basic/math.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub fn add(lhs: i32, rhs: i32) i32 {
return lhs + rhs;
}

pub fn multiply(lhs: i32, rhs: i32) i32 {
return lhs * rhs;
}
7 changes: 7 additions & 0 deletions tests/integration/basic/math_test.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const std = @import("std");
const math = @import("math.zig");

test "math helpers return expected values" {
try std.testing.expectEqual(@as(i32, 5), math.add(2, 3));
try std.testing.expectEqual(@as(i32, 42), math.multiply(6, 7));
}
1 change: 1 addition & 0 deletions tests/private/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Test helpers for rules_zig's own integration tests.
59 changes: 59 additions & 0 deletions tests/private/smoke_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Small test-only smoke test rule for executable Zig targets."""

def _binary_smoke_test_impl(ctx):
script = ctx.actions.declare_file(ctx.label.name + ".sh")
binary = ctx.executable.binary
ctx.actions.write(
output = script,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${RUNFILES_DIR:-$0.runfiles}"
"${runfiles_dir}/${TEST_WORKSPACE}/%s"
""" % binary.short_path,
)

return [
DefaultInfo(
executable = script,
runfiles = ctx.runfiles(files = [binary]),
),
]

def _build_smoke_test_impl(ctx):
script = ctx.actions.declare_file(ctx.label.name + ".sh")
ctx.actions.write(
output = script,
is_executable = True,
content = "#!/usr/bin/env bash\nset -euo pipefail\n",
)
return [
DefaultInfo(
executable = script,
runfiles = ctx.runfiles(files = ctx.files.target),
),
]

binary_smoke_test = rule(
implementation = _binary_smoke_test_impl,
attrs = {
"binary": attr.label(
doc = "Executable target to run as a smoke test.",
cfg = "target",
executable = True,
mandatory = True,
),
},
test = True,
)

build_smoke_test = rule(
implementation = _build_smoke_test_impl,
attrs = {
"target": attr.label(
doc = "Target whose default outputs must build.",
mandatory = True,
),
},
test = True,
)
2 changes: 2 additions & 0 deletions zig/private/zig_repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ exports_files(["{zig_exe}"])

zig_toolchain(
name = "zig_toolchain_impl",
target = "{target}",
zig_exe = "{zig_exe}",
zig_version = "{zig_version}",
)
""".format(
target = ctx.attr.index_key,
zig_exe = zig_exe,
zig_version = ctx.attr.zig_version,
),
Expand Down
11 changes: 7 additions & 4 deletions zig/rules.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ def _add_module_args(args, dep_modules, module_dep_graph):
if not progressed:
fail("cyclic Zig module dependencies detected in deps")

def _add_common_args(args, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, dep_archives):
def _add_common_args(args, zig_target, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, dep_archives):
args.add("-target")
args.add(zig_target)
for module_name in root_deps:
args.add("--dep")
args.add(module_name)
Expand Down Expand Up @@ -112,7 +114,7 @@ def _zig_binary_impl(ctx):

args = ctx.actions.args()
args.add("build-exe")
_add_common_args(args, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, dep_archives)
_add_common_args(args, zig_info.target, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, dep_archives)

ctx.actions.run(
outputs = [out, cache_dir, global_cache_dir],
Expand Down Expand Up @@ -170,7 +172,7 @@ def _zig_library_impl(ctx):

args = ctx.actions.args()
args.add("build-lib")
_add_common_args(args, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, depset(transitive = transitive_archives).to_list())
_add_common_args(args, zig_info.target, main_src, out, cache_dir, global_cache_dir, root_deps, dep_modules, module_dep_graph, depset(transitive = transitive_archives).to_list())

ctx.actions.run(
outputs = [out, cache_dir, global_cache_dir],
Expand Down Expand Up @@ -239,12 +241,13 @@ def _zig_test_impl(ctx):
for src in srcs:
copy_commands.append("cp '{}' '{}/{}'".format(src.path, src_tree.path, src.basename))

command = "set -euo pipefail\nROOT=\"$PWD\"\nmkdir -p '{src_tree}' '{cache_dir}' '{global_cache_dir}'\n{copies}\ncd '{src_tree}'\n\"$ROOT/{zig}\" test '{main}' -femit-bin=\"$ROOT/{out}\" --cache-dir \"$ROOT/{cache_dir}\" --global-cache-dir \"$ROOT/{global_cache_dir}\"".format(
command = "set -euo pipefail\nROOT=\"$PWD\"\nmkdir -p '{src_tree}' '{cache_dir}' '{global_cache_dir}'\n{copies}\ncd '{src_tree}'\n\"$ROOT/{zig}\" test -target '{target}' '{main}' -femit-bin=\"$ROOT/{out}\" --cache-dir \"$ROOT/{cache_dir}\" --global-cache-dir \"$ROOT/{global_cache_dir}\"".format(
src_tree = src_tree.path,
cache_dir = cache_dir.path,
global_cache_dir = global_cache_dir.path,
copies = "\n".join(copy_commands),
zig = zig_link.path,
target = zig_info.target,
main = main_src.basename,
out = out.path,
)
Expand Down
6 changes: 6 additions & 0 deletions zig/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
ZigInfo = provider(
doc = "Information about the Zig compiler.",
fields = {
"target": "string: The Zig target triple matching the selected SDK.",
"zig_exe": "File: The Zig compiler executable.",
"zig_version": "string: The Zig version.",
},
)

def _zig_toolchain_impl(ctx):
zig_info = ZigInfo(
target = ctx.attr.target,
zig_exe = ctx.file.zig_exe,
zig_version = ctx.attr.zig_version,
)
Expand All @@ -21,6 +23,10 @@ def _zig_toolchain_impl(ctx):
zig_toolchain = rule(
implementation = _zig_toolchain_impl,
attrs = {
"target": attr.string(
doc = "The Zig target triple matching this SDK.",
mandatory = True,
),
"zig_exe": attr.label(
doc = "The Zig compiler executable.",
mandatory = True,
Expand Down
Loading