1 # This file is licensed under the Apache License v2.0 with LLVM Exceptions.
2 # See https://llvm.org/LICENSE.txt for license information.
3 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 """Repository rules to configure the terminfo used by LLVM.
7 Most users should pick one of the explicit rules to configure their use of terminfo
9 - `llvm_terminfo_system` will detect and link against a terminfo-implementing
10 system library (non-hermetically).
11 - 'llvm_terminfo_disable` will disable terminfo completely.
13 If you would like to make your build configurable, you can use
14 `llvm_terminfo_from_env`. By default, this will disable terminfo, but will
15 inspect the environment variable (most easily set with a `--repo_env` flag to
16 the Bazel invocation) `BAZEL_LLVM_TERMINFO_STRATEGY`. If it is set to
17 `system` then it will behave the same as `llvm_terminfo_system`. Any other
18 setting will disable terminfo the same as not setting it at all.
21 def _llvm_terminfo_disable_impl(repository_ctx):
22 repository_ctx.template(
24 repository_ctx.attr._disable_build_template,
28 _terminfo_disable_attrs = {
29 "_disable_build_template": attr.label(
30 default = Label("//utils/bazel/deps_impl:terminfo_disable.BUILD"),
31 allow_single_file = True,
35 llvm_terminfo_disable = repository_rule(
36 implementation = _llvm_terminfo_disable_impl,
37 attrs = _terminfo_disable_attrs,
40 def _find_c_compiler(repository_ctx):
41 """Returns the path to a plausible C compiler.
43 This routine will only reliably work on roughly POSIX-y systems as it
44 ultimately falls back on the `cc` binary. Fortunately, the thing we are
45 trying to use it for (detecting if a trivial source file can compile and
46 link against a particular library) requires very little.
48 cc_env = repository_ctx.os.environ.get("CC")
52 return repository_ctx.path(cc_env)
54 return repository_ctx.which(cc_env)
56 # Look for Clang, GCC, and the POSIX / UNIX specified C compiler
58 for compiler in ["clang", "gcc", "c99", "c89", "cc"]:
59 cc = repository_ctx.which(compiler)
65 def _try_link(repository_ctx, cc, source, linker_flags):
66 """Returns `True` if able to link the source with the linker flag.
68 Given a source file that contains references to library routines, this
69 will check that when linked with the provided linker flag, those
70 references are successfully resolved. This routine assumes a generally
71 POSIX-y and GCC-ish compiler and environment and shouldn't be expected to
76 # Force discard the linked executable.
79 # Leave language detection to the compiler.
83 # The linker flag must be valid for a compiler invocation of the link step,
84 # so just append them to the command.
86 exec_result = repository_ctx.execute(cmd, timeout = 20)
87 return exec_result.return_code == 0
89 def _llvm_terminfo_system_impl(repository_ctx):
90 # LLVM doesn't need terminfo support on Windows, so just disable it.
91 if repository_ctx.os.name.lower().find("windows") != -1:
92 _llvm_terminfo_disable_impl(repository_ctx)
95 if len(repository_ctx.attr.system_linkopts) > 0:
96 linkopts = repository_ctx.attr.system_linkopts
98 required = repository_ctx.attr.system_required
100 # Find a C compiler we can use to detect viable linkopts on this system.
101 cc = _find_c_compiler(repository_ctx)
104 fail("Failed to find a C compiler executable")
106 _llvm_terminfo_disable_impl(repository_ctx)
109 # Get the source file we use to detect successful linking of terminfo.
110 source = repository_ctx.path(repository_ctx.attr._terminfo_test_source)
112 # Collect the candidate linkopts and wrap them into a list. Ideally,
113 # these would be provided as lists, but Bazel doesn't currently
114 # support that. See: https://github.com/bazelbuild/bazel/issues/12178
115 linkopts_candidates = [[x] for x in repository_ctx.attr.candidate_system_linkopts]
117 # For each candidate, try to use it to link our test source file.
118 for linkopts_candidate in linkopts_candidates:
119 if _try_link(repository_ctx, cc, source, linkopts_candidate):
120 linkopts = linkopts_candidate
123 # If we never found a viable linkopts candidate, either error or disable
127 fail("Failed to detect which linkopt would successfully provide the " +
128 "necessary terminfo functionality")
130 _llvm_terminfo_disable_impl(repository_ctx)
133 repository_ctx.template(
135 repository_ctx.attr._system_build_template,
137 "{TERMINFO_LINKOPTS}": str(linkopts),
142 def _merge_attrs(attrs_list):
144 for input_attrs in attrs_list:
145 attrs.update(input_attrs)
148 _terminfo_system_attrs = _merge_attrs([_terminfo_disable_attrs, {
149 "_system_build_template": attr.label(
150 default = Label("//utils/bazel/deps_impl:terminfo_system.BUILD"),
151 allow_single_file = True,
153 "_terminfo_test_source": attr.label(
154 default = Label("//utils/bazel/deps_impl:terminfo_test.c"),
155 allow_single_file = True,
157 "candidate_system_linkopts": attr.string_list(
165 doc = "Candidate linkopts to test and see if they can link " +
168 "system_required": attr.bool(
170 doc = "Require that one of the candidates is detected successfully on POSIX platforms where it is needed.",
172 "system_linkopts": attr.string_list(
174 doc = "If non-empty, a specific array of linkopts to use to " +
175 "successfully link against the terminfo library. No " +
176 "detection is performed if this option is provided, it " +
177 "directly forces the use of these link options. No test is " +
178 "run to determine if they are valid or work correctly either.",
182 llvm_terminfo_system = repository_rule(
183 implementation = _llvm_terminfo_system_impl,
186 attrs = _terminfo_system_attrs,
189 def _llvm_terminfo_from_env_impl(repository_ctx):
190 terminfo_strategy = repository_ctx.os.environ.get("BAZEL_LLVM_TERMINFO_STRATEGY")
191 if terminfo_strategy == "system":
192 _llvm_terminfo_system_impl(repository_ctx)
194 _llvm_terminfo_disable_impl(repository_ctx)
196 llvm_terminfo_from_env = repository_rule(
197 implementation = _llvm_terminfo_from_env_impl,
200 attrs = _merge_attrs([_terminfo_disable_attrs, _terminfo_system_attrs]),
201 environ = ["BAZEL_LLVM_TERMINFO_STRATEGY", "CC"],