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
24 changes: 24 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,30 @@ archive_override(
urls = ["https://github.com/bazel-contrib/toolchains_llvm/archive/ffe407ca2423f4d171b7a0f3897c23bbb7e40310.tar.gz"],
)

#################################
## Tools that ship with the OS ##
#################################

find_macos_codesign = use_repo_rule("//bazel/toolchains/codesign:configure.bzl", "find_macos_codesign")

find_macos_codesign(
name = "macos_codesign",
exec_compatible_with = ["@platforms//os:macos"],
)

find_macos_pkgbuild = use_repo_rule("//bazel/toolchains/pkgbuild:configure.bzl", "find_macos_pkgbuild")

find_macos_pkgbuild(
name = "macos_pkgbuild",
exec_compatible_with = ["@platforms//os:macos"],
)

register_toolchains(
"@macos_codesign//:all",
"@macos_pkgbuild//:all",
dev_dependency = True,
)

#########################
## Prebuilt binaries ##
#########################
Expand Down
9 changes: 9 additions & 0 deletions bazel/toolchains/codesign/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""toolchain to wrap the codesign binary.

Type: @codesign//:codesign_toolchain_type

Toolchains:
- @codesign//:codesign_toolchain: provides the tool
- @codesign//:codesign_missing_toolchain: provides a fallback toolchain for
exec platforms where codesign might not be available.
"""
8 changes: 8 additions & 0 deletions bazel/toolchains/codesign/configure.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Repository rule to autoconfigure a toolchain using the system codesign."""

load("//bazel/toolchains/common:defs.bzl", "make_toolchain_repository_rule")

# This must match the repository name used by register_toolchains in consuming MODULE.bazel files.
NAME = "macos_codesign"

find_macos_codesign = make_toolchain_repository_rule(name = NAME, tool_name = "codesign")
1 change: 1 addition & 0 deletions bazel/toolchains/common/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# //bazel/toolchains/common
63 changes: 63 additions & 0 deletions bazel/toolchains/common/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Utilitites for creating toolchains to wrap system provided tools."""

def _write_toolchain_repo(rctx, repo_name, tool_name, tool_path, tool_version = "<unknown>", exec_compatible_with = None):
if not tool_path:
tool_path = ""
rctx.template(
"BUILD",
Label("@@//bazel/toolchains/common:toolchain_BUILD.tpl"),
substitutions = {
"{AVAILABLE}": "1" if tool_path else "0",
"{EXEC_COMPATIBLE_WITH}": repr(exec_compatible_with),
"{GENERATOR}": "//bazel/toolchains/common:defs.bzl",
"{REPO_NAME}": repo_name,
"{TOOL_NAME}": tool_name,
"{TOOL_PATH}": str(tool_path),
"{TOOL_VERSION}": tool_version,
},
executable = False,
)
rctx.template(
"defs.bzl",
Label("@@//bazel/toolchains/common:toolchain_defs.bzl.tpl"),
substitutions = {
"{AVAILABLE}": "1" if tool_path else "0",
"{GENERATOR}": "//bazel/toolchains/common:defs.bzl",
"{REPO_NAME}": repo_name,
"{TOOL_NAME}": tool_name,
},
executable = False,
)

def _default_repo_builder_impl(rctx):
tool_name = rctx.attr.tool_name
tool_path = rctx.which(tool_name)
if rctx.attr.verbose:
if tool_path:
print("Found %s at '%s'" % (tool_name, tool_path)) # buildifier: disable=print
else:
print("No system %s found." % tool_name) # buildifier: disable=print
_write_toolchain_repo(
rctx = rctx,
repo_name = rctx.original_name,
tool_name = rctx.attr.tool_name,
tool_path = tool_path,
exec_compatible_with = rctx.attr.exec_compatible_with,
)

def make_toolchain_repository_rule(name, tool_name, impl = _default_repo_builder_impl):
return repository_rule(
implementation = impl,
doc = """Create a repository that defines a {name} toolchain based on tool in the default $PATH.""".format(name = name),
local = True,
environ = ["PATH"],
attrs = {
"tool_name": attr.string(doc = "The name of the tool to find.", default = tool_name),
"exec_compatible_with": attr.string_list(
doc = "exec_compatible_with list to apply to the created toolchain.",
),
"verbose": attr.bool(
doc = "If true, print status messages.",
),
},
)
51 changes: 51 additions & 0 deletions bazel/toolchains/common/toolchain_BUILD.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This content is generated by {GENERATOR}
load(":defs.bzl", "is_{TOOL_NAME}_available", "{TOOL_NAME}_toolchain")

toolchain_type(
name = "{TOOL_NAME}_toolchain_type",
visibility = ["//visibility:public"],
)

{TOOL_NAME}_toolchain(
name = "{TOOL_NAME}_auto",
path = "{TOOL_PATH}",
version = "{TOOL_VERSION}",
)

toolchain(
name = "{TOOL_NAME}_toolchain",
toolchain = ":{TOOL_NAME}_auto",
toolchain_type = ":{TOOL_NAME}_toolchain_type",
exec_compatible_with = {EXEC_COMPATIBLE_WITH},
)
Comment thread
alopezz marked this conversation as resolved.

# {TOOL_NAME}_missing_toolchain provides a fallback toolchain so that toolchain
# resolution can succeed even on platforms that do not have that tool.
# If this toolchain is selected, a rule can examine the valid field to determine
# if the tool is really available.
{TOOL_NAME}_toolchain(
name = "no_{TOOL_NAME}",
)

toolchain(
name = "zzz_{TOOL_NAME}_missing_toolchain", # keep name lexicographically last
toolchain = ":no_{TOOL_NAME}",
toolchain_type = ":{TOOL_NAME}_toolchain_type",
)

# Expose the availability of the actual tool as a flag, so we can
# create a config_setting from it.
is_{TOOL_NAME}_available(
Comment thread
alopezz marked this conversation as resolved.
name = "is_{TOOL_NAME}_available",
build_setting_default = {AVAILABLE},
Comment thread
alopezz marked this conversation as resolved.
)

# Expose the availability of the toolchain as a config_setting, so we can
# select() on it.
config_setting(
name = "have_{TOOL_NAME}",
flag_values = {
":is_{TOOL_NAME}_available": "1",
},
visibility = ["//visibility:public"],
)
48 changes: 48 additions & 0 deletions bazel/toolchains/common/toolchain_defs.bzl.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""toolchain to provide the {TOOL_NAME} binary."""

load("@@//bazel/toolchains:toolchain_info.bzl", "ToolInfo")

def _{TOOL_NAME}_toolchain_impl(ctx):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And another question that I have is whether we actually need to be templated with the TOOL_NAME baked in or whether a single common rule can be used to define all these toolchains instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For just the two I have done, yes. But that gets in the way of future expansion, where I want different things to customize the template. pkgbuild and codesign, do not have version commands, but for tools that do, I want the repo rule to extract the version (which has to be tool specific) and put that in as metadata so we can have the SBOM say things like otool: cctools-1030.6.3

I have that ready in another PR, but it's not needed yet. Essentially the repo_builder call will use a private impl function instead of the default, and your private impl will call do the lookup and then call write_toolchain_repo to emit the repo.

if ctx.attr.label and ctx.attr.path:
fail("{TOOL_NAME}_toolchain must not specify both label and path.")
valid = bool(ctx.attr.label) or bool(ctx.attr.path)
toolchain_info = platform_common.ToolchainInfo(
{TOOL_NAME} = ToolInfo(
name = str(ctx.label),
valid = valid,
label = ctx.attr.label,
path = ctx.attr.path,
version = ctx.attr.version,
),
)
return [toolchain_info]

{TOOL_NAME}_toolchain = rule(
implementation = _{TOOL_NAME}_toolchain_impl,
attrs = {
"label": attr.label(
doc = "A valid label of a target to build or a prebuilt binary. Mutually exclusive with path.",
cfg = "exec",
executable = True,
allow_files = True,
),
"path": attr.string(
doc = "The path to the executable. Mutually exclusive with label.",
),
"version": attr.string(
doc = "The version string of the executable. This should be manually set.",
),
},
)

# Expose the presence of {TOOL_NAME} as a flag.
def _is_{TOOL_NAME}_available_impl(ctx):
return [config_common.FeatureFlagInfo(
value = ("1" if ctx.build_setting_value else "0"),
)]

is_{TOOL_NAME}_available = rule(
implementation = _is_{TOOL_NAME}_available_impl,
attrs = {},
build_setting = config.bool(flag = False),
)
Comment thread
aiuto marked this conversation as resolved.
8 changes: 8 additions & 0 deletions bazel/toolchains/pkgbuild/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Toolchain to wrap the pkgbuild binary.

Type: @macos_pkgbuild//:tool_toolchain_type

Toolchains:
- @macos_pkgbuild//:macos_pkgbuild_toolchain: provides the tool
- @macos_pkgbuild//:macos_pkgbuild_missing_toolchain: provides a fallback toolchain for exec platforms where pkgbuild might not be available.
"""
8 changes: 8 additions & 0 deletions bazel/toolchains/pkgbuild/configure.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Repository rule to autoconfigure a toolchain using the system pkgbuild."""

load("//bazel/toolchains/common:defs.bzl", "make_toolchain_repository_rule")

# This must match the name used by register_toolchains in MODULE.bazel.
NAME = "macos_pkgbuild"

find_macos_pkgbuild = make_toolchain_repository_rule(name = NAME, tool_name = "pkgbuild")
Loading