Add windows support (#45)
Limitations:
- requires these two flags on windows (these have no effect or are
already default on other platforms)
- `startup --windows_enable_symlinks`
- `common --enable_runfiles`
diff --git a/.bazelrc b/.bazelrc
index 404de54..003790d 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -4,3 +4,12 @@
test --test_output=errors
test --test_summary=terse
+
+# best practice to disable
+common --nolegacy_external_runfiles
+
+# off by default on windows, required for this module
+startup --windows_enable_symlinks
+
+# off by default on windows, required for this module
+common --enable_runfiles
diff --git a/.bazelversion b/.bazelversion
index 66ce77b..a3fcc71 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-7.0.0
+7.1.0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0d6e430..0a5152c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,8 +25,6 @@
{"bazelversion": "5.4.0"},
{"bazelversion": "6.4.0"},
]
- # this ruleset only supports linux and macos
- exclude_windows: true
integration-tests:
runs-on: ubuntu-latest
steps:
diff --git a/examples/module/.bazelrc b/examples/module/.bazelrc
index 694f59b..fe93ecc 100644
--- a/examples/module/.bazelrc
+++ b/examples/module/.bazelrc
@@ -1,3 +1,12 @@
common --enable_bzlmod
common --lockfile_mode=off
+
+# best practice to disable
+common --nolegacy_external_runfiles
+
+# off by default on windows, required for this module
+startup --windows_enable_symlinks
+
+# off by default on windows, required for this module
+common --enable_runfiles
diff --git a/examples/module/.bazelversion b/examples/module/.bazelversion
index a8907c0..a3fcc71 100644
--- a/examples/module/.bazelversion
+++ b/examples/module/.bazelversion
@@ -1 +1 @@
-7.0.2
+7.1.0
diff --git a/examples/module/multitool.lock.json b/examples/module/multitool.lock.json
index bee250f..b3d578b 100644
--- a/examples/module/multitool.lock.json
+++ b/examples/module/multitool.lock.json
@@ -22,6 +22,13 @@
"sha256": "c8a09143e9fe6eccc4b27a6be92c5929e5a78034a8d0b4c43dbed4ee539ec903",
"os": "linux",
"cpu": "x86_64"
+ },
+ {
+ "kind": "file",
+ "url": "https://github.com/bazel-contrib/target-determinator/releases/download/v0.25.0/target-determinator.windows.amd64.exe",
+ "sha256": "e14fd75e33d193f579505cf3e641e07025904fc027686e13e154ba8e10ac0f58",
+ "os": "windows",
+ "cpu": "x86_64"
}
]
},
@@ -47,9 +54,17 @@
"kind": "archive",
"url": "https://github.com/cli/cli/releases/download/v2.44.1/gh_2.44.1_linux_amd64.tar.gz",
"sha256": "f11eefb646768e3f53e2185f6d3b01b4cb02112c2c60e65a4b5875150287ff97",
- "file": "gh_2.44.1_linux_amd64/bin",
+ "file": "gh_2.44.1_linux_amd64/bin/gh",
"os": "linux",
"cpu": "x86_64"
+ },
+ {
+ "kind": "archive",
+ "url": "https://github.com/cli/cli/releases/download/v2.44.1/gh_2.44.1_windows_amd64.zip",
+ "sha256": "5a19a334d5032cfcb01e71e418c5e355104f7afd9129daf8939b729ffa46aa08",
+ "file": "gh_2.44.1_windows_amd64/bin/gh.exe",
+ "os": "windows",
+ "cpu": "x86_64"
}
]
},
@@ -79,7 +94,6 @@
"os": "linux",
"cpu": "x86_64"
}
-
]
}
}
diff --git a/examples/workspace/.bazelrc b/examples/workspace/.bazelrc
index 694f59b..fe93ecc 100644
--- a/examples/workspace/.bazelrc
+++ b/examples/workspace/.bazelrc
@@ -1,3 +1,12 @@
common --enable_bzlmod
common --lockfile_mode=off
+
+# best practice to disable
+common --nolegacy_external_runfiles
+
+# off by default on windows, required for this module
+startup --windows_enable_symlinks
+
+# off by default on windows, required for this module
+common --enable_runfiles
diff --git a/examples/workspace/.bazelversion b/examples/workspace/.bazelversion
index a8907c0..a3fcc71 100644
--- a/examples/workspace/.bazelversion
+++ b/examples/workspace/.bazelversion
@@ -1 +1 @@
-7.0.2
+7.1.0
diff --git a/examples/workspace/multitool.lock.json b/examples/workspace/multitool.lock.json
index d640c22..a7fee4f 100644
--- a/examples/workspace/multitool.lock.json
+++ b/examples/workspace/multitool.lock.json
@@ -22,6 +22,13 @@
"sha256": "c8a09143e9fe6eccc4b27a6be92c5929e5a78034a8d0b4c43dbed4ee539ec903",
"os": "linux",
"cpu": "x86_64"
+ },
+ {
+ "kind": "file",
+ "url": "https://github.com/bazel-contrib/target-determinator/releases/download/v0.25.0/target-determinator.windows.amd64.exe",
+ "sha256": "e14fd75e33d193f579505cf3e641e07025904fc027686e13e154ba8e10ac0f58",
+ "os": "windows",
+ "cpu": "x86_64"
}
]
}
diff --git a/lockfile.schema.json b/lockfile.schema.json
index 04956a3..b08f62c 100644
--- a/lockfile.schema.json
+++ b/lockfile.schema.json
@@ -4,7 +4,7 @@
"type": "object",
"definitions": {
"supported_os": {
- "enum": ["macos", "linux"]
+ "enum": ["macos", "linux", "windows"]
},
"supported_cpu": {
"enum": ["x86_64", "arm64"]
diff --git a/multitool/private/BUILD.bazel b/multitool/private/BUILD.bazel
index b4859c1..0df68e1 100644
--- a/multitool/private/BUILD.bazel
+++ b/multitool/private/BUILD.bazel
@@ -1,6 +1,7 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
exports_files([
+ "cwd.template.bat",
"cwd.template.sh",
])
diff --git a/multitool/private/cwd.bzl b/multitool/private/cwd.bzl
index 97cc681..3703836 100644
--- a/multitool/private/cwd.bzl
+++ b/multitool/private/cwd.bzl
@@ -1,12 +1,20 @@
"cwd: a rule for executing an executable in the BUILD_WORKING_DIRECTORY"
def _cwd_impl(ctx):
- output = ctx.actions.declare_file(ctx.label.name)
+ # This algorithm requires --enable_runfiles (enabled by default on non-windows)
+ template = ctx.file._template_sh
+ wrapper_name = ctx.label.name
+ tool_short_path = ctx.file.tool.short_path
+ if ctx.file.tool.extension == "exe":
+ template = ctx.file._template_bat
+ wrapper_name = wrapper_name + ".bat"
+ tool_short_path = tool_short_path.replace("/", "\\")
+ output = ctx.actions.declare_file(wrapper_name)
ctx.actions.expand_template(
- template = ctx.file._template,
+ template = template,
output = output,
substitutions = {
- "{{tool}}": ctx.file.tool.short_path,
+ "{{tool}}": tool_short_path,
},
)
return [DefaultInfo(executable = output, runfiles = ctx.runfiles(files = [ctx.file.tool]))]
@@ -15,7 +23,8 @@
implementation = _cwd_impl,
attrs = {
"tool": attr.label(mandatory = True, allow_single_file = True, executable = True, cfg = "exec"),
- "_template": attr.label(default = "//multitool/private:cwd.template.sh", allow_single_file = True),
+ "_template_sh": attr.label(default = "//multitool/private:cwd.template.sh", allow_single_file = True),
+ "_template_bat": attr.label(default = "//multitool/private:cwd.template.bat", allow_single_file = True),
},
executable = True,
)
diff --git a/multitool/private/cwd.template.bat b/multitool/private/cwd.template.bat
new file mode 100644
index 0000000..fcaf9d4
--- /dev/null
+++ b/multitool/private/cwd.template.bat
@@ -0,0 +1,3 @@
+@set tool=%cd%\{{tool}}
+@cd %BUILD_WORKING_DIRECTORY%
+@%tool% %*
diff --git a/multitool/private/env_repo_tool_template/BUILD.bazel.template b/multitool/private/env_repo_tool_template/BUILD.bazel.template
index d2f7a23..4ac2013 100644
--- a/multitool/private/env_repo_tool_template/BUILD.bazel.template
+++ b/multitool/private/env_repo_tool_template/BUILD.bazel.template
@@ -1,3 +1,3 @@
# generated by multitool
-exports_files(glob(include=["*_executable"]))
+exports_files(glob(include=["*_executable", "*_executable.exe"]))
diff --git a/multitool/private/hub_repo_template/toolchain_info.bzl.template b/multitool/private/hub_repo_template/toolchain_info.bzl.template
index 836f850..90fa4b7 100644
--- a/multitool/private/hub_repo_template/toolchain_info.bzl.template
+++ b/multitool/private/hub_repo_template/toolchain_info.bzl.template
@@ -6,6 +6,7 @@
executable = ctx.file.executable,
cpu = ctx.attr.cpu,
os = ctx.attr.os,
+ ext = ctx.attr.ext,
),
]
@@ -13,17 +14,25 @@
attrs = dict(
executable = attr.label(allow_single_file = True),
cpu = attr.string(mandatory = True, values = ["arm64", "x86_64"]),
- os = attr.string(mandatory = True, values = ["linux", "macos"]),
+ os = attr.string(mandatory = True, values = ["linux", "macos", "windows"]),
+ ext = attr.string(mandatory = True, values = ["", ".exe"]),
),
implementation = _toolchain_info_impl,
)
+def _extension(os):
+ if os == "windows":
+ return ".exe"
+ return ""
+
def declare_toolchain(name, os, cpu, toolchain_type):
+ ext = _extension(os)
toolchain_info(
name = "{name}_{os}_{cpu}_toolchain_info".format(name=name, os=os, cpu=cpu),
- executable = "@@{hub_name}.{os}_{cpu}//tools/{name}:{os}_{cpu}_executable".format(name=name, os=os, cpu=cpu),
+ executable = "@@{hub_name}.{os}_{cpu}//tools/{name}:{os}_{cpu}_executable{ext}".format(name=name, os=os, cpu=cpu, ext=ext),
os = os,
cpu = cpu,
+ ext = ext,
)
native.toolchain(
diff --git a/multitool/private/hub_repo_tool_template/tool.bzl.template b/multitool/private/hub_repo_tool_template/tool.bzl.template
index 7d9af1a..c55b71a 100644
--- a/multitool/private/hub_repo_tool_template/tool.bzl.template
+++ b/multitool/private/hub_repo_tool_template/tool.bzl.template
@@ -6,7 +6,7 @@
def _tool_impl(ctx):
toolchain = ctx.toolchains[_TOOLCHAIN_TYPE]
- output = ctx.actions.declare_file(ctx.label.name)
+ output = ctx.actions.declare_file(ctx.label.name + toolchain.ext)
ctx.actions.symlink(output = output, target_file = toolchain.executable)
return [DefaultInfo(executable = output)]
diff --git a/multitool/private/multitool.bzl b/multitool/private/multitool.bzl
index 0242444..02648d3 100644
--- a/multitool/private/multitool.bzl
+++ b/multitool/private/multitool.bzl
@@ -51,6 +51,8 @@
("linux", "x86_64"),
("macos", "arm64"),
("macos", "x86_64"),
+ ("windows", "arm64"),
+ ("windows", "x86_64"),
]
def _check(condition, message):
@@ -58,6 +60,15 @@
if not condition:
fail(message)
+def _check_version(os):
+ # skip version check on windows if we don't have a release version. We can't tell from a hash what features we have.
+ if os == "windows" and native.bazel_version:
+ version = native.bazel_version.split(".")
+ if int(version[0]) > 7 or (int(version[0]) == 7 and int(version[1]) >= 1):
+ pass
+ else:
+ fail("rules_multitool: windows artifacts require bazel 7.1+; current bazel is " + native.bazel_version)
+
def _load_tools(rctx):
tools = {}
for lockfile in rctx.attr.lockfiles:
@@ -74,7 +85,7 @@
for tool_name, tool in tools.items():
for binary in tool["binaries"]:
_check(
- binary["os"] in ["linux", "macos"],
+ binary["os"] in ["linux", "macos", "windows"],
"{tool_name}: Unknown os '{os}'".format(
tool_name = tool_name,
os = binary["os"],
@@ -87,6 +98,7 @@
cpu = binary["cpu"],
),
)
+ _check_version(binary["os"])
return tools
@@ -97,6 +109,11 @@
return args
+def _extension(os):
+ if os == "windows":
+ return ".exe"
+ return ""
+
def _env_specific_tools_impl(rctx):
tools = _load_tools(rctx)
@@ -105,10 +122,11 @@
if binary["os"] != rctx.attr.os or binary["cpu"] != rctx.attr.cpu:
continue
- target_executable = "tools/{tool_name}/{os}_{cpu}_executable".format(
+ target_executable = "tools/{tool_name}/{os}_{cpu}_executable{ext}".format(
tool_name = tool_name,
cpu = binary["cpu"],
os = binary["os"],
+ ext = _extension(binary["os"]),
)
if binary["kind"] == "file":
diff --git a/readme.md b/readme.md
index 64a7f67..d13c8f8 100644
--- a/readme.md
+++ b/readme.md
@@ -17,7 +17,7 @@
"kind": "file",
"url": "https://...",
"sha256": "sha256 of the file",
- "os": "linux|macos",
+ "os": "linux|macos|windows",
"cpu": "x86_64|arm64"
}
]