Skip to content

Commit cc325fe

Browse files
committed
diff_test: adding a hermetic toolchain
Trying to add a diff hermetic toolchain based on https://github.com/uutils/diffutils
1 parent 475f87a commit cc325fe

7 files changed

+263
-4
lines changed

MODULE.bazel

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ bazel_lib_toolchains.tar()
2525
bazel_lib_toolchains.zstd()
2626
bazel_lib_toolchains.expand_template()
2727
bazel_lib_toolchains.bats()
28-
use_repo(bazel_lib_toolchains, "bats_toolchains", "bsd_tar_toolchains", "copy_directory_toolchains", "copy_to_directory_toolchains", "coreutils_toolchains", "expand_template_toolchains", "jq", "jq_toolchains", "yq", "yq_toolchains", "zstd_toolchains")
28+
bazel_lib_toolchains.diffutils()
29+
use_repo(bazel_lib_toolchains, "bats_toolchains", "bsd_tar_toolchains", "copy_directory_toolchains", "copy_to_directory_toolchains", "coreutils_toolchains", "diffutils_toolchains", "expand_template_toolchains", "jq", "jq_toolchains", "yq", "yq_toolchains", "zstd_toolchains")
2930

3031
register_toolchains(
3132
"@copy_directory_toolchains//:all",
3233
"@copy_to_directory_toolchains//:all",
34+
"@diffutils_toolchains//:all",
3335
"@jq_toolchains//:all",
3436
"@yq_toolchains//:all",
3537
"@coreutils_toolchains//:all",

lib/BUILD.bazel

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ toolchain_type(
7878
name = "bats_toolchain_type",
7979
)
8080

81+
toolchain_type(
82+
name = "diffutils_toolchain_type",
83+
)
84+
8185
bzl_library(
8286
name = "expand_make_vars",
8387
srcs = ["expand_make_vars.bzl"],

lib/extensions.bzl

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ load(
88
"DEFAULT_COPY_TO_DIRECTORY_REPOSITORY",
99
"DEFAULT_COREUTILS_REPOSITORY",
1010
"DEFAULT_COREUTILS_VERSION",
11+
"DEFAULT_DIFFUTILS_REPOSITORY",
1112
"DEFAULT_EXPAND_TEMPLATE_REPOSITORY",
1213
"DEFAULT_JQ_REPOSITORY",
1314
"DEFAULT_JQ_VERSION",
@@ -19,6 +20,7 @@ load(
1920
"register_copy_directory_toolchains",
2021
"register_copy_to_directory_toolchains",
2122
"register_coreutils_toolchains",
23+
"register_diffutils_toolchains",
2224
"register_expand_template_toolchains",
2325
"register_jq_toolchains",
2426
"register_tar_toolchains",
@@ -118,6 +120,15 @@ def _toolchains_extension_impl(mctx):
118120
get_version_fn = lambda attr: attr.core_version,
119121
)
120122

123+
extension_utils.toolchain_repos_bfs(
124+
mctx = mctx,
125+
get_tag_fn = lambda tags: tags.diffutils,
126+
toolchain_name = "diffutils",
127+
default_repository = DEFAULT_DIFFUTILS_REPOSITORY,
128+
toolchain_repos_fn = lambda name, version: register_diffutils_toolchains(name = name, register = False),
129+
get_version_fn = lambda attr: None,
130+
)
131+
121132
if bazel_features.external_deps.extension_metadata_has_reproducible:
122133
return mctx.extension_metadata(reproducible = True)
123134

@@ -138,5 +149,6 @@ toolchains = module_extension(
138149
"name": attr.string(default = DEFAULT_BATS_REPOSITORY),
139150
"core_version": attr.string(default = DEFAULT_BATS_CORE_VERSION),
140151
}),
152+
"diffutils": tag_class(attrs = {"name": attr.string(default = DEFAULT_DIFFUTILS_REPOSITORY)}),
141153
},
142154
)

lib/private/diff_test.bzl

+4-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def _diff_test_impl(ctx):
6363
template = ctx.file._diff_test_tmpl_sh
6464

6565
test_bin = ctx.actions.declare_file(ctx.label.name + test_suffix)
66+
diff_bin = ctx.toolchains["@aspect_bazel_lib//lib:diffutils_toolchain_type"].diffinfo.bin
6667
ctx.actions.expand_template(
6768
template = template,
6869
output = test_bin,
@@ -76,14 +77,15 @@ def _diff_test_impl(ctx):
7677
shell.quote(arg)
7778
for arg in ctx.attr.diff_args
7879
]),
80+
"{diff}": diff_bin.short_path,
7981
},
8082
is_executable = True,
8183
)
8284

8385
return DefaultInfo(
8486
executable = test_bin,
8587
files = depset(direct = [test_bin]),
86-
runfiles = ctx.runfiles(files = [test_bin, file1, file2]),
88+
runfiles = ctx.runfiles(files = [test_bin, file1, file2, diff_bin]),
8789
)
8890

8991
_diff_test = rule(
@@ -110,6 +112,7 @@ _diff_test = rule(
110112
},
111113
test = True,
112114
implementation = _diff_test_impl,
115+
toolchains = ["@aspect_bazel_lib//lib:diffutils_toolchain_type"],
113116
)
114117

115118
def diff_test(name, file1, file2, diff_args = [], size = "small", **kwargs):

lib/private/diff_test_tmpl.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ if [[ ! "$DF1" ]] && [[ "$DF2" ]]; then
7272
exit 1
7373
fi
7474
if [[ "$DF1" ]] || [[ "$DF2" ]]; then
75-
if ! diff {diff_args} -r "$RF1" "$RF2"; then
75+
if ! {diff} {diff_args} -r "$RF1" "$RF2"; then
7676
fail "directories \"{file1}\" and \"{file2}\" differ. {fail_msg}"
7777
fi
7878
else
79-
if ! diff {diff_args} "$RF1" "$RF2"; then
79+
if ! {diff} {diff_args} "$RF1" "$RF2"; then
8080
fail "files \"{file1}\" and \"{file2}\" differ. {fail_msg}"
8181
fi
8282
fi

lib/private/diff_test_toolchain.bzl

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
"Setup a diffutils toolchain repositories and rules"
2+
3+
load(":repo_utils.bzl", "repo_utils")
4+
5+
DIFFUTILS_PLATFORMS = {
6+
"darwin_amd64": struct(
7+
release_platform = "x86_64-apple-darwin",
8+
compatible_with = [
9+
"@platforms//os:macos",
10+
"@platforms//cpu:x86_64",
11+
],
12+
),
13+
"darwin_arm64": struct(
14+
release_platform = "aarch64-apple-darwin",
15+
compatible_with = [
16+
"@platforms//os:macos",
17+
"@platforms//cpu:aarch64",
18+
],
19+
),
20+
"linux_amd64": struct(
21+
release_platform = "x86_64-unknown-linux-gnu",
22+
compatible_with = [
23+
"@platforms//os:linux",
24+
"@platforms//cpu:x86_64",
25+
],
26+
),
27+
"windows_amd64": struct(
28+
release_platform = "x86_64-pc-windows-msvc",
29+
archive_extension = "zip",
30+
compatible_with = [
31+
"@platforms//os:windows",
32+
"@platforms//cpu:x86_64",
33+
],
34+
),
35+
}
36+
37+
DIFFUTILS_VERSION = "0.4.2"
38+
39+
# https://github.com/uutils/diffutils/releases
40+
#
41+
# The integrity hashes can be computed with
42+
# shasum -b -a 384 [downloaded file] | awk '{ print $1 }' | xxd -r -p | base64
43+
DIFFUTILS_INTEGRITIES = {
44+
"aarch64-apple-darwin": "sha384-m5EzRSRFl5NE8bFw+9rV4n06OeMY/tpieZiQepbpDp7/prL2Zr6sw4LWnfhZC+15",
45+
"x86_64-apple-darwin": "sha384-tj3xGzlcdggMrIdrqnrK468cReGkKNQW1ZbKinOHMiMhhILuB1lzyzO2Jmfj9/GM",
46+
"x86_64-pc-windows-msvc": "sha384-kHUBxaHOZPqiXd0exGQsQEPDrDb0O0boAFu6KWSE3K2i1OO5bzwyz8VhmjHlvjGv",
47+
"x86_64-unknown-linux-gnu": "sha384-7qAQT0YR+zaGDPLYkHcBxLzVI+0lwHQx/VMkgEmvgVXkCntM3xaRjUov1Eyz1VBN",
48+
}
49+
50+
DiffInfo = provider(
51+
doc = "Provide info for executing diff",
52+
fields = {
53+
"bin": "Executable diff binary",
54+
},
55+
)
56+
57+
def _diff_toolchain_impl(ctx):
58+
binary = ctx.file.bin
59+
60+
# Make the $(DIFFUTILS_BIN) variable available in places like genrules.
61+
# See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables
62+
template_variables = platform_common.TemplateVariableInfo({
63+
"DIFFUTILS_BIN": binary.path,
64+
})
65+
default_info = DefaultInfo(
66+
files = depset([binary]),
67+
runfiles = ctx.runfiles(files = [binary]),
68+
)
69+
diff_info = DiffInfo(
70+
bin = binary,
71+
)
72+
73+
# Export all the providers inside our ToolchainInfo
74+
# so the resolved_toolchain rule can grab and re-export them.
75+
toolchain_info = platform_common.ToolchainInfo(
76+
diffinfo = diff_info,
77+
template_variables = template_variables,
78+
default = default_info,
79+
)
80+
81+
return [default_info, toolchain_info, template_variables]
82+
83+
diff_toolchain = rule(
84+
implementation = _diff_toolchain_impl,
85+
attrs = {
86+
"bin": attr.label(
87+
mandatory = True,
88+
allow_single_file = True,
89+
),
90+
},
91+
)
92+
93+
def _diffutils_toolchains_repo_impl(rctx):
94+
# Expose a concrete toolchain which is the result of Bazel resolving the toolchain
95+
# for the execution or target platform.
96+
# Workaround for https://github.com/bazelbuild/bazel/issues/14009
97+
starlark_content = """# @generated by @aspect_bazel_lib//lib/private:diff_test_toolchain.bzl
98+
99+
# Forward all the providers
100+
def _resolved_toolchain_impl(ctx):
101+
toolchain_info = ctx.toolchains["@aspect_bazel_lib//lib:diffutils_toolchain_type"]
102+
return [
103+
toolchain_info,
104+
toolchain_info.default,
105+
toolchain_info.diffinfo,
106+
toolchain_info.template_variables,
107+
]
108+
109+
# Copied from java_toolchain_alias
110+
# https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl
111+
resolved_toolchain = rule(
112+
implementation = _resolved_toolchain_impl,
113+
toolchains = ["@aspect_bazel_lib//lib:diffutils_toolchain_type"],
114+
incompatible_use_toolchain_transition = True,
115+
)
116+
"""
117+
rctx.file("defs.bzl", starlark_content)
118+
119+
build_content = """# @generated by @aspect_bazel_lib//lib/private:diff_test_toolchain.bzl
120+
#
121+
# These can be registered in the workspace file or passed to --extra_toolchains flag.
122+
# By default all these toolchains are registered by the diff_register_toolchains macro
123+
# so you don't normally need to interact with these targets.
124+
125+
load(":defs.bzl", "resolved_toolchain")
126+
127+
resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"])
128+
129+
"""
130+
131+
for [platform, meta] in DIFFUTILS_PLATFORMS.items():
132+
build_content += """
133+
toolchain(
134+
name = "{platform}_toolchain",
135+
exec_compatible_with = {compatible_with},
136+
toolchain = "@{user_repository_name}_{platform}//:diff_toolchain",
137+
toolchain_type = "@aspect_bazel_lib//lib:diffutils_toolchain_type",
138+
)
139+
""".format(
140+
platform = platform,
141+
user_repository_name = rctx.attr.user_repository_name,
142+
compatible_with = meta.compatible_with,
143+
)
144+
145+
# Base BUILD file for this repository
146+
rctx.file("BUILD.bazel", build_content)
147+
148+
diffutils_toolchains_repo = repository_rule(
149+
_diffutils_toolchains_repo_impl,
150+
doc = """Creates a repository with toolchain definitions for all known platforms
151+
which can be registered or selected.""",
152+
attrs = {
153+
"user_repository_name": attr.string(doc = "Base name for toolchains repository"),
154+
},
155+
)
156+
157+
def _diffutils_platform_repo_impl(rctx):
158+
is_windows = rctx.attr.platform.startswith("windows_")
159+
meta = DIFFUTILS_PLATFORMS[rctx.attr.platform]
160+
release_platform = meta.release_platform if hasattr(meta, "release_platform") else rctx.attr.platform
161+
archive_extension = meta.archive_extension if hasattr(meta, "archive_extension") else "tar.xz"
162+
163+
url = "https://github.com/uutils/diffutils/releases/download/v{0}/diffutils-{1}.{2}".format(
164+
DIFFUTILS_VERSION,
165+
release_platform,
166+
archive_extension,
167+
)
168+
169+
rctx.download_and_extract(
170+
url = url,
171+
strip_prefix = "diffutils-{0}".format(release_platform),
172+
integrity = DIFFUTILS_INTEGRITIES[release_platform],
173+
)
174+
build_content = """# @generated by @aspect_bazel_lib//lib/private:diff_test_toolchain.bzl
175+
load("@aspect_bazel_lib//lib/private:diff_test_toolchain.bzl", "diff_toolchain")
176+
exports_files(["{0}"])
177+
diff_toolchain(name = "diff_toolchain", bin = "{0}", visibility = ["//visibility:public"])
178+
""".format("diffutils.exe" if is_windows else "diffutils")
179+
180+
# Base BUILD file for this repository
181+
rctx.file("BUILD.bazel", build_content)
182+
183+
diffutils_platform_repo = repository_rule(
184+
implementation = _diffutils_platform_repo_impl,
185+
doc = "Fetch external tools needed for diff toolchain",
186+
attrs = {
187+
"platform": attr.string(mandatory = True, values = DIFFUTILS_PLATFORMS.keys()),
188+
},
189+
)
190+
191+
def _diffutils_host_alias_repo(rctx):
192+
ext = ".exe" if repo_utils.is_windows(rctx) else ""
193+
194+
# Base BUILD file for this repository
195+
rctx.file("BUILD.bazel", """# @generated by @aspect_bazel_lib//lib/private:diff_test_toolchain.bzl
196+
package(default_visibility = ["//visibility:public"])
197+
exports_files(["diff{ext}"])
198+
""".format(
199+
ext = ext,
200+
))
201+
202+
rctx.symlink("../{name}_{platform}/diff{ext}".format(
203+
name = rctx.attr.name,
204+
platform = repo_utils.platform(rctx),
205+
ext = ext,
206+
), "diff{ext}".format(ext = ext))
207+
208+
diffutils_host_alias_repo = repository_rule(
209+
_diffutils_host_alias_repo,
210+
doc = """Creates a repository with a shorter name meant for the host platform, which contains
211+
a BUILD.bazel file that exports symlinks to the host platform's binaries
212+
""",
213+
)

lib/repositories.bzl

+25
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ load("//lib/private:bats_toolchain.bzl", "BATS_ASSERT_VERSIONS", "BATS_CORE_TEMP
55
load("//lib/private:copy_directory_toolchain.bzl", "COPY_DIRECTORY_PLATFORMS", "copy_directory_platform_repo", "copy_directory_toolchains_repo")
66
load("//lib/private:copy_to_directory_toolchain.bzl", "COPY_TO_DIRECTORY_PLATFORMS", "copy_to_directory_platform_repo", "copy_to_directory_toolchains_repo")
77
load("//lib/private:coreutils_toolchain.bzl", "COREUTILS_PLATFORMS", "coreutils_platform_repo", "coreutils_toolchains_repo", _DEFAULT_COREUTILS_VERSION = "DEFAULT_COREUTILS_VERSION")
8+
load("//lib/private:diff_test_toolchain.bzl", "DIFFUTILS_PLATFORMS", "diffutils_platform_repo", "diffutils_toolchains_repo")
89
load("//lib/private:expand_template_toolchain.bzl", "EXPAND_TEMPLATE_PLATFORMS", "expand_template_platform_repo", "expand_template_toolchains_repo")
910
load("//lib/private:jq_toolchain.bzl", "JQ_PLATFORMS", "jq_host_alias_repo", "jq_platform_repo", "jq_toolchains_repo", _DEFAULT_JQ_VERSION = "DEFAULT_JQ_VERSION")
1011
load("//lib/private:source_toolchains_repo.bzl", "source_toolchains_repo")
@@ -334,6 +335,29 @@ def register_expand_template_toolchains(name = DEFAULT_EXPAND_TEMPLATE_REPOSITOR
334335
user_repository_name = name,
335336
)
336337

338+
DEFAULT_DIFFUTILS_REPOSITORY = "diffutils"
339+
340+
def register_diffutils_toolchains(name = DEFAULT_DIFFUTILS_REPOSITORY, register = True):
341+
"""Registers expand_template toolchain and repositories
342+
343+
Args:
344+
name: override the prefix for the generated toolchain repositories
345+
register: whether to call through to native.register_toolchains.
346+
Should be True for WORKSPACE users, but false when used under bzlmod extension
347+
"""
348+
for [platform, _] in DIFFUTILS_PLATFORMS.items():
349+
diffutils_platform_repo(
350+
name = "%s_%s" % (name, platform),
351+
platform = platform,
352+
)
353+
if register:
354+
native.register_toolchains("@%s_%s//:diffutils_toolchain" % (name, platform))
355+
356+
diffutils_toolchains_repo(
357+
name = "%s_toolchains" % name,
358+
user_repository_name = name,
359+
)
360+
337361
# buildifier: disable=unnamed-macro
338362
def aspect_bazel_lib_register_toolchains():
339363
"""Register all bazel-lib toolchains at their default versions.
@@ -350,3 +374,4 @@ def aspect_bazel_lib_register_toolchains():
350374
register_tar_toolchains()
351375
register_zstd_toolchains()
352376
register_bats_toolchains()
377+
register_diffutils_toolchains()

0 commit comments

Comments
 (0)