Bug 1883861 - Part 1: Move visitMemoryBarrier into the common CodeGenerator file...
[gecko.git] / build / moz.configure / bindgen.configure
blob72f4d4173b9d2b413ccc5e5eb26a02d6630f3f14
1 # -*- Mode: python; 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 option(env="CBINDGEN", nargs=1, help="Path to cbindgen")
11 @imports(_from="textwrap", _import="dedent")
12 def check_cbindgen_version(cbindgen, fatal=False):
13     log.debug("trying cbindgen: %s" % cbindgen)
15     cbindgen_min_version = Version("0.26.0")
17     # cbindgen x.y.z
18     version = Version(check_cmd_output(cbindgen, "--version").strip().split(" ")[1])
19     log.debug("%s has version %s" % (cbindgen, version))
20     if version >= cbindgen_min_version:
21         return True
22     if not fatal:
23         return False
25     die(
26         dedent(
27             """\
28     cbindgen version {} is too old. At least version {} is required.
30     Please update using 'cargo install cbindgen --force' or running
31     './mach bootstrap', after removing the existing executable located at
32     {}.
33     """.format(
34                 version, cbindgen_min_version, cbindgen
35             )
36         )
37     )
40 # Similar behavior to what check_prog does.
41 has_cbindgen_input = depends("CBINDGEN")(lambda x: x)
42 bootstrap_cbindgen = depends(has_cbindgen_input)(lambda i: not i)
45 @depends_if(
46     "CBINDGEN",
47     bootstrap_search_path("cbindgen", when=bootstrap_cbindgen),
48     rust_search_path,
50 @checking("for cbindgen")
51 @imports(_from="textwrap", _import="dedent")
52 def cbindgen(cbindgen_override, bootstrap_search_path, rust_search_path):
53     if cbindgen_override:
54         check_cbindgen_version(cbindgen_override[0], fatal=True)
55         return cbindgen_override[0]
57     candidates = []
58     for path in bootstrap_search_path + rust_search_path:
59         candidate = find_program("cbindgen", [path])
60         if not candidate:
61             continue
62         if check_cbindgen_version(candidate):
63             return candidate
64         candidates.append(candidate)
66     if not candidates:
67         raise FatalCheckError(
68             dedent(
69                 """\
70         Cannot find cbindgen. Please run `mach bootstrap`,
71         `cargo install cbindgen`, ensure that `cbindgen` is on your PATH,
72         or point at an executable with `CBINDGEN`.
73         """
74             )
75         )
76     check_cbindgen_version(candidates[0], fatal=True)
79 set_config("CBINDGEN", cbindgen)
81 # Bindgen can use rustfmt to format Rust file, but it's not required.
82 option(env="RUSTFMT", nargs=1, help="Path to the rustfmt program")
84 rustfmt = check_prog(
85     "RUSTFMT",
86     ["rustfmt"],
87     paths=rust_search_path,
88     input="RUSTFMT",
89     allow_missing=True,
93 option(
94     "--with-libclang-path",
95     nargs=1,
96     help="Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)",
98 option(
99     "--with-clang-path",
100     nargs=1,
101     help="Absolute path to a Clang binary for bindgen (version 3.9.x or above)",
105 @depends(
106     "--with-clang-path",
107     configure_cache,
108     c_compiler,
109     cxx_compiler,
110     clang_search_path,
111     target,
112     target_sysroot.path,
113     android_version,
115 @checking("for clang for bindgen", lambda x: x.path if x else "not found")
116 def bindgen_clang_compiler(
117     clang_path,
118     configure_cache,
119     c_compiler,
120     cxx_compiler,
121     clang_search_path,
122     target,
123     sysroot_path,
124     android_version,
126     # When the target compiler is clang, use that, including flags.
127     if cxx_compiler.type == "clang":
128         if clang_path and clang_path[0] not in (
129             c_compiler.compiler,
130             cxx_compiler.compiler,
131         ):
132             die(
133                 "--with-clang-path is not valid when the target compiler is %s",
134                 cxx_compiler.type,
135             )
136         return namespace(
137             path=cxx_compiler.compiler,
138             flags=cxx_compiler.flags,
139         )
140     # When the target compiler is clang-cl, use clang in the same directory,
141     # and figure the right flags to use.
142     if cxx_compiler.type == "clang-cl":
143         if clang_path and os.path.dirname(clang_path[0]) != os.path.dirname(
144             cxx_compiler.compiler
145         ):
146             die(
147                 "--with-clang-path must point to clang in the same directory "
148                 "as the target compiler"
149             )
150         if not clang_path:
151             clang_path = [os.path.join(os.path.dirname(cxx_compiler.compiler), "clang")]
153     clang_path = find_program(
154         clang_path[0] if clang_path else "clang++", clang_search_path
155     )
156     if not clang_path:
157         return
158     # Hack before bug 1617793: if the compiler is clang-cl, hack the target
159     if cxx_compiler.type == "clang-cl":
160         target = split_triplet("%s-pc-windows-msvc" % target.raw_cpu)
161     flags = []
162     if sysroot_path:
163         flags.extend(("--sysroot", sysroot_path))
164     info = check_compiler(
165         configure_cache, [clang_path] + flags, "C++", target, android_version
166     )
167     # Usually, one check_compiler pass would be enough, but when cross-compiling
168     # and the host and target don't use the same default C++ standard, we don't
169     # get the --std flag, so try again. This is the same thing as valid_compiler()
170     # does in toolchain.configure.
171     if info.flags:
172         flags += info.flags
173         info = check_compiler(
174             configure_cache, [clang_path] + flags, "C++", target, android_version
175         )
176     return namespace(
177         path=clang_path,
178         flags=flags + info.flags,
179     )
182 @depends("--with-libclang-path", bindgen_clang_compiler, host_library_name_info, host)
183 @checking("for libclang for bindgen", lambda x: x if x else "not found")
184 @imports("glob")
185 @imports(_from="os", _import="pathsep")
186 @imports(_from="os.path", _import="split", _as="pathsplit")
187 @imports("re")
188 def bindgen_libclang_path(libclang_path, clang, library_name_info, host):
189     if not clang:
190         if libclang_path:
191             die(
192                 "--with-libclang-path is not valid without a clang compiler "
193                 "for bindgen"
194             )
195         return
197     # Try to ensure that the clang shared library that bindgen is going
198     # to look for is actually present.  The files that we search for
199     # mirror the logic in clang-sys/build.rs.
200     libclang_choices = []
201     if host.os == "WINNT":
202         libclang_choices.append("libclang.dll")
203     libclang_choices.append(
204         "%sclang%s" % (library_name_info.dll.prefix, library_name_info.dll.suffix)
205     )
206     if host.kernel == "Linux":
207         libclang_choices.append("libclang.so.*")
209     if host.os == "OpenBSD":
210         libclang_choices.append("libclang.so.*.*")
212     candidates = []
213     if not libclang_path:
214         # Try to find libclang_path based on clang search dirs.
215         clang_search_dirs = check_cmd_output(clang.path, "-print-search-dirs")
216         for line in clang_search_dirs.splitlines():
217             name, _, value = line.partition(": =")
218             if host.os == "WINNT" and name == "programs":
219                 # On Windows, libclang.dll is in bin/ rather than lib/,
220                 # so scan the programs search dirs.
221                 # To make matters complicated, clang before version 9 uses `:`
222                 # separate between paths (and `;` in newer versions)
223                 if pathsep in value:
224                     candidates.extend(value.split(pathsep))
225                 else:
226                     for part in value.split(":"):
227                         # Assume that if previous "candidate" was of length 1,
228                         # it's a drive letter and the current part is the rest of
229                         # the corresponding full path.
230                         if candidates and len(candidates[-1]) == 1:
231                             candidates[-1] += ":" + part
232                         else:
233                             candidates.append(part)
234             elif host.os != "WINNT" and name == "libraries":
235                 # On other platforms, use the directories from the libraries
236                 # search dirs that looks like $something/clang/$version.
237                 for dir in value.split(pathsep):
238                     dir, version = pathsplit(dir)
239                     if re.match(r"[0-9.]+", version):
240                         dir, name = pathsplit(dir)
241                         if name == "clang":
242                             candidates.append(dir)
243     else:
244         candidates.append(libclang_path[0])
246     for dir in candidates:
247         for pattern in libclang_choices:
248             log.debug('Trying "%s" in "%s"', pattern, dir)
249             libs = glob.glob(os.path.join(dir, pattern))
250             if libs:
251                 return libs[0]
254 @depends(bindgen_clang_compiler, bindgen_libclang_path, build_project)
255 def bindgen_config_paths(clang, libclang, build_project):
256     # XXX: we want this code to be run for both Gecko and JS, but we don't
257     # necessarily want to force a bindgen/Rust dependency on JS just yet.
258     # Actually, we don't want to force an error if we're not building the
259     # browser generally.  We therefore whitelist the projects that require
260     # bindgen facilities at this point and leave it at that.
261     if build_project in ("browser", "mobile/android"):
262         if not clang:
263             die(
264                 "Could not find clang to generate run bindings for C/C++. "
265                 "Please install the necessary packages, run `mach bootstrap`, "
266                 "or use --with-clang-path to give the location of clang."
267             )
269         if not libclang:
270             die(
271                 "Could not find libclang to generate rust bindings for C/C++. "
272                 "Please install the necessary packages, run `mach bootstrap`, "
273                 "or use --with-libclang-path to give the path containing it."
274             )
276     if clang and libclang:
277         return namespace(
278             libclang=libclang,
279             libclang_path=os.path.dirname(libclang),
280             clang_path=clang.path,
281             clang_flags=clang.flags,
282         )
285 @depends(bindgen_config_paths.libclang, when=bindgen_config_paths)
286 @checking("that libclang is new enough", lambda s: "yes" if s else "no")
287 @imports(_from="ctypes", _import="CDLL")
288 @imports(_from="textwrap", _import="dedent")
289 def min_libclang_version(libclang):
290     try:
291         lib = CDLL(libclang)
292         # We want at least 5.0. The API we test below is enough for that.
293         # Just accessing it should throw if not found.
294         fun = lib.clang_getAddressSpace
295         return True
296     except:
297         die(
298             dedent(
299                 """\
300         The libclang located at {} is too old (need at least 5.0).
302         Please make sure to update it or point to a newer libclang using
303         --with-libclang-path.
304         """.format(
305                     libclang
306                 )
307             )
308         )
309         return False
312 set_config("MOZ_LIBCLANG_PATH", bindgen_config_paths.libclang_path)
313 set_config("MOZ_CLANG_PATH", bindgen_config_paths.clang_path)
316 @depends(
317     target,
318     cxx_compiler,
319     bindgen_cflags_android,
320     bindgen_config_paths.clang_flags,
321     all_clang_arm_flags,
323 def basic_bindgen_cflags(
324     target, compiler_info, android_cflags, clang_flags, all_arm_flags
326     args = [
327         "-x",
328         "c++",
329         "-fno-sized-deallocation",
330         "-fno-aligned-new",
331         "-DTRACING=1",
332         "-DIMPL_LIBXUL",
333         "-DMOZILLA_INTERNAL_API",
334         "-DRUST_BINDGEN",
335     ]
337     if target.os == "Android":
338         args += android_cflags
340     args += {
341         "WINNT": [
342             "-DWIN32=1",
343         ],
344     }.get(target.os, [])
346     if compiler_info.type == "clang-cl":
347         args += [
348             # To enable the builtin __builtin_offsetof so that CRT wouldn't
349             # use reinterpret_cast in offsetof() which is not allowed inside
350             # static_assert().
351             "-D_CRT_USE_BUILTIN_OFFSETOF",
352             # Enable hidden attribute (which is not supported by MSVC and
353             # thus not enabled by default with a MSVC-compatibile build)
354             # to exclude hidden symbols from the generated file.
355             "-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1",
356         ]
358     return args + (clang_flags or []) + (all_arm_flags or [])
361 option(
362     env="BINDGEN_CFLAGS",
363     nargs=1,
364     help="Options bindgen should pass to the C/C++ parser",
368 @depends(basic_bindgen_cflags, "BINDGEN_CFLAGS")
369 @checking("bindgen cflags", lambda s: s if s else "no")
370 def bindgen_cflags(base_flags, extra_flags):
371     flags = base_flags
372     if extra_flags and len(extra_flags):
373         flags += extra_flags[0].split()
374     return " ".join(flags)
377 set_config("BINDGEN_SYSTEM_FLAGS", bindgen_cflags)