|
14 | 14 |
|
15 | 15 | load("@bazel_skylib//lib:paths.bzl", "paths") |
16 | 16 |
|
| 17 | +def _textual_header(file, *, include_prefixes, execroot_prefix): |
| 18 | + path = file.path |
| 19 | + for include_prefix in include_prefixes: |
| 20 | + if path.startswith(include_prefix): |
| 21 | + return " textual header \"{}{}\"".format(execroot_prefix, path) |
| 22 | + |
| 23 | + # The file is not under any of the include prefixes, |
| 24 | + return None |
| 25 | + |
| 26 | +def _umbrella_submodule(path): |
| 27 | + return """ |
| 28 | + module "{path}" {{ |
| 29 | + umbrella "{path}" |
| 30 | + }}""".format(path = path) |
| 31 | + |
17 | 32 | def _system_module_map(ctx): |
18 | 33 | module_map = ctx.actions.declare_file(ctx.attr.name + ".modulemap") |
19 | 34 |
|
20 | | - dirs = [] |
21 | | - non_hermetic = False |
22 | | - for dir in ctx.attr.cxx_builtin_include_directories: |
23 | | - if ctx.attr.sysroot_path and dir.startswith("%sysroot%"): |
24 | | - dir = ctx.attr.sysroot_path + dir[len("%sysroot%"):] |
25 | | - if dir.startswith("/"): |
26 | | - non_hermetic = True |
27 | | - dirs.append(paths.normalize(dir)) |
28 | | - |
29 | | - # If the action references a file outside of the execroot, it isn't safe to |
30 | | - # cache or run remotely. |
31 | | - execution_requirements = {} |
32 | | - if non_hermetic: |
33 | | - execution_requirements = { |
34 | | - "no-cache": "", |
35 | | - "no-remote": "", |
36 | | - } |
| 35 | + absolute_path_dirs = [] |
| 36 | + relative_include_prefixes = [] |
| 37 | + for include_dir in ctx.attr.cxx_builtin_include_directories: |
| 38 | + if ctx.attr.sysroot_path and include_dir.startswith("%sysroot%"): |
| 39 | + include_dir = ctx.attr.sysroot_path + include_dir[len("%sysroot%"):] |
| 40 | + include_dir = paths.normalize(include_dir) |
| 41 | + if include_dir.startswith("/"): |
| 42 | + absolute_path_dirs.append(include_dir) |
| 43 | + else: |
| 44 | + relative_include_prefixes.append(include_dir + "/") |
37 | 45 |
|
38 | 46 | # The builtin include directories are relative to the execroot, but the |
39 | 47 | # paths in the module map must be relative to the directory that contains |
40 | 48 | # the module map. |
41 | 49 | execroot_prefix = (module_map.dirname.count("/") + 1) * "../" |
| 50 | + textual_header_closure = lambda file: _textual_header( |
| 51 | + file, |
| 52 | + include_prefixes = relative_include_prefixes, |
| 53 | + execroot_prefix = execroot_prefix, |
| 54 | + ) |
42 | 55 |
|
43 | | - ctx.actions.run_shell( |
44 | | - outputs = [module_map], |
45 | | - inputs = ctx.attr.cxx_builtin_include_files[DefaultInfo].files, |
46 | | - command = """ |
47 | | -{tool} "$@" > {module_map} |
48 | | -""".format( |
49 | | - tool = ctx.executable._generate_system_module_map.path, |
50 | | - module_map = module_map.path, |
51 | | - ), |
52 | | - arguments = dirs, |
53 | | - tools = [ctx.executable._generate_system_module_map], |
54 | | - env = {"EXECROOT_PREFIX": execroot_prefix}, |
55 | | - execution_requirements = execution_requirements, |
56 | | - mnemonic = "LLVMSystemModuleMap", |
57 | | - progress_message = "Generating system module map", |
| 56 | + template_dict = ctx.actions.template_dict() |
| 57 | + template_dict.add_joined( |
| 58 | + "%textual_headers%", |
| 59 | + ctx.attr.cxx_builtin_include_files[DefaultInfo].files, |
| 60 | + join_with = "\n", |
| 61 | + map_each = textual_header_closure, |
| 62 | + allow_closure = True, |
| 63 | + ) |
| 64 | + template_dict.add_joined( |
| 65 | + "%umbrella_submodules%", |
| 66 | + depset(absolute_path_dirs), |
| 67 | + join_with = "\n", |
| 68 | + map_each = _umbrella_submodule, |
| 69 | + ) |
| 70 | + |
| 71 | + ctx.actions.expand_template( |
| 72 | + template = ctx.file._module_map_template, |
| 73 | + output = module_map, |
| 74 | + computed_substitutions = template_dict, |
58 | 75 | ) |
59 | 76 | return DefaultInfo(files = depset([module_map])) |
60 | 77 |
|
61 | 78 | system_module_map = rule( |
62 | | - doc = """Generates a Clang module map for the toolchain and sysroot headers.""", |
| 79 | + doc = """Generates a Clang module map for the toolchain and sysroot headers. |
| 80 | +
|
| 81 | + Files under the configured built-in include directories that are managed by |
| 82 | + Bazel are included as textual headers. All directories referenced by |
| 83 | + absolute paths are included as umbrella submodules.""", |
63 | 84 | implementation = _system_module_map, |
64 | 85 | attrs = { |
65 | 86 | "cxx_builtin_include_files": attr.label(mandatory = True), |
66 | 87 | "cxx_builtin_include_directories": attr.string_list(mandatory = True), |
67 | 88 | "sysroot_path": attr.string(), |
68 | | - "_generate_system_module_map": attr.label( |
69 | | - default = ":generate_system_module_map.sh", |
| 89 | + "_module_map_template": attr.label( |
| 90 | + default = "template.modulemap", |
70 | 91 | allow_single_file = True, |
71 | | - cfg = "exec", |
72 | | - executable = True, |
73 | 92 | ), |
74 | 93 | }, |
75 | 94 | ) |
0 commit comments