diff --git a/.bazelrc b/.bazelrc index 4827c89..1e8b663 100644 --- a/.bazelrc +++ b/.bazelrc @@ -16,6 +16,8 @@ try-import %workspace%/.aspect/bazelrc/bazel6.bazelrc # For testing our --stamp behavior. # Normally users would use a --workspace_status_command with a script that calls `git describe`. build --embed_label=v1.2.3 +# Mock versioning command to test the --stamp behavior +build --workspace_status_command="echo BUILD_SCM_VERSION 1.2.3" # For releasing, use --workspace_status_command and stamp build:release --workspace_status_command "${PWD}/workspace_status.sh" diff --git a/MODULE.bazel b/MODULE.bazel index 4e5a400..7177319 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -13,12 +13,14 @@ bazel_dep(name = "stardoc", repo_name = "io_bazel_stardoc", version = "0.5.0") ext = use_extension("@aspect_bazel_lib//lib:extensions.bzl", "ext") -use_repo(ext, "copy_directory_toolchains", "copy_to_directory_toolchains", "jq_toolchains", "yq_toolchains", "coreutils_toolchains") +use_repo(ext, "copy_directory_toolchains", "copy_to_directory_toolchains", "jq_toolchains", "yq_toolchains", "coreutils_toolchains", "expand_template_toolchains") + register_toolchains( "@copy_directory_toolchains//:all", "@copy_to_directory_toolchains//:all", "@jq_toolchains//:all", "@yq_toolchains//:all", - "@coreutils_toolchains//:all" + "@coreutils_toolchains//:all", + "@expand_template_toolchains//:all" ) diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index a582123..1c5b824 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -37,6 +37,11 @@ stardoc_with_diff_test( bzl_library_target = "//lib:expand_make_vars", ) +stardoc_with_diff_test( + name = "expand_template", + bzl_library_target = "//lib:expand_template", +) + stardoc_with_diff_test( name = "params_file", bzl_library_target = "//lib:params_file", diff --git a/docs/expand_make_vars.md b/docs/expand_make_vars.md index 5c59b39..21ca544 100644 --- a/docs/expand_make_vars.md +++ b/docs/expand_make_vars.md @@ -7,7 +7,7 @@ Public API for expanding variables ## expand_template
-expand_template(name, data, is_executable, out, substitutions, template)
+expand_template(name, data, is_executable, out, stamp, stamp_substitutions, substitutions, template)
 
Template expansion @@ -31,7 +31,9 @@ such as `$(BINDIR)`, `$(TARGET_CPU)`, and `$(COMPILATION_MODE)` as documented in | data | List of targets for additional lookup information. | List of labels | optional | [] | | is_executable | Whether to mark the output file as executable. | Boolean | optional | False | | out | Where to write the expanded file. | Label | required | | -| substitutions | Mapping of strings to substitutions. | Dictionary: String -> String | required | | +| stamp | Whether to encode build information into the output. Possible values:

- stamp = 1: Always stamp the build information into the output, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it is non-deterministic. It potentially causes remote cache misses for the target and any downstream actions that depend on the result. - stamp = 0: Never stamp, instead replace build information by constant values. This gives good build result caching. - stamp = -1: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 | +| stamp_substitutions | Mapping of strings to substitutions.

There are overlayed on top of substitutions when stamping is enabled for the target.

Substitutions can contain $(execpath :target) and $(rootpath :target) expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when stamping is enabled for the target. | Dictionary: String -> String | optional | {} | +| substitutions | Mapping of strings to substitutions.

Substitutions can contain $(execpath :target) and $(rootpath :target) expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when stamping is enabled for the target. | Dictionary: String -> String | optional | {} | | template | The template file to expand. | Label | required | | diff --git a/docs/expand_template.md b/docs/expand_template.md new file mode 100644 index 0000000..72b6457 --- /dev/null +++ b/docs/expand_template.md @@ -0,0 +1,39 @@ + + +Public API for expand template + + + +## expand_template + +
+expand_template(name, data, is_executable, out, stamp, stamp_substitutions, substitutions, template)
+
+ +Template expansion + +This performs a simple search over the template file for the keys in substitutions, +and replaces them with the corresponding values. + +Values may also use location templates as documented in +[expand_locations](https://github.com/aspect-build/bazel-lib/blob/main/docs/expand_make_vars.md#expand_locations) +as well as [configuration variables](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#var) +such as `$(BINDIR)`, `$(TARGET_CPU)`, and `$(COMPILATION_MODE)` as documented in +[expand_variables](https://github.com/aspect-build/bazel-lib/blob/main/docs/expand_make_vars.md#expand_variables). + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| data | List of targets for additional lookup information. | List of labels | optional | [] | +| is_executable | Whether to mark the output file as executable. | Boolean | optional | False | +| out | Where to write the expanded file. | Label | required | | +| stamp | Whether to encode build information into the output. Possible values:

- stamp = 1: Always stamp the build information into the output, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it is non-deterministic. It potentially causes remote cache misses for the target and any downstream actions that depend on the result. - stamp = 0: Never stamp, instead replace build information by constant values. This gives good build result caching. - stamp = -1: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 | +| stamp_substitutions | Mapping of strings to substitutions.

There are overlayed on top of substitutions when stamping is enabled for the target.

Substitutions can contain $(execpath :target) and $(rootpath :target) expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when stamping is enabled for the target. | Dictionary: String -> String | optional | {} | +| substitutions | Mapping of strings to substitutions.

Substitutions can contain $(execpath :target) and $(rootpath :target) expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when stamping is enabled for the target. | Dictionary: String -> String | optional | {} | +| template | The template file to expand. | Label | required | | + + diff --git a/docs/repositories.md b/docs/repositories.md index 350899b..18714d1 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -78,6 +78,25 @@ Registers coreutils toolchain and repositories | register | whether to call through to native.register_toolchains. Should be True for WORKSPACE users, but false when used under bzlmod extension | True | + + +## register_expand_template_toolchains + +
+register_expand_template_toolchains(name, register)
+
+ +Registers expand_template toolchain and repositories + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | override the prefix for the generated toolchain repositories | "expand_template" | +| register | whether to call through to native.register_toolchains. Should be True for WORKSPACE users, but false when used under bzlmod extension | True | + + ## register_jq_toolchains diff --git a/e2e/smoke/.bazelrc b/e2e/smoke/.bazelrc index 1482aca..94b8d05 100644 --- a/e2e/smoke/.bazelrc +++ b/e2e/smoke/.bazelrc @@ -9,9 +9,11 @@ import %workspace%/.aspect/bazelrc/debug.bazelrc import %workspace%/.aspect/bazelrc/javascript.bazelrc ### PROJECT SPECIFIC OPTIONS ### +# for testing expand_template +build --workspace_status_command="echo BUILD_SCM_VERSION 1.2.3" # Load any settings & overrides specific to the current user from `.aspect/bazelrc/user.bazelrc`. # This file should appear in `.gitignore` so that settings are not shared with team members. This # should be last statement in this config so the user configuration is able to overwrite flags from # this file. See https://bazel.build/configure/best-practices#bazelrc-file. -try-import %workspace%/.aspect/bazelrc/user.bazelrc +try-import %workspace%/.aspect/bazelrc/user.bazelrc \ No newline at end of file diff --git a/e2e/smoke/BUILD.bazel b/e2e/smoke/BUILD.bazel index 722df23..e6dd954 100644 --- a/e2e/smoke/BUILD.bazel +++ b/e2e/smoke/BUILD.bazel @@ -1,6 +1,7 @@ load("@aspect_bazel_lib//lib:copy_directory.bzl", "copy_directory") load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") load("@aspect_bazel_lib//lib:diff_test.bzl", "diff_test") +load("@aspect_bazel_lib//lib:expand_make_vars.bzl", "expand_template") load("@aspect_bazel_lib//lib:jq.bzl", "jq") load("@aspect_bazel_lib//lib:yq.bzl", "yq") @@ -61,3 +62,28 @@ diff_test( # Source directories are not support on remote execution. tags = ["no-remote-exec"], ) + +# Validate that expand_template works and resolves its toolchain +expand_template( + name = "a_tmpl_stamp", + out = "a_stamp", + data = ["a.tmpl"], + stamp = 1, + stamp_substitutions = { + "{{VERSION}}": "v{{BUILD_SCM_VERSION}}", + }, + substitutions = { + "{{VERSION}}": "v0.0.0", + "{{WORKSPACE}}": "$(WORKSPACE)", + }, + template = "a.tmpl", +) + +diff_test( + name = "a_stamp_test", + file1 = ":a_stamp", + file2 = select({ + "@aspect_bazel_lib//lib:bzlmod": "a_stamp_expected_bzlmod", + "//conditions:default": "a_stamp_expected", + }), +) \ No newline at end of file diff --git a/e2e/smoke/a.tmpl b/e2e/smoke/a.tmpl new file mode 100644 index 0000000..0bdf04b --- /dev/null +++ b/e2e/smoke/a.tmpl @@ -0,0 +1,4 @@ +WORKSPACE: {{WORKSPACE}} +VERSION: {{VERSION}} +WORKSPACE: {{WORKSPACE}} +VERSION: {{VERSION}} \ No newline at end of file diff --git a/e2e/smoke/a_stamp_expected b/e2e/smoke/a_stamp_expected new file mode 100644 index 0000000..a2200ca --- /dev/null +++ b/e2e/smoke/a_stamp_expected @@ -0,0 +1,4 @@ +WORKSPACE: __main__ +VERSION: v1.2.3 +WORKSPACE: __main__ +VERSION: v1.2.3 \ No newline at end of file diff --git a/e2e/smoke/a_stamp_expected_bzlmod b/e2e/smoke/a_stamp_expected_bzlmod new file mode 100644 index 0000000..51188ac --- /dev/null +++ b/e2e/smoke/a_stamp_expected_bzlmod @@ -0,0 +1,4 @@ +WORKSPACE: _main +VERSION: v1.2.3 +WORKSPACE: _main +VERSION: v1.2.3 \ No newline at end of file diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index 0515bdd..9220319 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -51,6 +51,10 @@ toolchain_type( name = "coreutils_toolchain_type", ) +toolchain_type( + name = "expand_template_toolchain_type", +) + bzl_library( name = "docs", srcs = ["docs.bzl"], @@ -62,8 +66,19 @@ bzl_library( srcs = ["expand_make_vars.bzl"], deps = [ "//lib/private/docs:expand_locations", - "//lib/private/docs:expand_template", "//lib/private/docs:expand_variables", + ":expand_template", + ], +) + + +bzl_library( + name = "expand_template", + srcs = ["expand_template.bzl"], + deps = [ + "//lib/private/docs:expand_locations", + "//lib/private/docs:expand_variables", + "//lib/private/docs:expand_template", ], ) @@ -216,6 +231,7 @@ bzl_library( "//lib/private/docs:copy_directory_toolchain", "//lib/private/docs:copy_to_directory_toolchain", "//lib/private/docs:coreutils_toolchain", + "//lib/private/docs:expand_template_toolchain", "//lib/private/docs:jq_toolchain", "//lib/private/docs:local_config_platform", "//lib/private/docs:yq_toolchain", diff --git a/lib/expand_make_vars.bzl b/lib/expand_make_vars.bzl index f219bdc..5a1ca30 100644 --- a/lib/expand_make_vars.bzl +++ b/lib/expand_make_vars.bzl @@ -2,8 +2,9 @@ load("//lib/private:expand_locations.bzl", _expand_locations = "expand_locations") load("//lib/private:expand_variables.bzl", _expand_variables = "expand_variables") -load("//lib/private:expand_template.bzl", _expand_template = "expand_template") +load(":expand_template.bzl", _expand_template = "expand_template") expand_locations = _expand_locations expand_variables = _expand_variables +# TODO: 2.0 remove re-export from this file. expand_template = _expand_template diff --git a/lib/expand_template.bzl b/lib/expand_template.bzl new file mode 100644 index 0000000..7a2e64c --- /dev/null +++ b/lib/expand_template.bzl @@ -0,0 +1,5 @@ +"Public API for expand template" + +load("//lib/private:expand_template.bzl", _expand_template = "expand_template") + +expand_template = _expand_template \ No newline at end of file diff --git a/lib/extensions.bzl b/lib/extensions.bzl index 0282539..380f074 100644 --- a/lib/extensions.bzl +++ b/lib/extensions.bzl @@ -6,7 +6,8 @@ load( "register_copy_to_directory_toolchains", "register_jq_toolchains", "register_yq_toolchains", - "register_coreutils_toolchains" + "register_coreutils_toolchains", + "register_expand_template_toolchains", ) load("//lib/private:host_repo.bzl", "host_repo") @@ -16,7 +17,8 @@ def _toolchain_extension(mctx): register_jq_toolchains(register = False) register_yq_toolchains(register = False) register_coreutils_toolchains(register = False) - + register_expand_template_toolchains(register = False) + create_host_repo = False for module in mctx.modules: if len(module.tags.host) > 0: diff --git a/lib/private/docs/BUILD.bazel b/lib/private/docs/BUILD.bazel index f0b0147..58ca4db 100644 --- a/lib/private/docs/BUILD.bazel +++ b/lib/private/docs/BUILD.bazel @@ -102,6 +102,7 @@ bzl_library( deps = [ ":expand_locations", "@bazel_skylib//lib:dicts", + "//lib:stamping" ], ) @@ -247,6 +248,11 @@ bzl_library( srcs = ["//lib/private:coreutils_toolchain.bzl"], ) +bzl_library( + name = "expand_template_toolchain", + srcs = ["//lib/private:expand_template_toolchain.bzl"], + deps = ["//lib:stamping"] +) bzl_library( name = "strings", diff --git a/lib/private/expand_template.bzl b/lib/private/expand_template.bzl index 20a0a62..c8fd066 100644 --- a/lib/private/expand_template.bzl +++ b/lib/private/expand_template.bzl @@ -1,21 +1,63 @@ "expand_template rule" - +load("@bazel_skylib//lib:dicts.bzl", "dicts") load(":expand_locations.bzl", _expand_locations = "expand_locations") load(":expand_variables.bzl", _expand_variables = "expand_variables") +load("//lib:stamping.bzl", "STAMP_ATTRS", "maybe_stamp") + + +def _expand_substitutions(ctx, substitutions): + result = {} + for k, v in substitutions.items(): + result[k] = " ".join([ + _expand_variables(ctx, e, outs = [ctx.outputs.out], attribute_name = "substitutions") + for e in _expand_locations(ctx, v, ctx.attr.data).split(" ") + ]) + return result def _expand_template_impl(ctx): - template = ctx.file.template + substitutions = _expand_substitutions(ctx, ctx.attr.substitutions) + expand_template_info = ctx.toolchains["@aspect_bazel_lib//lib:expand_template_toolchain_type"].expand_template_info + stamp = maybe_stamp(ctx) + if stamp: + substitutions = dicts.add(substitutions, _expand_substitutions(ctx, ctx.attr.stamp_substitutions)) + substitutions_out = ctx.actions.declare_file("{}_substitutions.json".format(ctx.label.name)) + ctx.actions.write( + output = substitutions_out, + content = json.encode(substitutions) + ) - substitutions = {} - for k, v in ctx.attr.substitutions.items(): - substitutions[k] = " ".join([_expand_variables(ctx, e, outs = [ctx.outputs.out], attribute_name = "substitutions") for e in _expand_locations(ctx, v, ctx.attr.data).split(" ")]) + inputs = [ + ctx.file.template, + stamp.volatile_status_file, + stamp.stable_status_file, + substitutions_out, + ] - ctx.actions.expand_template( - template = template, - output = ctx.outputs.out, - substitutions = substitutions, - is_executable = ctx.attr.is_executable, - ) + args = ctx.actions.args() + args.add(ctx.file.template) + args.add(ctx.outputs.out) + args.add(substitutions_out) + args.add(stamp.volatile_status_file) + args.add(stamp.stable_status_file) + args.add(ctx.attr.is_executable) + + ctx.actions.run( + arguments = [args], + outputs = [ctx.outputs.out], + inputs = inputs, + executable = expand_template_info.bin, + ) + else: + ctx.actions.expand_template( + template = ctx.file.template, + output = ctx.outputs.out, + substitutions = substitutions, + is_executable = ctx.attr.is_executable, + ) + + all_outs = [ctx.outputs.out] + runfiles = ctx.runfiles(files = all_outs) + return [DefaultInfo(files = depset(all_outs), runfiles = runfiles)] expand_template_lib = struct( doc = """Template expansion @@ -30,34 +72,49 @@ such as `$(BINDIR)`, `$(TARGET_CPU)`, and `$(COMPILATION_MODE)` as documented in [expand_variables](https://github.com/aspect-build/bazel-lib/blob/main/docs/expand_make_vars.md#expand_variables). """, implementation = _expand_template_impl, - attrs = { - "template": attr.label( - doc = "The template file to expand.", - mandatory = True, - allow_single_file = True, + toolchains = ["@aspect_bazel_lib//lib:expand_template_toolchain_type"], + attrs = dicts.add({ + "data": attr.label_list( + doc = "List of targets for additional lookup information.", + allow_files = True, ), - "substitutions": attr.string_dict( - doc = "Mapping of strings to substitutions.", - mandatory = True, + "is_executable": attr.bool( + doc = "Whether to mark the output file as executable.", ), "out": attr.output( doc = "Where to write the expanded file.", mandatory = True, ), - "is_executable": attr.bool( - doc = "Whether to mark the output file as executable.", - default = False, - mandatory = False, + "stamp_substitutions": attr.string_dict( + doc = """Mapping of strings to substitutions. + + There are overlayed on top of substitutions when stamping is enabled + for the target. + + Substitutions can contain $(execpath :target) and $(rootpath :target) + expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when + stamping is enabled for the target. + """, ), - "data": attr.label_list( - doc = "List of targets for additional lookup information.", - allow_files = True, + "substitutions": attr.string_dict( + doc = """Mapping of strings to substitutions. + + Substitutions can contain $(execpath :target) and $(rootpath :target) + expansions, $(MAKEVAR) expansions and {{STAMP_VAR}} expansions when + stamping is enabled for the target. + """, ), - }, + "template": attr.label( + doc = "The template file to expand.", + mandatory = True, + allow_single_file = True, + ), + }, **STAMP_ATTRS), ) expand_template = rule( doc = expand_template_lib.doc, implementation = expand_template_lib.implementation, + toolchains = expand_template_lib.toolchains, attrs = expand_template_lib.attrs, ) diff --git a/lib/private/expand_template_toolchain.bzl b/lib/private/expand_template_toolchain.bzl new file mode 100644 index 0000000..fac9623 --- /dev/null +++ b/lib/private/expand_template_toolchain.bzl @@ -0,0 +1,188 @@ +"Setup expand_template toolchain repositories and rules" + +# https://github.com/aspect-build/bazel-lib/releases +# +# The integrity hashes can be automatically fetched for the latest expand_template release by running +# `tools/expand_template/mirror_release.sh`. To calculate for a specific release run +# `tools/expand_template/mirror_release.sh ` + +EXPAND_TEMPLATE_VERSION = "1.31.3" +EXPAND_TEMPLATE_INTEGRITY = { + "darwin_amd64": "sha256-2QaptqytZw9YQeV8cDA8MagRp998VTGs65btCdbVA0w=", + "darwin_arm64": "sha256-5W65OozVW+qSDRKqcXLjalh/hN/50EX8Z63V6MX/Wwc=", + "linux_amd64": "sha256-hc4+R/U1nq/3VXFerYimWyHFcxocVmt3gwGNvDL94sY=", + "linux_arm64": "sha256-DCm5ns8geCWGg1kpCMAE7RsoZt0OWp4vml7rpT0dYBs=", + "windows_amd64": "sha256-GDHRDOIptZAXHFmC1OOWDQl+3blwhi47RJBciLym4U8=", +} + +# Platform names follow the platform naming convention in @aspect_bazel_lib//:lib/private/repo_utils.bzl +EXPAND_TEMPLATE_PLATFORMS = { + "darwin_amd64": struct( + compatible_with = [ + "@platforms//os:macos", + "@platforms//cpu:x86_64", + ], + ), + "darwin_arm64": struct( + compatible_with = [ + "@platforms//os:macos", + "@platforms//cpu:aarch64", + ], + ), + "linux_amd64": struct( + compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + ), + "linux_arm64": struct( + compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], + ), + "windows_amd64": struct( + compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + ), +} + +ExpandTemplateInfo = provider( + doc = "Provide info for executing expand_template", + fields = { + "bin": "Executable expand_template binary", + }, +) + +def _expand_template_toolchain_impl(ctx): + binary = ctx.executable.bin + + default_info = DefaultInfo( + files = depset([binary]), + runfiles = ctx.runfiles(files = [binary]), + ) + expand_template_info = ExpandTemplateInfo( + bin = binary, + ) + + # Export all the providers inside our ToolchainInfo + # so the resolved_toolchain rule can grab and re-export them. + toolchain_info = platform_common.ToolchainInfo( + expand_template_info = expand_template_info, + default = default_info, + ) + + return [default_info, toolchain_info] + +expand_template_toolchain = rule( + implementation = _expand_template_toolchain_impl, + attrs = { + "bin": attr.label( + mandatory = True, + allow_single_file = True, + executable = True, + cfg = "exec", + ), + }, +) + +def _expand_template_toolchains_repo_impl(rctx): + # Expose a concrete toolchain which is the result of Bazel resolving the toolchain + # for the execution or target platform. + # Workaround for https://github.com/bazelbuild/bazel/issues/14009 + starlark_content = """# @generated by @aspect_bazel_lib//lib/private:expand_template_toolchain.bzl + +# Forward all the providers +def _resolved_toolchain_impl(ctx): + toolchain_info = ctx.toolchains["@aspect_bazel_lib//lib:expand_template_toolchain_type"] + return [ + toolchain_info, + toolchain_info.default, + toolchain_info.expand_template_info, + toolchain_info.template_variables, + ] + +# Copied from java_toolchain_alias +# https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl +resolved_toolchain = rule( + implementation = _resolved_toolchain_impl, + toolchains = ["@aspect_bazel_lib//lib:expand_template_toolchain_type"], + incompatible_use_toolchain_transition = True, +) +""" + rctx.file("defs.bzl", starlark_content) + + build_content = """# @generated by @aspect_bazel_lib//lib/private:expand_template_toolchain.bzl +# +# These can be registered in the workspace file or passed to --extra_toolchains flag. +# By default all these toolchains are registered by the expand_template_register_toolchains macro +# so you don't normally need to interact with these targets. + +load(":defs.bzl", "resolved_toolchain") + +resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"]) + +""" + + for [platform, meta] in EXPAND_TEMPLATE_PLATFORMS.items(): + build_content += """ +toolchain( + name = "{platform}_toolchain", + exec_compatible_with = {compatible_with}, + toolchain = "@{user_repository_name}_{platform}//:expand_template_toolchain", + toolchain_type = "@aspect_bazel_lib//lib:expand_template_toolchain_type", +) +""".format( + platform = platform, + user_repository_name = rctx.attr.user_repository_name, + compatible_with = meta.compatible_with, + ) + + # Base BUILD file for this repository + rctx.file("BUILD.bazel", build_content) + +expand_template_toolchains_repo = repository_rule( + _expand_template_toolchains_repo_impl, + doc = """Creates a repository with toolchain definitions for all known platforms + which can be registered or selected.""", + attrs = { + "user_repository_name": attr.string(doc = "Base name for toolchains repository"), + }, +) + +def _expand_template_platform_repo_impl(rctx): + is_windows = rctx.attr.platform.startswith("windows_") + meta = EXPAND_TEMPLATE_PLATFORMS[rctx.attr.platform] + release_platform = meta.release_platform if hasattr(meta, "release_platform") else rctx.attr.platform + + # https://github.com/aspect-build/bazel-lib/releases/download/v1.19.0/expand_template-linux_amd64 + url = "https://github.com/aspect-build/bazel-lib/releases/download/v{0}/expand_template-{1}{2}".format( + EXPAND_TEMPLATE_VERSION, + release_platform, + ".exe" if is_windows else "", + ) + + rctx.download( + url = url, + output = "expand_template.exe" if is_windows else "expand_template", + executable = True, + integrity = EXPAND_TEMPLATE_INTEGRITY[release_platform], + ) + build_content = """# @generated by @aspect_bazel_lib//lib/private:expand_template_toolchain.bzl +load("@aspect_bazel_lib//lib/private:expand_template_toolchain.bzl", "expand_template_toolchain") +exports_files(["{0}"]) +expand_template_toolchain(name = "expand_template_toolchain", bin = "{0}", visibility = ["//visibility:public"]) +""".format("expand_template.exe" if is_windows else "expand_template") + + # Base BUILD file for this repository + rctx.file("BUILD.bazel", build_content) + +expand_template_platform_repo = repository_rule( + implementation = _expand_template_platform_repo_impl, + doc = "Fetch external tools needed for expand_template toolchain", + attrs = { + "platform": attr.string(mandatory = True, values = EXPAND_TEMPLATE_PLATFORMS.keys()), + }, +) diff --git a/lib/repositories.bzl b/lib/repositories.bzl index 6e5bf5f..2435d8b 100644 --- a/lib/repositories.bzl +++ b/lib/repositories.bzl @@ -5,6 +5,7 @@ load("//lib/private:yq_toolchain.bzl", "YQ_PLATFORMS", "yq_host_alias_repo", "yq load("//lib/private:copy_to_directory_toolchain.bzl", "COPY_TO_DIRECTORY_PLATFORMS", "copy_to_directory_platform_repo", "copy_to_directory_toolchains_repo") load("//lib/private:coreutils_toolchain.bzl", "COREUTILS_PLATFORMS", "coreutils_platform_repo", "coreutils_toolchains_repo", _DEFAULT_COREUTILS_VERSION = "DEFAULT_COREUTILS_VERSION") load("//lib/private:copy_directory_toolchain.bzl", "COPY_DIRECTORY_PLATFORMS", "copy_directory_platform_repo", "copy_directory_toolchains_repo") +load("//lib/private:expand_template_toolchain.bzl", "EXPAND_TEMPLATE_PLATFORMS", "expand_template_platform_repo", "expand_template_toolchains_repo") load("//lib/private:local_config_platform.bzl", "local_config_platform") load("//lib:utils.bzl", "is_bazel_6_or_greater", http_archive = "maybe_http_archive") @@ -39,6 +40,9 @@ def aspect_bazel_lib_dependencies(override_local_config_platform = False): register_copy_directory_toolchains() register_copy_to_directory_toolchains() + # Always register the expand_template toolchain + register_expand_template_toolchains() + # Always register the coreutils toolchain register_coreutils_toolchains() @@ -161,3 +165,25 @@ def register_copy_to_directory_toolchains(name = "copy_to_directory", register = name = "%s_toolchains" % name, user_repository_name = name, ) + + +def register_expand_template_toolchains(name = "expand_template", register = True): + """Registers expand_template toolchain and repositories + + Args: + name: override the prefix for the generated toolchain repositories + register: whether to call through to native.register_toolchains. + Should be True for WORKSPACE users, but false when used under bzlmod extension + """ + for [platform, meta] in EXPAND_TEMPLATE_PLATFORMS.items(): + expand_template_platform_repo( + name = "%s_%s" % (name, platform), + platform = platform, + ) + if register: + native.register_toolchains("@%s_toolchains//:%s_toolchain" % (name, platform)) + + expand_template_toolchains_repo( + name = "%s_toolchains" % name, + user_repository_name = name, + ) diff --git a/lib/tests/expand_template/BUILD.bazel b/lib/tests/expand_template/BUILD.bazel new file mode 100644 index 0000000..902a9ea --- /dev/null +++ b/lib/tests/expand_template/BUILD.bazel @@ -0,0 +1,60 @@ +load("@aspect_bazel_lib//lib:diff_test.bzl", "diff_test") +load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") +load("@aspect_bazel_lib//lib:expand_make_vars.bzl", "expand_template") + +expand_template( + name = "a_tmpl_stamp", + out = "a_stamp", + data = ["a.tmpl"], + stamp = 1, + stamp_substitutions = { + "{{VERSION}}": "v{{BUILD_SCM_VERSION}}", + }, + substitutions = { + "{{TMPL_PATH}}": "$(rootpath a.tmpl)", + "{{VERSION}}": "v0.0.0", + "{{WORKSPACE}}": "$(WORKSPACE)", + }, + template = "a.tmpl", +) + +diff_test( + name = "a_stamp_test", + file1 = ":a_stamp", + file2 = select({ + "@aspect_bazel_lib//lib:bzlmod": "a_stamp_expected_bzlmod", + "//conditions:default": "a_stamp_expected", + }), +) + +expand_template( + name = "a_tmpl", + out = "a", + data = ["a.tmpl"], + stamp = 0, + stamp_substitutions = { + "{{VERSION}}": "v{{BUILD_SCM_VERSION}}", + }, + substitutions = { + "{{TMPL_PATH}}": "$(rootpath a.tmpl)", + "{{VERSION}}": "v0.0.0", + "{{WORKSPACE}}": "$(WORKSPACE)", + }, + template = "a.tmpl", +) + +diff_test( + name = "a_test", + file1 = ":a", + file2 = select({ + "@aspect_bazel_lib//lib:bzlmod": "a_expected_bzlmod", + "//conditions:default": "a_expected", + }), +) + +# This test requires that DefaultInfo be returned with the proper outputs. +assert_contains( + name = "default_info_test", + actual = ":a_tmpl_stamp", + expected = "WORKSPACE:", +) \ No newline at end of file diff --git a/lib/tests/expand_template/a.tmpl b/lib/tests/expand_template/a.tmpl new file mode 100644 index 0000000..7388dd8 --- /dev/null +++ b/lib/tests/expand_template/a.tmpl @@ -0,0 +1,6 @@ +WORKSPACE: {{WORKSPACE}} +VERSION: {{VERSION}} +TMPL_PATH: {{TMPL_PATH}} +WORKSPACE: {{WORKSPACE}} +VERSION: {{VERSION}} +TMPL_PATH: {{TMPL_PATH}} \ No newline at end of file diff --git a/lib/tests/expand_template/a_expected b/lib/tests/expand_template/a_expected new file mode 100644 index 0000000..662651f --- /dev/null +++ b/lib/tests/expand_template/a_expected @@ -0,0 +1,6 @@ +WORKSPACE: aspect_bazel_lib +VERSION: v0.0.0 +TMPL_PATH: lib/tests/expand_template/a.tmpl +WORKSPACE: aspect_bazel_lib +VERSION: v0.0.0 +TMPL_PATH: lib/tests/expand_template/a.tmpl \ No newline at end of file diff --git a/lib/tests/expand_template/a_expected_bzlmod b/lib/tests/expand_template/a_expected_bzlmod new file mode 100644 index 0000000..0630ddb --- /dev/null +++ b/lib/tests/expand_template/a_expected_bzlmod @@ -0,0 +1,6 @@ +WORKSPACE: _main +VERSION: v0.0.0 +TMPL_PATH: lib/tests/expand_template/a.tmpl +WORKSPACE: _main +VERSION: v0.0.0 +TMPL_PATH: lib/tests/expand_template/a.tmpl diff --git a/lib/tests/expand_template/a_stamp_expected b/lib/tests/expand_template/a_stamp_expected new file mode 100644 index 0000000..040438f --- /dev/null +++ b/lib/tests/expand_template/a_stamp_expected @@ -0,0 +1,6 @@ +WORKSPACE: aspect_bazel_lib +VERSION: v1.2.3 +TMPL_PATH: lib/tests/expand_template/a.tmpl +WORKSPACE: aspect_bazel_lib +VERSION: v1.2.3 +TMPL_PATH: lib/tests/expand_template/a.tmpl \ No newline at end of file diff --git a/lib/tests/expand_template/a_stamp_expected_bzlmod b/lib/tests/expand_template/a_stamp_expected_bzlmod new file mode 100644 index 0000000..039b41c --- /dev/null +++ b/lib/tests/expand_template/a_stamp_expected_bzlmod @@ -0,0 +1,6 @@ +WORKSPACE: _main +VERSION: v1.2.3 +TMPL_PATH: lib/tests/expand_template/a.tmpl +WORKSPACE: _main +VERSION: v1.2.3 +TMPL_PATH: lib/tests/expand_template/a.tmpl diff --git a/tools/expand_template/mirror_release.sh b/tools/expand_template/mirror_release.sh index aea046d..3dba59c 100755 --- a/tools/expand_template/mirror_release.sh +++ b/tools/expand_template/mirror_release.sh @@ -12,8 +12,8 @@ else fi # Extract the checksums and output a starlark map entry -echo "expand_template_VERSION = \"$version\"" -echo "expand_template_INTEGRITY = {" +echo "EXPAND_TEMPLATE_VERSION = \"$version\"" +echo "EXPAND_TEMPLATE_INTEGRITY = {" platforms=(darwin_{amd64,arm64} linux_{amd64,arm64} windows_amd64) for release in ${platforms[@]}; do integrity="https://github.com/aspect-build/bazel-lib/releases/download/v$version/expand_template-$release"