summaryrefslogtreecommitdiffstats
path: root/comm/third_party/openpgp.configure
blob: 03f760ed57589796d97f54b5a96eee606ed17954 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# -*- 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)",
    )

    jsonc_pkg = pkg_check_modules("MZLA_JSONC", "json-c >= 0.11", when="--with-system-jsonc")
    set_config("MZLA_SYSTEM_JSONC", depends_if(jsonc_pkg)(lambda _: True))

    @depends("--with-system-jsonc")
    def in_tree_jsonc(system_jsonc):
        if not system_jsonc:
            return True

    # Bzip2 --with-system-bz2
    system_lib_option(
        "--with-system-bz2",
        nargs="?",
        help="Use system Bzip2 for librnp (pkgconfig/given prefix)",
    )
    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)
    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)
    def librnp_backend_choices(is_linux):
        if is_linux:
            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)",
        )

        botan_pkg = pkg_check_modules("MZLA_BOTAN", "botan-2 >= 2.8.0", when="--with-system-botan")
        set_config("MZLA_SYSTEM_BOTAN", depends_if(botan_pkg)(lambda _: True))

    # OpenSSL backend
    with only_when(rnp_openssl):
        option(
            "--with-openssl",
            nargs=1,
            help="OpenSSL library prefix (when not found by pkgconfig)",
        )
        openssl_pkg = pkg_check_modules(
            "MZLA_LIBRNP_OPENSSL", "openssl >= 1.1.1e", allow_missing=True, config=False
        )

        @depends_if("--with-openssl", openssl_pkg)
        @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
    # =============================================
    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")

        check_headers("stdarg.h", "dlfcn.h", "endian.h", "memory.h", "xlocale.h")

        set_define("JSON_C_HAVE_INTTYPES_H", noset_check_header("inttypes.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))