mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-22 07:53:11 -05:00
0730422bce
Because the `macros` crate exposes procedural macros, it must be compiled as a dynamic library (so it can be loaded by the compiler at compile-time). Before this change the resulting artifact was always named `libmacros.so`, which works on hosts where this matches the naming convention for dynamic libraries. However the proper name on macOS would be `libmacros.dylib`. This turns out to matter even when the dependency is passed with a path (`--extern macros=path/to/libmacros.so` rather than `--extern macros`) because rustc uses the file name to infer the type of the library (see link). This is because there's no way to specify both the path to and the type of the external library via CLI flags. The compiler could speculatively parse the file to determine its type, but it does not do so today. This means that libraries that match neither rustc's naming convention for static libraries nor the platform's naming convention for dynamic libraries are *rejected*. The only solution I've found is to follow the host platform's naming convention. This patch does that by querying the compiler to determine the appropriate name for the artifact. This allows the kernel to build with CONFIG_RUST=y on macOS. Link: https://github.com/rust-lang/rust/blob/d829780/compiler/rustc_metadata/src/locator.rs#L728-L752 Tested-by: Daniel Gomez <da.gomez@samsung.com> Co-developed-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Tamir Duberstein <tamird@gmail.com> Tested-by: Andreas Hindborg <a.hindborg@kernel.org> Link: https://lore.kernel.org/r/20241216-b4-dylib-host-macos-v7-1-cfc507681447@gmail.com [ Added `MAKEFLAGS=`s to avoid jobserver warnings. Removed space. Reworded title. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
169 lines
5.1 KiB
Python
Executable file
169 lines
5.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
"""generate_rust_analyzer - Generates the `rust-project.json` file for `rust-analyzer`.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
|
|
def args_crates_cfgs(cfgs):
|
|
crates_cfgs = {}
|
|
for cfg in cfgs:
|
|
crate, vals = cfg.split("=", 1)
|
|
crates_cfgs[crate] = vals.replace("--cfg", "").split()
|
|
|
|
return crates_cfgs
|
|
|
|
def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
|
|
# Generate the configuration list.
|
|
cfg = []
|
|
with open(objtree / "include" / "generated" / "rustc_cfg") as fd:
|
|
for line in fd:
|
|
line = line.replace("--cfg=", "")
|
|
line = line.replace("\n", "")
|
|
cfg.append(line)
|
|
|
|
# Now fill the crates list -- dependencies need to come first.
|
|
#
|
|
# Avoid O(n^2) iterations by keeping a map of indexes.
|
|
crates = []
|
|
crates_indexes = {}
|
|
crates_cfgs = args_crates_cfgs(cfgs)
|
|
|
|
def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False):
|
|
crate = {
|
|
"display_name": display_name,
|
|
"root_module": str(root_module),
|
|
"is_workspace_member": is_workspace_member,
|
|
"is_proc_macro": is_proc_macro,
|
|
"deps": [{"crate": crates_indexes[dep], "name": dep} for dep in deps],
|
|
"cfg": cfg,
|
|
"edition": "2021",
|
|
"env": {
|
|
"RUST_MODFILE": "This is only for rust-analyzer"
|
|
}
|
|
}
|
|
if is_proc_macro:
|
|
proc_macro_dylib_name = subprocess.check_output(
|
|
[os.environ["RUSTC"], "--print", "file-names", "--crate-name", display_name, "--crate-type", "proc-macro", "-"],
|
|
stdin=subprocess.DEVNULL,
|
|
).decode('utf-8').strip()
|
|
crate["proc_macro_dylib_path"] = f"{objtree}/rust/{proc_macro_dylib_name}"
|
|
crates_indexes[display_name] = len(crates)
|
|
crates.append(crate)
|
|
|
|
# First, the ones in `rust/` since they are a bit special.
|
|
append_crate(
|
|
"core",
|
|
sysroot_src / "core" / "src" / "lib.rs",
|
|
[],
|
|
cfg=crates_cfgs.get("core", []),
|
|
is_workspace_member=False,
|
|
)
|
|
|
|
append_crate(
|
|
"compiler_builtins",
|
|
srctree / "rust" / "compiler_builtins.rs",
|
|
[],
|
|
)
|
|
|
|
append_crate(
|
|
"macros",
|
|
srctree / "rust" / "macros" / "lib.rs",
|
|
[],
|
|
is_proc_macro=True,
|
|
)
|
|
|
|
append_crate(
|
|
"build_error",
|
|
srctree / "rust" / "build_error.rs",
|
|
["core", "compiler_builtins"],
|
|
)
|
|
|
|
append_crate(
|
|
"bindings",
|
|
srctree / "rust"/ "bindings" / "lib.rs",
|
|
["core"],
|
|
cfg=cfg,
|
|
)
|
|
crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True))
|
|
|
|
append_crate(
|
|
"kernel",
|
|
srctree / "rust" / "kernel" / "lib.rs",
|
|
["core", "macros", "build_error", "bindings"],
|
|
cfg=cfg,
|
|
)
|
|
crates[-1]["source"] = {
|
|
"include_dirs": [
|
|
str(srctree / "rust" / "kernel"),
|
|
str(objtree / "rust")
|
|
],
|
|
"exclude_dirs": [],
|
|
}
|
|
|
|
def is_root_crate(build_file, target):
|
|
try:
|
|
return f"{target}.o" in open(build_file).read()
|
|
except FileNotFoundError:
|
|
return False
|
|
|
|
# Then, the rest outside of `rust/`.
|
|
#
|
|
# We explicitly mention the top-level folders we want to cover.
|
|
extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers"))
|
|
if external_src is not None:
|
|
extra_dirs = [external_src]
|
|
for folder in extra_dirs:
|
|
for path in folder.rglob("*.rs"):
|
|
logging.info("Checking %s", path)
|
|
name = path.name.replace(".rs", "")
|
|
|
|
# Skip those that are not crate roots.
|
|
if not is_root_crate(path.parent / "Makefile", name) and \
|
|
not is_root_crate(path.parent / "Kbuild", name):
|
|
continue
|
|
|
|
logging.info("Adding %s", name)
|
|
append_crate(
|
|
name,
|
|
path,
|
|
["core", "kernel"],
|
|
cfg=cfg,
|
|
)
|
|
|
|
return crates
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--verbose', '-v', action='store_true')
|
|
parser.add_argument('--cfgs', action='append', default=[])
|
|
parser.add_argument("srctree", type=pathlib.Path)
|
|
parser.add_argument("objtree", type=pathlib.Path)
|
|
parser.add_argument("sysroot", type=pathlib.Path)
|
|
parser.add_argument("sysroot_src", type=pathlib.Path)
|
|
parser.add_argument("exttree", type=pathlib.Path, nargs="?")
|
|
args = parser.parse_args()
|
|
|
|
logging.basicConfig(
|
|
format="[%(asctime)s] [%(levelname)s] %(message)s",
|
|
level=logging.INFO if args.verbose else logging.WARNING
|
|
)
|
|
|
|
# Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain.
|
|
assert args.sysroot in args.sysroot_src.parents
|
|
|
|
rust_project = {
|
|
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs),
|
|
"sysroot": str(args.sysroot),
|
|
}
|
|
|
|
json.dump(rust_project, sys.stdout, sort_keys=True, indent=4)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|