Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support more Zig SDK versions #106

Merged
merged 13 commits into from
Nov 21, 2023
6 changes: 5 additions & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ load("//zig:repositories.bzl", "rules_zig_dependencies", "zig_register_toolchain
# Fetch dependencies which users need as well
rules_zig_dependencies()

# Use the latest known Zig SDK version for testing
# buildifier: disable=bzl-visibility
load("//zig/private:versions.bzl", "TOOL_VERSIONS")

zig_register_toolchains(
name = "zig",
zig_version = "0.10.1",
zig_version = TOOL_VERSIONS.keys()[0],
)

# For running our own unit tests
Expand Down
10 changes: 10 additions & 0 deletions util/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,13 @@ py_binary(
data = ["@buildifier_prebuilt//:buildozer"],
deps = ["@rules_python//python/runfiles"],
)

py_binary(
name = "update_zig_versions",
srcs = ["update_zig_versions.py"],
args = [
"--output",
"$(rootpath //zig/private:versions.bzl)",
],
data = ["//zig/private:versions.bzl"],
)
133 changes: 133 additions & 0 deletions util/update_zig_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env python3

import requests
import base64
import argparse


_ZIG_INDEX_URL = "https://ziglang.org/download/index.json"

_UNSUPPORTED_VERSIONS = [
"0.9.1",
"0.9.0",
"0.8.1",
"0.8.0",
"0.7.1",
"0.7.0",
"0.6.0",
"0.5.0",
"0.4.0",
"0.3.0",
"0.2.0",
"0.1.1"]

_SUPPORTED_PLATFORMS = [
"aarch64-linux",
"aarch64-macos",
"aarch64-windows",
"x86_64-linux",
"x86_64-macos",
"x86_64-windows",
"x86-linux",
"x86-windows"]


def fetch_zig_versions(url):
response = requests.get(url)
response.raise_for_status()
return response.json()


def convert_sha256(sha256_hex):
return "sha256-" + base64.b64encode(bytes.fromhex(sha256_hex)).decode()


_HEADER = '''\
"""Mirror of Zig release info.

Generated from {url}.
"""
'''


_PLATFORM = '''\
"{platform}": struct(
url = "{url}",
integrity = "{integrity}",
),\
'''


def _parse_semver(version_str):
"""Split a semantic version into its components.

Raises an error if the version is malformed.

If the version contains no pre-release component, then a sentinel of
`0x10FFFF` is returned. The intent is that it sorts higher than any other
code-point, therefore making versions without pre-release component sort
higher than this with.

If the version is the string `master` then it returns a maximum version
comprising `float("inf")` components and the pre-release sentinel.

Returns:
(major, minor, patch, pre_release)
"""
max_component = float("inf")
max_prerelease = chr(0x10FFFF) # Highest valid code point in Unicode

if version_str == "master":
return max_component, max_component, max_component, max_prerelease

pre_version, *_ = version_str.split("+", maxsplit=1)
main_version, *pre_release = pre_version.split("-", maxsplit=1)
major, minor, patch = map(int, main_version.split("."))

pre_release_segment = pre_release[0] if pre_release else max_prerelease

return major, minor, patch, pre_release_segment


def generate_bzl_content(url, data, unsupported_versions, supported_platforms):
content = [_HEADER.format(url = url)]
content.append("TOOL_VERSIONS = {")

for version, platforms in sorted(data.items(), key=lambda x: _parse_semver(x[0]), reverse=True):
if version in unsupported_versions or version == "master":
continue

content.append(' "{}": {{'.format(version))

for platform, info in sorted(platforms.items()):
if platform not in supported_platforms or not isinstance(info, dict):
continue
content.append(_PLATFORM.format(
platform = platform,
url = info["tarball"],
integrity = convert_sha256(info["shasum"])
))

content.append(' },')

content.append('}')

return '\n'.join(content)


def main():
parser = argparse.ArgumentParser(description="Generate Starlark file for Zig compiler versions.")
parser.add_argument("--output", type=argparse.FileType('w'), default='-', help="Output file path or '-' for stdout.")
parser.add_argument("--url", default=_ZIG_INDEX_URL, help="URL to fetch Zig versions JSON")
parser.add_argument("--unsupported-versions", nargs="*", default=_UNSUPPORTED_VERSIONS, help="List of unsupported Zig versions")
parser.add_argument("--supported-platforms", nargs="*", default=_SUPPORTED_PLATFORMS, help="List of supported platforms")
args = parser.parse_args()

zig_data = fetch_zig_versions(args.url)
bzl_content = generate_bzl_content(args.url, zig_data, set(args.unsupported_versions), set(args.supported_platforms))

args.output.write(bzl_content)


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions zig/private/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")

exports_files(
["versions.bzl"],
visibility = ["//util:__pkg__"],
)

bzl_library(
name = "zig_package",
srcs = ["zig_package.bzl"],
Expand Down
28 changes: 26 additions & 2 deletions zig/private/toolchains_repo.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,42 @@ with only the toolchain attribute pointing into the platform-specific repositori
# Add more platforms as needed to mirror all the binaries
# published by the upstream project.
PLATFORMS = {
"linux-x86_64": struct(
"aarch64-linux": struct(
compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
],
),
"aarch64-macos": struct(
compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:aarch64",
],
),
"aarch64-windows": struct(
compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:aarch64",
],
),
"x86_64-linux": struct(
compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
),
"macos-x86_64": struct(
"x86_64-macos": struct(
compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:x86_64",
],
),
"x86_64-windows": struct(
compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
),
}

def _toolchains_repo_impl(repository_ctx):
Expand Down
61 changes: 53 additions & 8 deletions zig/private/versions.bzl
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
"""Mirror of release info
"""Mirror of Zig release info.

TODO[AH]: generate this file from https://ziglang.org/download/index.json"""
Generated from https://ziglang.org/download/index.json.
"""

# The integrity hashes can be computed with
# shasum -b -a 384 [downloaded file] | awk '{ print $1 }' | xxd -r -p | base64
# Or using the sha256 hashes from ziglang.org converted with Python:
# "sha256-" + base64.b64encode(bytes.fromhex("SHA256")).decode()
TOOL_VERSIONS = {
"0.10.1": {
"linux-x86_64": "sha256-Zpnw5ykwgbQkKPMsnZyYOFQJS9Ff7lSJ8SxM9FGMw4A=",
"macos-x86_64": "sha256-Akg1ULidKjBwwu0AM1f9bmowWXB7juP7wMZ/g8qJhDc=",
"aarch64-linux": struct(
url = "https://ziglang.org/download/0.10.1/zig-linux-aarch64-0.10.1.tar.xz",
integrity = "sha256-2wdhZk9fIqpbvXRCoWF91pbAdtVxfd78ydi5Unj3H10=",
),
"aarch64-macos": struct(
url = "https://ziglang.org/download/0.10.1/zig-macos-aarch64-0.10.1.tar.xz",
integrity = "sha256-ubAEd+xfofG4nzWn0qWGiOAZkQq4CmXqwqdBcWJzdlY=",
),
"aarch64-windows": struct(
url = "https://ziglang.org/download/0.10.1/zig-windows-aarch64-0.10.1.zip",
integrity = "sha256-7Ok7DXeyqwPEDbme98y8Y+C2vWWK8SuXiYlg9iEwVCg=",
),
"x86_64-linux": struct(
url = "https://ziglang.org/download/0.10.1/zig-linux-x86_64-0.10.1.tar.xz",
integrity = "sha256-Zpnw5ykwgbQkKPMsnZyYOFQJS9Ff7lSJ8SxM9FGMw4A=",
),
"x86_64-macos": struct(
url = "https://ziglang.org/download/0.10.1/zig-macos-x86_64-0.10.1.tar.xz",
integrity = "sha256-Akg1ULidKjBwwu0AM1f9bmowWXB7juP7wMZ/g8qJhDc=",
),
"x86_64-windows": struct(
url = "https://ziglang.org/download/0.10.1/zig-windows-x86_64-0.10.1.zip",
integrity = "sha256-V2gATl4nTHlpw4kuiRWW5Rxd8rQi15iGVHHgUEmYgSU=",
),
},
"0.10.0": {
"aarch64-linux": struct(
url = "https://ziglang.org/download/0.10.0/zig-linux-aarch64-0.10.0.tar.xz",
integrity = "sha256-Ce9QyL5zOAeZgEFpGXgg7nh2ByOwQw+oI/Vu1CsG6g8=",
),
"aarch64-macos": struct(
url = "https://ziglang.org/download/0.10.0/zig-macos-aarch64-0.10.0.tar.xz",
integrity = "sha256-Aveng5tqHhJ+6uIupyyHYD+3KYxYvDWCKpUUedU8dVc=",
),
"aarch64-windows": struct(
url = "https://ziglang.org/download/0.10.0/zig-windows-aarch64-0.10.0.zip",
integrity = "sha256-G72o0SPUTzrk+pDQ2gSx6Qk8P53a40KaSr7OHhwL8Zo=",
),
"x86_64-linux": struct(
url = "https://ziglang.org/download/0.10.0/zig-linux-x86_64-0.10.0.tar.xz",
integrity = "sha256-Yx7HvLZJzWeVq+QN8ETSRztZtE4QvmicFWMqBFjd6lU=",
),
"x86_64-macos": struct(
url = "https://ziglang.org/download/0.10.0/zig-macos-x86_64-0.10.0.tar.xz",
integrity = "sha256-OiLLbEdJiEFWqU6ptg86KM9OCYpp8IwY+8qBxzPr/to=",
),
"x86_64-windows": struct(
url = "https://ziglang.org/download/0.10.0/zig-windows-x86_64-0.10.0.zip",
integrity = "sha256-pm4v9VXG5IeB3hvLBmLvKO5LiK868qV397GVDkMIl+4=",
),
},
}
23 changes: 11 additions & 12 deletions zig/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,21 @@ _ATTRS = {
}

def _zig_repo_impl(repository_ctx):
# TODO[AH] read URLs from https://ziglang.org/download/index.json
basename = "zig-{}-{}".format(
repository_ctx.attr.platform,
repository_ctx.attr.zig_version,
)
url = "https://ziglang.org/download/{}/{}.tar.xz".format(
repository_ctx.attr.zig_version,
basename,
)
url = TOOL_VERSIONS[repository_ctx.attr.zig_version][repository_ctx.attr.platform].url
integrity = TOOL_VERSIONS[repository_ctx.attr.zig_version][repository_ctx.attr.platform].integrity
basename = url.rsplit("/", 1)[1]
if basename.endswith(".tar.gz") or basename.endswith(".tar.xz"):
prefix = basename[:-7]
elif basename.endswith(".zip"):
prefix = basename[:-4]
else:
fail("Cannot download Zig SDK at {}. Unsupported file extension.".format(url))
repository_ctx.download_and_extract(
url = url,
integrity = TOOL_VERSIONS[repository_ctx.attr.zig_version][repository_ctx.attr.platform],
stripPrefix = basename,
integrity = integrity,
stripPrefix = prefix,
)

# TODO[AH] compiler and lib files
build_content = """#Generated by zig/repositories.bzl
load("@rules_zig//zig:toolchain.bzl", "zig_toolchain")
zig_toolchain(
Expand Down
28 changes: 27 additions & 1 deletion zig/tests/integration_tests/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
load("//zig:defs.bzl", "zig_test")
load("//zig/private:versions.bzl", "TOOL_VERSIONS")
load("@bazel_binaries//:defs.bzl", "bazel_binaries")
load(
"@rules_bazel_integration_test//bazel_integration_test:defs.bzl",
"bazel_integration_test",
"bazel_integration_tests",
"integration_test_utils",
)

# gazelle:exclude workspace

zig_test(
name = "integration_tests_runner",
srcs = ["integration_testing.zig"],
main = "integration_tests_runner.zig",
tags = ["manual"],
)
Expand All @@ -33,6 +38,27 @@ bazel_integration_tests(
workspace_path = "workspace",
)

zig_test(
name = "zig_version_tests_runner",
srcs = ["integration_testing.zig"],
main = "zig_version_tests_runner.zig",
tags = ["manual"],
)

zig_version_tests = {
"zig_version_test_" + zig_version: bazel_integration_test(
name = "zig_version_test_" + zig_version,
bazel_version = bazel_binaries.versions.current,
env = {"ZIG_VERSION": zig_version},
test_runner = ":zig_version_tests_runner",
workspace_files = integration_test_utils.glob_workspace_files("workspace") + [
"//:all_files",
],
workspace_path = "workspace",
)
for zig_version in TOOL_VERSIONS.keys()
}.keys()

test_suite(
name = "integration_tests",
tags = integration_test_utils.DEFAULT_INTEGRATION_TEST_TAGS,
Expand All @@ -42,5 +68,5 @@ test_suite(
) + [integration_test_utils.bazel_integration_test_name(
"bzlmod_test",
bazel_binaries.versions.current,
)],
)] + zig_version_tests,
)
Loading