diff --git a/.bazelrc b/.bazelrc index 010fef67..7500e6d5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -2,6 +2,8 @@ # Take care to document any settings that you expect users to apply. # Settings that apply only to CI are in .github/workflows/ci.bazelrc +import %workspace%/.bazelrc.flags + # Deleted packages for integration tests. # To update these lines, execute # `bazel run @contrib_rules_bazel_integration_test//tools:update_deleted_packages` diff --git a/.bazelrc.flags b/.bazelrc.flags new file mode 100644 index 00000000..8990d30f --- /dev/null +++ b/.bazelrc.flags @@ -0,0 +1,6 @@ +# Defines aliases for rules_zig build settings flags, +# see https://bazel.build/extending/config#using-build-setting-aliases +# Copy this file into your project and import it into your `.bazelrc` to enable +# the same aliases. + +build --flag_alias=zig_mode=@rules_zig//zig/settings:mode diff --git a/util/update_filegroups.py b/util/update_filegroups.py index cb4e77bf..d3d6bf13 100755 --- a/util/update_filegroups.py +++ b/util/update_filegroups.py @@ -15,7 +15,7 @@ import subprocess # Add exlusions for packages to skip here. -PACKAGE_PATTERN = "//... - //docs/... - //util/... - //zig/tests/..." +PACKAGE_PATTERN = "//...:* - //docs/...:* - //util/...:* - //zig/tests/...:*" # Add extra source files to capture here. EXTRA_SRCS = { diff --git a/zig/BUILD.bazel b/zig/BUILD.bazel index a528836e..d31b2b02 100644 --- a/zig/BUILD.bazel +++ b/zig/BUILD.bazel @@ -58,7 +58,9 @@ filegroup( ":defs.bzl", ":repositories.bzl", ":toolchain.bzl", + "//zig/config:all_files", "//zig/private:all_files", + "//zig/settings:all_files", ], visibility = ["//:__pkg__"], ) diff --git a/zig/config/BUILD.bazel b/zig/config/BUILD.bazel new file mode 100644 index 00000000..b6fabcc6 --- /dev/null +++ b/zig/config/BUILD.bazel @@ -0,0 +1,9 @@ +# Execute `bazel run //util:update_filegroups` to update this target. +filegroup( + name = "all_files", + srcs = [ + ":BUILD.bazel", + "//zig/config/mode:all_files", + ], + visibility = ["//zig:__pkg__"], +) diff --git a/zig/config/mode/BUILD.bazel b/zig/config/mode/BUILD.bazel new file mode 100644 index 00000000..d3dee151 --- /dev/null +++ b/zig/config/mode/BUILD.bazel @@ -0,0 +1,34 @@ +config_setting( + name = "debug", + flag_values = { + "//zig/settings:mode": "debug", + }, +) + +config_setting( + name = "release_safe", + flag_values = { + "//zig/settings:mode": "release_safe", + }, +) + +config_setting( + name = "release_small", + flag_values = { + "//zig/settings:mode": "release_small", + }, +) + +config_setting( + name = "release_fast", + flag_values = { + "//zig/settings:mode": "release_fast", + }, +) + +# Execute `bazel run //util:update_filegroups` to update this target. +filegroup( + name = "all_files", + srcs = [":BUILD.bazel"], + visibility = ["//zig/config:__pkg__"], +) diff --git a/zig/private/BUILD.bazel b/zig/private/BUILD.bazel index 8ae10878..78b29974 100644 --- a/zig/private/BUILD.bazel +++ b/zig/private/BUILD.bazel @@ -1,46 +1,50 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") bzl_library( - name = "zig_binary", - srcs = ["zig_binary.bzl"], + name = "zig_package", + srcs = ["zig_package.bzl"], visibility = ["//zig:__subpackages__"], deps = [ "//zig/private/common:filetypes", - "//zig/private/common:zig_cache", "//zig/private/providers:zig_package_info", ], ) bzl_library( - name = "zig_package", - srcs = ["zig_package.bzl"], + name = "zig_test", + # Gazelle removes the srcs attribute for some reason. + srcs = ["zig_test.bzl"], # keep visibility = ["//zig:__subpackages__"], - deps = [ - "//zig/private/common:filetypes", - "//zig/private/providers:zig_package_info", - ], ) bzl_library( - name = "zig_library", - srcs = ["zig_library.bzl"], + name = "settings", + srcs = ["settings.bzl"], + visibility = ["//zig:__subpackages__"], + deps = ["@bazel_skylib//rules:common_settings"], +) + +bzl_library( + name = "zig_binary", + srcs = ["zig_binary.bzl"], visibility = ["//zig:__subpackages__"], deps = [ "//zig/private/common:filetypes", "//zig/private/common:zig_cache", "//zig/private/providers:zig_package_info", + "//zig/private/providers:zig_settings_info", ], ) bzl_library( - name = "zig_test", - # Gazelle removes the srcs attribute for some reason. - srcs = ["zig_test.bzl"], # keep + name = "zig_library", + srcs = ["zig_library.bzl"], visibility = ["//zig:__subpackages__"], deps = [ "//zig/private/common:filetypes", "//zig/private/common:zig_cache", "//zig/private/providers:zig_package_info", + "//zig/private/providers:zig_settings_info", ], ) @@ -68,6 +72,7 @@ filegroup( srcs = [ ":BUILD.bazel", ":resolved_toolchain.bzl", + ":settings.bzl", ":toolchains_repo.bzl", ":versions.bzl", ":zig_binary.bzl", diff --git a/zig/private/providers/BUILD.bazel b/zig/private/providers/BUILD.bazel index ed5d7cd1..d30464fa 100644 --- a/zig/private/providers/BUILD.bazel +++ b/zig/private/providers/BUILD.bazel @@ -6,12 +6,19 @@ bzl_library( visibility = ["//zig:__subpackages__"], ) +bzl_library( + name = "zig_settings_info", + srcs = ["zig_settings_info.bzl"], + visibility = ["//zig:__subpackages__"], +) + # Execute `bazel run //util:update_filegroups` to update this target. filegroup( name = "all_files", srcs = [ ":BUILD.bazel", ":zig_package_info.bzl", + ":zig_settings_info.bzl", ], visibility = ["//zig/private:__pkg__"], ) diff --git a/zig/private/providers/zig_settings_info.bzl b/zig/private/providers/zig_settings_info.bzl new file mode 100644 index 00000000..3da9762a --- /dev/null +++ b/zig/private/providers/zig_settings_info.bzl @@ -0,0 +1,18 @@ +"""Defines providers for the settings rule.""" + +ZigSettingsInfo = provider( + doc = "Collection of all active Zig build settings.", + fields = { + "mode": "The Zig build mode.", + "args": "The collected compiler arguments for all active settings.", + }, +) + +def zig_settings(*, settings, args): + """Set flags for the given Zig build settings. + + Args: + settings: ZigSettingsInfo, The active Zig build settings. + args: Args; mutable, Append the needed Zig compiler flags to this object. + """ + args.add_all(settings.args) diff --git a/zig/private/settings.bzl b/zig/private/settings.bzl new file mode 100644 index 00000000..5b63fc67 --- /dev/null +++ b/zig/private/settings.bzl @@ -0,0 +1,50 @@ +"""Implementation of the settings rule.""" + +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") +load("//zig/private/providers:zig_settings_info.bzl", "ZigSettingsInfo") + +DOC = """\ +""" + +ATTRS = { + "mode": attr.label( + doc = "The build mode setting.", + mandatory = True, + ), +} + +MODE_ARGS = { + "debug": ["-O", "Debug"], + "release_safe": ["-O", "ReleaseSafe"], + "release_small": ["-O", "ReleaseSmall"], + "release_fast": ["-O", "ReleaseFast"], +} + +def _settings_impl(ctx): + args = [] + + mode = ctx.attr.mode[BuildSettingInfo].value + args.extend(MODE_ARGS[mode]) + + settings_info = ZigSettingsInfo( + mode = mode, + args = args, + ) + + settings_json = ctx.actions.declare_file(ctx.label.name + ".json") + ctx.actions.write(settings_json, settings_info.to_json(), is_executable = False) + + default_info = DefaultInfo( + files = depset([settings_json]), + ) + + return [ + default_info, + settings_info, + ] + +settings = rule( + _settings_impl, + attrs = ATTRS, + doc = DOC, +) diff --git a/zig/private/zig_binary.bzl b/zig/private/zig_binary.bzl index c95cfc01..2a590cfc 100644 --- a/zig/private/zig_binary.bzl +++ b/zig/private/zig_binary.bzl @@ -7,6 +7,11 @@ load( "ZigPackageInfo", "zig_package_dependencies", ) +load( + "//zig/private/providers:zig_settings_info.bzl", + "ZigSettingsInfo", + "zig_settings", +) DOC = """\ """ @@ -27,6 +32,11 @@ ATTRS = { mandatory = False, providers = [ZigPackageInfo], ), + "_settings": attr.label( + default = "//zig/settings", + doc = "Zig build settings.", + providers = [ZigSettingsInfo], + ), } def _zig_binary_impl(ctx): @@ -62,6 +72,11 @@ def _zig_binary_impl(ctx): args = args, ) + zig_settings( + settings = ctx.attr._settings[ZigSettingsInfo], + args = args, + ) + ctx.actions.run( outputs = outputs, inputs = depset(direct = direct_inputs, transitive = transitive_inputs), diff --git a/zig/private/zig_library.bzl b/zig/private/zig_library.bzl index 51cef3dc..95d77277 100644 --- a/zig/private/zig_library.bzl +++ b/zig/private/zig_library.bzl @@ -7,6 +7,11 @@ load( "ZigPackageInfo", "zig_package_dependencies", ) +load( + "//zig/private/providers:zig_settings_info.bzl", + "ZigSettingsInfo", + "zig_settings", +) DOC = """\ """ @@ -27,6 +32,11 @@ ATTRS = { mandatory = False, providers = [ZigPackageInfo], ), + "_settings": attr.label( + default = "//zig/settings", + doc = "Zig build settings.", + providers = [ZigSettingsInfo], + ), } def _zig_library_impl(ctx): @@ -63,6 +73,11 @@ def _zig_library_impl(ctx): args = args, ) + zig_settings( + settings = ctx.attr._settings[ZigSettingsInfo], + args = args, + ) + ctx.actions.run( outputs = outputs, inputs = depset(direct = direct_inputs, transitive = transitive_inputs), diff --git a/zig/private/zig_test.bzl b/zig/private/zig_test.bzl index f408701c..a269db7a 100644 --- a/zig/private/zig_test.bzl +++ b/zig/private/zig_test.bzl @@ -7,6 +7,11 @@ load( "ZigPackageInfo", "zig_package_dependencies", ) +load( + "//zig/private/providers:zig_settings_info.bzl", + "ZigSettingsInfo", + "zig_settings", +) DOC = """\ """ @@ -27,6 +32,11 @@ ATTRS = { mandatory = False, providers = [ZigPackageInfo], ), + "_settings": attr.label( + default = "//zig/settings", + doc = "Zig build settings.", + providers = [ZigSettingsInfo], + ), } def _zig_test_impl(ctx): @@ -62,6 +72,11 @@ def _zig_test_impl(ctx): args = args, ) + zig_settings( + settings = ctx.attr._settings[ZigSettingsInfo], + args = args, + ) + ctx.actions.run( outputs = outputs, inputs = depset(direct = direct_inputs, transitive = transitive_inputs), diff --git a/zig/settings/BUILD.bazel b/zig/settings/BUILD.bazel new file mode 100644 index 00000000..752e93d5 --- /dev/null +++ b/zig/settings/BUILD.bazel @@ -0,0 +1,26 @@ +load("@bazel_skylib//rules:common_settings.bzl", "string_flag") +load("//zig/private:settings.bzl", "settings") + +settings( + name = "settings", + mode = ":mode", + visibility = ["//visibility:public"], +) + +string_flag( + name = "mode", + build_setting_default = "debug", + values = [ + "debug", + "release_safe", + "release_small", + "release_fast", + ], +) + +# Execute `bazel run //util:update_filegroups` to update this target. +filegroup( + name = "all_files", + srcs = [":BUILD.bazel"], + visibility = ["//zig:__pkg__"], +) diff --git a/zig/tests/BUILD.bazel b/zig/tests/BUILD.bazel index a6204fff..9bf0f4c1 100644 --- a/zig/tests/BUILD.bazel +++ b/zig/tests/BUILD.bazel @@ -1,9 +1,12 @@ -load(":rules_test.bzl", "rules_test_suite") +load(":mode_test.bzl", "mode_test_suite") load(":package_info_test.bzl", "package_info_test_suite") +load(":rules_test.bzl", "rules_test_suite") load(":versions_test.bzl", "versions_test_suite") -rules_test_suite(name = "rules_test") +mode_test_suite(name = "mode_test") package_info_test_suite(name = "package_info_test") +rules_test_suite(name = "rules_test") + versions_test_suite(name = "versions_test") diff --git a/zig/tests/integration_tests/integration_tests_runner.zig b/zig/tests/integration_tests/integration_tests_runner.zig index 23b7512f..de071099 100644 --- a/zig/tests/integration_tests/integration_tests_runner.zig +++ b/zig/tests/integration_tests/integration_tests_runner.zig @@ -98,3 +98,63 @@ test "failing zig_test fails" { // See https://bazel.build/run/scripts for Bazel exit codes. try std.testing.expectEqual(std.ChildProcess.Term{ .Exited = 3 }, result.term); } + +test "target build mode defaults to Debug" { + const ctx = try BitContext.init(); + + const result = try ctx.exec_bazel(.{ + .argv = &[_][]const u8{ "run", "//:print_build_mode" }, + }); + defer result.deinit(); + + try std.testing.expectEqual(std.ChildProcess.Term{ .Exited = 0 }, result.term); + try std.testing.expectEqualStrings("Debug", result.stdout); +} + +test "exec build mode defaults to Debug" { + const ctx = try BitContext.init(); + + const result = try ctx.exec_bazel(.{ + .argv = &[_][]const u8{ "build", "//:exec_build_mode" }, + }); + defer result.deinit(); + + try std.testing.expectEqual(std.ChildProcess.Term{ .Exited = 0 }, result.term); + + var workspace = try std.fs.cwd().openDir(ctx.workspace_path, .{}); + defer workspace.close(); + + const build_mode = try workspace.readFileAlloc(std.testing.allocator, "bazel-bin/exec_build_mode.out", 16); + defer std.testing.allocator.free(build_mode); + try std.testing.expectEqualStrings("Debug", build_mode); +} + +test "target build mode can be set on the command line" { + const ctx = try BitContext.init(); + + const result = try ctx.exec_bazel(.{ + .argv = &[_][]const u8{ "run", "//:print_build_mode", "--@rules_zig//zig/settings:mode=release_small" }, + }); + defer result.deinit(); + + try std.testing.expectEqual(std.ChildProcess.Term{ .Exited = 0 }, result.term); + try std.testing.expectEqualStrings("ReleaseSmall", result.stdout); +} + +test "exec build mode can be set on the command line" { + const ctx = try BitContext.init(); + + const result = try ctx.exec_bazel(.{ + .argv = &[_][]const u8{ "build", "//:exec_build_mode", "--@rules_zig//zig/settings:mode=release_small" }, + }); + defer result.deinit(); + + try std.testing.expectEqual(std.ChildProcess.Term{ .Exited = 0 }, result.term); + + var workspace = try std.fs.cwd().openDir(ctx.workspace_path, .{}); + defer workspace.close(); + + const build_mode = try workspace.readFileAlloc(std.testing.allocator, "bazel-bin/exec_build_mode.out", 16); + defer std.testing.allocator.free(build_mode); + try std.testing.expectEqualStrings("ReleaseSmall", build_mode); +} diff --git a/zig/tests/integration_tests/workspace/BUILD.bazel b/zig/tests/integration_tests/workspace/BUILD.bazel index acfb7818..c84b13e9 100644 --- a/zig/tests/integration_tests/workspace/BUILD.bazel +++ b/zig/tests/integration_tests/workspace/BUILD.bazel @@ -14,3 +14,15 @@ zig_test( name = "test-fails", main = "test-fails.zig", ) + +zig_binary( + name = "print_build_mode", + main = "print_build_mode.zig", +) + +genrule( + name = "exec_build_mode", + outs = ["exec_build_mode.out"], + cmd = "$(execpath :print_build_mode) > $(OUTS)", + tools = [":print_build_mode"], +) diff --git a/zig/tests/integration_tests/workspace/print_build_mode.zig b/zig/tests/integration_tests/workspace/print_build_mode.zig new file mode 100644 index 00000000..18ea213b --- /dev/null +++ b/zig/tests/integration_tests/workspace/print_build_mode.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +pub fn main() void { + std.io.getStdOut().writeAll( + std.meta.tagName(builtin.mode), + ) catch unreachable; +} diff --git a/zig/tests/mode_test.bzl b/zig/tests/mode_test.bzl new file mode 100644 index 00000000..83f5f570 --- /dev/null +++ b/zig/tests/mode_test.bzl @@ -0,0 +1,108 @@ +"""Analysis tests for Zig build mode settings.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest") +load("@bazel_skylib//lib:partial.bzl", "partial") +load("//zig/private/providers:zig_settings_info.bzl", "ZigSettingsInfo") + +# TODO[AH] Canonicalize this label (`str(Label(...))`) for `bzlmod` support. +# Note, that canonicalization is not compatible with Bazel 5.3.2, where it will +# strip the requried `@` prefix. +_SETTINGS_MODE = "@//zig/settings:mode" + +def _assert_find_unique_option(env, name, args): + index = -1 + for i, arg in enumerate(args): + if arg == name: + asserts.equals(env, -1, index, "The option {} should be unique.".format(name)) + index = i + asserts.true(env, index + 1 <= len(args), "The option {} should have an argument.".format(name)) + asserts.false(env, index == -1, "The option {} should be set.".format(name)) + if index != -1: + return args[index + 1] + else: + return None + +def _define_settings_mode_test(mode, option): + def _test_impl(ctx): + env = analysistest.begin(ctx) + + settings = analysistest.target_under_test(env)[ZigSettingsInfo] + asserts.equals(env, mode, settings.mode) + + mode_option = _assert_find_unique_option(env, "-O", settings.args) + asserts.equals(env, option, mode_option) + + return analysistest.end(env) + + return analysistest.make( + _test_impl, + config_settings = {_SETTINGS_MODE: mode}, + ) + +_settings_mode_debug_test = _define_settings_mode_test("debug", "Debug") +_settings_mode_release_safe_test = _define_settings_mode_test("release_safe", "ReleaseSafe") +_settings_mode_release_small_test = _define_settings_mode_test("release_small", "ReleaseSmall") +_settings_mode_release_fast_test = _define_settings_mode_test("release_fast", "ReleaseFast") + +def _assert_find_action(env, mnemonic): + actions = analysistest.target_actions(env) + for action in actions: + if action.mnemonic == mnemonic: + return action + asserts.true(env, False, "Expected an action with mnemonic {}.".format(mnemonic)) + return None + +def _define_build_mode_test(mnemonic, mode, option): + def _test_impl(ctx): + env = analysistest.begin(ctx) + + action = _assert_find_action(env, mnemonic) + mode_option = _assert_find_unique_option(env, "-O", action.argv) + asserts.equals(env, option, mode_option) + + return analysistest.end(env) + + return analysistest.make( + _test_impl, + config_settings = {_SETTINGS_MODE: mode}, + ) + +_build_exe_mode_debug_test = _define_build_mode_test("ZigBuildExe", "debug", "Debug") +_build_exe_mode_release_safe_test = _define_build_mode_test("ZigBuildExe", "release_safe", "ReleaseSafe") +_build_exe_mode_release_small_test = _define_build_mode_test("ZigBuildExe", "release_small", "ReleaseSmall") +_build_exe_mode_release_fast_test = _define_build_mode_test("ZigBuildExe", "release_fast", "ReleaseFast") + +_build_lib_mode_debug_test = _define_build_mode_test("ZigBuildLib", "debug", "Debug") +_build_lib_mode_release_safe_test = _define_build_mode_test("ZigBuildLib", "release_safe", "ReleaseSafe") +_build_lib_mode_release_small_test = _define_build_mode_test("ZigBuildLib", "release_small", "ReleaseSmall") +_build_lib_mode_release_fast_test = _define_build_mode_test("ZigBuildLib", "release_fast", "ReleaseFast") + +_build_test_mode_debug_test = _define_build_mode_test("ZigBuildTest", "debug", "Debug") +_build_test_mode_release_safe_test = _define_build_mode_test("ZigBuildTest", "release_safe", "ReleaseSafe") +_build_test_mode_release_small_test = _define_build_mode_test("ZigBuildTest", "release_small", "ReleaseSmall") +_build_test_mode_release_fast_test = _define_build_mode_test("ZigBuildTest", "release_fast", "ReleaseFast") + +def mode_test_suite(name): + unittest.suite( + name, + # Test Zig build mode on the settings target + partial.make(_settings_mode_debug_test, target_under_test = "//zig/settings"), + partial.make(_settings_mode_release_safe_test, target_under_test = "//zig/settings"), + partial.make(_settings_mode_release_small_test, target_under_test = "//zig/settings"), + partial.make(_settings_mode_release_fast_test, target_under_test = "//zig/settings"), + # Test Zig build mode on a binary target + partial.make(_build_exe_mode_debug_test, target_under_test = "//zig/tests/simple-binary:binary"), + partial.make(_build_exe_mode_release_safe_test, target_under_test = "//zig/tests/simple-binary:binary"), + partial.make(_build_exe_mode_release_small_test, target_under_test = "//zig/tests/simple-binary:binary"), + partial.make(_build_exe_mode_release_fast_test, target_under_test = "//zig/tests/simple-binary:binary"), + # Test Zig build mode on a library target + partial.make(_build_lib_mode_debug_test, target_under_test = "//zig/tests/simple-library:library"), + partial.make(_build_lib_mode_release_safe_test, target_under_test = "//zig/tests/simple-library:library"), + partial.make(_build_lib_mode_release_small_test, target_under_test = "//zig/tests/simple-library:library"), + partial.make(_build_lib_mode_release_fast_test, target_under_test = "//zig/tests/simple-library:library"), + # Test Zig build mode on a test target + partial.make(_build_test_mode_debug_test, target_under_test = "//zig/tests/simple-test:test"), + partial.make(_build_test_mode_release_safe_test, target_under_test = "//zig/tests/simple-test:test"), + partial.make(_build_test_mode_release_small_test, target_under_test = "//zig/tests/simple-test:test"), + partial.make(_build_test_mode_release_fast_test, target_under_test = "//zig/tests/simple-test:test"), + ) diff --git a/zig/tests/simple-library/BUILD.bazel b/zig/tests/simple-library/BUILD.bazel new file mode 100644 index 00000000..a30a9cd6 --- /dev/null +++ b/zig/tests/simple-library/BUILD.bazel @@ -0,0 +1,7 @@ +load("@rules_zig//zig:defs.bzl", "zig_library") + +zig_library( + name = "library", + main = "main.zig", + visibility = ["//zig/tests:__pkg__"], +) diff --git a/zig/tests/simple-library/main.zig b/zig/tests/simple-library/main.zig new file mode 100644 index 00000000..c215a20e --- /dev/null +++ b/zig/tests/simple-library/main.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +export fn sayHello() void { + std.io.getStdOut().writeAll( + "Hello World!\n", + ) catch unreachable; +} diff --git a/zig/tests/simple-test/BUILD.bazel b/zig/tests/simple-test/BUILD.bazel new file mode 100644 index 00000000..7020c9ed --- /dev/null +++ b/zig/tests/simple-test/BUILD.bazel @@ -0,0 +1,7 @@ +load("@rules_zig//zig:defs.bzl", "zig_test") + +zig_test( + name = "test", + main = "main.zig", + visibility = ["//zig/tests:__pkg__"], +) diff --git a/zig/tests/simple-test/main.zig b/zig/tests/simple-test/main.zig new file mode 100644 index 00000000..1fdeeb06 --- /dev/null +++ b/zig/tests/simple-test/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +test "test" { + try std.testing.expectEqual(2, 1 + 1); +}