1 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
2 # vim: set filetype=python:
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 # Generates a test program and attempts to compile it. In case of failure, the
9 # resulting check will return None. If the test program succeeds, it will return
10 # the output of the test program.
11 # - `includes` are the includes (as file names) that will appear at the top of
12 # the generated test program.
13 # - `body` is the code that will appear in the main function of the generated
14 # test program. `return 0;` is appended to the function body automatically.
15 # - `language` is the language selection, so that the appropriate compiler is
17 # - `flags` are the flags to be passed to the compiler, in addition to `-c`.
18 # - `check_msg` is the message to be printed to accompany compiling the test
35 return compiler.try_compile(
36 includes, body, flags, check_msg, when=when, onerror=onerror
40 # Checks for the presence of the given header on the target system by compiling
41 # a test program including that header. The return value of the template is a
42 # check function returning True if the header is present, and None if it is not.
43 # The value of this check function is also used to set a variable (with set_define)
44 # corresponding to the checked header. For instance, HAVE_MALLOC_H will be set in
45 # defines if check_header if called with 'malloc.h' as input and malloc.h is
46 # present on the target.
47 # - `header` is the header, as a file name, to check for.
48 # - `language` is the language selection, so that the appropriate compiler is
50 # - `flags` are the flags to be passed to the compiler, in addition to `-c`.
51 # - `includes` are additional includes, as file names, to appear before the
53 # - `when` is a depends function that if present will make performing the check
54 # conditional on the value of that function.
57 header, language="C++", flags=None, includes=None, when=None, onerror=lambda: None
63 includes = includes[:]
66 includes.append(header)
68 have_header = try_compile(
72 check_msg="for %s" % header,
76 header_var = "HAVE_%s" % (
77 header.upper().replace("-", "_").replace("/", "_").replace(".", "_")
79 set_define(header_var, have_header)
83 # A convenience wrapper for check_header for checking multiple headers.
84 # returns an array of the resulting checks in order corresponding to the
86 # - `headers` are the headers to be checked.
87 # - `kwargs` are keyword arguments passed verbatim to check_header.
91 def check_headers(*headers, **kwargs):
93 for header in headers:
94 checks.append(check_header(header, **kwargs))
98 @depends(linker_ldflags, target.kernel)
99 def check_symbol_flags(linker_ldflags, kernel):
100 if kernel == "WINNT":
101 # The build doesn't use the compiler to link things as of writing,
102 # but some compilation checks do. When using clang-cl, the only
103 # linker we really support is lld.link, but clang-cl defaults to
104 # link.exe (even when cross-compiling). So we force the use of
105 # lld.link for the linkage checks.
106 return ["-fuse-ld=lld"]
107 return linker_ldflags
110 # Checks for the presence of the given symbol on the target system by compiling
111 # a test program. The return value of the template is a check function
112 # returning True if the symbol can be found, and None if it is not.
114 def check_symbol(symbol, language="C", flags=None, when=None, onerror=lambda: None):
118 compiler, extern_c = {
119 "C": (c_compiler, ""),
120 "C++": (cxx_compiler, 'extern "C" '),
123 # Stolen from autoconf 2.13 ; might be irrelevant now, but it doesn't hurt to
124 # keep using a char return type.
126 "/* Override any gcc2 internal prototype to avoid an error. */",
127 "/* We use char because int might match the return type of a gcc2",
128 " builtin and then its argument prototype would still apply. */",
133 @depends(check_symbol_flags, dependable(flags))
134 def flags(base_flags, extra_flags):
135 if base_flags and extra_flags:
136 return base_flags + list(extra_flags)
142 flags = check_symbol_flags
144 return compiler.try_run(
145 header=comment + ["%schar %s();" % (extern_c, symbol)],
146 body="%s();" % symbol,
148 check_msg="for %s" % symbol,
154 # Determine whether to add a given flag to the given lists of flags for C or
156 # - `flag` is the flag to test
157 # - `flags_collection` is a @depends function for a namespace of lists of
158 # C/C++ compiler flags to add to.
159 # - `test_flags` is a list of flags to pass to the compiler instead of merely
160 # passing `flag`. This is especially useful for checking warning flags. If
161 # this list is empty, `flag` will be passed on its own.
162 # - `compiler` (optional) is the compiler to test against (c_compiler or
163 # cxx_compiler, from toolchain.configure). When omitted, both compilers
164 # are tested; the list of flags added to is dependent on the compiler tested.
165 # - `when` (optional) is a @depends function or option name conditioning
166 # when the warning flag is wanted.
167 # - `check`, when not set, skips checking whether the flag is supported and
168 # adds it to the list of flags unconditionally.
170 def check_and_add_flags(
171 flag, flags_collection, test_flags, compiler=None, when=None, check=True
173 if compiler is not None:
174 compilers = (compiler,)
176 compilers = (c_compiler, cxx_compiler)
189 assert c in {c_compiler, cxx_compiler, host_c_compiler, host_cxx_compiler}
190 lang, list_of_flags = {
191 c_compiler: ("C", flags_collection.cflags),
192 cxx_compiler: ("C++", flags_collection.cxxflags),
193 host_c_compiler: ("host C", flags_collection.host_cflags),
194 host_cxx_compiler: ("host C++", flags_collection.host_cxxflags),
201 @depends(c, dependable(flags))
203 # Don't error out just because clang complains about other things.
204 if c.type in ("clang", "clang-cl"):
205 flags += ["-Wno-error=unused-command-line-argument"]
209 result = c.try_compile(
212 check_msg="whether the %s compiler supports %s" % (lang, flag),
215 @depends(result, list_of_flags)
216 def maybe_add_flag(result, list_of_flags):
218 list_of_flags.append(flag)
220 results.append(result)
222 return tuple(results)
226 def warnings_flags():
227 return namespace(cflags=[], cxxflags=[], host_cflags=[], host_cxxflags=[])
230 # Tests whether GCC or clang support the given warning flag, and if it is,
231 # add it to the list of warning flags for the build.
232 # - `warning` is the warning flag (e.g. -Wfoo)
233 # - `compiler` (optional) is the compiler to test against (c_compiler or
234 # cxx_compiler, from toolchain.configure). When omitted, both compilers
236 # - `when` (optional) is a @depends function or option name conditioning
237 # when the warning flag is wanted.
238 # - `check`, when not set, skips checking whether the flag is supported and
239 # adds it to the list of warning flags unconditionally. This is only meant
242 def check_and_add_warning(warning, compiler=None, when=None, check=True):
243 # GCC and clang will fail if given an unknown warning option like
244 # -Wfoobar. But later versions won't fail if given an unknown negated
245 # warning option like -Wno-foobar. So when we are checking for support
246 # of a negated warning option, we actually test the positive form, but
247 # add the negated form to the flags variable.
248 if warning.startswith("-Wno-") and not warning.startswith("-Wno-error="):
249 flags = ["-Werror", "-W" + warning[5:]]
250 elif warning.startswith("-Werror="):
253 flags = ["-Werror", warning]
255 return check_and_add_flags(
256 warning, warnings_flags, flags, compiler=compiler, when=when, check=check
260 # Add the given warning to the list of warning flags for the build.
261 # - `warning` is the warning flag (e.g. -Wfoo)
262 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
263 # cxx_compiler, from toolchain.configure). When omitted, the warning flag
264 # is added for both compilers.
265 # - `when` (optional) is a @depends function or option name conditioning
266 # when the warning flag is wanted.
270 def add_warning(warning, compiler=None, when=None):
271 check_and_add_warning(warning, compiler, when, check=False)
274 # Like the warning checks above, but for general compilation flags.
276 def compilation_flags():
277 return namespace(cflags=[], cxxflags=[], host_cflags=[], host_cxxflags=[])
280 # Tests whether GCC or clang support the given compilation flag; if the flag
281 # is supported, add it to the list of compilation flags for the build.
282 # - `flag` is the flag to test
283 # - `compiler` (optional) is the compiler to test against (c_compiler or
284 # cxx_compiler, from toolchain.configure). When omitted, both compilers
286 # - `when` (optional) is a @depends function or option name conditioning
287 # when the warning flag is wanted.
288 # - `check`, when not set, skips checking whether the flag is supported and
289 # adds it to the list of flags unconditionally. This is only meant for
292 def check_and_add_flag(flag, compiler=None, when=None, check=True):
293 flags = ["-Werror", flag]
295 return check_and_add_flags(
296 flag, compilation_flags, flags, compiler=compiler, when=when, check=check
300 # Add the given flag to the list of flags for the build.
301 # - `flag` is the flag (e.g. -fno-sized-deallocation)
302 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
303 # cxx_compiler, from toolchain.configure). When omitted, the flag is added
304 # for both compilers.
305 # - `when` (optional) is a @depends function or option name conditioning
306 # when the flag is wanted.
308 def add_flag(warning, compiler=None, when=None):
309 check_and_add_flag(warning, compiler, when, check=False)