Bug 1904139 - Don't re-initialize platform font list from GetDefaultFont. r=jfkthame
[gecko.git] / build / moz.configure / compile-checks.configure
blobd50ea2e8bd1690bea4b4e67d35b7ca3d49fc7270
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(
153     symbol, language="C", flags=None, msg_extra="", when=None, onerror=lambda: None
155     if when is None:
156         when = always
158     compiler, extern_c = {
159         "C": (c_compiler, ""),
160         "C++": (cxx_compiler, 'extern "C" '),
161     }[language]
163     # Stolen from autoconf 2.13 ; might be irrelevant now, but it doesn't hurt to
164     # keep using a char return type.
165     comment = [
166         "/* Override any gcc2 internal prototype to avoid an error.  */",
167         "/* We use char because int might match the return type of a gcc2",
168         "    builtin and then its argument prototype would still apply.  */",
169     ]
171     if flags:
173         @depends(check_symbol_flags, dependable(flags))
174         def flags(base_flags, extra_flags):
175             if base_flags and extra_flags:
176                 return base_flags + list(extra_flags)
177             if extra_flags:
178                 return extra_flags
179             return base_flags
181     else:
182         flags = check_symbol_flags
184     return compiler.try_run(
185         header=comment + ["%schar %s();" % (extern_c, symbol)],
186         body="%s();" % symbol,
187         flags=flags,
188         check_msg="for %s%s" % (symbol, msg_extra),
189         when=when,
190         onerror=onerror,
191     )
194 # Checks for the presence of the given symbol in the given library on the
195 # target system by compiling a test program. The return value of the template
196 # is a check function returning True if the symbol can be found, and None if it
197 # is not.
198 @template
199 def check_symbol_in_lib(libname, symbol, language="C", when=None, onerror=lambda: None):
200     flag = f"-l{libname}"
201     have_symbol = check_symbol(
202         symbol,
203         flags=[flag],
204         msg_extra=f" in {flag}",
205         language=language,
206         when=when,
207         onerror=onerror,
208     )
210     return have_symbol
213 # Same as check_symbol_in_lib but iteratively try libraries in the given libnames until it
214 # finds one that contains the given symbol.
215 # Returns None if not found, empty tuple if None is in the given libnames and
216 # the symbol is present without library linked, a singleton containing the
217 # library name if found in that library.
218 @template
219 def check_symbol_in_libs(
220     libnames, symbol, language="C", when=None, onerror=lambda: None
222     have_symbol = never
224     found_lib = dependable(namespace(found=False, lib=None))
226     kwargs = {
227         "symbol": symbol,
228         "language": language,
229         "when": when or always,
230         "onerror": onerror,
231     }
233     for libname in libnames:
234         kwargs["when"] &= ~have_symbol
235         if libname:
236             have_symbol = check_symbol_in_lib(libname, **kwargs)
237         else:
238             have_symbol = check_symbol(**kwargs)
240         add_lib = namespace(found=True, lib=libname)
241         found_lib = depends(have_symbol, found_lib)(lambda h, f: add_lib if h else f)
243     return found_lib
246 # Determine whether to add a given flag to the given lists of flags for C or
247 # C++ compilation.
248 # - `flag` is the flag to test
249 # - `flags_collection` is a @depends function for a namespace of lists of
250 #    C/C++ compiler flags to add to.
251 # - `test_flags` is a list of flags to pass to the compiler instead of merely
252 #   passing `flag`. This is especially useful for checking warning flags. If
253 #   this list is empty, `flag` will be passed on its own.
254 # - `compiler` (optional) is the compiler to test against (c_compiler or
255 #   cxx_compiler, from toolchain.configure). When omitted, both compilers
256 #   are tested; the list of flags added to is dependent on the compiler tested.
257 # - `when` (optional) is a @depends function or option name conditioning
258 #   when the warning flag is wanted.
259 # - `check`, when not set, skips checking whether the flag is supported and
260 #   adds it to the list of flags unconditionally.
261 # - `mode`, can be "compile", "link" or "assemble"
262 @template
263 def check_and_add_flags(
264     flag,
265     flags_collection,
266     test_flags=(),
267     compiler=None,
268     when=None,
269     check=True,
270     mode="compile",
272     assert mode in ("compile", "link", "assemble")
274     if compiler is not None:
275         compilers = (compiler,)
276     elif mode in ("link", "assemble"):
277         compilers = (c_compiler,)
278     else:
279         compilers = (c_compiler, cxx_compiler)
281     if when is None:
282         when = always
284     results = []
286     if test_flags:
287         flags = test_flags
288     else:
289         flags = [flag]
291     for c in compilers:
292         assert c in {c_compiler, cxx_compiler, host_c_compiler, host_cxx_compiler}
293         if mode == "compile":
294             lang, list_of_flags = {
295                 c_compiler: ("C", flags_collection.cflags),
296                 cxx_compiler: ("C++", flags_collection.cxxflags),
297                 host_c_compiler: ("host C", flags_collection.host_cflags),
298                 host_cxx_compiler: ("host C++", flags_collection.host_cxxflags),
299             }[c]
300         elif mode == "assemble":
301             lang, list_of_flags = {
302                 c_compiler: ("C", flags_collection.asflags),
303                 host_c_compiler: ("host C", flags_collection.host_asflags),
304             }[c]
305         elif mode == "link":
306             lang, list_of_flags = {
307                 c_compiler: ("C", flags_collection.ldflags),
308                 cxx_compiler: ("C++", flags_collection.ldflags),
309                 host_c_compiler: ("host C", flags_collection.host_ldflags),
310                 host_cxx_compiler: ("host C++", flags_collection.host_ldflags),
311             }[c]
313         result = when
315         if check:
317             @depends(c, dependable(flags))
318             def flags(c, flags):
319                 # Don't error out just because clang complains about other things.
320                 if c.type in ("clang", "clang-cl"):
321                     flags += ["-Wno-error=unused-command-line-argument"]
323                 return flags
325             if mode == "link":
327                 def runner(*args, **kwargs):
328                     if c in (c_compiler, cxx_compiler):
329                         return c.try_link(linker_ldflags, *args, **kwargs)
330                     else:
331                         return c.try_link(host_linker_ldflags, *args, **kwargs)
333                 tool = "linker"
334             elif mode == "assemble":
335                 runner = c.try_compile
336                 tool = "assembler"
337             else:
338                 runner = c.try_compile
339                 tool = "compiler"
341             result = runner(
342                 flags=flags,
343                 when=result,
344                 check_msg="whether the %s %s supports %s" % (lang, tool, flag),
345             )
347         @depends(result, list_of_flags)
348         def maybe_add_flag(result, list_of_flags):
349             if result:
350                 list_of_flags.append(flag)
352         results.append(result)
354     return tuple(results)
357 @dependable
358 def warnings_flags():
359     return namespace(cflags=[], cxxflags=[], host_cflags=[], host_cxxflags=[])
362 # Tests whether GCC or clang support the given warning flag, and if it is,
363 # add it to the list of warning flags for the build.
364 # - `warning` is the warning flag (e.g. -Wfoo)
365 # - `compiler` (optional) is the compiler to test against (c_compiler or
366 #   cxx_compiler, from toolchain.configure). When omitted, both compilers
367 #   are tested.
368 # - `when` (optional) is a @depends function or option name conditioning
369 #   when the warning flag is wanted.
370 # - `check`, when not set, skips checking whether the flag is supported and
371 #   adds it to the list of warning flags unconditionally. This is only meant
372 #   for add_warning().
373 @template
374 def check_and_add_warning(warning, compiler=None, when=None, check=True):
375     # GCC and clang will fail if given an unknown warning option like
376     # -Wfoobar. But later versions won't fail if given an unknown negated
377     # warning option like -Wno-foobar. So when we are checking for support
378     # of a negated warning option, we actually test the positive form, but
379     # add the negated form to the flags variable.
380     if warning.startswith("-Wno-") and not warning.startswith("-Wno-error="):
381         flags = ["-Werror", "-W" + warning[5:]]
382     elif warning.startswith("-Werror="):
383         flags = [warning]
384     else:
385         flags = ["-Werror", warning]
387     return check_and_add_flags(
388         warning, warnings_flags, flags, compiler=compiler, when=when, check=check
389     )
392 # Add the given warning to the list of warning flags for the build.
393 # - `warning` is the warning flag (e.g. -Wfoo)
394 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
395 #   cxx_compiler, from toolchain.configure). When omitted, the warning flag
396 #   is added for both compilers.
397 # - `when` (optional) is a @depends function or option name conditioning
398 #   when the warning flag is wanted.
401 @template
402 def add_warning(warning, compiler=None, when=None):
403     check_and_add_warning(warning, compiler, when, check=False)
406 # Like the warning checks above, but for general compilation flags.
407 @dependable
408 def compilation_flags():
409     return namespace(
410         cflags=[],
411         cxxflags=[],
412         host_cflags=[],
413         host_cxxflags=[],
414         cmflags=[],
415         cmmflags=[],
416     )
419 # Tests whether GCC or clang support the given compilation flag; 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 # - `compiler` (optional) is the compiler to test against (c_compiler or
423 #   cxx_compiler, from toolchain.configure). When omitted, both compilers
424 #   are tested.
425 # - `when` (optional) is a @depends function or option name conditioning
426 #   when the warning flag is wanted.
427 # - `check`, when not set, skips checking whether the flag is supported and
428 #   adds it to the list of flags unconditionally. This is only meant for
429 #   add_flag().
430 @template
431 def check_and_add_flag(flag, compiler=None, when=None, check=True):
432     flags = ["-Werror", flag]
434     return check_and_add_flags(
435         flag, compilation_flags, flags, compiler=compiler, when=when, check=check
436     )
439 # Add the given flag to the list of flags for the build.
440 # - `flag` is the flag (e.g. -fno-sized-deallocation)
441 # - `compiler` (optional) is the compiler to add the flag for (c_compiler or
442 #   cxx_compiler, from toolchain.configure). When omitted, the flag is added
443 #   for both compilers.
444 # - `when` (optional) is a @depends function or option name conditioning
445 #   when the flag is wanted.
446 @template
447 def add_flag(warning, compiler=None, when=None):
448     check_and_add_flag(warning, compiler, when, check=False)
451 # Like the compilation checks above, but for asm flags.
452 @dependable
453 def asm_flags():
454     return namespace(asflags=[], host_asflags=[])
457 # Tests the given assembler flag is supported; if the flag
458 # is supported, add it to the list of compilation flags for the build.
459 # - `flag` is the flag to test
460 # - `when` (optional) is a @depends function or option name conditioning
461 #   when the warning flag is wanted.
462 # - `check`, when not set, skips checking whether the flag is supported and
463 #   adds it to the list of flags unconditionally. This is only meant for
464 #   add_flag().
465 @template
466 def check_and_add_asm_flag(flag, when=None, check=True):
467     return check_and_add_flags(
468         flag,
469         asm_flags,
470         [flag],
471         when=when,
472         check=check,
473         mode="assemble",
474     )
477 # Like the compilation checks above, but for linker flags.
478 @dependable
479 def linker_flags():
480     return namespace(ldflags=[], host_ldflags=[])
483 # Tests the given linker flag is supported; if the flag
484 # is supported, add it to the list of compilation flags for the build.
485 # - `flag` is the flag to test
486 # - `when` (optional) is a @depends function or option name conditioning
487 #   when the warning flag is wanted.
488 # - `check`, when not set, skips checking whether the flag is supported and
489 #   adds it to the list of flags unconditionally. This is only meant for
490 #   add_flag().
491 @template
492 def check_and_add_linker_flag(flag, compiler=None, when=None, check=True):
493     return check_and_add_flags(
494         flag,
495         linker_flags,
496         [flag],
497         when=when,
498         check=check,
499         mode="link",
500     )
503 # Like the compilation checks above, but for linker optimization flags.
504 @dependable
505 def linker_optimize_flags():
506     return namespace(ldflags=[])
509 @template
510 def check_and_add_linker_optimize_flag(flag, compiler=None, when=None, check=True):
511     return check_and_add_flags(
512         flag,
513         linker_optimize_flags,
514         [flag],
515         when=when,
516         check=check,
517         mode="link",
518     )
521 # Add the given flag to the list of linker flags for the build.
522 # - `flag` is the flag (e.g. -fno-sized-deallocation)
523 # - `when` (optional) is a @depends function or option name conditioning
524 #   when the flag is wanted.
525 @template
526 def add_linker_flag(flag, compiler=None, when=None):
527     check_and_add_linker_flag(flag, compiler, when, check=False)