From 4deb57e881ad3fd4e43b49bc5a6724ad0db68ffd Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:44:52 +0000 Subject: [PATCH 01/13] feat: add `//toolchain/constraint/libc:ucrt` Universal C runtime is used on Windows. --- toolchain/constraint/libc/BUILD.bazel | 5 +++++ toolchain/constraint/libc/ucrt/BUILD.bazel | 13 +++++++++++++ toolchain/constraint/libc/ucrt/versions.bzl | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 toolchain/constraint/libc/ucrt/BUILD.bazel create mode 100644 toolchain/constraint/libc/ucrt/versions.bzl diff --git a/toolchain/constraint/libc/BUILD.bazel b/toolchain/constraint/libc/BUILD.bazel index bddb17a..9c54d39 100644 --- a/toolchain/constraint/libc/BUILD.bazel +++ b/toolchain/constraint/libc/BUILD.bazel @@ -6,3 +6,8 @@ constraint_value( name = "gnu", constraint_setting = ":libc", ) + +constraint_value( + name = "ucrt", + constraint_setting = ":libc", +) diff --git a/toolchain/constraint/libc/ucrt/BUILD.bazel b/toolchain/constraint/libc/ucrt/BUILD.bazel new file mode 100644 index 0000000..ad07588 --- /dev/null +++ b/toolchain/constraint/libc/ucrt/BUILD.bazel @@ -0,0 +1,13 @@ +load(":versions.bzl", "VERSIONS") + +package(default_visibility = ["//visibility:public"]) + +constraint_setting(name = "ucrt") + +[ + constraint_value( + name = version, + constraint_setting = ":ucrt", + ) + for version in VERSIONS +] diff --git a/toolchain/constraint/libc/ucrt/versions.bzl b/toolchain/constraint/libc/ucrt/versions.bzl new file mode 100644 index 0000000..6fe7743 --- /dev/null +++ b/toolchain/constraint/libc/ucrt/versions.bzl @@ -0,0 +1,9 @@ +load("@local//:triplet.bzl", "TRIPLET") + +visibility("//toolchain/...") + +LOCAL = TRIPLET.libc.version and TRIPLET.libc.version.value + +# TODO: figure out a way to generate Universal CRT versions + +VERSIONS = tuple([LOCAL] if LOCAL != None else []) -- GitLab From 064ecafcc9b0a1ed3afc3c62c459f0b541438b27 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:46:32 +0000 Subject: [PATCH 02/13] feat: add `//toolchain/constraint/os:windows` --- toolchain/constraint/os/BUILD.bazel | 19 +++++++++---------- toolchain/constraint/os/windows/BUILD.bazel | 13 +++++++++++++ toolchain/constraint/os/windows/versions.bzl | 9 +++++++++ 3 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 toolchain/constraint/os/windows/BUILD.bazel create mode 100644 toolchain/constraint/os/windows/versions.bzl diff --git a/toolchain/constraint/os/BUILD.bazel b/toolchain/constraint/os/BUILD.bazel index 642cd8e..a46a359 100644 --- a/toolchain/constraint/os/BUILD.bazel +++ b/toolchain/constraint/os/BUILD.bazel @@ -1,12 +1,11 @@ package(default_visibility = ["//visibility:public"]) -# TODO: add more OS aliases when we are ready to support them. -[ - alias( - name = v, - actual = "@platforms//os:{}".format(v), - ) - for v in ( - "linux", - ) -] +alias( + name = "linux", + actual = "@platforms//os:linux", +) + +alias( + name = "windows", + actual = "@platforms//os:windows", +) diff --git a/toolchain/constraint/os/windows/BUILD.bazel b/toolchain/constraint/os/windows/BUILD.bazel new file mode 100644 index 0000000..6860041 --- /dev/null +++ b/toolchain/constraint/os/windows/BUILD.bazel @@ -0,0 +1,13 @@ +load(":versions.bzl", "VERSIONS") + +package(default_visibility = ["//visibility:public"]) + +constraint_setting(name = "windows") + +[ + constraint_value( + name = version, + constraint_setting = ":windows", + ) + for version in VERSIONS +] diff --git a/toolchain/constraint/os/windows/versions.bzl b/toolchain/constraint/os/windows/versions.bzl new file mode 100644 index 0000000..435be9b --- /dev/null +++ b/toolchain/constraint/os/windows/versions.bzl @@ -0,0 +1,9 @@ +load("@local//:triplet.bzl", "TRIPLET") + +visibility("//toolchain/...") + +LOCAL = TRIPLET.os.version and TRIPLET.os.version.value + +# TODO: figure out a way to generate Windows versions + +VERSIONS = tuple([LOCAL] if LOCAL != None else []) -- GitLab From cd8b097bd731403b3502214fffefb15089a975be Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:48:43 +0000 Subject: [PATCH 03/13] feat: add Universal C runtime detection --- toolchain/local/triplet/libc.bzl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/toolchain/local/triplet/libc.bzl b/toolchain/local/triplet/libc.bzl index d1d5c0b..a83a64a 100644 --- a/toolchain/local/triplet/libc.bzl +++ b/toolchain/local/triplet/libc.bzl @@ -49,6 +49,23 @@ def _ldd(rctx, path): fail("Failed to detect `{}` version:\n{}".format(path, result.stdout)) +def _powershell(rctx, path): + result = rctx.execute([path, "-Command", "Get-Package -Name 'Universal CRT Redistributable'| Format-Wide -Property Version"]) + if result.return_code != 0: + fail("Failed to retrieve `Get-Package` version output:\n{}".format(result.stderr)) + + version = result.stdout.strip() + + major, minor, build, patch = split(version, ".", { + 3: lambda w, x, y, z: (w, x, y, None), + 4: lambda w, x, y, z: (w, x, y, z), + }) + + if patch: + return VersionedInfo("ucrt.{}.{}.{}+{}".format(int(major), int(minor), int(build), int(patch))) + + return VersionedInfo("ucrt.{}.{}.{}".format(int(major), int(minor), int(build))) + def libc(rctx): """ Detects the host C library. @@ -67,4 +84,8 @@ def libc(rctx): if path.exists: return _release(rctx, path) + path = rctx.which("powershell.exe") + if path: + return _powershell(rctx, path) + fail("Failed to detect host C library") -- GitLab From e298c3e5e0674809bd62def624aaf0104089468e Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:50:13 +0000 Subject: [PATCH 04/13] fix: fail fast when not three Linux version parts There _may_ be systems that do not report all three versions. We want to catch that in the split call as the error shows the string that was to be split. --- toolchain/local/triplet/os.bzl | 2 -- 1 file changed, 2 deletions(-) diff --git a/toolchain/local/triplet/os.bzl b/toolchain/local/triplet/os.bzl index 4b64c09..7a16b76 100644 --- a/toolchain/local/triplet/os.bzl +++ b/toolchain/local/triplet/os.bzl @@ -68,8 +68,6 @@ def _uname(rctx, path): version, _ = result.stdout.split("-", 1) major, minor, patch = split(version, ".", { - 1: lambda x: (x, None, None), - 2: lambda x, y: (x, y, None), 3: lambda x, y, z: (x, y, z), }) -- GitLab From a090eabe2dae4e52eb611c610c617437a31a46f0 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:52:19 +0000 Subject: [PATCH 05/13] feat: add simple Windows OS detection Re-uses the Java detection. --- toolchain/local/triplet/os.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/toolchain/local/triplet/os.bzl b/toolchain/local/triplet/os.bzl index 7a16b76..a12acb4 100644 --- a/toolchain/local/triplet/os.bzl +++ b/toolchain/local/triplet/os.bzl @@ -96,4 +96,5 @@ def os(rctx): return VersionedInfo({ "linux": "linux", + "windows 10": "windows.10", }[rctx.os.name]) -- GitLab From 6f4242c19284b424ffd4079c0a02ed6a2bdef103 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:52:56 +0000 Subject: [PATCH 06/13] fix: be graceful when `uname` is not found --- toolchain/local/triplet/os.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/local/triplet/os.bzl b/toolchain/local/triplet/os.bzl index a12acb4..a398fdf 100644 --- a/toolchain/local/triplet/os.bzl +++ b/toolchain/local/triplet/os.bzl @@ -91,7 +91,7 @@ def os(rctx): return _header(rctx, path) path = rctx.which("uname") - if path.exists: + if path: return _uname(rctx, path) return VersionedInfo({ -- GitLab From b2d0c53752ca2488a7ae9c70697591098bdb06b3 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:53:13 +0000 Subject: [PATCH 07/13] feat: add Windows version detection --- toolchain/local/triplet/os.bzl | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/toolchain/local/triplet/os.bzl b/toolchain/local/triplet/os.bzl index a398fdf..e9011b1 100644 --- a/toolchain/local/triplet/os.bzl +++ b/toolchain/local/triplet/os.bzl @@ -76,6 +76,35 @@ def _uname(rctx, path): return VersionedInfo("linux.{}.{}.{}".format(int(major), int(minor), int(patch))) +def _cmd(rctx, path): + """ + Determines the operating system version from `ver`, a `cmd` built-in. + + Args: + rctx: The repository context that can execute commands on the host machine. + path: the path to the `cmd` executable. + + Returns: + The `VersionedInfo` provider + """ + result = rctx.execute((path, "/C", "ver")) + if result.return_code != 0: + fail("Failed to get `ver` release: {}".format(result.stderr)) + + version = result.stdout.strip() + sku, version = version.split(" [Version ") + version = version.removesuffix("]") + + major, minor, build, patch = split(version, ".", { + 4: lambda w, x, y, z: (w, x, y, z), + }) + + variant = { + "Microsoft Windows": "windows", + }[sku] + + return VersionedInfo("{}.{}.{}.{}+{}".format(variant, int(major), int(minor), int(build), int(patch))) + def os(rctx): """ Detects the host operating system. @@ -94,6 +123,10 @@ def os(rctx): if path: return _uname(rctx, path) + path = rctx.which("cmd.exe") + if path: + return _cmd(rctx, path) + return VersionedInfo({ "linux": "linux", "windows 10": "windows.10", -- GitLab From 892c6233cce27d1ff2a6b950d3ddca1e34b807ff Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:54:41 +0000 Subject: [PATCH 08/13] fix: correct `{{basename}}` in `which` We were not forwarding it through to the templating. --- toolchain/local/which/repository.bzl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolchain/local/which/repository.bzl b/toolchain/local/which/repository.bzl index ba190b1..864f518 100644 --- a/toolchain/local/which/repository.bzl +++ b/toolchain/local/which/repository.bzl @@ -61,6 +61,7 @@ ATTRS = _ATTRS | { def implementation(rctx): program = rctx.attr.program or rctx.attr.name.rsplit("~", 1)[1] + basename = rctx.attr.basename or program path = rctx.which(program) if not path: @@ -70,7 +71,7 @@ def implementation(rctx): rctx.template("resolved.bzl", rctx.attr.resolved, { "{{toolchain_type}}": str(rctx.attr.toolchain_type), - "{{basename}}": str(rctx.attr.basename), + "{{basename}}": basename, }, executable = False) rctx.template("entrypoint", rctx.attr.entrypoint, { @@ -80,6 +81,7 @@ def implementation(rctx): rctx.template("BUILD.bazel", rctx.attr.build, { "{{name}}": rctx.attr.target or program, "{{program}}": program, + "{{basename}}": basename, "{{path}}": str(path.realpath), "{{variable}}": rctx.attr.variable or program.upper(), "{{toolchain_type}}": str(rctx.attr.toolchain_type), -- GitLab From 52384217e24ccf40b09091b0b4d9e8f24cc1264a Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:55:15 +0000 Subject: [PATCH 09/13] test: set size of triplet test to "small" --- toolchain/triplet/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/toolchain/triplet/BUILD.bazel b/toolchain/triplet/BUILD.bazel index 7a7903f..4def3e4 100644 --- a/toolchain/triplet/BUILD.bazel +++ b/toolchain/triplet/BUILD.bazel @@ -18,5 +18,6 @@ alias( test( name = "test", + size = "small", target_under_test = ":amd64-linux-gnu", ) -- GitLab From e6ceb6657f12d3d0a83d92e75ab85e9ac9a3b209 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:56:23 +0000 Subject: [PATCH 10/13] docs(toolchain_symlink_target): move documentation into a variable --- toolchain/symlink/target/rule.bzl | 72 ++++++++++++++++--------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/toolchain/symlink/target/rule.bzl b/toolchain/symlink/target/rule.bzl index 1c4a94c..fabca2d 100644 --- a/toolchain/symlink/target/rule.bzl +++ b/toolchain/symlink/target/rule.bzl @@ -1,5 +1,41 @@ visibility("//toolchain/...") +DOC = """Creates a executable symlink to a binary target file. + +This rule can be used to symlink a executable target and export the necessary toolchain providers. + +Often used with downloaded binary targets: + +```py +load("@rules_toolchain//toolchain:defs.bzl", "ToolchainTripletInfo") + +toolchain_type( + name = "type", +) + +# Setup a toolchain for each downloaded binary +[ + ( + toolchain_symlink_target( + name = "something-{}".format(triplet.value), + target = "@downloaded-{}//:something".format(triplet), + ), + toolchain( + name = triplet.value, + toolchain = ":something-{}".format(triplet.value), + exec_compatible_with = triplet.constraints, + ) + ) + for triplet in ( + ToolchainTripletInfo("arm64-linux-gnu"), + ToolchainTripletInfo("arm64-linux-musl"), + ) +] +``` + +`rules_download` has a `download.archive` and `download.file` extension that can help with retrieving remote binaries. +""" + ATTRS = { "target": attr.label( doc = "The binary file to symlink.", @@ -50,41 +86,7 @@ def implementation(ctx): return [variables, toolchain, default] target = rule( - doc = """Creates a executable symlink to a binary target file. - -This rule can be used to symlink a executable target and export the necessary toolchain providers. - -Often used with downloaded binary targets: - -```py -load("@rules_toolchain//toolchain:defs.bzl", "ToolchainTripletInfo") - -toolchain_type( - name = "type", -) - -# Setup a toolchain for each downloaded binary -[ - ( - toolchain_symlink_target( - name = "something-{}".format(triplet.value), - target = "@downloaded-{}//:something".format(triplet), - ), - toolchain( - name = triplet.value, - toolchain = ":something-{}".format(triplet.value), - exec_compatible_with = triplet.constraints, - ) - ) - for triplet in ( - ToolchainTripletInfo("arm64-linux-gnu"), - ToolchainTripletInfo("arm64-linux-musl"), - ) -] -``` - -`rules_download` has a `download.archive` and `download.file` extension that can help with retrieving remote binaries. -""", + doc = DOC, attrs = ATTRS, implementation = implementation, provides = [ -- GitLab From 32e2220df6254455713d331f9cbd4509fb9d9bc8 Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:57:48 +0000 Subject: [PATCH 11/13] feat(toolchain_triplet): add Windows support --- toolchain/triplet/BUILD.bazel | 14 ++++++++++++++ toolchain/triplet/nt.tmpl.bat | 21 +++++++++++++++++++++ toolchain/triplet/posix.tmpl.sh | 11 +++++++++++ toolchain/triplet/rule.bzl | 17 ++++++++++++++--- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 toolchain/triplet/nt.tmpl.bat create mode 100644 toolchain/triplet/posix.tmpl.sh diff --git a/toolchain/triplet/BUILD.bazel b/toolchain/triplet/BUILD.bazel index 4def3e4..a178f9e 100644 --- a/toolchain/triplet/BUILD.bazel +++ b/toolchain/triplet/BUILD.bazel @@ -2,6 +2,11 @@ load(":triplets.bzl", "TRIPLETS") load(":rule.bzl", "triplet") load(":test.bzl", "test") +exports_files([ + "nt.tmpl.bat", + "posix.tmpl.sh", +]) + [ triplet( name = t.value, @@ -21,3 +26,12 @@ test( size = "small", target_under_test = ":amd64-linux-gnu", ) + +alias( + name = "template", + actual = select({ + "//toolchain/constraint/os:windows": ":nt.tmpl.bat", + "//conditions:default": ":posix.tmpl.sh", + }), + visibility = ["//toolchain:__subpackages__"], +) diff --git a/toolchain/triplet/nt.tmpl.bat b/toolchain/triplet/nt.tmpl.bat new file mode 100644 index 0000000..7231aad --- /dev/null +++ b/toolchain/triplet/nt.tmpl.bat @@ -0,0 +1,21 @@ +:: Enable Batch extensions +@verify other 2>nul +@setlocal EnableExtensions +@if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) + +:: Enable delayed expansion of variables with `!VAR!` +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) + +:: Bazel substitutions +@set "TRIPLET={{triplet}}" + +:: Execute! +@echo.%TRIPLET% diff --git a/toolchain/triplet/posix.tmpl.sh b/toolchain/triplet/posix.tmpl.sh new file mode 100644 index 0000000..63432f1 --- /dev/null +++ b/toolchain/triplet/posix.tmpl.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Strict shell +set -o errexit -o nounset + +# Bazel substitutions +TRIPLET="{{triplet}}" +readonly TRIPLET + +# Execute! +printf '%s\n' "${TRIPLET}" diff --git a/toolchain/triplet/rule.bzl b/toolchain/triplet/rule.bzl index 2fa03d3..c916db8 100644 --- a/toolchain/triplet/rule.bzl +++ b/toolchain/triplet/rule.bzl @@ -6,6 +6,13 @@ ATTRS = { "value": attr.string( doc = "A triplet value that overrides `name`.", ), + "template": attr.label( + doc = "The executable script template.", + default = ":template", + allow_single_file = True, + executable = False, + cfg = "exec", + ), } def implementation(ctx): @@ -18,10 +25,14 @@ def implementation(ctx): content = value, ) - executable = ctx.actions.declare_file("{}.sh".format(value)) - ctx.actions.write( + substitutions = ctx.actions.template_dict() + substitutions.add("{{triplet}}", value) + + executable = ctx.actions.declare_file("{}.{}".format(value, ctx.file.template.extension)) + ctx.actions.expand_template( output = executable, - content = "#!/bin/sh\nprintf '%s\n' {}".format(value), + template = ctx.file.template, + computed_substitutions = substitutions, is_executable = True, ) -- GitLab From 0e0d6f4e4740ebc88f71a80920adcd1d47cc71bb Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 09:59:41 +0000 Subject: [PATCH 12/13] test: correctly register hermetic `echo` toolchain --- e2e/toolchain/echo/BUILD.bazel | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/e2e/toolchain/echo/BUILD.bazel b/e2e/toolchain/echo/BUILD.bazel index 5fe7176..9e560e2 100644 --- a/e2e/toolchain/echo/BUILD.bazel +++ b/e2e/toolchain/echo/BUILD.bazel @@ -6,20 +6,21 @@ toolchain_type( visibility = ["//visibility:public"], ) -toolchain_symlink_target( - name = "hermetic", - target = ":echo.sh", -) - toolchain( name = "local", toolchain = "@echo//:echo", toolchain_type = ":type", ) -alias( - name = "echo", - actual = "hermetic", +toolchain_symlink_target( + name = "script", + target = ":echo.sh", +) + +toolchain( + name = "hermetic", + toolchain = ":script", + toolchain_type = ":type", ) alias( -- GitLab From bdbfcd45974536ff7234b52d023104c532509bfa Mon Sep 17 00:00:00 2001 From: Matt Clarkson Date: Tue, 5 Dec 2023 12:42:10 +0000 Subject: [PATCH 13/13] chore: update lockfile --- MODULE.bazel.lock | 2 +- e2e/MODULE.bazel.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 4ca6567..877fb1e 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -656,7 +656,7 @@ "moduleExtensions": { "//:MODULE.bazel%_repo_rules": { "general": { - "bzlTransitiveDigest": "VGGV2/aS8KP0wlEaxXfdomUzHtZ4Ry+aV/L2Ktp3UCo=", + "bzlTransitiveDigest": "4dF26p1sSSHcbwj60ndza5fdK5QeD94q2c21V9FovL0=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { diff --git a/e2e/MODULE.bazel.lock b/e2e/MODULE.bazel.lock index c918f39..a7fbb5f 100644 --- a/e2e/MODULE.bazel.lock +++ b/e2e/MODULE.bazel.lock @@ -808,7 +808,7 @@ "moduleExtensions": { "//:MODULE.bazel%_repo_rules": { "general": { - "bzlTransitiveDigest": "/R69egUTLvkHkALvRLGxIJMgJWsQ0N6s2oGkMp4YL30=", + "bzlTransitiveDigest": "L6CiLxS6v0YN9d8yxQi+1oUtoYFLVWWho6J7A4BgHB8=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { @@ -1468,7 +1468,7 @@ }, "@@rules_toolchain~override//:MODULE.bazel%_repo_rules": { "general": { - "bzlTransitiveDigest": "VGGV2/aS8KP0wlEaxXfdomUzHtZ4Ry+aV/L2Ktp3UCo=", + "bzlTransitiveDigest": "4dF26p1sSSHcbwj60ndza5fdK5QeD94q2c21V9FovL0=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { -- GitLab