Skip to content

Commit 7732d7e

Browse files
committed
build: introduce custom prelude to improve Cargo compatibility
Signed-off-by: jl.jiang <[email protected]>
1 parent 3500cbe commit 7732d7e

File tree

781 files changed

+8645
-14093
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

781 files changed

+8645
-14093
lines changed

.buckconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
prelude = prelude
44
toolchains = toolchains
55
none = none
6+
buckal = buckal
67

78
[cell_aliases]
89
config = prelude

PACKAGE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# @generated by `cargo buckal`
2+
3+
load("@prelude//cfg/modifier:set_cfg_modifiers.bzl", "set_cfg_modifiers")
4+
load("@prelude//rust:with_workspace.bzl", "with_rust_workspace")
5+
load("@buckal//config:set_cfg_constructor.bzl", "set_cfg_constructor")
6+
7+
ALIASES = {
8+
"debug": "buckal//config/mode:debug",
9+
"release": "buckal//config/mode:release",
10+
}
11+
set_cfg_constructor(aliases = ALIASES)
12+
13+
set_cfg_modifiers(
14+
cfg_modifiers = [
15+
# default is debug mode.
16+
# override with -m release
17+
"buckal//config/mode:debug",
18+
],
19+
)

buckal/cargo_buildscript.bzl

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is dual-licensed under either the MIT license found in the
4+
# LICENSE-MIT file in the root directory of this source tree or the Apache
5+
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
6+
# of this source tree. You may select, at your option, one of the
7+
# above-listed licenses.
8+
9+
# Cargo build script runner compatible with Reindeer-generated targets.
10+
#
11+
# Use this reindeer.toml configuration to refer to this rule:
12+
#
13+
# [buck]
14+
# buckfile_imports = """
15+
# load("@prelude//rust:cargo_buildscript.bzl", "buildscript_run")
16+
# """
17+
#
18+
# # optional (this matches the default rule name):
19+
# buildscript_genrule = "buildscript_run"
20+
#
21+
22+
load("@prelude//decls:common.bzl", "buck")
23+
load("@prelude//decls:toolchains_common.bzl", "toolchains_common")
24+
load("@prelude//os_lookup:defs.bzl", "Os", "OsLookup")
25+
load("@prelude//rust:rust_toolchain.bzl", "RustToolchainInfo")
26+
load("@prelude//rust:targets.bzl", "targets")
27+
load(
28+
"@prelude//rust/tools:buildscript_platform.bzl",
29+
"buildscript_platform_constraints",
30+
"transition_alias",
31+
)
32+
load("@prelude//utils:cmd_script.bzl", "cmd_script")
33+
load("@prelude//utils:selects.bzl", "selects")
34+
load("@prelude//rust:build.bzl", "dependency_args")
35+
load("@prelude//rust:build_params.bzl", "MetadataKind")
36+
load(
37+
"@prelude//rust:cargo_package.bzl",
38+
"apply_platform_attrs",
39+
"get_reindeer_platform_names",
40+
"get_reindeer_platforms",
41+
)
42+
load("@prelude//rust:context.bzl", "DepCollectionContext")
43+
load(
44+
"@prelude//rust:link_info.bzl",
45+
"DEFAULT_STATIC_LINK_STRATEGY",
46+
"RustProcMacroPlugin",
47+
"gather_explicit_sysroot_deps",
48+
"resolve_rust_deps_inner",
49+
)
50+
load("@prelude//rust:rust_toolchain.bzl", "PanicRuntime")
51+
52+
def _make_rustc_shim(ctx: AnalysisContext, cwd: Artifact) -> cmd_args:
53+
# Build scripts expect to receive a `rustc` which "just works." However,
54+
# our rustc sometimes has no sysroot available, so we need to make a shim
55+
# which supplies the sysroot deps if necessary
56+
toolchain_info = ctx.attrs._rust_toolchain[RustToolchainInfo]
57+
explicit_sysroot_deps = toolchain_info.explicit_sysroot_deps
58+
if explicit_sysroot_deps:
59+
dep_ctx = DepCollectionContext(
60+
advanced_unstable_linking = False,
61+
include_doc_deps = False,
62+
is_proc_macro = False,
63+
explicit_sysroot_deps = explicit_sysroot_deps,
64+
panic_runtime = PanicRuntime("unwind"), # not actually used
65+
)
66+
deps = gather_explicit_sysroot_deps(dep_ctx)
67+
deps = resolve_rust_deps_inner(ctx, deps)
68+
dep_args, _ = dependency_args(
69+
ctx = ctx,
70+
compile_ctx = None,
71+
toolchain_info = toolchain_info,
72+
deps = deps,
73+
subdir = "any",
74+
dep_link_strategy = DEFAULT_STATIC_LINK_STRATEGY,
75+
dep_metadata_kind = MetadataKind("full"),
76+
is_rustdoc_test = False,
77+
)
78+
79+
null_path = "nul" if ctx.attrs._exec_os_type[OsLookup].os == Os("windows") else "/dev/null"
80+
dep_args = cmd_args("--sysroot=" + null_path, dep_args, relative_to = cwd)
81+
dep_file, _ = ctx.actions.write("rustc_dep_file", dep_args, allow_args = True)
82+
sysroot_args = cmd_args("@", dep_file, delimiter = "", hidden = dep_args)
83+
else:
84+
sysroot_args = cmd_args()
85+
86+
shim = cmd_script(
87+
ctx = ctx,
88+
name = "__rustc_shim",
89+
cmd = cmd_args(toolchain_info.compiler, sysroot_args, relative_to = cwd),
90+
language = ctx.attrs._exec_os_type[OsLookup].script,
91+
)
92+
93+
return cmd_args(shim, relative_to = cwd)
94+
95+
def _cargo_buildscript_impl(ctx: AnalysisContext) -> list[Provider]:
96+
toolchain_info = ctx.attrs._rust_toolchain[RustToolchainInfo]
97+
98+
cwd = ctx.actions.declare_output("cwd", dir = True)
99+
out_dir = ctx.actions.declare_output("OUT_DIR", dir = True)
100+
rustc_flags = ctx.actions.declare_output("rustc_flags")
101+
# *BUCKAL-ONLY* metadata environment variables for *dependent* buildscript runners to consume
102+
metadata = ctx.actions.declare_output("METADATA")
103+
104+
if ctx.attrs.manifest_dir != None:
105+
manifest_dir = ctx.attrs.manifest_dir[DefaultInfo].default_outputs[0]
106+
else:
107+
manifest_dir = ctx.actions.symlinked_dir("manifest_dir", ctx.attrs.filegroup_for_manifest_dir)
108+
109+
cmd = [
110+
ctx.attrs.runner[RunInfo],
111+
cmd_args("--buildscript=", ctx.attrs.buildscript[RunInfo], delimiter = ""),
112+
cmd_args("--rustc-cfg=", ctx.attrs.rustc_cfg[DefaultInfo].default_outputs[0], delimiter = ""),
113+
cmd_args("--manifest-dir=", manifest_dir, delimiter = ""),
114+
cmd_args("--create-cwd=", cwd.as_output(), delimiter = ""),
115+
cmd_args("--outfile=", rustc_flags.as_output(), delimiter = ""),
116+
cmd_args("--outenv=", metadata.as_output(), delimiter = ""),
117+
]
118+
119+
# *BUCKAL-ONLY* read environment variable source files and set them for buildscript runner
120+
for env_src in ctx.attrs.env_srcs:
121+
cmd.append(cmd_args("--extra-env=", env_src[DefaultInfo].default_outputs[0], delimiter = ""))
122+
123+
# See https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
124+
125+
env = {}
126+
env["CARGO"] = "/bin/false"
127+
env["CARGO_PKG_NAME"] = ctx.attrs.package_name
128+
env["CARGO_PKG_VERSION"] = ctx.attrs.version
129+
env["OUT_DIR"] = out_dir.as_output()
130+
env["RUSTC"] = _make_rustc_shim(ctx, cwd)
131+
env["RUSTC_LINKER"] = "/bin/false"
132+
env["RUST_BACKTRACE"] = "1"
133+
134+
if toolchain_info.rustc_target_triple:
135+
env["TARGET"] = toolchain_info.rustc_target_triple
136+
else:
137+
cmd.append(cmd_args("--rustc-host-tuple=", ctx.attrs.rustc_host_tuple[DefaultInfo].default_outputs[0], delimiter = ""))
138+
139+
# \037 == \x1f == the magic delimiter specified in the environment variable
140+
# reference above.
141+
env["CARGO_ENCODED_RUSTFLAGS"] = cmd_args(toolchain_info.rustc_flags, delimiter = "\037")
142+
143+
host_triple = targets.exec_triple(ctx)
144+
if host_triple:
145+
env["HOST"] = host_triple
146+
147+
for feature in ctx.attrs.features:
148+
upper_feature = feature.upper().replace("-", "_")
149+
env["CARGO_FEATURE_{}".format(upper_feature)] = "1"
150+
151+
# Environment variables specified in the target's attributes get priority
152+
# over all the above.
153+
for k, v in ctx.attrs.env.items():
154+
env[k] = cmd_args(v, relative_to = cwd)
155+
156+
ctx.actions.run(
157+
cmd,
158+
env = env,
159+
category = "buildscript",
160+
)
161+
162+
return [DefaultInfo(
163+
default_output = None,
164+
sub_targets = {
165+
"out_dir": [DefaultInfo(default_output = out_dir)],
166+
"rustc_flags": [DefaultInfo(default_output = rustc_flags)],
167+
"metadata": [DefaultInfo(default_output = metadata)],
168+
},
169+
)]
170+
171+
_cargo_buildscript_rule = rule(
172+
impl = _cargo_buildscript_impl,
173+
attrs = {
174+
"buildscript": attrs.exec_dep(providers = [RunInfo]),
175+
"env": attrs.dict(key = attrs.string(), value = attrs.arg(), default = {}),
176+
# *BUCKAL-ONLY* list of environment variable source files to set for the buildscript
177+
"env_srcs": attrs.list(attrs.dep(), default = []),
178+
"features": attrs.list(attrs.string(), default = []),
179+
"filegroup_for_manifest_dir": attrs.option(attrs.dict(key = attrs.string(), value = attrs.source()), default = None),
180+
"manifest_dir": attrs.option(attrs.dep(), default = None),
181+
"package_name": attrs.string(),
182+
"runner": attrs.default_only(attrs.exec_dep(providers = [RunInfo], default = "buckal//tool:buildscript_run")),
183+
# *IMPORTANT* rustc_cfg must be a `dep` and not an `exec_dep` because
184+
# we want the `rustc --cfg` for the target platform, not the exec platform.
185+
"rustc_cfg": attrs.dep(default = "prelude//rust/tools:rustc_cfg"),
186+
"rustc_host_tuple": attrs.dep(default = "prelude//rust/tools:rustc_host_tuple"),
187+
"version": attrs.string(),
188+
"_exec_os_type": buck.exec_os_type_arg(),
189+
"_rust_toolchain": toolchains_common.rust(),
190+
},
191+
# Always empty, but needed to prevent errors
192+
uses_plugins = [RustProcMacroPlugin],
193+
)
194+
195+
def buildscript_run(
196+
name,
197+
buildscript_rule,
198+
package_name,
199+
version,
200+
platform = {},
201+
# path to crate's directory in source tree, e.g. "vendor/serde-1.0.100"
202+
local_manifest_dir = None,
203+
# target or subtarget containing crate, e.g. ":serde.git[serde]"
204+
manifest_dir = None,
205+
buildscript_compatible_with = None,
206+
**kwargs):
207+
kwargs = apply_platform_attrs(platform, kwargs)
208+
209+
if manifest_dir == None and local_manifest_dir == None:
210+
existing_filegroup_name = "{}-{}.crate".format(package_name, version)
211+
if rule_exists(existing_filegroup_name):
212+
manifest_dir = ":{}".format(existing_filegroup_name)
213+
else:
214+
local_manifest_dir = "vendor/{}-{}".format(package_name, version)
215+
216+
filegroup_for_manifest_dir = None
217+
if local_manifest_dir != None:
218+
prefix_with_trailing_slash = "{}/".format(local_manifest_dir)
219+
filegroup_for_manifest_dir = {
220+
path.removeprefix(prefix_with_trailing_slash): path
221+
for path in glob(["{}/**".format(local_manifest_dir)])
222+
}
223+
224+
def platform_buildscript_build_name(plat):
225+
if name.endswith("-build-script-run"):
226+
# This is the expected case for Reindeer-generated targets, which
227+
# come in pairs build-script-run and build-script-build.
228+
return "{}-build-script-build-{}".format(
229+
name.removesuffix("-build-script-run"),
230+
plat,
231+
)
232+
else:
233+
return "{}-{}".format(name, plat)
234+
235+
if not rule_exists("buildscript_for_platform="):
236+
buildscript_platform_constraints(
237+
name = "buildscript_for_platform=",
238+
reindeer_platforms = get_reindeer_platform_names(),
239+
)
240+
241+
for plat in get_reindeer_platform_names():
242+
transition_alias(
243+
name = platform_buildscript_build_name(plat),
244+
actual = buildscript_rule,
245+
incoming_transition = ":buildscript_for_platform=[{}]".format(plat),
246+
target_compatible_with = buildscript_compatible_with,
247+
visibility = [],
248+
)
249+
250+
buildscript_rule = selects.apply(
251+
get_reindeer_platforms(),
252+
lambda plat: buildscript_rule if plat == None else ":{}".format(platform_buildscript_build_name(plat)),
253+
)
254+
255+
_cargo_buildscript_rule(
256+
name = name,
257+
buildscript = buildscript_rule,
258+
package_name = package_name,
259+
version = version,
260+
filegroup_for_manifest_dir = filegroup_for_manifest_dir,
261+
manifest_dir = manifest_dir,
262+
**kwargs
263+
)

buckal/cargo_manifest.bzl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# @generated by `cargo buckal`
2+
3+
def _cargo_manifest_impl(ctx: AnalysisContext) -> list[Provider]:
4+
env_dict = ctx.actions.declare_output("ENV_DICT")
5+
env_flags = ctx.actions.declare_output("ENV_FLAGS")
6+
7+
cmd = [
8+
ctx.attrs.executor[RunInfo],
9+
cmd_args("--vendor=", ctx.attrs.vendor[DefaultInfo].default_outputs[0], delimiter = ""),
10+
cmd_args("--out-dict=", env_dict.as_output(), delimiter = ""),
11+
cmd_args("--out-flags=", env_flags.as_output(), delimiter = ""),
12+
]
13+
14+
ctx.actions.run(
15+
cmd,
16+
category = "cargo_manifest",
17+
)
18+
19+
return [DefaultInfo(
20+
default_output = None,
21+
sub_targets = {
22+
"env_dict": [DefaultInfo(default_output = env_dict)],
23+
"env_flags": [DefaultInfo(default_output = env_flags)],
24+
},
25+
)]
26+
27+
cargo_manifest = rule(
28+
impl = _cargo_manifest_impl,
29+
attrs = {
30+
"vendor": attrs.dep(),
31+
"executor": attrs.default_only(attrs.exec_dep(providers = [RunInfo], default = "buckal//tool:manifest_parse")),
32+
},
33+
)

buckal/config/mode/BUCK

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# @generated by `cargo buckal`
2+
3+
config_setting(
4+
name = "debug",
5+
constraint_values = [
6+
"buckal//config/mode/constraints:debug",
7+
],
8+
visibility = ["PUBLIC"],
9+
)
10+
11+
config_setting(
12+
name = "release",
13+
constraint_values = [
14+
"buckal//config/mode/constraints:release",
15+
],
16+
visibility = ["PUBLIC"],
17+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# @generated by `cargo buckal`
2+
3+
constraint_setting(
4+
name = "mode",
5+
visibility = ["PUBLIC"],
6+
)
7+
8+
constraint_value(
9+
name = "debug",
10+
constraint_setting = ":mode",
11+
visibility = ["PUBLIC"],
12+
)
13+
14+
constraint_value(
15+
name = "release",
16+
constraint_setting = ":mode",
17+
visibility = ["PUBLIC"],
18+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# @generated by `cargo buckal`
2+
3+
load("@prelude//cfg/modifier:cfg_constructor.bzl", "cfg_constructor_post_constraint_analysis", "cfg_constructor_pre_constraint_analysis")
4+
load("@prelude//cfg/modifier:common.bzl", "MODIFIER_METADATA_KEY")
5+
6+
def set_cfg_constructor(aliases = dict()):
7+
project_root_cell = read_root_config("cell_aliases", "root")
8+
current_root_cell = read_config("cell_aliases", "root")
9+
if project_root_cell == current_root_cell:
10+
native.set_cfg_constructor(
11+
stage0 = cfg_constructor_pre_constraint_analysis,
12+
stage1 = cfg_constructor_post_constraint_analysis,
13+
key = MODIFIER_METADATA_KEY,
14+
aliases = struct(**aliases),
15+
extra_data = struct(),
16+
)

buckal/tool/BUCK

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# @generated by `cargo buckal`
2+
3+
native.python_bootstrap_binary(
4+
name = "manifest_parse",
5+
main = "manifest_parse.py",
6+
visibility = ["PUBLIC"],
7+
)
8+
9+
native.python_bootstrap_binary(
10+
name = "buildscript_run",
11+
main = "buildscript_run.py",
12+
visibility = ["PUBLIC"],
13+
)

0 commit comments

Comments
 (0)