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 @depends(build_project, "--enable-smoosh")
9 def cbindgen_is_needed(build_project, js_enable_smoosh):
10 if build_project != "js":
11 # cbindgen is needed by the style system build and webrender.
14 # cbindgen is needed by SmooshMonkey.
15 return js_enable_smoosh
18 option(env="CBINDGEN", nargs=1, when=cbindgen_is_needed, help="Path to cbindgen")
21 @imports(_from="textwrap", _import="dedent")
22 def check_cbindgen_version(cbindgen, fatal=False):
23 log.debug("trying cbindgen: %s" % cbindgen)
25 cbindgen_min_version = Version("0.26.0")
28 version = Version(check_cmd_output(cbindgen, "--version").strip().split(" ")[1])
29 log.debug("%s has version %s" % (cbindgen, version))
30 if version >= cbindgen_min_version:
38 cbindgen version {} is too old. At least version {} is required.
40 Please update using 'cargo install cbindgen --force' or running
41 './mach bootstrap', after removing the existing executable located at
44 version, cbindgen_min_version, cbindgen
50 # Similar behavior to what check_prog does.
51 has_cbindgen_input = depends("CBINDGEN", when=cbindgen_is_needed)(lambda x: x)
52 bootstrap_cbindgen = depends(cbindgen_is_needed, has_cbindgen_input)(
53 lambda n, i: n and not i
59 bootstrap_search_path("cbindgen", when=bootstrap_cbindgen),
61 when=cbindgen_is_needed,
63 @checking("for cbindgen")
64 @imports(_from="textwrap", _import="dedent")
65 def cbindgen(cbindgen_override, bootstrap_search_path, rust_search_path):
67 check_cbindgen_version(cbindgen_override[0], fatal=True)
68 return cbindgen_override[0]
71 for path in bootstrap_search_path + rust_search_path:
72 candidate = find_program("cbindgen", [path])
75 if check_cbindgen_version(candidate):
77 candidates.append(candidate)
80 raise FatalCheckError(
83 Cannot find cbindgen. Please run `mach bootstrap`,
84 `cargo install cbindgen`, ensure that `cbindgen` is on your PATH,
85 or point at an executable with `CBINDGEN`.
89 check_cbindgen_version(candidates[0], fatal=True)
92 set_config("CBINDGEN", cbindgen)
94 # Bindgen can use rustfmt to format Rust file, but it's not required.
95 option(env="RUSTFMT", nargs=1, help="Path to the rustfmt program")
100 paths=rust_search_path,
107 "--with-libclang-path",
109 help="Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)",
114 help="Absolute path to a Clang binary for bindgen (version 3.9.x or above)",
128 @checking("for clang for bindgen", lambda x: x.path if x else "not found")
129 def bindgen_clang_compiler(
139 # When the target compiler is clang, use that, including flags.
140 if cxx_compiler.type == "clang":
141 if clang_path and clang_path[0] not in (
143 cxx_compiler.compiler,
146 "--with-clang-path is not valid when the target compiler is %s",
150 path=cxx_compiler.compiler,
151 flags=cxx_compiler.flags,
153 # When the target compiler is clang-cl, use clang in the same directory,
154 # and figure the right flags to use.
155 if cxx_compiler.type == "clang-cl":
156 if clang_path and os.path.dirname(clang_path[0]) != os.path.dirname(
157 cxx_compiler.compiler
160 "--with-clang-path must point to clang in the same directory "
161 "as the target compiler"
164 clang_path = [os.path.join(os.path.dirname(cxx_compiler.compiler), "clang")]
166 clang_path = find_program(
167 clang_path[0] if clang_path else "clang++", clang_search_path
171 # Hack before bug 1617793: if the compiler is clang-cl, hack the target
172 if cxx_compiler.type == "clang-cl":
173 target = split_triplet("%s-pc-windows-msvc" % target.raw_cpu)
176 flags.extend(("--sysroot", sysroot_path))
177 info = check_compiler(
178 configure_cache, [clang_path] + flags, "C++", target, android_version
180 # Usually, one check_compiler pass would be enough, but when cross-compiling
181 # and the host and target don't use the same default C++ standard, we don't
182 # get the --std flag, so try again. This is the same thing as valid_compiler()
183 # does in toolchain.configure.
186 info = check_compiler(
187 configure_cache, [clang_path] + flags, "C++", target, android_version
191 flags=flags + info.flags,
195 @depends("--with-libclang-path", bindgen_clang_compiler, host_library_name_info, host)
196 @checking("for libclang for bindgen", lambda x: x if x else "not found")
198 @imports(_from="os", _import="pathsep")
199 @imports(_from="os.path", _import="split", _as="pathsplit")
201 def bindgen_libclang_path(libclang_path, clang, library_name_info, host):
205 "--with-libclang-path is not valid without a clang compiler "
210 # Try to ensure that the clang shared library that bindgen is going
211 # to look for is actually present. The files that we search for
212 # mirror the logic in clang-sys/build.rs.
213 libclang_choices = []
214 if host.os == "WINNT":
215 libclang_choices.append("libclang.dll")
216 libclang_choices.append(
217 "%sclang%s" % (library_name_info.dll.prefix, library_name_info.dll.suffix)
219 if host.kernel == "Linux":
220 libclang_choices.append("libclang.so.*")
222 if host.os == "OpenBSD":
223 libclang_choices.append("libclang.so.*.*")
226 if not libclang_path:
227 # Try to find libclang_path based on clang search dirs.
228 clang_search_dirs = check_cmd_output(clang.path, "-print-search-dirs")
229 for line in clang_search_dirs.splitlines():
230 name, _, value = line.partition(": =")
231 if host.os == "WINNT" and name == "programs":
232 # On Windows, libclang.dll is in bin/ rather than lib/,
233 # so scan the programs search dirs.
234 # To make matters complicated, clang before version 9 uses `:`
235 # separate between paths (and `;` in newer versions)
237 candidates.extend(value.split(pathsep))
239 for part in value.split(":"):
240 # Assume that if previous "candidate" was of length 1,
241 # it's a drive letter and the current part is the rest of
242 # the corresponding full path.
243 if candidates and len(candidates[-1]) == 1:
244 candidates[-1] += ":" + part
246 candidates.append(part)
247 elif host.os != "WINNT" and name == "libraries":
248 # On other platforms, use the directories from the libraries
249 # search dirs that looks like $something/clang/$version.
250 for dir in value.split(pathsep):
251 dir, version = pathsplit(dir)
252 if re.match(r"[0-9.]+", version):
253 dir, name = pathsplit(dir)
255 candidates.append(dir)
257 candidates.append(libclang_path[0])
259 for dir in candidates:
260 for pattern in libclang_choices:
261 log.debug('Trying "%s" in "%s"', pattern, dir)
262 libs = glob.glob(os.path.join(dir, pattern))
267 @depends(bindgen_clang_compiler, bindgen_libclang_path, build_project)
268 def bindgen_config_paths(clang, libclang, build_project):
269 # XXX: we want this code to be run for both Gecko and JS, but we don't
270 # necessarily want to force a bindgen/Rust dependency on JS just yet.
271 # Actually, we don't want to force an error if we're not building the
272 # browser generally. We therefore whitelist the projects that require
273 # bindgen facilities at this point and leave it at that.
274 if build_project in ("browser", "mobile/android"):
277 "Could not find clang to generate run bindings for C/C++. "
278 "Please install the necessary packages, run `mach bootstrap`, "
279 "or use --with-clang-path to give the location of clang."
284 "Could not find libclang to generate rust bindings for C/C++. "
285 "Please install the necessary packages, run `mach bootstrap`, "
286 "or use --with-libclang-path to give the path containing it."
289 if clang and libclang:
292 libclang_path=os.path.dirname(libclang),
293 clang_path=clang.path,
294 clang_flags=clang.flags,
298 @depends(bindgen_config_paths.libclang, when=bindgen_config_paths)
299 @checking("that libclang is new enough", lambda s: "yes" if s else "no")
300 @imports(_from="ctypes", _import="CDLL")
301 @imports(_from="textwrap", _import="dedent")
302 def min_libclang_version(libclang):
305 # We want at least 5.0. The API we test below is enough for that.
306 # Just accessing it should throw if not found.
307 fun = lib.clang_getAddressSpace
313 The libclang located at {} is too old (need at least 5.0).
315 Please make sure to update it or point to a newer libclang using
316 --with-libclang-path.
325 set_config("MOZ_LIBCLANG_PATH", bindgen_config_paths.libclang_path)
326 set_config("MOZ_CLANG_PATH", bindgen_config_paths.clang_path)
332 bindgen_cflags_android,
333 bindgen_config_paths.clang_flags,
336 def basic_bindgen_cflags(
337 target, compiler_info, android_cflags, clang_flags, all_arm_flags
342 "-fno-sized-deallocation",
346 "-DMOZILLA_INTERNAL_API",
350 if target.os == "Android":
351 args += android_cflags
359 if compiler_info.type == "clang-cl":
361 # To enable the builtin __builtin_offsetof so that CRT wouldn't
362 # use reinterpret_cast in offsetof() which is not allowed inside
364 "-D_CRT_USE_BUILTIN_OFFSETOF",
365 # Enable hidden attribute (which is not supported by MSVC and
366 # thus not enabled by default with a MSVC-compatibile build)
367 # to exclude hidden symbols from the generated file.
368 "-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1",
371 return args + (clang_flags or []) + (all_arm_flags or [])
375 env="BINDGEN_CFLAGS",
377 help="Options bindgen should pass to the C/C++ parser",
381 @depends(basic_bindgen_cflags, "BINDGEN_CFLAGS")
382 @checking("bindgen cflags", lambda s: s if s else "no")
383 def bindgen_cflags(base_flags, extra_flags):
385 if extra_flags and len(extra_flags):
386 flags += extra_flags[0].split()
387 return " ".join(flags)
390 set_config("BINDGEN_SYSTEM_FLAGS", bindgen_cflags)