blob: 70281e1119656cd841ee4d0aa74705e91e32cf86 [file] [log] [blame]
Geoffrey Martin-Noble4aeb2e62021-05-18 22:42:251# 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
4
5"""Repository rules to configure the terminfo used by LLVM.
6
7Most users should pick one of the explicit rules to configure their use of terminfo
8with LLVM:
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.
12
13If you would like to make your build configurable, you can use
14`llvm_terminfo_from_env`. By default, this will disable terminfo, but will
15inspect the environment variable (most easily set with a `--repo_env` flag to
16the 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
18setting will disable terminfo the same as not setting it at all.
19"""
20
21def _llvm_terminfo_disable_impl(repository_ctx):
22 repository_ctx.template(
23 "BUILD",
24 repository_ctx.attr._disable_build_template,
25 executable = False,
26 )
27
28_terminfo_disable_attrs = {
29 "_disable_build_template": attr.label(
Christian Sigg81d54122021-08-18 07:14:4230 default = Label("//utils/bazel/deps_impl:terminfo_disable.BUILD"),
Geoffrey Martin-Noble4aeb2e62021-05-18 22:42:2531 allow_single_file = True,
32 ),
33}
34
35llvm_terminfo_disable = repository_rule(
36 implementation = _llvm_terminfo_disable_impl,
37 attrs = _terminfo_disable_attrs,
38)
39
40def _find_c_compiler(repository_ctx):
41 """Returns the path to a plausible C compiler.
42
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.
47 """
48 cc_env = repository_ctx.os.environ.get("CC")
49 cc = None
50 if cc_env:
51 if "/" in cc_env:
52 return repository_ctx.path(cc_env)
53 else:
54 return repository_ctx.which(cc_env)
55
56 # Look for Clang, GCC, and the POSIX / UNIX specified C compiler
57 # binaries.
58 for compiler in ["clang", "gcc", "c99", "c89", "cc"]:
59 cc = repository_ctx.which(compiler)
60 if cc:
61 return cc
62
63 return None
64
65def _try_link(repository_ctx, cc, source, linker_flags):
66 """Returns `True` if able to link the source with the linker flag.
67
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
72 work outside of that.
73 """
74 cmd = [
75 cc,
76 # Force discard the linked executable.
77 "-o",
78 "/dev/null",
79 # Leave language detection to the compiler.
80 source,
81 ]
82
83 # The linker flag must be valid for a compiler invocation of the link step,
84 # so just append them to the command.
85 cmd += linker_flags
86 exec_result = repository_ctx.execute(cmd, timeout = 20)
87 return exec_result.return_code == 0
88
89def _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)
93 return
94
95 if len(repository_ctx.attr.system_linkopts) > 0:
96 linkopts = repository_ctx.attr.system_linkopts
97 else:
98 required = repository_ctx.attr.system_required
99
100 # Find a C compiler we can use to detect viable linkopts on this system.
101 cc = _find_c_compiler(repository_ctx)
102 if not cc:
103 if required:
104 fail("Failed to find a C compiler executable")
105 else:
106 _llvm_terminfo_disable_impl(repository_ctx)
107 return
108
109 # Get the source file we use to detect successful linking of terminfo.
110 source = repository_ctx.path(repository_ctx.attr._terminfo_test_source)
111
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]
jonmeow4691f002022-01-26 19:02:20116 linkopts = None
Geoffrey Martin-Noble4aeb2e62021-05-18 22:42:25117
118 # For each candidate, try to use it to link our test source file.
119 for linkopts_candidate in linkopts_candidates:
120 if _try_link(repository_ctx, cc, source, linkopts_candidate):
121 linkopts = linkopts_candidate
122 break
123
124 # If we never found a viable linkopts candidate, either error or disable
125 # terminfo for LLVM.
126 if not linkopts:
127 if required:
128 fail("Failed to detect which linkopt would successfully provide the " +
129 "necessary terminfo functionality")
130 else:
131 _llvm_terminfo_disable_impl(repository_ctx)
132 return
133
134 repository_ctx.template(
135 "BUILD",
136 repository_ctx.attr._system_build_template,
137 substitutions = {
138 "{TERMINFO_LINKOPTS}": str(linkopts),
139 },
140 executable = False,
141 )
142
143def _merge_attrs(attrs_list):
144 attrs = {}
145 for input_attrs in attrs_list:
146 attrs.update(input_attrs)
147 return attrs
148
149_terminfo_system_attrs = _merge_attrs([_terminfo_disable_attrs, {
150 "_system_build_template": attr.label(
Christian Sigg81d54122021-08-18 07:14:42151 default = Label("//utils/bazel/deps_impl:terminfo_system.BUILD"),
Geoffrey Martin-Noble4aeb2e62021-05-18 22:42:25152 allow_single_file = True,
153 ),
154 "_terminfo_test_source": attr.label(
Christian Sigg81d54122021-08-18 07:14:42155 default = Label("//utils/bazel/deps_impl:terminfo_test.c"),
Geoffrey Martin-Noble4aeb2e62021-05-18 22:42:25156 allow_single_file = True,
157 ),
158 "candidate_system_linkopts": attr.string_list(
159 default = [
160 "-lterminfo",
161 "-ltinfo",
162 "-lcurses",
163 "-lncurses",
164 "-lncursesw",
165 ],
166 doc = "Candidate linkopts to test and see if they can link " +
167 "successfully.",
168 ),
169 "system_required": attr.bool(
170 default = False,
171 doc = "Require that one of the candidates is detected successfully on POSIX platforms where it is needed.",
172 ),
173 "system_linkopts": attr.string_list(
174 default = [],
175 doc = "If non-empty, a specific array of linkopts to use to " +
176 "successfully link against the terminfo library. No " +
177 "detection is performed if this option is provided, it " +
178 "directly forces the use of these link options. No test is " +
179 "run to determine if they are valid or work correctly either.",
180 ),
181}])
182
183llvm_terminfo_system = repository_rule(
184 implementation = _llvm_terminfo_system_impl,
185 configure = True,
186 local = True,
187 attrs = _terminfo_system_attrs,
188)
189
190def _llvm_terminfo_from_env_impl(repository_ctx):
191 terminfo_strategy = repository_ctx.os.environ.get("BAZEL_LLVM_TERMINFO_STRATEGY")
192 if terminfo_strategy == "system":
193 _llvm_terminfo_system_impl(repository_ctx)
194 else:
195 _llvm_terminfo_disable_impl(repository_ctx)
196
197llvm_terminfo_from_env = repository_rule(
198 implementation = _llvm_terminfo_from_env_impl,
199 configure = True,
200 local = True,
201 attrs = _merge_attrs([_terminfo_disable_attrs, _terminfo_system_attrs]),
202 environ = ["BAZEL_LLVM_TERMINFO_STRATEGY", "CC"],
203)