Improve schema, support 'type', prep for 'headers' (breaking for WORKSPACE) (#23)
Improve the schema to specify a union over the binary kinds with more pedantic definitions. Add keyword args for `type` on archive binary kind and `headers` on all binary kinds when Bazel version is >= 7.1.0.
Note: this includes a breaking change for WORKSPACE users and requires loading bazel_features ahead of rules_multitool. Richer instructions for WORKSPACE usage will now be available in the release notes.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 79dbb5f..6956046 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@
[
".",
"examples/module",
- "examples/target-determinator"
+ "examples/workspace"
]
# we only support Bazel 7, and only with bzlmod enabled
exclude: |
@@ -31,5 +31,5 @@
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - working-directory: examples/target-determinator
+ - working-directory: examples/workspace
run: ./minimal_download_test.sh
diff --git a/.github/workflows/release_prep.sh b/.github/workflows/release_prep.sh
index bc2839c..133f9e5 100755
--- a/.github/workflows/release_prep.sh
+++ b/.github/workflows/release_prep.sh
@@ -18,12 +18,49 @@
SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}')
cat << EOF
-## Using Bzlmod with Bazel 6
+## Using Bzlmod (preferred)
-1. Enable with \`common --enable_bzlmod\` in \`.bazelrc\`.
+1. Create a multitool.lock.json ([schema](https://github.com/theoremlp/rules_multitool/blob/main/lockfile.schema.json))
2. Add to your \`MODULE.bazel\` file:
\`\`\`starlark
bazel_dep(name = "rules_multitool", version = "${TAG:1}")
+
+multitool = use_extension("@rules_multitool//multitool:extension.bzl", "multitool")
+multitool.hub(lockfile = "//:multitool.lock.json")
+use_repo(multitool, "multitool")
+\`\`\`
+
+## Using WORKSPACE
+1. Create a multitool.lock.json ([schema](https://github.com/theoremlp/rules_multitool/blob/main/lockfile.schema.json))
+2. Add to your \`WORKSPACE\` file:
+
+\`\`\`starlark
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "bazel_features",
+ sha256 = "06f02b97b6badb3227df2141a4b4622272cdcd2951526f40a888ab5f43897f14",
+ strip_prefix = "bazel_features-1.9.0",
+ url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.9.0/bazel_features-v1.9.0.tar.gz",
+)
+
+http_archive(
+ name = "rules_multitool",
+ sha256 = "${SHA}",
+ strip_prefix = "rules_multitool-${TAG:1}",
+ url = "https://github.com/bazel-contrib/bazel_features/releases/download/v${TAG:1}/bazel_features-${TAG:1}.tar.gz",
+)
+
+load("@bazel_features//:deps.bzl", "bazel_features_deps")
+
+bazel_features_deps()
+
+load("@rules_multitool//multitool:multitool.bzl", "multitool")
+
+multitool(
+ name = "multitool",
+ lockfile = "//:multitool.lock.json",
+)
\`\`\`
EOF
diff --git a/MODULE.bazel b/MODULE.bazel
index 46427b6..3fb6c5e 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -6,7 +6,8 @@
compatibility_level = 1,
)
-bazel_dep(name = "bazel_skylib", version = "1.4.1")
+bazel_dep(name = "bazel_features", version = "1.9.0")
+bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "buildifier_prebuilt", version = "6.1.2")
bazel_dep(name = "platforms", version = "0.0.8")
diff --git a/examples/module/.bazelversion b/examples/module/.bazelversion
index 66ce77b..a8907c0 100644
--- a/examples/module/.bazelversion
+++ b/examples/module/.bazelversion
@@ -1 +1 @@
-7.0.0
+7.0.2
diff --git a/examples/module/MODULE.bazel b/examples/module/MODULE.bazel
index 6625e33..3b2029a 100644
--- a/examples/module/MODULE.bazel
+++ b/examples/module/MODULE.bazel
@@ -1,7 +1,7 @@
"multitool example using target-determinator"
module(
- name = "multitool_examples__target_determinator",
+ name = "multitool_examples__module",
version = "0.0.0",
compatibility_level = 1,
)
diff --git a/examples/target-determinator/.bazelrc b/examples/workspace/.bazelrc
similarity index 100%
rename from examples/target-determinator/.bazelrc
rename to examples/workspace/.bazelrc
diff --git a/examples/workspace/.bazelversion b/examples/workspace/.bazelversion
new file mode 100644
index 0000000..a8907c0
--- /dev/null
+++ b/examples/workspace/.bazelversion
@@ -0,0 +1 @@
+7.0.2
diff --git a/examples/target-determinator/BUILD.bazel b/examples/workspace/BUILD.bazel
similarity index 100%
rename from examples/target-determinator/BUILD.bazel
rename to examples/workspace/BUILD.bazel
diff --git a/examples/target-determinator/MODULE.bazel b/examples/workspace/MODULE.bazel
similarity index 71%
rename from examples/target-determinator/MODULE.bazel
rename to examples/workspace/MODULE.bazel
index 4886c26..ace0f68 100644
--- a/examples/target-determinator/MODULE.bazel
+++ b/examples/workspace/MODULE.bazel
@@ -1,11 +1,12 @@
"multitool example using target-determinator"
module(
- name = "multitool_examples__target_determinator",
+ name = "multitool_examples__workspace",
version = "0.0.0",
compatibility_level = 1,
)
+bazel_dep(name = "bazel_features", version = "1.9.0")
bazel_dep(name = "rules_multitool", version = "0.0.0")
local_path_override(
module_name = "rules_multitool",
diff --git a/examples/target-determinator/WORKSPACE.bazel b/examples/workspace/WORKSPACE.bazel
similarity index 63%
rename from examples/target-determinator/WORKSPACE.bazel
rename to examples/workspace/WORKSPACE.bazel
index 6146eae..99dc0cb 100644
--- a/examples/target-determinator/WORKSPACE.bazel
+++ b/examples/workspace/WORKSPACE.bazel
@@ -1,3 +1,7 @@
+load("@bazel_features//:deps.bzl", "bazel_features_deps")
+
+bazel_features_deps()
+
load("@rules_multitool//multitool:multitool.bzl", "multitool")
multitool(
diff --git a/examples/target-determinator/integration_test.sh b/examples/workspace/integration_test.sh
similarity index 100%
rename from examples/target-determinator/integration_test.sh
rename to examples/workspace/integration_test.sh
diff --git a/examples/target-determinator/minimal_download_test.sh b/examples/workspace/minimal_download_test.sh
similarity index 100%
rename from examples/target-determinator/minimal_download_test.sh
rename to examples/workspace/minimal_download_test.sh
diff --git a/examples/target-determinator/multitool.lock.json b/examples/workspace/multitool.lock.json
similarity index 100%
rename from examples/target-determinator/multitool.lock.json
rename to examples/workspace/multitool.lock.json
diff --git a/lockfile.schema.json b/lockfile.schema.json
index d76d038..04956a3 100644
--- a/lockfile.schema.json
+++ b/lockfile.schema.json
@@ -1,41 +1,112 @@
{
- "$id": "https://github.com/theoremlp/rules_multitool/lockfile.schema.json",
- "title": "Describes binaries and instructions for fetching them under Bazel",
+ "$id": "https://github.com/theoremlp/rules_multitool/lockfile.schema.json",
+ "title": "Describes binaries and instructions for fetching them under Bazel",
+ "type": "object",
+ "definitions": {
+ "supported_os": {
+ "enum": ["macos", "linux"]
+ },
+ "supported_cpu": {
+ "enum": ["x86_64", "arm64"]
+ },
+ "file_type": {
+ "type": "object",
+ "properties": {
+ "kind": { "enum": ["file"] },
+ "url": { "type": "string" },
+ "sha256": { "type": "string" },
+ "os": { "$ref": "#/definitions/supported_os" },
+ "cpu": { "$ref": "#/definitions/supported_cpu" },
+ "headers": {
+ "type": "object",
+ "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["kind", "url", "os", "cpu"]
+ },
+ "archive_type": {
+ "type": "object",
+ "properties": {
+ "kind": { "enum": ["archive"] },
+ "url": { "type": "string" },
+ "sha256": { "type": "string" },
+ "file": {
+ "type": "string",
+ "docs": "archive root relative path to binary"
+ },
+ "os": { "$ref": "#/definitions/supported_os" },
+ "cpu": { "$ref": "#/definitions/supported_cpu" },
+ "headers": {
+ "type": "object",
+ "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "type": {
+ "enum": [
+ "zip",
+ "jar",
+ "war",
+ "aar",
+ "tar",
+ "tar.gz",
+ "tgz",
+ "tar.xz",
+ "txz",
+ ".tar.zst",
+ ".tzst",
+ "tar.bz2",
+ ".tbz",
+ ".ar",
+ ".deb"
+ ]
+ }
+ },
+ "required": ["kind", "url", "os", "cpu", "file"]
+ },
+ "pkg_type": {
+ "type": "object",
+ "properties": {
+ "kind": { "enum": ["pkg"] },
+ "url": { "type": "string" },
+ "sha256": { "type": "string" },
+ "file": {
+ "type": "string",
+ "docs": "pkg archive root relative path to binary"
+ },
+ "os": { "$ref": "#/definitions/supported_os" },
+ "cpu": { "$ref": "#/definitions/supported_cpu" },
+ "headers": {
+ "type": "object",
+ "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["kind", "url", "os", "cpu", "file"]
+ }
+ },
+ "properties": {
+ "$schema": { "type": "string" }
+ },
+ "additionalProperties": {
"type": "object",
"properties": {
- "$schema": { "type": "string" }
- },
- "additionalProperties": {
- "type": "object",
- "properties": {
- "binaries": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "kind": {
- "enum": ["file", "archive", "pkg"]
- },
- "url": {
- "type": "string"
- },
- "file": {
- "type": "string",
- "description": "when kind is archive or pkg, relative path to the binary within it"
- },
- "sha256": {
- "type": "string"
- },
- "os": {
- "enum": ["macos", "linux"]
- },
- "cpu": {
- "enum": ["x86_64", "arm64"]
- }
- },
- "additionalProperties": false
- }
- }
+ "binaries": {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ { "$ref": "#/definitions/file_type" },
+ { "$ref": "#/definitions/archive_type" },
+ { "$ref": "#/definitions/pkg_type" }
+ ]
}
+ }
}
+ }
}
diff --git a/multitool/extension.bzl b/multitool/extension.bzl
index 3ef40ee..3d980fd 100644
--- a/multitool/extension.bzl
+++ b/multitool/extension.bzl
@@ -1,4 +1,4 @@
-"multitool"
+"multitool module extension"
load("//multitool/private:multitool.bzl", _hub = "hub")
diff --git a/multitool/multitool.bzl b/multitool/multitool.bzl
index 5c29db4..e4c0315 100644
--- a/multitool/multitool.bzl
+++ b/multitool/multitool.bzl
@@ -1,5 +1,21 @@
-"multitool"
+"multitool workspace macros"
-load("//multitool/private:multitool.bzl", _multitool = "multitool")
+load("//multitool/private:multitool.bzl", "hub")
-multitool = _multitool
+def multitool(name, lockfile = None, lockfiles = None):
+ """(non-bzlmod) Create a multitool hub and register its toolchains.
+
+ Args:
+ name: resulting "hub" repo name to load tools from
+ lockfile: a label for a lockfile, see /lockfile.schema.json
+ lockfiles: a list of labels of multiple lockfiles
+
+ Note: exactly one of lockfile or lockfiles may be set.
+ """
+ if (not lockfile and not lockfiles) or (lockfile and lockfiles):
+ fail("Exactly one of lockfile and lockfiles must be set")
+
+ lockfiles = lockfiles if lockfiles else [lockfile]
+
+ hub(name, lockfiles)
+ native.register_toolchains("@{name}//toolchains:all".format(name = name))
diff --git a/multitool/private/multitool.bzl b/multitool/private/multitool.bzl
index e0fe76b..0242444 100644
--- a/multitool/private/multitool.bzl
+++ b/multitool/private/multitool.bzl
@@ -39,11 +39,11 @@
This implementation depends on rendering a number of templates, which are defined in sibling
folders and managed by the templates starlark file.
-To maintain support both bzlmod and non-bzlmod setups, we provide two entrypoints to the rule:
- - (bzlmod) hub : invoked by the hub tag in extension.bzl
- - (non-bzlmod) multitool : invoked in WORKSPACE or related macros, and additionally registers toolchains
+Note that we intend to support both bzlmod and non-bzlmod setups, so `hub` intentionally avoids
+a register_toolchains call.
"""
+load("@bazel_features//:features.bzl", "bazel_features")
load(":templates.bzl", "templates")
_SUPPORTED_ENVS = [
@@ -90,6 +90,13 @@
return tools
+def _feature_sensitive_args(binary):
+ args = {}
+ if bazel_features.external_deps.download_has_headers_param:
+ args["headers"] = binary.get("headers", {})
+
+ return args
+
def _env_specific_tools_impl(rctx):
tools = _load_tools(rctx)
@@ -110,6 +117,7 @@
sha256 = binary["sha256"],
output = target_executable,
executable = True,
+ **_feature_sensitive_args(binary)
)
elif binary["kind"] == "archive":
archive_path = "tools/{tool_name}/{os}_{cpu}_archive".format(
@@ -122,6 +130,8 @@
url = binary["url"],
sha256 = binary["sha256"],
output = archive_path,
+ type = binary.get("type", ""),
+ **_feature_sensitive_args(binary)
)
# link to the executable
@@ -147,6 +157,7 @@
url = binary["url"],
sha256 = binary["sha256"],
output = archive_path + ".pkg",
+ **_feature_sensitive_args(binary)
)
rctx.execute([pkgutil_cmd, "--expand-full", archive_path + ".pkg", archive_path])
@@ -231,21 +242,3 @@
cpu = env[1],
)
_multitool_hub(name = name, lockfiles = lockfiles)
-
-def multitool(name, lockfile = None, lockfiles = None):
- """(non-bzlmod) Create a multitool hub and register its toolchains.
-
- Args:
- name: resulting "hub" repo name to load tools from
- lockfile: a label for a lockfile, see /lockfile.schema.json
- lockfiles: a list of labels of multiple lockfiles
- """
- if lockfile and lockfiles:
- fail("Only one of lockfile and lockfiles may be set")
- if not lockfile and not lockfiles:
- fail("Exactly one of lockfile and lockfiles must be set")
- if lockfile:
- hub(name, [lockfile])
- else:
- hub(name, lockfiles)
- native.register_toolchains("@{name}//toolchains:all".format(name = name))
diff --git a/readme.md b/readme.md
index 4700779..f8cd389 100644
--- a/readme.md
+++ b/readme.md
@@ -4,9 +4,9 @@
## Usage
-For a quickstart, see the [bzlmod example](examples/module/).
+For a quickstart, see the [module example](examples/module/) or [workspace example](examples/workspace/).
-Define a lockfile that references the tools to load:
+Define a [lockfile](lockfile.schema.json) that references the tools to load:
```json
{
@@ -43,7 +43,9 @@
Save your lockfile and ensure the file is exported using `export_files` so that it's available to Bazel.
-Once your lockfile is defined, load the ruleset in your **MODULE.bazel** and create a hub that refers to your lockfile:
+### Bazel Module Usage
+
+Once your lockfile is defined, load the ruleset in your MODULE.bazel and create a hub that refers to your lockfile:
```python
bazel_dep(name = "rules_multitool", version = "0.0.0")
@@ -54,3 +56,7 @@
```
Tools may then be accessed using `@multitool//tools/tool-name`.
+
+### Workspace Usage
+
+Instructions for using with WORKSPACE may be found in [release notes](https://github.com/theoremlp/rules_multitool/releases).