diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3d6d5c3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: bazel ${{ matrix.bazel }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + bazel: + - 9.0.1 + - 9.0.2 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Bazelisk + uses: bazelbuild/setup-bazelisk@v3 + + - name: Select Bazel version + run: echo "USE_BAZEL_VERSION=${{ matrix.bazel }}" >> "$GITHUB_ENV" + + - name: Show Bazel version + run: bazel version + + - name: Run test suite + run: bazel test //... diff --git a/README.md b/README.md index e03a116..8997454 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Bazel rules for building [Zig](https://ziglang.org/) projects. - `zig_binary` — compile Zig source files into an executable - `zig_library` — compile Zig source files into a static library (`.a`) +- `zig_test` — compile and run Zig tests with `bazel test` - Bazel toolchain integration with platform-aware compiler resolution ## Requirements @@ -13,6 +14,22 @@ Bazel rules for building [Zig](https://ziglang.org/) projects. - Zig: 0.13.0 - Bazel: 9.0+ +## CI and support matrix + +The repository currently runs CI on: + +- Bazel 9.0.1 on Linux and macOS +- Bazel 9.0.2 on Linux and macOS + +This gives us coverage for the minimum supported Bazel 9 line and a newer Bazel 9 patch release that is currently available on GitHub-hosted runners. + +CI currently runs: + +- `bazel build //...` +- `bazel test ...` when Bazel test targets are present + +As the ruleset grows, this matrix can expand to include more Bazel versions, additional examples, and stricter validation. + ## Setup ### Bzlmod (MODULE.bazel) @@ -79,3 +96,24 @@ zig_library( |-----------|-------------|----------| | `srcs` | List of `.zig` source files | Yes | | `main` | Root source file for the library. Defaults to the first file in `srcs`. | No | + +### `zig_test` example + +Compiles and runs Zig tests through `bazel test`. + +```starlark +load("@rules_zig//zig:defs.bzl", "zig_test") + +zig_test( + name = "math_test", + srcs = [ + "math_test.zig", + "math.zig", + ], +) +``` + +| Attribute | Description | Required | +|-----------|-------------|----------| +| `srcs` | List of `.zig` source files needed by the test, including imported sibling files | Yes | +| `main` | Root test source file. Defaults to the first file in `srcs`. | No | diff --git a/examples/math_lib/BUILD.bazel b/examples/math_lib/BUILD.bazel index f5a33ee..3b2f882 100644 --- a/examples/math_lib/BUILD.bazel +++ b/examples/math_lib/BUILD.bazel @@ -1,6 +1,14 @@ -load("//zig:defs.bzl", "zig_library") +load("//zig:defs.bzl", "zig_library", "zig_test") zig_library( name = "math", srcs = ["math.zig"], ) + +zig_test( + name = "math_test", + srcs = [ + "math_test.zig", + "math.zig", + ], +) diff --git a/examples/math_lib/math_test.zig b/examples/math_lib/math_test.zig new file mode 100644 index 0000000..d54201c --- /dev/null +++ b/examples/math_lib/math_test.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const math = @import("math.zig"); + +test "basic arithmetic helpers" { + try std.testing.expectEqual(@as(i32, 5), math.add(2, 3)); + try std.testing.expectEqual(@as(i32, 6), math.multiply(2, 3)); + try std.testing.expectEqual(@as(i32, -1), math.subtract(2, 3)); +} diff --git a/zig/defs.bzl b/zig/defs.bzl index e76baea..b6dd43d 100644 --- a/zig/defs.bzl +++ b/zig/defs.bzl @@ -1,6 +1,7 @@ """Public API for rules_zig.""" -load("//zig:rules.bzl", _zig_binary = "zig_binary", _zig_library = "zig_library") +load("//zig:rules.bzl", _zig_binary = "zig_binary", _zig_library = "zig_library", _zig_test = "zig_test") zig_binary = _zig_binary zig_library = _zig_library +zig_test = _zig_test diff --git a/zig/rules.bzl b/zig/rules.bzl index 7acbbc8..2044d6d 100644 --- a/zig/rules.bzl +++ b/zig/rules.bzl @@ -134,3 +134,74 @@ zig_library = rule( }, toolchains = ["//zig:toolchain_type"], ) + +def _zig_test_impl(ctx): + zig_toolchain = ctx.toolchains["//zig:toolchain_type"] + zig_info = zig_toolchain.zig_info + zig_exe = zig_info.zig_exe + + srcs = ctx.files.srcs + if len(srcs) == 0: + fail("zig_test requires at least one source file in srcs.") + + main_filename = ctx.attr.main if ctx.attr.main else srcs[0].basename + main_src = None + for src in srcs: + if src.basename == main_filename or src.short_path.endswith(main_filename): + main_src = src + break + if not main_src: + fail("main file '{}' not found in srcs.".format(main_filename)) + + out = ctx.actions.declare_file(ctx.label.name) + src_tree = ctx.actions.declare_directory(ctx.label.name + "_srcs") + zig_link = ctx.actions.declare_file(ctx.label.name + "_zig") + + ctx.actions.symlink( + output = zig_link, + target_file = zig_exe, + is_executable = True, + ) + + copy_commands = [] + 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}'\n{copies}\ncd '{src_tree}'\n\"$ROOT/{zig}\" test '{main}' -femit-bin=\"$ROOT/{out}\" --cache-dir /tmp/zig-cache --global-cache-dir /tmp/zig-global-cache".format( + src_tree = src_tree.path, + copies = "\n".join(copy_commands), + zig = zig_link.path, + main = main_src.basename, + out = out.path, + ) + + ctx.actions.run_shell( + outputs = [out, src_tree], + inputs = srcs + [zig_link], + command = command, + mnemonic = "ZigCompileTest", + progress_message = "Compiling Zig test %{label}", + ) + + return [ + DefaultInfo( + files = depset([out]), + executable = out, + ), + ] + +zig_test = rule( + implementation = _zig_test_impl, + attrs = { + "srcs": attr.label_list( + doc = "Zig test source files to compile.", + allow_files = [".zig"], + mandatory = True, + ), + "main": attr.string( + doc = "The root source file for the test. Defaults to the first file in srcs.", + ), + }, + test = True, + toolchains = ["//zig:toolchain_type"], +)