使用 LLVM clang C/C++ 编译器编译 OpenSSL 3.X库

本文介绍了 OpenSSL 3.X 库的编译配置方法。需先下载源码放到待编译目录,解压后进入根目录,复制并修改配置文件,将 CC、CXX、CPP、LD 改为 /usr/bin/clang-8 或 /usr/bin/clang++-8。因 OpenSSL 3.X 用了 AVX512 指令集,最好用 clang8 编译。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、下载 OpenSSL 3.X 库的源代码放到待编译目录

2、解压并接入 OpenSSL 3.X 库源码的根目录

3、复制 ./Configure 一个取名为 ./Configure-clang

4、修改 ./Configure-clang

找到配置段:

CC=

CXX=

CPP=

LD=

把它们改成

CC          => "/usr/bin/clang-8",

CXX         => "/usr/bin/clang++-8",

CPP         => "/usr/bin/clang++-8",

LD           => "/usr/bin/clang++-8",

OpenSSL 3.X最好用 clang8 来编译,这是因为 OpenSSL 3.X 用了AVX512 指令集(汇编)本人试过 clang6 是无法编译OpenSSL使用的AVX512指令集汇编的。

完整配置参考:

#! /usr/bin/env perl
# -*- mode: perl; -*-
# Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

##  Configure -- OpenSSL source tree configuration script

use 5.10.0;
use strict;
use Config;
use FindBin;
use lib "$FindBin::Bin/util/perl";
use File::Basename;
use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs splitdir/;
use File::Path qw/mkpath/;
use OpenSSL::fallback "$FindBin::Bin/external/perl/MODULES.txt";
use OpenSSL::Glob;
use OpenSSL::Template;
use OpenSSL::config;

# see INSTALL.md for instructions.

my $orig_death_handler = $SIG{__DIE__};
$SIG{__DIE__} = \&death_handler;

my $usage="Usage: Configure [no-<feature> ...] [enable-<feature> ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]thread-pool] [[no-]default-thread-pool] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] os/compiler[:flags]\n";

my $banner = <<"EOF";

**********************************************************************
***                                                                ***
***   OpenSSL has been successfully configured                     ***
***                                                                ***
***   If you encounter a problem while building, please open an    ***
***   issue on GitHub <https://github.com/openssl/openssl/issues>  ***
***   and include the output from the following command:           ***
***                                                                ***
***       perl configdata.pm --dump                                ***
***                                                                ***
***   (If you are new to OpenSSL, you might want to consult the    ***
***   'Troubleshooting' section in the INSTALL.md file first)      ***
***                                                                ***
**********************************************************************
EOF

# Options:
#
# --config      add the given configuration file, which will be read after
#               any "Configurations*" files that are found in the same
#               directory as this script.
# --prefix      prefix for the OpenSSL installation, which includes the
#               directories bin, lib, include, share/man, share/doc/openssl
#               This becomes the value of INSTALLTOP in Makefile
#               (Default: /usr/local)
# --openssldir  OpenSSL data area, such as openssl.cnf, certificates and keys.
#               If it's a relative directory, it will be added on the directory
#               given with --prefix.
#               This becomes the value of OPENSSLDIR in Makefile and in C.
#               (Default: PREFIX/ssl)
# --banner=".." Output specified text instead of default completion banner
#
# -w            Don't wait after showing a Configure warning
#
# --cross-compile-prefix Add specified prefix to binutils components.
#
# --api         One of 0.9.8, 1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, or 3.0
#               Define the public APIs as they were for that version
#               including patch releases.  If 'no-deprecated' is also
#               given, do not compile support for interfaces deprecated
#               up to and including the specified OpenSSL version.
#
# no-hw-xxx     do not compile support for specific crypto hardware.
#               Generic OpenSSL-style methods relating to this support
#               are always compiled but return NULL if the hardware
#               support isn't compiled.
# no-hw         do not compile support for any crypto hardware.
# [no-]threads  [don't] try to create a library that is suitable for
#               multithreaded applications (default is "threads" if we
#               know how to do it)
# [no-]thread-pool
#               [don't] allow thread pool functionality
# [no-]default-thread-pool
#               [don't] allow default thread pool functionality
# [no-]shared   [don't] try to create shared libraries when supported.
# [no-]pic      [don't] try to build position independent code when supported.
#               If disabled, it also disables shared and dynamic-engine.
# no-asm        do not use assembler
# no-egd        do not compile support for the entropy-gathering daemon APIs
# [no-]zlib     [don't] compile support for zlib compression.
# zlib-dynamic  Like "zlib", but the zlib library is expected to be a shared
#               library and will be loaded at run-time by the OpenSSL library.
# sctp          include SCTP support
# no-quic       disable QUIC support
# no-uplink     Don't build support for UPLINK interface.
# enable-weak-ssl-ciphers
#               Enable weak ciphers that are disabled by default.
# 386           generate 80386 code in assembly modules
# no-sse2       disables IA-32 SSE2 code in assembly modules, the above
#               mentioned '386' option implies this one
# no-<cipher>   build without specified algorithm (dsa, idea, rc5, ...)
# -<xxx> +<xxx> All options which are unknown to the 'Configure' script are
# /<xxx>        passed through to the compiler. Unix-style options beginning
#               with a '-' or '+' are recognized, as well as Windows-style
#               options beginning with a '/'. If the option contains arguments
#               separated by spaces, then the URL-style notation %20 can be
#               used for the space character in order to avoid having to quote
#               the option. For example, -opt%20arg gets expanded to -opt arg.
#               In fact, any ASCII character can be encoded as %xx using its
#               hexadecimal encoding.
# -static       while -static is also a pass-through compiler option (and
#               as such is limited to environments where it's actually
#               meaningful), it triggers a number configuration options,
#               namely no-pic, no-shared and no-threads. It is
#               argued that the only reason to produce statically linked
#               binaries (and in context it means executables linked with
#               -static flag, and not just executables linked with static
#               libcrypto.a) is to eliminate dependency on specific run-time,
#               a.k.a. libc version. The mentioned config options are meant
#               to achieve just that. Unfortunately on Linux it's impossible
#               to eliminate the dependency completely for openssl executable
#               because of getaddrinfo and gethostbyname calls, which can
#               invoke dynamically loadable library facility anyway to meet
#               the lookup requests. For this reason on Linux statically
#               linked openssl executable has rather debugging value than
#               production quality.
#
# BN_LLONG      use the type 'long long' in crypto/bn/bn.h
# RC4_CHAR      use 'char' instead of 'int' for RC4_INT in crypto/rc4/rc4.h
# Following are set automatically by this script
#
# MD5_ASM       use some extra md5 assembler,
# SHA1_ASM      use some extra sha1 assembler, must define L_ENDIAN for x86
# RMD160_ASM    use some extra ripemd160 assembler,
# SHA256_ASM    sha256_block is implemented in assembler
# SHA512_ASM    sha512_block is implemented in assembler
# AES_ASM       AES_[en|de]crypt is implemented in assembler

# Minimum warning options... any contributions to OpenSSL should at least
# get past these.  Note that we only use these with C compilers, not with
# C++ compilers.

# -DPEDANTIC complements -pedantic and is meant to mask code that
# is not strictly standard-compliant and/or implementation-specific,
# e.g. inline assembly, disregards to alignment requirements, such
# that -pedantic would complain about. Incidentally -DPEDANTIC has
# to be used even in sanitized builds, because sanitizer too is
# supposed to and does take notice of non-standard behaviour. Then
# -pedantic with pre-C9x compiler would also complain about 'long
# long' not being supported. As 64-bit algorithms are common now,
# it grew impossible to resolve this without sizeable additional
# code, so we just tell compiler to be pedantic about everything
# but 'long long' type.

my @gcc_devteam_warn = qw(
    -DPEDANTIC -pedantic -Wno-long-long -DUNUSEDRESULT_DEBUG
    -Wall
    -Wmissing-declarations
    -Wextra
    -Wno-unused-parameter
    -Wno-missing-field-initializers
    -Wswitch
    -Wsign-compare
    -Wshadow
    -Wformat
    -Wno-type-limits
    -Wno-tautological-constant-out-of-range-compare
    -Wundef
    -Werror
    -Wmissing-prototypes
    -Wstrict-prototypes
);

# These are used in addition to $gcc_devteam_warn when the compiler is clang.
# TODO(openssl-team): fix problems and investigate if (at least) the
# following warnings can also be enabled:
#       -Wcast-align
#       -Wunreachable-code -- no, too ugly/compiler-specific
#       -Wlanguage-extension-token -- no, we use asm()
#       -Wunused-macros -- no, too tricky for BN and _XOPEN_SOURCE etc
#       -Wextended-offsetof -- no, needed in CMS ASN1 code
my @clang_devteam_warn = qw(
    -Wno-unknown-warning-option
    -Wswitch-default
    -Wno-parentheses-equality
    -Wno-language-extension-token
    -Wno-extended-offsetof
    -Wno-missing-braces
    -Wconditional-uninitialized
    -Wincompatible-pointer-types-discards-qualifiers
    -Wmissing-variable-declarations
);

my @cl_devteam_warn = qw(
    /WX
);

my $strict_warnings = 0;

# As for $BSDthreads. Idea is to maintain "collective" set of flags,
# which would cover all BSD flavors. -pthread applies to them all,
# but is treated differently. OpenBSD expands is as -D_POSIX_THREAD
# -lc_r, which is sufficient. FreeBSD 4.x expands it as -lc_r,
# which has to be accompanied by explicit -D_THREAD_SAFE and
# sometimes -D_REENTRANT. FreeBSD 5.x expands it as -lc_r, which
# seems to be sufficient?
our $BSDthreads="-pthread -D_THREAD_SAFE -D_REENTRANT";

#
# API compatibility name to version number mapping.
#
my $apitable = {
    # This table expresses when API additions or changes can occur.
    # The numbering used changes from 3.0 and on because we updated
    # (solidified) our version numbering scheme at that point.

    # From 3.0 and on, we internalise the given version number in decimal
    # as MAJOR * 10000 + MINOR * 100 + 0
    "3.0.0" => 30000,
    "3.0"   => 30000,

    # Note that before 3.0, we didn't have the same version number scheme.
    # Still, the numbering we use here covers what we need.
    "1.1.1" => 10101,
    "1.1.0" => 10100,
    "1.0.2" => 10002,
    "1.0.1" => 10001,
    "1.0.0" => 10000,
    "0.9.8" =>   908,
};

# For OpenSSL::config::get_platform
my %guess_opts = ();

my $dryrun = 0;

our %table = ();
our %config = ();
our %withargs = ();
our $now_printing;      # set to current entry's name in print_table_entry
                        # (todo: right thing would be to encapsulate name
                        # into %target [class] and make print_table_entry
                        # a method)

# Forward declarations ###############################################

# read_config(filename)
#
# Reads a configuration file and populates %table with the contents
# (which the configuration file places in %targets).
sub read_config;

# resolve_config(target)
#
# Resolves all the late evaluations, inheritances and so on for the
# chosen target and any target it inherits from.
sub resolve_config;


# Information collection #############################################

# Unified build supports separate build dir
my $srcdir = catdir(absolutedir(dirname($0))); # catdir ensures local syntax
my $blddir = catdir(absolutedir("."));         # catdir ensures local syntax

# File::Spec::Unix doesn't detect case insensitivity, so we make sure to
# check if the source and build directory are really the same, and make
# them so.  This avoids all kinds of confusion later on.
# We must check @File::Spec::ISA rather than using File::Spec->isa() to
# know if File::Spec ended up loading File::Spec::Unix.
$srcdir = $blddir
    if (grep(/::Unix$/, @File::Spec::ISA)
        && samedir($srcdir, $blddir));

my $dofile = abs2rel(catfile($srcdir, "util/dofile.pl"));

my $local_config_envname = 'OPENSSL_LOCAL_CONFIG_DIR';

$config{sourcedir} = abs2rel($srcdir, $blddir);
$config{builddir} = abs2rel($blddir, $blddir);
# echo -n 'holy hand grenade of antioch' | openssl sha256
$config{FIPSKEY} =
    'f4556650ac31d35461610bac4ed81b1a181b2d8a43ea2854cbae22ca74560813';

# Collect reconfiguration information if needed
my @argvcopy=@ARGV;

if (grep /^reconf(igure)?$/, @argvcopy) {
    die "reconfiguring with other arguments present isn't supported"
        if scalar @argvcopy > 1;
    if (-f "./configdata.pm") {
        my $file = "./configdata.pm";
        unless (my $return = do $file) {
            die "couldn't parse $file: $@" if $@;
            die "couldn't do $file: $!"    unless defined $return;
            die "couldn't run $file"       unless $return;
        }

        @argvcopy = defined($configdata::config{perlargv}) ?
            @{$configdata::config{perlargv}} : ();
        die "Incorrect data to reconfigure, please do a normal configuration\n"
            if (grep(/^reconf/,@argvcopy));
        $config{perlenv} = $configdata::config{perlenv} // {};
    } else {
        die "Insufficient data to reconfigure, please do a normal configuration\n";
    }
}

$config{perlargv} = [ @argvcopy ];

# Historical: if known directories in crypto/ have been removed, it means
# that those sub-systems are disabled.
# (the other option would be to removed them from the SUBDIRS statement in
# crypto/build.info)
# We reverse the input list for cosmetic purely reasons, to compensate that
# 'unshift' adds at the front of the list (i.e. in reverse input order).
foreach ( reverse sort( 'aes', 'aria', 'bf', 'camellia', 'cast', 'des', 'dh',
                        'dsa', 'ec', 'hmac', 'idea', 'md2', 'md5', 'mdc2',
                        'rc2', 'rc4', 'rc5', 'ripemd', 'seed', 'sha',
                        'sm2', 'sm3', 'sm4') ) {
    unshift @argvcopy, "no-$_" if ! -d catdir($srcdir, 'crypto', $_);
}

# Collect version numbers
my %version = ();

collect_information(
    collect_from_file(catfile($srcdir,'VERSION.dat')),
    qr/\s*(\w+)\s*=\s*(.*?)\s*$/ =>
        sub {
            # Only define it if there is a value at all
            if ($2 ne '') {
                my $k = $1;
                my $v = $2;
                # Some values are quoted.  Trim the quotes
                $v = $1 if $v =~ /^"(.*)"$/;
                $version{uc $k} = $v;
            }
        },
    "OTHERWISE" =>
        sub { die "Something wrong with this line:\n$_\nin $srcdir/VERSION.dat" },
    );

$config{major} = $version{MAJOR} // 'unknown';
$config{minor} = $version{MINOR} // 'unknown';
$config{patch} = $version{PATCH} // 'unknown';
$config{prerelease} =
    defined $version{PRE_RELEASE_TAG} ? "-$version{PRE_RELEASE_TAG}" : '';
$config{build_metadata} =
    defined $version{BUILD_METADATA} ? "+$version{BUILD_METADATA}" : '';
$config{shlib_version} = $version{SHLIB_VERSION} // 'unknown';
$config{release_date} = $version{RELEASE_DATE} // 'xx XXX xxxx';

$config{version} = "$config{major}.$config{minor}.$config{patch}";
$config{full_version} = "$config{version}$config{prerelease}$config{build_metadata}";

die "erroneous version information in VERSION.dat: ",
    "$config{version}, $config{shlib_version}\n"
    unless (defined $version{MAJOR}
            && defined $version{MINOR}
            && defined $version{PATCH}
            && defined $version{SHLIB_VERSION});

# Collect target configurations

my $pattern = catfile(dirname($0), "Configurations", "*.conf");
foreach (sort glob($pattern)) {
    &read_config($_);
}

if (defined env($local_config_envname)) {
    if ($^O eq 'VMS') {
        # VMS environment variables are logical names,
        # which can be used as is
        $pattern = $local_config_envname . ':' . '*.conf';
    } else {
        $pattern = catfile(env($local_config_envname), '*.conf');
    }

    foreach (sort glob($pattern)) {
        &read_config($_);
    }
}

# Save away perl command information
$config{perl_cmd} = $^X;
$config{perl_version} = $Config{version};
$config{perl_archname} = $Config{archname};

$config{prefix}="";
$config{openssldir}="";
$config{processor}="";
$config{libdir}="";
my $auto_threads=1;    # enable threads automatically? true by default
my $default_ranlib;

# Known TLS and DTLS protocols
my @tls = qw(ssl3 tls1 tls1_1 tls1_2 tls1_3);
my @dtls = qw(dtls1 dtls1_2);

# Explicitly known options that are possible to disable.  They can
# be regexps, and will be used like this: /^no-${option}$/
# For developers: keep it sorted alphabetically

my @disablables = (
    "acvp-tests",
    "afalgeng",
    "apps",
    "argon2",
    "aria",
    "asan",
    "asm",
    "async",
    "autoalginit",
    "autoerrinit",
    "autoload-config",
    "bf",
    "blake2",
    "brotli",
    "brotli-dynamic",
    "buildtest-c++",
    "bulk",
    "cached-fetch",
    "camellia",
    "capieng",
    "winstore",
    "cast",
    "chacha",
    "cmac",
    "cmp",
    "cms",
    "comp",
    "crypto-mdebug",
    "ct",
    "default-thread-pool",
    "deprecated",
    "des",
    "devcryptoeng",
    "dgram",
    "dh",
    "docs",
    "dsa",
    "dso",
    "dtls",
    "dynamic-engine",
    "ec",
    "ec2m",
    "ec_nistp_64_gcc_128",
    "ecdh",
    "ecdsa",
    "ecx",
    "egd",
    "engine",
    "err",
    "external-tests",
    "filenames",
    "fips",
    "fips-securitychecks",
    "fuzz-afl",
    "fuzz-libfuzzer",
    "gost",
    "http",
    "idea",
    "ktls",
    "legacy",
    "loadereng",
    "makedepend",
    "md2",
    "md4",
    "mdc2",
    "module",
    "msan",
    "multiblock",
    "nextprotoneg",
    "ocb",
    "ocsp",
    "padlockeng",
    "pic",
    "pinshared",
    "poly1305",
    "posix-io",
    "psk",
    "quic",
    "rc2",
    "rc4",
    "rc5",
    "rdrand",
    "rfc3779",
    "rmd160",
    "scrypt",
    "sctp",
    "secure-memory",
    "seed",
    "shared",
    "siphash",
    "siv",
    "sm2",
    "sm2-precomp",
    "sm3",
    "sm4",
    "sock",
    "srp",
    "srtp",
    "sse2",
    "ssl",
    "ssl-trace",
    "static-engine",
    "stdio",
    "tests",
    "tfo",
    "thread-pool",
    "threads",
    "tls",
    "trace",
    "ts",
    "ubsan",
    "ui-console",
    "unit-test",
    "uplink",
    "weak-ssl-ciphers",
    "whirlpool",
    "zlib",
    "zlib-dynamic",
    "zstd",
    "zstd-dynamic",
    );
foreach my $proto ((@tls, @dtls))
        {
        push(@disablables, $proto);
        push(@disablables, "$proto-method") unless $proto eq "tls1_3";
        }

# Internal disablables, for aliasing purposes.  They serve no special
# purpose here, but allow scripts to get to know them through configdata.pm,
# where these are merged with @disablables.
# The actual aliasing mechanism is done via %disable_cascades
my @disablables_int = qw(
    crmf
    );

my %deprecated_disablables = (
    "ssl2" => undef,
    "buf-freelists" => undef,
    "crypto-mdebug-backtrace" => undef,
    "hw" => "hw",               # causes cascade, but no macro
    "hw-padlock" => "padlockeng",
    "ripemd" => "rmd160",
    "ui" => "ui-console",
    "heartbeats" => undef,
    );

# All of the following are disabled by default:

our %disabled = ( # "what"         => "comment"
                  "fips"                => "default",
                  "asan"                => "default",
                  "brotli"              => "default",
                  "brotli-dynamic"      => "default",
                  "buildtest-c++"       => "default",
                  "crypto-mdebug"       => "default",
                  "crypto-mdebug-backtrace" => "default",
                  "devcryptoeng"        => "default",
                  "ec_nistp_64_gcc_128" => "default",
                  "egd"                 => "default",
                  "external-tests"      => "default",
                  "fuzz-afl"            => "default",
                  "fuzz-libfuzzer"      => "default",
                  "ktls"                => "default",
                  "md2"                 => "default",
                  "msan"                => "default",
                  "rc5"                 => "default",
                  "sctp"                => "default",
                  "ssl3"                => "default",
                  "ssl3-method"         => "default",
                  "tfo"                 => "default",
                  "trace"               => "default",
                  "ubsan"               => "default",
                  "unit-test"           => "default",
                  "weak-ssl-ciphers"    => "default",
                  "zlib"                => "default",
                  "zlib-dynamic"        => "default",
                  "zstd"                => "default",
                  "zstd-dynamic"        => "default",
                );

# Note: => pair form used for aesthetics, not to truly make a hash table
my @disable_cascades = (
    # "what"            => [ "cascade", ... ]
    "bulk"              => [ "shared", "dso",
                             "aria", "async", "autoload-config",
                             "blake2", "bf", "camellia", "cast", "chacha",
                             "cmac", "cms", "cmp", "comp", "ct",
                             "des", "dgram", "dh", "dsa",
                             "ec", "engine",
                             "filenames",
                             "idea", "ktls",
                             "md4", "multiblock", "nextprotoneg",
                             "ocsp", "ocb", "poly1305", "psk",
                             "rc2", "rc4", "rmd160",
                             "seed", "siphash", "siv",
                             "sm3", "sm4", "srp",
                             "srtp", "ssl3-method", "ssl-trace",
                             "tfo",
                             "ts", "ui-console", "whirlpool",
                             "fips-securitychecks" ],
    sub { $config{processor} eq "386" }
                        => [ "sse2" ],
    "ssl"               => [ "ssl3" ],
    "ssl3-method"       => [ "ssl3" ],
    "zlib"              => [ "zlib-dynamic" ],
    "brotli"            => [ "brotli-dynamic" ],
    "zstd"              => [ "zstd-dynamic" ],
    "des"               => [ "mdc2" ],
    "ec"                => [ "ec2m", "ecdsa", "ecdh", "sm2", "gost", "ecx" ],
    "dgram"             => [ "dtls", "quic", "sctp" ],
    "sock"              => [ "dgram", "tfo" ],
    "dtls"              => [ @dtls ],
    sub { 0 == scalar grep { !$disabled{$_} } @dtls }
                        => [ "dtls" ],

    "tls"               => [ @tls ],
    sub { 0 == scalar grep { !$disabled{$_} } @tls }
                        => [ "tls" ],
    "tls1_3"            => [ "quic" ],

    "crypto-mdebug"     => [ "crypto-mdebug-backtrace" ],

    "module"            => [ "dynamic-engine", "fips" ],

    # Without shared libraries, dynamic engines aren't possible.
    # This is due to them having to link with libcrypto and register features
    # using the ENGINE functionality, and since that relies on global tables,
    # those *have* to be exactly the same as the ones accessed from the app,
    # which cannot be guaranteed if shared libraries aren't present.
    # (note that even with shared libraries, both the app and dynamic engines
    # must be linked with the same library)
    "shared"            => [ "dynamic-engine", "uplink" ],
    "dso"               => [ "dynamic-engine", "module" ],
    # Other modules don't necessarily have to link with libcrypto, so shared
    # libraries do not have to be a condition to produce those.

    # Without position independent code, there can be no shared libraries
    # or modules.
    "pic"               => [ "shared", "module" ],

    "engine"            => [ "dynamic-engine", grep(/eng$/, @disablables) ],
    "dynamic-engine"    => [ "loadereng" ],
    "hw"                => [ "padlockeng" ],

    # no-autoalginit is only useful when building non-shared
    "autoalginit"       => [ "shared", "apps", "fips" ],

    "stdio"             => [ "apps", "capieng", "egd" ],
    "apps"              => [ "tests" ],
    "tests"             => [ "external-tests" ],
    "comp"              => [ "zlib", "brotli", "zstd" ],
    "sm3"               => [ "sm2" ],
    sub { !$disabled{"unit-test"} } => [ "heartbeats" ],

    sub { !$disabled{"msan"} } => [ "asm" ],

    "cmac"              => [ "siv" ],
    "legacy"            => [ "md2" ],

    "cmp"               => [ "crmf" ],

    "fips"              => [ "fips-securitychecks", "acvp-tests" ],

    "threads"           => [ "thread-pool" ],
    "thread-pool"       => [ "default-thread-pool" ],

    "blake2"            => [ "argon2" ],

    "deprecated-3.0"    => [ "engine", "srp" ],

    "http"              => [ "ocsp" ]
    );

# Avoid protocol support holes.  Also disable all versions below N, if version
# N is disabled while N+1 is enabled.
#
my @list = (reverse @tls);
while ((my $first, my $second) = (shift @list, shift @list)) {
    last unless @list;
    push @disable_cascades, ( sub { !$disabled{$first} && $disabled{$second} }
                              => [ @list ] );
    unshift @list, $second;
}
my @list = (reverse @dtls);
while ((my $first, my $second) = (shift @list, shift @list)) {
    last unless @list;
    push @disable_cascades, ( sub { !$disabled{$first} && $disabled{$second} }
                              => [ @list ] );
    unshift @list, $second;
}

# Explicit "no-..." options will be collected in %disabled along with the defaults.
# To remove something from %disabled, use "enable-foo".
# For symmetry, "disable-foo" is a synonym for "no-foo".

# For the "make variables" CPPINCLUDES and CPPDEFINES, we support lists with
# platform specific list separators.  Users from those platforms should
# recognise those separators from how you set up the PATH to find executables.
# The default is the Unix like separator, :, but as an exception, we also
# support the space as separator.
my $list_separator_re =
    { VMS           => qr/(?<!\^),/,
      MSWin32       => qr/(?<!\\);/ } -> {$^O} // qr/(?<!\\)[:\s]/;
# All the "make variables" we support
# Some get pre-populated for the sake of backward compatibility
# (we supported those before the change to "make variable" support.
my %user = (
    AR          => env('AR'),
    ARFLAGS     => [],
    AS          => undef,
    ASFLAGS     => [],
    CC          => "/usr/bin/clang-8",
    CFLAGS      => [ env('CFLAGS') || () ],
    CXX         => "/usr/bin/clang++-8",
    CXXFLAGS    => [ env('CXXFLAGS') || () ],
    CPP         => "/usr/bin/clang++-8",
    CPPFLAGS    => [ env('CPPFLAGS') || () ],  # -D, -I, -Wp,
    CPPDEFINES  => [],  # Alternative for -D
    CPPINCLUDES => [],  # Alternative for -I
    CROSS_COMPILE => env('CROSS_COMPILE'),
    HASHBANGPERL=> env('HASHBANGPERL') || env('PERL'),
    LD