Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / build / moz.configure / compile-checks.configure
blob1dddc684dac8042b6b58760a19023e9d14f78598
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
16 #   used.
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
19 #   program.
20 @template
21 def try_compile(
22     includes=None,
23     body="",
24     language="C++",
25     flags=None,
26     check_msg=None,
27     when=None,
28     onerror=lambda: None,
30     compiler = {
31         "C": c_compiler,
32         "C++": cxx_compiler,
33     }[language]
35     return compiler.try_compile(
36         includes, body, flags, check_msg, when=when, onerror=onerror
37     )
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
42 # True
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
48 #   used.
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
51 #   program.
52 @template
53 def try_link(
54     includes=None,
55     body="",
56     language="C++",
57     flags=None,
58     check_msg=None,
59     when=None,
60     onerror=lambda: None,
62     compiler = {
63         "C": c_compiler,
64         "C++": cxx_compiler,
65     }[language]
67     return compiler.try_link(
68         linker_ldflags,
69         includes,
70         body,
71         flags,
72         check_msg,
73         when=when,
74         onerror=onerror,
75     )
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
87 #   used.
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
90 #   header checked for.
91 # - `when` is a depends function that if present will make performing the check
92 #   conditional on the value of that function.
93 @template
94 def check_header(
95     header, language="C++", flags=None, includes=None, when=None, onerror=lambda: None
97     if when is None:
98         when = always
100     if includes:
101         includes = includes[:]
102     else:
103         includes = []
104     includes.append(header)
106     have_header = try_compile(
107         includes=includes,
108         language=language,
109         flags=flags,
110         check_msg="for %s" % header,
111         when=when,
112         onerror=onerror,
113     )
114     header_var = "HAVE_%s" % (
115         header.upper().replace("-", "_").replace("/", "_").replace(".", "_")
116     )
117     set_define(header_var, have_header)
118     return 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
123 # provided headers.
124 # - `headers` are the headers to be checked.
125 # - `kwargs` are keyword arguments passed verbatim to check_header.
128 @template
129 def check_headers(*headers, **kwargs):
130     checks = []
131     for header in headers:
132         checks.append(check_header(header, **kwargs))
133     return checks
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.
151 @template
152 def check_symbol(symbol, language="C", flags=None, when=None, onerror=lambda: None):
153     if when is None:
154         when = always
156     compiler, extern_c = {
157         "C": (c_compiler, ""),
158         "C++": (cxx_compiler, 'extern "C" '),
159     }[language]
161     # Stolen from autoconf 2.13 ; might be irrelevant now, but it doesn't hurt to
162     # keep using a char return type.
163     comment = [
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.  */",
167     ]
169     if flags:
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)
175             if extra_flags:
176                 return extra_flags
177             return base_flags
179     else:
180         flags = check_symbol_flags
182     return compiler.try_run(
183         header=comment + ["%schar %s();" % (extern_c, symbol)],
184         body="%s();" % symbol,
185         flags=flags,
186         check_msg="for %s" % symbol,
187         when=when,
188         onerror=onerror,
189     )
192 # Determine whether to add a given flag to the given lists of flags for C or
193 # C++ compilation.
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"
208 @template
209 def check_and_add_flags(
210     flag,
211     flags_collection,
212     test_flags=(),
213     compiler=None,
214     when=None,
215     check=True,
216     mode="compile",
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,)
224     else:
225         compilers = (c_compiler, cxx_compiler)
227     if when is None:
228         when = always
230     results = []
232     if test_flags:
233         flags = test_flags
234     else:
235         flags = [flag]
237     for c in compilers:
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),
245             }[c]
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),
250             }[c]
251         elif mode == "link":
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),
257             }[c]
259         result = when
261         if check:
263             @depends(c, dependable(flags))
264             def flags(c, 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"]
269                 return flags
271             if mode == "link":
273                 def runner(*args, **kwargs):
274                     if c in (c_compiler, cxx_compiler):
275                         return c.try_link(linker_ldflags, *args, **kwargs)
276                     else:
277                         return c.try_link(host_linker_ldflags, *args, **kwargs)
279                 tool = "linker"
280             else:
281                 runner = c.try_compile
282                 tool = "compiler"
284             result = runner(
285                 flags=flags,
286                 when=result,
287                 check_msg="whether the %s %s supports %s" % (lang, tool, flag),
288             )
290         @depends(result, list_of_flags)
291         def maybe_add_flag(result, list_of_flags):
292             if result:
293                 list_of_flags.append(flag)
295         results.append(result)
297     return tuple(results)
300 @dependable
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
310 #   are tested.
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
315 #   for add_warning().
316 @template
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="):
326         flags = [warning]
327     else:
328         flags = ["-Werror", warning]
330     return check_and_add_flags(
331         warning, warnings_flags, flags, compiler=compiler, when=when, check=check
332     )
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.
344 @template
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.
350 @dependable
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
360 #   are tested.
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
365 #   add_flag().
366 @template
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
372     )
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.
382 @template
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.
388 @dependable
389 def 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
400 #   add_flag().
401 @template
402 def check_and_add_asm_flag(flag, when=None, check=True):
403     return check_and_add_flags(
404         flag,
405         asm_flags,
406         [flag],
407         when=when,
408         check=check,
409         mode="assemble",
410     )
413 # Like the compilation checks above, but for linker flags.
414 @dependable
415 def 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
426 #   add_flag().
427 @template
428 def check_and_add_linker_flag(flag, compiler=None, when=None, check=True):
429     return check_and_add_flags(
430         flag,
431         linker_flags,
432         [flag],
433         when=when,
434         check=check,
435         mode="link",
436     )
439 # Like the compilation checks above, but for linker optimization flags.
440 @dependable
441 def linker_optimize_flags():
442     return namespace(ldflags=[])
445 @template
446 def check_and_add_linker_optimize_flag(flag, compiler=None, when=None, check=True):
447     return check_and_add_flags(
448         flag,
449         linker_optimize_flags,
450         [flag],
451         when=when,
452         check=check,
453         mode="link",
454     )
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.
461 @template
462 def add_linker_flag(flag, compiler=None, when=None):
463     check_and_add_linker_flag(flag, compiler, when, check=False)