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 # Generates a test program and attempts to link it. In case of failure, the
41 # resulting check will return None. If the link succeeds, it will return
43 # - `includes` are the includes (as file names) that will appear at the top of
44 # the generated test program.
45 # - `body` is the code that will appear in the main function of the generated
46 # test program. `return 0;` is appended to the function body automatically.
47 # - `language` is the language selection, so that the appropriate compiler is
49 # - `flags` are the flags to be passed to the compiler.
50 # - `check_msg` is the message to be printed to accompany compiling the test
67 return compiler.try_link(
78 # Checks for the presence of the given header on the target system by compiling
79 # a test program including that header. The return value of the template is a
80 # check function returning True if the header is present, and None if it is not.
81 # The value of this check function is also used to set a variable (with set_define)
82 # corresponding to the checked header. For instance, HAVE_MALLOC_H will be set in
83 # defines if check_header if called with 'malloc.h' as input and malloc.h is
84 # present on the target.
85 # - `header` is the header, as a file name, to check for.
86 # - `language` is the language selection, so that the appropriate compiler is
88 # - `flags` are the flags to be passed to the compiler, in addition to `-c`.
89 # - `includes` are additional includes, as file names, to appear before the
91 # - `when` is a depends function that if present will make performing the check
92 # conditional on the value of that function.
95 header, language="C++", flags=None, includes=None, when=None, onerror=lambda: None
101 includes = includes[:]
104 includes.append(header)
106 have_header = try_compile(
110 check_msg="for %s" % header,
114 header_var = "HAVE_%s" % (
115 header.upper().replace("-", "_").replace("/", "_").replace(".", "_")
117 set_define(header_var, have_header)
121 # A convenience wrapper for check_header for checking multiple headers.
122 # returns an array of the resulting checks in order corresponding to the
124 # - `headers` are the headers to be checked.
125 # - `kwargs` are keyword arguments passed verbatim to check_header.
129 def check_headers(*headers, **kwargs):
131 for header in headers:
132 checks.append(check_header(header, **kwargs))
136 @depends(linker_ldflags, target.kernel)
137 def check_symbol_flags(linker_ldflags, kernel):
138 if kernel == "WINNT":
139 # The build doesn't use the compiler to link things as of writing,
140 # but some compilation checks do. When using clang-cl, the only
141 # linker we really support is lld.link, but clang-cl defaults to
142 # link.exe (even when cross-compiling). So we force the use of
143 # lld.link for the linkage checks.
144 return ["-fuse-ld=lld"]
145 return linker_ldflags
148 # Checks for the presence of the given symbol on the target system by compiling
149 # a test program. The return value of the template is a check function
150 # returning True if the symbol can be found, and None if it is not.
152 def check_symbol(symbol, language="C", flags=None, when=None, onerror=lambda: None):
156 compiler, extern_c = {
157 "C": (c_compiler, ""),
158 "C++": (cxx_compiler, 'extern "C" '),
161 # Stolen from autoconf 2.13 ; might be irrelevant now, but it doesn't hurt to
162 # keep using a char return type.
164 "/* Override any gcc2 internal prototype to avoid an error. */",
165 "/* We use char because int might match the return type of a gcc2",
166 " builtin and then its argument prototype would still apply. */",
171 @depends(check_symbol_flags, dependable(flags))
172 def flags(base_flags, extra_flags):
173 if base_flags and extra_flags:
174 return base_flags + list(extra_flags)
180 flags = check_symbol_flags
182 return compiler.try_run(
183 header=comment + ["%schar %s();" % (extern_c, symbol)],
184 body="%s();" % symbol,
186 check_msg="for %s" % symbol,
192 # Determine whether to add a given flag to the given lists of flags for C or
194 # - `flag` is the flag to test
195 # - `flags_collection` is a @depends function for a namespace of lists of
196 # C/C++ compiler flags to add to.
197 # - `test_flags` is a list of flags to pass to the compiler instead of merely
198 # passing `flag`. This is especially useful for checking warning flags. If
199 # this list is empty, `flag` will be passed on its own.
200 # - `compiler` (optional) is the compiler to test against (c_compiler or
201 # cxx_compiler, from toolchain.configure). When omitted, both compilers
202 # are tested; the list of flags added to is dependent on the compiler tested.
203 # - `when` (optional) is a @depends function or option name conditioning
204 # when the warning flag is wanted.
205 # - `check`, when not set, skips checking whether the flag is supported and
206 # adds it to the list of flags unconditionally.
207 # - `mode`, can be "compile", "link" or "assemble"
209 def check_and_add_flags(
218 assert mode in ("compile", "link", "assemble")
220 if compiler is not None:
221 compilers = (compiler,)
222 elif mode in ("link", "assemble"):
223 compilers = (c_compiler,)
225 compilers = (c_compiler, cxx_compiler)
238 assert c in {c_compiler, cxx_compiler, host_c_compiler, host_cxx_compiler}
239 if mode == "compile":
240 lang, list_of_flags = {
241 c_compiler: ("C", flags_collection.cflags),
242 cxx_compiler: ("C++", flags_collection.cxxflags),
243 host_c_compiler: ("host C", flags_collection.host_cflags),
244 host_cxx_compiler: ("host C++", flags_collection.host_cxxflags),
246 elif mode == "assemble":
247 lang, list_of_flags = {
248 c_compiler: ("C", flags_collection.asflags),
249 host_c_compiler: ("host C", flags_collection.host_asflags),
252 lang, list_of_flags = {
253 c_compiler: ("C", flags_collection.ldflags),
254 cxx_compiler: ("C++", flags_collection.ldflags),
255 host_c_compiler: ("host C", flags_collection.host_ldflags),
256 host_cxx_compiler: ("host C++", flags_collection.host_ldflags),
263 @depends(c, dependable(flags))
265 # Don't error out just because clang complains about other things.
266 if c.type in ("clang", "clang-cl"):
267 flags += ["-Wno-error=unused-command-line-argument"]
273 def runner(*args, **kwargs):
274 if c in (c_compiler, cxx_compiler):
275 return c.try_link(linker_ldflags, *args, **kwargs)
277 return c.try_link(host_linker_ldflags, *args, **kwargs)
281 runner = c.try_compile
287 check_msg="whether the %s %s supports %s" % (lang, tool, flag),
290 @depends(result, list_of_flags)
291 def maybe_add_flag(result, list_of_flags):
293 list_of_flags.append(flag)
295 results.append(result)
297 return tuple(results)
301 def warnings_flags():
302 return namespace(cflags=[], cxxflags=[], host_cflags=[], host_cxxflags=[])
305 # Tests whether GCC or clang support the given warning flag, and if it is,
306 # add it to the list of warning flags for the build.
307 # - `warning` is the warning flag (e.g. -Wfoo)
308 # - `compiler` (optional) is the compiler to test against (c_compiler or
309 # cxx_compiler, from toolchain.configure). When omitted, both compilers
311 # - `when` (optional) is a @depends function or option name conditioning
312 # when the warning flag is wanted.
313 # - `check`, when not set, skips checking whether the flag is supported and
314 # adds it to the list of warning flags unconditionally. This is only meant
317 def check_and_add_warning(warning, compiler=None, when=None, check=True):
318 # GCC and clang will fail if given an unknown warning option like
319 # -Wfoobar. But later versions won't fail if given an unknown negated
320 # warning option like -Wno-foobar. So when we are checking for support
321 # of a negated warning option, we actually test the positive form, but
322 # add the negated form to the flags variable.
323 if warning.startswith("-Wno-") and not warning.startswith("-Wno-error="):
324 flags = ["-Werror", "-W" + warning[5:]]
325 elif warning.startswith("-Werror="):
328 flags = ["-Werror", warning]
330 return check_and_add_flags(
331 warning, warnings_flags, flags, compiler=compiler, when=when, check=check
335 # Add the given warning to the list of warning flags for the build.
336 # - `warning` is the warning flag (e.g. -Wfoo)
337 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
338 # cxx_compiler, from toolchain.configure). When omitted, the warning flag
339 # is added for both compilers.
340 # - `when` (optional) is a @depends function or option name conditioning
341 # when the warning flag is wanted.
345 def add_warning(warning, compiler=None, when=None):
346 check_and_add_warning(warning, compiler, when, check=False)
349 # Like the warning checks above, but for general compilation flags.
351 def compilation_flags():
352 return namespace(cflags=[], cxxflags=[], host_cflags=[], host_cxxflags=[])
355 # Tests whether GCC or clang support the given compilation flag; if the flag
356 # is supported, add it to the list of compilation flags for the build.
357 # - `flag` is the flag to test
358 # - `compiler` (optional) is the compiler to test against (c_compiler or
359 # cxx_compiler, from toolchain.configure). When omitted, both compilers
361 # - `when` (optional) is a @depends function or option name conditioning
362 # when the warning flag is wanted.
363 # - `check`, when not set, skips checking whether the flag is supported and
364 # adds it to the list of flags unconditionally. This is only meant for
367 def check_and_add_flag(flag, compiler=None, when=None, check=True):
368 flags = ["-Werror", flag]
370 return check_and_add_flags(
371 flag, compilation_flags, flags, compiler=compiler, when=when, check=check
375 # Add the given flag to the list of flags for the build.
376 # - `flag` is the flag (e.g. -fno-sized-deallocation)
377 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
378 # cxx_compiler, from toolchain.configure). When omitted, the flag is added
379 # for both compilers.
380 # - `when` (optional) is a @depends function or option name conditioning
381 # when the flag is wanted.
383 def add_flag(warning, compiler=None, when=None):
384 check_and_add_flag(warning, compiler, when, check=False)
387 # Like the compilation checks above, but for asm flags.
390 return namespace(asflags=[], host_asflags=[])
393 # Tests the given assembler flag is supported; if the flag
394 # is supported, add it to the list of compilation flags for the build.
395 # - `flag` is the flag to test
396 # - `when` (optional) is a @depends function or option name conditioning
397 # when the warning flag is wanted.
398 # - `check`, when not set, skips checking whether the flag is supported and
399 # adds it to the list of flags unconditionally. This is only meant for
402 def check_and_add_asm_flag(flag, when=None, check=True):
403 return check_and_add_flags(
413 # Like the compilation checks above, but for linker flags.
416 return namespace(ldflags=[], host_ldflags=[])
419 # Tests the given linker flag is supported; if the flag
420 # is supported, add it to the list of compilation flags for the build.
421 # - `flag` is the flag to test
422 # - `when` (optional) is a @depends function or option name conditioning
423 # when the warning flag is wanted.
424 # - `check`, when not set, skips checking whether the flag is supported and
425 # adds it to the list of flags unconditionally. This is only meant for
428 def check_and_add_linker_flag(flag, compiler=None, when=None, check=True):
429 return check_and_add_flags(
439 # Like the compilation checks above, but for linker optimization flags.
441 def linker_optimize_flags():
442 return namespace(ldflags=[])
446 def check_and_add_linker_optimize_flag(flag, compiler=None, when=None, check=True):
447 return check_and_add_flags(
449 linker_optimize_flags,
457 # Add the given flag to the list of linker flags for the build.
458 # - `flag` is the flag (e.g. -fno-sized-deallocation)
459 # - `when` (optional) is a @depends function or option name conditioning
460 # when the flag is wanted.
462 def add_linker_flag(flag, compiler=None, when=None):
463 check_and_add_linker_flag(flag, compiler, when, check=False)