Revision control
Copy as Markdown
Other Tools
# -*- 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
@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,
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,
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=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 <openssl/opensslv.h>
#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))