# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. @template def noset_check_header( header, language="C++", flags=None, includes=None, when=None, onerror=lambda: None ): if when is None: when = always if includes: includes = includes[:] else: includes = [] includes.append(header) return try_compile( includes=includes, language=language, flags=flags, check_msg="for %s" % header, when=when, onerror=onerror, ) @template def check_symbol_exists( symbol, header, language="C", flags=None, includes=None, when=None, onerror=lambda: None, ): if when is None: when = always if includes: includes = includes[:] else: includes = [] includes.append("stdio.h") if isinstance(header, str): header = [header] includes.extend(header) body = """#ifndef %s (void) %s; #endif """ % ( symbol, symbol, ) return try_compile( includes, body, language=language, flags=flags, check_msg="for %s" % symbol, when=when, onerror=onerror, ) with only_when("--enable-compile-environment"): option( "--with-system-librnp", help="Use system RNP (librnp) for OpenPGP support.", ) @depends("--with-system-librnp") def in_tree_librnp(system_librnp): if system_librnp: log.info("System librnp will be used at runtime.") return False return True set_config("MZLA_LIBRNP", depends_if(in_tree_librnp)(lambda _: True)) set_define("MZLA_LIBRNP", depends_if(in_tree_librnp)(lambda _: True)) with only_when(in_tree_librnp): # JSON-C --with-system-json system_lib_option( "--with-system-jsonc", help="Use system JSON-C for librnp (located with pkgconfig)", when=use_pkg_config, ) @depends("--with-system-jsonc", when=use_pkg_config) def with_system_json_c_option(with_system_json_c): return with_system_json_c jsonc_pkg = pkg_check_modules("MZLA_JSONC", "json-c >= 0.11", when=with_system_json_c_option) set_config("MZLA_SYSTEM_JSONC", depends_if(jsonc_pkg)(lambda _: True)) # Bzip2 --with-system-bz2 system_lib_option( "--with-system-bz2", nargs="?", help="Use system Bzip2 for librnp (pkgconfig/given prefix)", when=use_pkg_config, ) set_config("MZLA_SYSTEM_BZIP2", True, when="--with-system-bz2") # Bzip2 does not include a pkgconfig file, but some Linux distributions add one bzip2_pkg = pkg_check_modules( "MZLA_BZIP2", "bzip2 >= 1.0.6", when="--with-system-bz2", allow_missing=True, config=False, ) @depends_if("--with-system-bz2", bzip2_pkg, when=use_pkg_config) def bzip2_flags(value, bzip2_pkg): if len(value): # A path (eg. /usr/local was given) return namespace( cflags=("-I%s/include" % value[0],), ldflags=("-L%s/lib" % value[0], "-lbz2"), ) if bzip2_pkg: cflags = list(bzip2_pkg.cflags) libs = bzip2_pkg.libs return namespace( cflags=cflags, ldflags=libs, ) # Fallback return namespace( ldflags=["-lbz2"], ) with only_when("--with-system-bz2"): check_symbol( "BZ2_bzread", flags=bzip2_flags.ldflags, onerror=lambda: die("--with-system-bz2 requested but symbol " "BZ2_bzread not found."), ) c_compiler.try_compile( includes=[ "stdio.h", "sys/types.h", "bzlib.h", ], body=""" #ifndef _BZLIB_H #error _BZLIB_H bzlib.h not found #endif """, flags=bzip2_flags.cflags, check_msg="for bzlib.h", onerror=lambda: die("bzlib.h header not found"), ) set_config("MZLA_BZIP2_CFLAGS", bzip2_flags.cflags) set_config("MZLA_BZIP2_LIBS", bzip2_flags.ldflags) # librnp crypto backend selection @depends(target_has_linux_kernel, "--help") def librnp_backend_choices(is_linux, is_help): if is_linux or is_help: return ("botan", "openssl") else: return ("botan",) option( "--with-librnp-backend", help="Build librnp with the selected backend", choices=librnp_backend_choices, nargs=1, default="botan", ) @depends("--with-librnp-backend") def librnp_backend(backend): if backend: return backend[0] set_config("MZLA_LIBRNP_BACKEND", librnp_backend) @depends(librnp_backend) def rnp_botan(backend): return backend == "botan" @depends(librnp_backend) def rnp_openssl(backend): return backend == "openssl" # Botan backend (--with-system-botan) with only_when(rnp_botan): system_lib_option( "--with-system-botan", help="Use system Botan for librnp (located with pkgconfig)", when=use_pkg_config, ) botan_pkg = pkg_check_modules("MZLA_BOTAN", "botan-3 >= 3.8.0", when="--with-system-botan") set_config("MZLA_SYSTEM_BOTAN", depends_if(botan_pkg)(lambda _: True)) @depends( botan_pkg, build_environment, target, cxx_compiler, milestone, stdcxx_compat, asan, target_sysroot.bootstrapped, target_sysroot.path, ) @imports("json") @imports("os") @imports("subprocess") @imports("sys") @imports(_from="mozbuild.dirutils", _import="ensureParentDir") @imports(_from="importlib", _import="import_module") @imports(_from="shutil", _import="move") @imports(_from="shlex", _import="split") @imports(_from="__builtin__", _import="open") @imports(_from="__builtin__", _import="Exception") def configure_botan( botan_pkg, build_env, target, cxx_compiler, milestone, stdcxx_compat, is_asan, bootstrapped, sysroot_path, ): if botan_pkg: return log.info("Configuring Botan...") if target.kernel == "WINNT": botan_os = "windows" elif target.kernel == "Darwin" and target.os == "OSX": botan_os = "macos" elif target.kernel == "Linux": botan_os = "linux" else: botan_os = target.kernel.lower() botan_objdir = os.path.join(build_env.topobjdir, "comm/third_party/botan") botan_distinfo = f"Thunderbird_{milestone.app_version_display}" botan_modules = ",".join( ( "aead", "aes", "auto_rng", "bigint", "blowfish", "camellia", "cast128", "cbc", "cfb", "crc24", "des", "dl_group", "dsa", "eax", "ec_group", "ecdh", "ecdsa", "ed25519", "elgamal", "eme_pkcs1", "emsa_pkcs1", "emsa_raw", "ffi", "hash", "hkdf", "hmac", "hmac_drbg", "idea", "kdf", "md5", "ocb", "pcurves_brainpool256r1", "pcurves_brainpool384r1", "pcurves_brainpool512r1", "pcurves_generic", "pcurves_numsp512d1", "pcurves_secp192r1", "pcurves_secp224r1", "pcurves_secp256k1", "pcurves_secp256r1", "pcurves_secp384r1", "pcurves_secp521r1", "pcurves_sm2p256v1", "pgp_s2k", "pubkey", "raw_hash", "rfc3394", "rmd160", "rsa", "sha1", "sha2_32", "sha2_64", "sha3", "sm2", "sm3", "sm4", "sp800_56a", "system_rng", "twofish", "x25519", ) ) botan_flags = [ "--cc-bin={}".format(cxx_compiler.compiler), "--cc-abi-flags=--target={}".format(target.alias), "--cpu={}".format(target.cpu), "--os={}".format(botan_os), "--with-build-dir={}".format(botan_objdir), "--build-targets=static", "--minimized-build", "--link-method=copy", "--without-documentation", "--distribution-info={}".format(botan_distinfo), "--enable-modules={}".format(botan_modules), ] if botan_os == "linux" and (stdcxx_compat or bootstrapped): botan_flags += [ "--without-os-features=getrandom", "--without-os-features=explicit_bzero", ] if botan_os == "windows": win_cxx_flags = [ "-clang:-fno-force-enable-int128", "-D_ENABLE_EXTENDED_ALIGNED_STORAGE=1", ] botan_flags += [ "--cc=msvc", # This forces MSVC style arguments out of Botan with clang-cl "--msvc-runtime={}".format("MD" if is_asan else "MT"), "--extra-cxxflags={}".format(" ".join(win_cxx_flags)), ] if bootstrapped: botan_flags += ["--with-sysroot-dir={}".format(sysroot_path)] command = [ "python", "--virtualenv", "build", os.path.join( build_env.topsrcdir, "comm/third_party/botan/configure.py", ), ] log.debug(" ".join(command + botan_flags)) output = check_cmd_output( sys.executable, os.path.join(build_env.topsrcdir, "mach"), "--log-no-times", *command, *botan_flags, cwd=os.path.join(build_env.topobjdir), ) log.info(output) with open(os.path.join(botan_objdir, "build", "build_config.json")) as f: build_config = json.load(f) cxxflags = ( split(build_config["cxx_abi_flags"]) + split(build_config["cc_lang_flags"]) + split(build_config["os_feature_macros"]) + split(build_config["cc_compile_flags"]) + split(build_config["cc_warning_flags"]) ) cxxflags = [ f for f in cxxflags if not f.startswith("/Zc:") ] # Avoid unused argument warnings on Windows return namespace( libfile=build_config["static_lib_name"], libname=build_config["libname"], cxxflags=cxxflags, ) set_config("BOTAN_LIB_FILE", configure_botan.libfile, when=configure_botan) set_config("BOTAN_LIB_NAME", configure_botan.libname, when=configure_botan) set_config("BOTAN_CXXFLAGS", configure_botan.cxxflags, when=configure_botan) # OpenSSL backend with only_when(rnp_openssl): option( "--with-openssl", nargs=1, help="OpenSSL library prefix (when not found by pkgconfig)", when=use_pkg_config, ) openssl_pkg = pkg_check_modules( "MZLA_LIBRNP_OPENSSL", "openssl >= 1.1.1e", allow_missing=True, config=False ) @depends_if("--with-openssl", openssl_pkg, when=use_pkg_config) @imports(_from="os.path", _import="isdir") @imports(_from="os.path", _import="join") def openssl_flags(openssl_prefix, openssl_pkg): if openssl_prefix: openssl_prefix = openssl_prefix[0] include = join(openssl_prefix, "include") lib = join(openssl_prefix, "lib") if not isdir(lib): lib = join(openssl_prefix, "lib64") if isdir(include) and isdir(lib): log.info(f"Using OpenSSL at {openssl_prefix}.") return namespace( cflags=(f"-I{include}",), ldflags=(f"-L{lib}", "-lssl", "-lcrypto"), ) if openssl_pkg: return namespace( cflags=openssl_pkg.cflags, ldflags=openssl_pkg.libs, ) set_config("MZLA_LIBRNP_OPENSSL_CFLAGS", openssl_flags.cflags) set_config("MZLA_LIBRNP_OPENSSL_LIBS", openssl_flags.ldflags) @depends(configure_cache, c_compiler, openssl_flags) @imports(_from="textwrap", _import="dedent") @imports(_from="__builtin__", _import="chr") def openssl_version(configure_cache, compiler, openssl_flags): log.info("Checking for OpenSSL >= 1.1.1e") if openssl_flags is None: die("OpenSSL not found. Must be locatable with pkg-config or use --with-openssl.") def ossl_hexver(hex_str): # See opensshlv.h for description of OPENSSL_VERSION_NUMBER MIN_OSSL_VER = 0x1010105F # Version 1.1.1e ver_as_int = int(hex_str[:-1], 16) ossl_major = (ver_as_int & 0xF0000000) >> 28 ossl_minor = (ver_as_int & 0x0FF00000) >> 20 ossl_fix = (ver_as_int & 0x000FF000) >> 12 # as a letter a-z ossl_patch = chr(96 + ((ver_as_int & 0x00000FF0) >> 4)) ver_as_str = f"{ossl_major}.{ossl_minor}.{ossl_fix}{ossl_patch}" if ver_as_int < MIN_OSSL_VER: die(f"OpenSSL version {ver_as_str} is too old.") return ver_as_str check = dedent( """\ #include #ifdef OPENSSL_VERSION_STR OPENSSL_VERSION_STR #elif defined(OPENSSL_VERSION_NUMBER) OPENSSL_VERSION_NUMBER #else #error Unable to determine OpenSSL version. #endif """ ) result = try_preprocess( configure_cache, compiler.wrapper + [compiler.compiler] + compiler.flags + list(openssl_flags.cflags), "C", check, ) if result: openssl_ver = result.splitlines()[-1] if openssl_ver.startswith("0x"): # OpenSSL 1.x.x - like 0x1010107fL openssl_ver = ossl_hexver(openssl_ver) else: # OpenSSL 3.x.x - quoted version like "3.0.7" openssl_ver = openssl_ver.replace('"', "") major_version = openssl_ver.split(".")[0] if major_version != "3": die( "Unrecognized OpenSSL version {openssl_version} found. Require >= 1.1.1e or 3.x.x" ) log.info(f"Found OpenSSL {openssl_ver}.") return openssl_ver set_config("MZLA_LIBRNP_OPENSSL_VERSION", openssl_version) # Checks for building librnp itself # ================================= have_fcntl_h = check_header("fcntl.h") have_string_h = check_header("string.h") check_headers( "limits.h", "sys/auxv.h", "sys/cdefs.h", "sys/resource.h", "sys/param.h", "sys/stat.h", "sys/wait.h", ) set_define("HAVE_MKDTEMP", check_symbol_exists("mkdtemp", ["stdlib.h", "unistd.h"])) set_define("HAVE_MKSTEMP", check_symbol_exists("mkstemp", ["stdlib.h", "unistd.h"])) set_define("HAVE_REALPATH", check_symbol_exists("realpath", "stdlib.h")) set_define("HAVE_O_BINARY", check_symbol_exists("O_BINARY", "fcntl.h")) set_define("HAVE__O_BINARY", check_symbol_exists("_O_BINARY", "fcntl.h")) # Checks when building JSON-C from tree sources # ============================================= @depends(with_system_json_c_option) def in_tree_jsonc(system_jsonc): return not system_jsonc with only_when(in_tree_jsonc): have_stdlib_h = check_header("stdlib.h") have_locale_h = check_header("locale.h") have_strings_h = check_header("strings.h") set_config("HAVE_STDINT_H", noset_check_header("stdint.h")) check_headers("stdarg.h", "endian.h", "memory.h", "xlocale.h") set_define("HAVE_DECL__ISNAN", check_symbol_exists("_isnan", "float.h")) set_define("HAVE_DECL__FINITE", check_symbol_exists("_finite", "float.h")) set_define("HAVE_DECL_INFINITY", check_symbol_exists("INFINITY", "math.h")) set_define("HAVE_DECL_ISINF", check_symbol_exists("isinf", "math.h")) set_define("HAVE_DECL_ISNAN", check_symbol_exists("isnan", "math.h")) set_define("HAVE_DECL_NAN", check_symbol_exists("nan", "math.h")) set_define("HAVE_DOPRNT", check_symbol_exists("_doprnt", "stdio.h")) set_define("HAVE_SNPRINTF", check_symbol_exists("snprintf", "stdio.h")) set_define( "HAVE_VASPRINTF", check_symbol_exists("vasprintf", "stdio.h", flags=["-D_GNU_SOURCE"]), ) set_define("HAVE_VSNPRINTF", check_symbol_exists("vsnprintf", "stdio.h")) set_define("HAVE_VPRINTF", check_symbol_exists("vprintf", "stdio.h")) set_define("HAVE_OPEN", check_symbol_exists("open", "fcntl.h", when=have_fcntl_h)) set_define( "HAVE_REALLOC", check_symbol_exists("realloc", "stdlib.h", when=have_stdlib_h), ) set_define( "HAVE_SETLOCALE", check_symbol_exists("setlocale", "locale.h", when=have_locale_h), ) set_define( "HAVE_USELOCALE", check_symbol_exists("uselocale", "locale.h", when=have_locale_h), ) set_define( "HAVE_STRCASECMP", check_symbol_exists("strcasecmp", "strings.h", when=have_strings_h), ) set_define( "HAVE_STRNCASECMP", check_symbol_exists("strncasecmp", "strings.h", when=have_strings_h), ) set_define("HAVE_STRDUP", check_symbol_exists("strdup", "string.h", when=have_string_h))