Skip to content

Commit 8ab8fd1

Browse files
authored
Add bzlmod/canonical-repo-names (#336)
This is the example code for the upcoming EngFlow blog post "Migrating to Bazel Modules (a.k.a. Bzlmod) - Repo Names, Macros, and Variables."
1 parent 81d892c commit 8ab8fd1

File tree

11 files changed

+1396
-6
lines changed

11 files changed

+1396
-6
lines changed

.bazelignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
# - https://github.com/bazelbuild/bazel/issues/22208
55
# - https://github.com/bazelbuild/bazel/issues/21515
66
runfiles/
7+
bzlmod/

MODULE.bazel.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bzlmod/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Bazel Modules (a.k.a. Bzlmod) Examples
2+
3+
This directory contains sample projects demonstraing concepts specifically
4+
related to [Bazel modules](https://bazel.build/external/overview#bzlmod).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.aspect/
+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Copyright 2024 EngFlow Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4+
# use this file except in compliance with the License. You may obtain a copy of
5+
# the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations under
13+
# the License.
14+
15+
load(
16+
"//:repo-names.bzl",
17+
"canonical_repo",
18+
"workspace_root",
19+
"repo_name_variable",
20+
"repo_dir_variable",
21+
"gen_js_constants",
22+
)
23+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
24+
load("@rules_js//js:defs.bzl", "js_binary")
25+
26+
# This value propagates through the rules below.
27+
# Try it with other targets of your choosing!
28+
repo_target = "@pnpm"
29+
30+
# The following value inspired the `repo_name_variable` rule.
31+
# - A macro executes during the loading phase and doesn't see resolved aliases.
32+
# - A rule executes during the analysis phase, after aliases have resolved.
33+
#repo_target = "@bison_v3.3.2//bin:bison"
34+
35+
# Demonstrates the macro-based approach.
36+
genrule(
37+
name = "repo-macros",
38+
outs = ["repo-macros.txt"],
39+
cmd = "printf '%s\n canonical_name: %s\n workspace_root: %s\n' >$@" % (
40+
repo_target, canonical_repo(repo_target), workspace_root(repo_target)
41+
)
42+
)
43+
44+
# Demonstrates the custom Make variable-based approach. Depends on the
45+
# ":repo-target" alias to show how the variables have access to the underlying
46+
# target.
47+
genrule(
48+
name = "repo-vars",
49+
outs = ["repo-vars.txt"],
50+
cmd = r"""printf '%%s\n' \
51+
'%s' \
52+
' repo-name: $(repo-name)' \
53+
' repo-dir: $(repo-dir)' >$@""" % repo_target,
54+
toolchains = [
55+
":repo-name",
56+
":repo-dir",
57+
]
58+
)
59+
60+
# This alias demonstrates how the rules from `repo-names.bzl` work with resolved
61+
# aliases during the analysis phase. Calling the `canonical_repo()` and
62+
# `workspace_root()` macros with ":repo-target" in this file will return the
63+
# empty string, since macros execute during the loading phase.
64+
alias(
65+
name = "repo-target",
66+
actual = repo_target,
67+
)
68+
69+
# These rules create custom variables that other rules can use by adding
70+
# ":repo-name" and ":repo-dir" to their `toolchains` attribute.
71+
# - https://bazel.build/reference/be/make-variables#custom_variables
72+
# - https://bazel.build/rules/lib/providers/TemplateVariableInfo
73+
repo_name_variable(
74+
name = "repo-name",
75+
dep = ":repo-target",
76+
)
77+
78+
repo_dir_variable(
79+
name = "repo-dir",
80+
dep = ":repo-target",
81+
)
82+
83+
# Run this program via the following commands for different values of
84+
# `repo_target` above. `node` must be installed for the invocations outside of
85+
# `bazel run`.
86+
#
87+
# ```txt
88+
# bazel run --//:constants=genrule //:repo-dir-check
89+
# node bazel-bin/repo-dir-check.mjs
90+
#
91+
# bazel run --//:constants=custom //:repo-dir-check
92+
# node bazel-bin/repo-dir-check.mjs
93+
# ```
94+
#
95+
# Note the differences between:
96+
# - the macro-generated repo dir and the variable- or rule-generated repo dir
97+
# - where the repo dir is found when running under `bazel run` vs `node`
98+
js_binary(
99+
name = "repo-dir-check",
100+
entry_point = "repo-dir-check.mjs",
101+
data = [
102+
":constants-impl",
103+
repo_target,
104+
],
105+
)
106+
107+
# Determines which rule generates the `constants.js` module used by
108+
# `:repo-dir-check`, based on the value of the `--//:constants` flag.
109+
genrule(
110+
name = "constants-impl",
111+
outs = ["constants.js"],
112+
srcs = select({
113+
":genrule": [":genrule-constants"],
114+
":custom-rule": [":custom-rule-constants"],
115+
}),
116+
cmd = "cp $< $@",
117+
)
118+
119+
# The `--//:constants` command line flag determines whether `:constants-impl`
120+
# selects `:genrule-constants` or `:custom-rule-constants` as input.
121+
string_flag(
122+
name = "constants",
123+
values = [
124+
"genrule",
125+
"custom-rule",
126+
],
127+
build_setting_default = "genrule",
128+
)
129+
130+
config_setting(
131+
name = "genrule",
132+
flag_values = {":constants": "genrule"},
133+
)
134+
135+
config_setting(
136+
name = "custom-rule",
137+
flag_values = {":constants": "custom-rule"},
138+
)
139+
140+
# A genrule producing a constants module illustrating how to incorporate:
141+
# - the `BINDIR` predefined Make variable
142+
# - the `rlocationpaths` predefined variable, called on the ":repo-target" alias
143+
# - the `canonical_name()` and `workspace_root()` macros, called during the
144+
# loading phase on a variable
145+
# - the `repo_name_variable` and `repo_dir_variable` targets, evaluated during
146+
# the analysis phase, which supplies a custom variable as a `toolchains`
147+
# attribute target
148+
#
149+
# Replacing `repo_target` with the string ":repo-target" in the macros below
150+
# will produce the empty string, since the macros won't see the resolved alias.
151+
genrule(
152+
name = "genrule-constants",
153+
srcs = [":repo-target"],
154+
outs = ["genrule-constants.js"],
155+
cmd = r"""printf 'module.exports.%%s;\n' \
156+
'ruleName = "genrule-constants"' \
157+
'target = "%s"' \
158+
'binDir = "$(BINDIR)"' \
159+
'location = "$(rlocationpaths :repo-target)"' \
160+
'macroName = "%s"' \
161+
'macroDir = "%s"' \
162+
'repoName = "$(repo-name)"' \
163+
'repoDir = "$(repo-dir)"' >$@""" % (
164+
repo_target,
165+
canonical_repo(repo_target),
166+
workspace_root(repo_target)
167+
),
168+
toolchains = [
169+
":repo-name",
170+
":repo-dir",
171+
],
172+
)
173+
174+
# A custom rule producing a constants module from its `vars`, `repo_names`, and
175+
# `repo_dirs` attributes.
176+
#
177+
# This rule illustrates how to use:
178+
# - The `rlocationpaths` predefined variable, called on the ":repo-target" alias
179+
# - The `canonical_name()` and `workspace_root()` macros, called during the
180+
# loading phase on a variable
181+
# - The `repo_names` and `repo_dirs` attributes of type
182+
# `attr.label_keyed_string_dict`, whose keys are resolved Target values
183+
# (including aliases) during the analysis phase
184+
#
185+
# Note that:
186+
# - For the `vars` attribute, the keys become constant names, and the values
187+
# become constant values.
188+
# - For the `repo_names` and `repo_dirs` attributes, the keys become constant
189+
# values, and the values become constant names.
190+
#
191+
# Replacing `repo_target` with the string ":repo-target" in the macros below
192+
# will produce the empty string, since the macros won't see the resolved alias.
193+
gen_js_constants(
194+
name = "custom-rule-constants",
195+
deps = [":repo-target"],
196+
vars = {
197+
"target": repo_target,
198+
"location": "$(rlocationpaths :repo-target)",
199+
"macroName": canonical_repo(repo_target),
200+
"macroDir": workspace_root(repo_target),
201+
},
202+
repo_names = {":repo-target": "repoName"},
203+
repo_dirs = {":repo-target": "repoDir"},
204+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2024 EngFlow Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4+
# use this file except in compliance with the License. You may obtain a copy of
5+
# the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations under
13+
# the License.
14+
15+
"""Example module for canonical repo name injection"""
16+
17+
# Main module
18+
module(name = "frobozz", version = "1.2.3")
19+
20+
# aspect_rules_js
21+
bazel_dep(name = "aspect_rules_js", version = "2.0.1", repo_name = "rules_js")
22+
23+
pnpm = use_extension("@rules_js//npm:extensions.bzl", "pnpm")
24+
use_repo(pnpm, "pnpm")
25+
26+
# rules_bison
27+
bazel_dep(name = "rules_bison", version = "0.2.2")
28+
29+
bison = use_extension(
30+
"@rules_bison//bison/extensions:bison_repository_ext.bzl",
31+
"bison_repository_ext",
32+
)
33+
bison.repository(version = "3.3.2")
34+
use_repo(bison, "bison_v3.3.2")

0 commit comments

Comments
 (0)