blob: 8b427f40bcb28821767a9f25cad4da667e68928c [file] [log] [blame]
# -*- python -*-
#
# ====================================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ====================================================================
#
import sys
import os
import re
EnsureSConsVersion(2,3,0)
# Compatibility with old versions of SCons
try:
# Python 2 / SCons 2.3.0 etc.
from cStringIO import StringIO
print("warning: replaced StringIO() for Python version < 3.")
except ImportError:
# Python 3
from io import StringIO
# Set up our additional config tests.
src_dir = File('SConstruct').rfile().get_dir().abspath
sys.path.insert(0, src_dir)
import build.scons_extras
import build.exports
build.scons_extras.AddEnvironmentMethods()
custom_tests = {'CheckGnuCC': build.scons_extras.CheckGnuCC}
# SCons 4.7 introduced the function argument list parameter to CheckFunc.
try:
import SCons.Conftest as _conftest
_conftest.CheckFunc(None, 'clock', '#include <time.h>', 'C', '')
except AttributeError:
# This comes from the 'None' context argument, above. It's fine, we just
# proved that CheckFunc has the funcargs parameter, so we don't have to
# replace it with our own implementation.
pass
except TypeError:
# We have version < 4.7 without funcargs, use our replacement CheckFunc.
custom_tests['CheckFunc'] = build.scons_extras.CheckFunc
print('warning: replaced Conftest.CheckFunc() for SCons version < 4.7.')
# By default, we silence all warnings when compiling MockHTTP.
# Set this to True to show them instead.
SHOW_MOCKHTTP_WARNINGS = False
HEADER_FILES = ['serf.h',
'serf_bucket_types.h',
'serf_bucket_util.h',
]
# where we save the configuration variables
SAVED_CONFIG = '.saved_config'
# Variable class that does no validation on the input
def _converter(val):
"""
"""
if val == 'none':
val = []
else:
val = val.split(' ')
return val
def RawListVariable(key, help, default):
"""
The input parameters describe a 'raw string list' option. This class
accepts a space-separated string and converts it to a list.
"""
return (key, '%s' % (help), default, None, lambda val: _converter(val))
# Custom path validator, creates directory when a specified option is set.
# To be used to ensure a PREFIX directory is only created when installing.
def createPathIsDirCreateWithTarget(target):
def my_validator(key, val, env):
build_targets = (map(str, BUILD_TARGETS))
if target in build_targets:
return PathVariable.PathIsDirCreate(key, val, env)
else:
return PathVariable.PathAccept(key, val, env)
return my_validator
def filter_cflags(env, cmd, unique=0):
'''Filter all debugging, optimization and warning flags from 'cmd'.'''
cmd = re.sub(r'(^|\s)-[gOW]\S*', '', cmd)
return env.MergeFlags(cmd, unique)
def unsubstable(string):
'''There are things that SCons just shouldn't Subst.'''
return string.replace('$', '$$')
# default directories
if sys.platform == 'win32':
default_incdir='$PREFIX'
default_libdir='$PREFIX/lib'
default_prefix='./install'
else:
default_incdir='/usr'
default_libdir='$PREFIX/lib'
default_prefix='/usr/local'
opts = Variables(files=[SAVED_CONFIG])
opts.AddVariables(
PathVariable('PREFIX',
'Directory to install under',
default_prefix,
createPathIsDirCreateWithTarget('install')),
PathVariable('LIBDIR',
'Directory to install architecture dependent libraries under',
default_libdir,
createPathIsDirCreateWithTarget('install')),
PathVariable('APR',
"Path to apr-1-config, or to APR's install area",
default_incdir,
PathVariable.PathAccept),
PathVariable('APU',
"Path to apu-1-config, or to APR's install area",
default_incdir,
PathVariable.PathAccept),
PathVariable('EXPAT',
"Path to Expat's install area, used with APR_STATIC",
None,
PathVariable.PathIsDir),
PathVariable('OPENSSL',
"Path to OpenSSL's install area",
default_incdir,
PathVariable.PathIsDir),
PathVariable('ZLIB',
"Path to zlib's install area",
default_incdir,
PathVariable.PathIsDir),
PathVariable('GSSAPI',
"Path to GSSAPI's install area",
None,
None),
PathVariable('BROTLI',
"Path to Brotli's install area",
None,
PathVariable.PathIsDir),
PathVariable('UNBOUND',
"Path to libunbound's install area",
None,
PathVariable.PathIsDir),
BoolVariable('DEBUG',
"Enable debugging info and strict compile warnings",
False),
BoolVariable('APR_STATIC',
"Enable using a static compiled APR on Windows",
False),
BoolVariable('DISABLE_LOGGING',
"Disable the logging framework at compile time",
False),
BoolVariable('ENABLE_SLOW_TESTS',
"Enable long-running unit tests",
False),
RawListVariable('CC', "Command name or path of the C compiler", None),
RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)",
None),
RawListVariable('LIBS', "Extra libraries passed to the linker, "
"e.g. \"-l<library1> -l<library2>\" (space separated)", None),
RawListVariable('LINKFLAGS', "Extra flags for the linker (space-separated)",
None),
RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor "
"(space separated)", None),
)
if sys.platform == 'win32':
opts.AddVariables(
# By default SCons builds for the host platform on Windows, when using
# a supported compiler (E.g. VS2010/VS2012). Allow overriding
# Note that Scons 1.3 only supports this on Windows and only when
# constructing Environment(). Later changes to TARGET_ARCH are ignored
EnumVariable('TARGET_ARCH',
"Platform to build for",
'x86',
allowed_values=('x86', 'x86_64', 'arm64', 'ia64'),
map={'X86' : 'x86',
'win32': 'x86',
'Win32': 'x86',
'x64' : 'x86_64',
'X64' : 'x86_64',
'ARM64': 'arm64'
}),
EnumVariable('MSVC_VERSION',
"Visual C++ to use for building",
None,
allowed_values=('14.3', '14.2', '14.1', '14.0', '12.0',
'11.0', '10.0', '9.0', '8.0', '6.0'),
map={'2005' : '8.0',
'2008' : '9.0',
'2010' : '10.0',
'2012' : '11.0',
'2013' : '12.0',
'2015' : '14.0',
'2017' : '14.1',
'2019' : '14.2',
'2022' : '14.3',
}),
# We always documented that we handle an install layout, but in fact we
# hardcoded source layouts. Allow disabling this behavior.
# ### Fix default?
BoolVariable('SOURCE_LAYOUT',
"Assume a source layout instead of install layout",
True),
)
env = Environment(variables=opts,
tools=('default', 'textfile',),
CPPPATH=['.', ],
)
# Export symbol generator (for Windows DLL, Mach-O and ELF)
export_generator = build.exports.ExportGenerator()
if export_generator.target is None:
# Detect if the build target is an ELF platform the the
# generator doesn't know about.
sys.stdout.write("Checking if the build target is ELF ...")
conf = Configure(env) # No custom tests, we want a clean environment.
if (conf.TryLink('int main(void) { return 0; }', '.c')):
header = conf.lastTarget.get_contents()[:4]
if header == b'\x7fELF': # This is the ELF magic number.
print(" yes")
elf = build.exports.TARGET_ELF
export_generator = build.exports.ExportGenerator(elf)
else:
print(" no")
else:
print(" failed")
conf.Finish()
# Now try again...
if export_generator.target is None:
# Nothing to do on this platform
export_generator = None
else:
def generate_exports(target, source, env):
for target_path in (str(t) for t in target):
stream = open(target_path, 'wt')
export_generator.generate(stream, *(str(s) for s in source))
stream.close()
env.Append(BUILDERS = {
'GenExports': Builder(action=generate_exports,
suffix=export_generator.target.ext,
src_suffix='.h')
})
match = re.search('SERF_MAJOR_VERSION ([0-9]+).*'
'SERF_MINOR_VERSION ([0-9]+).*'
'SERF_PATCH_VERSION ([0-9]+)',
env.File('serf.h').get_contents().decode('utf-8'),
re.DOTALL)
MAJOR, MINOR, PATCH = [int(x) for x in match.groups()]
env.Append(MAJOR=str(MAJOR))
env.Append(MINOR=str(MINOR))
env.Append(PATCH=str(PATCH))
# Calling external programs is okay if we're not cleaning or printing help.
# (cleaning: no sense in fetching information; help: we may not know where
# they are)
CALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help'))
# HANDLING OF OPTION VARIABLES
unknown = opts.UnknownVariables()
if unknown:
print('Warning: Used unknown variables:', ', '.join(unknown.keys()))
apr = str(env['APR'])
apu = str(env['APU'])
zlib = str(env['ZLIB'])
expat = env.get('EXPAT', None)
gssapi = env.get('GSSAPI', None)
brotli = env.get('BROTLI', None)
unbound = env.get('UNBOUND', None)
if gssapi and os.path.isdir(gssapi):
krb5_config = os.path.join(gssapi, 'bin', 'krb5-config')
if os.path.isfile(krb5_config):
gssapi = krb5_config
env['GSSAPI'] = krb5_config
debug = env.get('DEBUG', None)
aprstatic = env.get('APR_STATIC', None)
disablelogging = env.get('DISABLE_LOGGING', None)
Help(opts.GenerateHelpText(env))
opts.Save(SAVED_CONFIG, env)
# PLATFORM-SPECIFIC BUILD TWEAKS
thisdir = os.getcwd()
libdir = '$LIBDIR'
incdir = '$PREFIX/include/serf-$MAJOR'
pkgdir = '$LIBDIR/pkgconfig'
# This version string is used in the dynamic library name, and for Mac OS X also
# for the compatibility_version option in the .dylib.
soversion = '%d.%d.%d' % (MAJOR, MINOR, 0)
libversion = '%d.%d.%d' % (MAJOR, MINOR, PATCH)
if sys.platform != 'sunos5':
env['SHLIBVERSION'] = soversion
# XXX Since SCons 2.4.1, the SHLIBVERSION variable is no longer used to set the
# shared library versions on Mac OS X, so we add explicit linker flags to set
# the current_version and compatibility_version options.
if sys.platform == 'darwin':
env.Append(LINKFLAGS=['-Wl,-current_version,' + libversion,
'-Wl,-compatibility_version,' + soversion])
LIBNAME = '%sserf-%d' % (env['LIBPREFIX'], MAJOR)
if sys.platform == 'win32':
# On Win32 SHLIBPREFIX and LIBPREFIX are empty and both produce a .lib file.
SHLIBNAME = 'libserf-%d' % (MAJOR, )
elif env['SHLIBPREFIX'] == '$LIBPREFIX':
# Let's avoid constructing '$LIBPREFIXserf...' which evaluates to ''
SHLIBNAME = LIBNAME
else:
SHLIBNAME = '%sserf-%d' % (env['SHLIBPREFIX'], MAJOR)
if export_generator is None:
export_filter = None
else:
export_filter = '%s%s' % (SHLIBNAME, export_generator.target.ext)
env.Append(RPATH=[libdir],
PDB='${TARGET.filebase}.pdb')
if sys.platform != 'win32':
conf = Configure(env, custom_tests=custom_tests)
have_gcc = conf.CheckGnuCC()
env = conf.Finish()
if have_gcc:
# env.Append(CFLAGS=['-std=c89'])
env.SerfAppendIf(['CFLAGS'], r'-(ansi|std=c\d+)', CFLAGS=['-std=c89'])
env.Append(CCFLAGS=['-Wdeclaration-after-statement',
'-Wmissing-prototypes',
'-Wshadow',
'-Wall'])
if debug:
# env.Append(CCFLAGS=['-g'])
env.SerfAppendIf(['CFLAGS', 'CCFLAGS'], r'-g\S*', CCFLAGS=['-g'])
env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
for flag in ['-Werror=unknown-warning-option',
'-Wimplicit-function-declaration',
'-Wmissing-variable-declarations',
'-Wunreachable-code',
'-Wshorten-64-to-32',
'-Wno-system-headers',
'-Wextra-tokens',
'-Wnewline-eof']:
if env.SerfCheckCFlag(flag):
env.Append(CCFLAGS=[flag])
else:
# env.Append(CCFLAGS=['-O2'])
env.SerfAppendIf(['CFLAGS', 'CCFLAGS'], r'-O\S*', CCFLAGS=['-O2'])
env.Append(CPPDEFINES=['NDEBUG'])
### works for Mac OS. probably needs to change
env.Append(LIBS=['ssl', 'crypto', 'z', ])
if sys.platform == 'sunos5':
env.Append(LIBS=['m'])
env.Append(PLATFORM='posix')
if brotli:
env.Append(LIBS=['brotlicommon', 'brotlidec'])
if unbound:
env.Append(LIBS=['unbound'])
else:
# Warning level 4, no unused argument warnings
env.Append(CCFLAGS=['/W4',
'/wd4100', # Unused argument
'/wd4127', # Conditional expression is constant
'/wd4706', # Assignment within conditional expression
'/we4013', # 'function' undefined; assuming extern returning int
])
# Choose runtime and optimization
if debug:
# Disable optimizations for debugging, use debug DLL runtime
env.Append(CCFLAGS=['/Od', '/MDd'])
env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
else:
# Optimize for speed, use DLL runtime
env.Append(CCFLAGS=['/O2', '/MD'])
env.Append(CPPDEFINES=['NDEBUG'])
env.Append(LINKFLAGS=['/RELEASE'])
# PLAN THE BUILD
SHARED_SOURCES = []
if sys.platform == 'win32':
SHARED_SOURCES.append(env.RES(['serf.rc']))
SOURCES = Glob('src/*.c') + Glob('buckets/*.c') + Glob('auth/*.c') + \
Glob('protocols/*.c')
lib_static = env.StaticLibrary(LIBNAME, SOURCES)
lib_shared = env.SharedLibrary(SHLIBNAME, SOURCES + SHARED_SOURCES)
if export_filter is not None:
env.GenExports(target=export_filter, source=HEADER_FILES)
env.Depends(lib_shared, export_filter)
# We do not want or need OpenSSL's compatibility macros.
env.Append(CPPDEFINES=['OPENSSL_NO_DEPRECATED'])
# Define OPENSSL_NO_STDIO to prevent using _fp() API.
env.Append(CPPDEFINES=['OPENSSL_NO_STDIO'])
if aprstatic:
env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC'])
# Prepare lists for the pkg-config file
pc_requires = ['libssl', 'libcrypto']
pc_cppflags = []
pc_private_libs = []
win_std_libs = []
if sys.platform == 'win32':
source_layout = env.get('SOURCE_LAYOUT', None)
win_std_libs = ['crypt32.lib', 'mswsock.lib', 'rpcrt4.lib',
'secur32.lib', 'ws2_32.lib']
# Get apr/apu information into our build
env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER',
'NOGDI', 'NONLS','NOCRYPT',
'_CRT_SECURE_NO_WARNINGS',
'_CRT_NONSTDC_NO_WARNINGS'])
if env.get('TARGET_ARCH', None) in ('x86_64', 'arm64', 'ia64'):
env.Append(CPPDEFINES=['WIN64'])
# Get the APR-Util version number to check if we need an external Expat
if expat:
expat_lib_name = 'expat.lib'
else:
expat_lib_name = 'xml.lib'
apuversion = os.path.join(apu, 'include', 'apu_version.h')
if not os.path.isfile(apuversion):
apuversion = os.path.join(apu, 'include', 'apr-1', 'apu_version.h')
if os.path.isfile(apuversion):
apu_major = 0
apu_minor = 0
with open(apuversion, 'r') as vfd:
major_rx = re.compile(r'^\s*#\s*define\s+APU_MAJOR_VERSION\s+(\d+)')
minor_rx = re.compile(r'^\s*#\s*define\s+APU_MINOR_VERSION\s+(\d+)')
for line in vfd:
m = major_rx.match(line)
if m:
apu_major = int(m.group(1))
continue
m = minor_rx.match(line)
if m:
apu_minor = int(m.group(1))
print('Found APR-Util version %d.%d' % (apu_major, apu_minor))
if apu_major >= 2 or apu_major == 1 and apu_minor >= 6:
expat_lib_name = 'expat.lib'
else:
print("Warning: Missing header " + apuversion)
if aprstatic:
apr_libs='apr-1.lib'
apu_libs='aprutil-1.lib'
win_std_libs.append('shell32.lib')
else:
apr_libs='libapr-1.lib'
apu_libs='libaprutil-1.lib'
if not source_layout:
apr_libdir = '$APR/lib'
apu_libdir = '$APU/lib'
env.Append(CPPPATH=['$APR/include', '$APR/include/apr-1',
'$APU/include', '$APU/include/apr-1'])
elif aprstatic:
apr_libdir = '$APR/LibR'
apu_libdir = '$APU/LibR'
env.Append(CPPPATH=['$APR/include', '$APU/include'])
else:
apr_libdir = '$APR/Release'
apu_libdir = '$APU/Release'
env.Append(CPPPATH=['$APR/include', '$APU/include'])
env.Append(LIBPATH=[apr_libdir, apu_libdir],
LIBS=[apr_libs, apu_libs])
pc_private_libs.append('/'.join([apr_libdir, apr_libs]))
pc_private_libs.append('/'.join([apu_libdir, apu_libs]))
if expat and aprstatic:
env.Append(LIBPATH=[expat],
LIBS=[expat_lib_name])
pc_private_libs.append('/'.join([expat, expat_lib_name]))
# zlib
env.Append(LIBS=['zlib.lib'])
if not source_layout:
env.Append(CPPPATH=['$ZLIB/include'],
LIBPATH=['$ZLIB/lib'])
pc_private_libs.append('$ZLIB/lib/zlib.lib')
else:
env.Append(CPPPATH=['$ZLIB'],
LIBPATH=['$ZLIB'])
pc_private_libs.append('$ZLIB/zlib.lib')
# openssl
if not source_layout:
env.Append(CPPPATH=['$OPENSSL/include'],
LIBPATH=['$OPENSSL/lib'])
else:
env.Append(CPPPATH=['$OPENSSL/inc32'],
LIBPATH=['$OPENSSL/out32dll'])
conf = Configure(env, custom_tests=custom_tests)
if conf.CheckLib('libcrypto'):
# OpenSSL 1.1.0+
env.Append(LIBS=['libcrypto.lib', 'libssl.lib'])
else:
# Legacy OpenSSL
env.Append(LIBS=['libeay32.lib', 'ssleay32.lib'])
conf.Finish()
# brotli
if brotli:
env.Append(LIBS=['brotlicommon.lib', 'brotlidec.lib'])
if not source_layout:
env.Append(CPPPATH=['$BROTLI/include'],
LIBPATH=['$BROTLI/lib'])
pc_private_libs.append('$BROTLI/lib/brotlicommon.lib')
pc_private_libs.append('$BROTLI/lib/brotlidec.lib')
else:
env.Append(CPPPATH=['$BROTLI/include'],
LIBPATH=['$BROTLI/Release'])
pc_private_libs.append('$BROTLI/Release/brotlicommon.lib')
pc_private_libs.append('$BROTLI/Release/brotlidec.lib')
# unbound
if unbound:
env.Append(CPPPATH=['$UNBOUND/include'],
LIBPATH=['$UNBOUND/lib'],
LIBS=['unbound.lib'])
pc_private_libs.append('$UNBOUND/lib/unbound.lib')
env.Append(LIBS=win_std_libs)
else:
if CALLOUT_OKAY:
if os.path.isdir(apr):
possible_apr = os.path.join(apr, 'bin', 'apr-2-config')
if os.path.isfile(possible_apr):
apr = possible_apr
else:
apr = os.path.join(apr, 'bin', 'apr-1-config')
env['APR'] = apr
apr_version = os.popen(env.subst('$APR --version')).read().strip()
apr_major, apr_minor, apr_patch = map(int, apr_version.split('.'))
if os.path.isdir(apu):
apu = os.path.join(apu, 'bin', 'apu-1-config')
env['APU'] = apu
### we should use --cc, but that is giving some scons error about an implicit
### dependency upon gcc. probably ParseConfig doesn't know what to do with
### the apr-1-config output
env.ParseConfig('$APR --cflags --cppflags --includes'
' --ldflags --link-ld --libs',
filter_cflags)
if apr_major < 2:
env.ParseConfig('$APU --includes --ldflags --link-ld --libs',
filter_cflags)
### there is probably a better way to run/capture output.
### env.ParseConfig() may be handy for getting this stuff into the build
apr_defs = os.popen(env.subst('$APR --cppflags')).read().strip()
apr_libs = os.popen(env.subst('$APR --link-ld --libs')).read().strip()
pc_cppflags.append(apr_defs)
pc_private_libs.append(apr_libs)
if apr_major < 2:
apu_libs = os.popen(env.subst('$APU --link-ld --libs')).read().strip()
pc_private_libs.append(apu_libs)
env.Append(CPPPATH=['$ZLIB/include'])
env.Append(LIBPATH=['$ZLIB/lib'])
if env.subst('$ZLIB') not in ('', '/usr'):
pc_private_libs.append('-L$ZLIB/lib')
pc_private_libs.append('-lz')
# MacOS ships ancient OpenSSL libraries, but no headers, so we can
# assume we're building with an OpenSSL installed outside the
# default include and link paths. To prevent accidentally linking to
# the old shared libraries, make sure that the OpenSSL paths are
# first in the search lists.
if sys.platform == 'darwin':
env.Prepend(CPPPATH=['$OPENSSL/include'])
env.Prepend(LIBPATH=['$OPENSSL/lib'])
else:
env.Append(CPPPATH=['$OPENSSL/include'])
env.Append(LIBPATH=['$OPENSSL/lib'])
if brotli:
env.Append(CPPPATH=['$BROTLI/include'],
LIBPATH=['$BROTLI/lib'])
if env.subst('$BROTLI') not in ('', '/usr'):
pc_private_libs.append('-L$BROTLI/lib')
pc_private_libs.append('-lbrotlicommon -lbrotlidec')
if unbound:
env.Append(CPPPATH=['$UNBOUND/include'],
LIBPATH=['$UNBOUND/lib'])
if env.subst('$UNBOUND') not in ('', '/usr'):
pc_private_libs.append('-L$UNBOUND/lib')
pc_private_libs.append('-lunbound')
# Check for OpenSSL functions which are only available in some of
# the versions we support. Also handles forks like LibreSSL.
ssl_include_rx = re.compile(r'^\s*#\s*include\s+<openssl/[^>]+>')
ssl_include_list = []
stream = StringIO(env.File('buckets/ssl_buckets.c')
.rfile().get_text_contents())
for line in stream.readlines():
if ssl_include_rx.match(line):
ssl_include_list.append(line.rstrip())
ssl_includes = '\n'.join(ssl_include_list)
conf = Configure(env, custom_tests=custom_tests)
if not conf.CheckFunc('BIO_set_init', ssl_includes, 'C', 'NULL, 0'):
env.Append(CPPDEFINES=['SERF_NO_SSL_BIO_WRAPPERS'])
if not conf.CheckFunc('X509_STORE_get0_param', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_NO_SSL_X509_STORE_WRAPPERS'])
if not conf.CheckFunc('X509_get0_notBefore', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_NO_SSL_X509_GET0_NOTBEFORE'])
if not conf.CheckFunc('X509_get0_notAfter', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_NO_SSL_X509_GET0_NOTAFTER'])
if not conf.CheckFunc('X509_STORE_CTX_get0_chain', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_NO_SSL_X509_GET0_CHAIN'])
if not conf.CheckFunc('ASN1_STRING_get0_data', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_NO_SSL_ASN1_STRING_GET0_DATA'])
if conf.CheckFunc('CRYPTO_set_locking_callback', ssl_includes, 'C', 'NULL'):
env.Append(CPPDEFINES=['SERF_HAVE_SSL_LOCKING_CALLBACKS'])
if conf.CheckFunc('OPENSSL_malloc_init', ssl_includes):
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_MALLOC_INIT'])
if conf.CheckFunc('SSL_library_init', ssl_includes):
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_SSL_LIBRARY_INIT'])
if conf.CheckFunc('OpenSSL_version_num', ssl_includes):
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_VERSION_NUM'])
if conf.CheckFunc('SSL_set_alpn_protos', ssl_includes, 'C', 'NULL, NULL, 0'):
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_ALPN'])
if conf.CheckFunc('OSSL_STORE_open_ex', ssl_includes, 'C',
'NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL'):
env.Append(CPPDEFINES=['SERF_HAVE_OSSL_STORE_OPEN_EX'])
if conf.CheckType('OSSL_HANDSHAKE_STATE', ssl_includes):
env.Append(CPPDEFINES=['SERF_HAVE_OSSL_HANDSHAKE_STATE'])
env = conf.Finish()
# If build with gssapi, get its information and define SERF_HAVE_GSSAPI
if gssapi and CALLOUT_OKAY:
env.ParseConfig('$GSSAPI --cflags gssapi')
def parse_libs(env, cmd, unique=1):
env['GSSAPI_LIBS'] = cmd.strip()
return env.MergeFlags(cmd, unique)
env.ParseConfig('$GSSAPI --libs gssapi', parse_libs)
env.Append(CPPDEFINES=['SERF_HAVE_GSSAPI'])
pc_private_libs.append(env['GSSAPI_LIBS'])
if sys.platform == 'win32':
env.Append(CPPDEFINES=['SERF_HAVE_SSPI'])
if brotli and CALLOUT_OKAY:
conf = Configure(env, custom_tests=custom_tests)
if (conf.CheckCHeader('brotli/decode.h')
and conf.CheckFunc('BrotliDecoderTakeOutput',
'#include <brotli/decode.h>',
'C', 'NULL, NULL')):
env.Append(CPPDEFINES=['SERF_HAVE_BROTLI'])
else:
print("Cannot find Brotli library >= 1.0.0 in '%s'." % env.get('BROTLI'))
Exit(1)
env = conf.Finish()
if unbound and CALLOUT_OKAY:
print(custom_tests)
conf = Configure(env, custom_tests=custom_tests)
if (conf.CheckCHeader('unbound.h')
and conf.CheckFunc('ub_ctx_create',
'#include <unbound.h>',
'C', '')
and conf.CheckFunc('ub_resolve_async',
'#include <stddef.h>\n'
'#include <unbound.h>',
'C', 'NULL, NULL, 0, 0, NULL, NULL, NULL')):
env.Append(CPPDEFINES=['SERF_HAVE_ASYNC_RESOLVER=1',
'SERF_HAVE_UNBOUND=1'])
else:
print("Cannot find Unbound library in '%s'." % env.get('UNBOUND'))
Exit(1)
env = conf.Finish()
if CALLOUT_OKAY:
conf = Configure(env, custom_tests=custom_tests)
### some configuration stuffs
if conf.CheckCHeader('stdbool.h'):
env.Append(CPPDEFINES=['HAVE_STDBOOL_H'])
env = conf.Finish()
# Tweak the link flags for selecting exported symbols. Must come after the
# config checks, because the symbols file doesn't exist yet.
if export_filter is not None:
env.Append(LINKFLAGS=[export_generator.target.link_flag % export_filter])
# Set preprocessor define to disable the logging framework
if disablelogging:
env.Append(CPPDEFINES=['SERF_DISABLE_LOGGING'])
# On some systems, the -R values that APR describes never make it into actual
# RPATH flags. We'll manually map all directories in LIBPATH into new
# flags to set RPATH values.
for d in env['LIBPATH']:
env.Append(RPATH=[':'+d])
# Set up the construction of serf-*.pc
pkgprefix = os.path.relpath(env.subst('$PREFIX'), env.subst(pkgdir)
).replace(os.path.sep, '/')
pkglibdir = os.path.relpath(env.subst('$LIBDIR'), env.subst('$PREFIX')
).replace(os.path.sep, '/')
pkglibs = env.subst(' '.join(pc_private_libs + win_std_libs)
).replace(os.path.sep, '/')
pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,),
env.File('build/serf.pc.in'),
SUBST_DICT = {
'@MAJOR@': str(MAJOR),
'@PREFIX@': unsubstable('${pcfiledir}/' + pkgprefix),
'@LIBDIR@': unsubstable('${prefix}/' + pkglibdir),
'@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,),
'@REQUIRES@': ' '.join(pc_requires),
'@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH),
'@CFLAGS@': unsubstable(' '.join(pc_cppflags)),
'@LIBS@': unsubstable(pkglibs),
})
env.Default(lib_static, lib_shared, pkgconfig)
# INSTALLATION STUFF
install_static = env.Install(libdir, lib_static)
install_shared = env.InstallVersionedLib(libdir, lib_shared)
if sys.platform == 'darwin':
# Change the shared library install name (id) to its final name and location.
# Notes:
# If --install-sandbox=<path> is specified, install_shared_path will point
# to a path in the sandbox. We can't use that path because the sandbox is
# only a temporary location. The id should be the final target path.
# Also, we shouldn't use the complete version number for id, as that'll
# make applications depend on the exact major.minor.patch version of serf.
install_shared_path = install_shared[0].abspath
target_install_shared_path = os.path.join(libdir, '%s.dylib' % SHLIBNAME)
env.AddPostAction(install_shared, ('install_name_tool -id %s %s'
% (target_install_shared_path,
install_shared_path)))
env.Alias('install-lib', [install_static, install_shared])
env.Alias('install-inc', env.Install(incdir, HEADER_FILES))
env.Alias('install-pc', env.Install(pkgdir, pkgconfig))
env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ])
# TESTS
### make move to a separate scons file in the test/ subdir?
tenv = env.Clone()
tenv.Append(CPPDEFINES=['MOCKHTTP_OPENSSL'])
# Build the MockHTTP static library. MockHTTP needs C99 and OpenSSL's
# deprecated APIs. Also silence all warnings from MockHTTP.
mockenv = tenv.Clone()
mockenv.Replace(CFLAGS = [f.replace('-std=c89', '-std=c99')
for f in mockenv['CFLAGS']])
mockenv.Replace(CCFLAGS = list(
filter(lambda f: (SHOW_MOCKHTTP_WARNINGS
# NOTE: SCons flags are sometimes tuples, not strings.
# In those cases, the first element is the flag
# and the rest ar the flag's value(s).
or (# GCC-like warning flags
not re.match(r'^\s*-W[a-z][a-z-]+',
f if type(f) == type('') else f[0])
# MSVC-like warning flags
and not re.match(r'^\s*/(W|w[de])\d+',
f if type(f) == type('') else f[0]))),
mockenv['CCFLAGS'])
))
if not SHOW_MOCKHTTP_WARNINGS:
mockenv.Append(CCFLAGS = ['-w' if sys.platform != 'win32' else '/w'])
mockenv.Replace(CPPDEFINES = list(filter(lambda d: d != 'OPENSSL_NO_DEPRECATED',
mockenv['CPPDEFINES'])))
mockhttpinc = mockenv.StaticLibrary('mockhttpinc',
['test/MockHTTPinC/MockHTTP.c',
'test/MockHTTPinC/MockHTTP_server.c'])
# Check if long-running tests should be enabled
if tenv.get('ENABLE_SLOW_TESTS', None):
tenv.Append(CPPDEFINES=['SERF_TEST_DEFLATE_4GBPLUS_BUCKETS'])
TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider',
'serf_httpd',
'test_all', 'serf_bwtp' ]
_exe = '.exe' if sys.platform == 'win32' else ''
TEST_EXES = [os.path.join('test', '%s%s' % (prog, _exe)) for prog in TEST_PROGRAMS]
check_script = env.File('build/check.py').rstr()
test_dir = env.File('test/test_all.c').rfile().get_dir()
test_app = ("%s %s %s %s") % (sys.executable, check_script, test_dir, 'test')
# Set the library search path for the test programs
test_env = {'PATH' : os.environ['PATH'],
'srcdir' : src_dir}
if sys.platform != 'win32':
os_library_path = os.environ.get('LD_LIBRARY_PATH')
os_library_path = [os_library_path] if os_library_path else []
ld_library_path = [tenv.subst(p) for p in tenv.get('LIBPATH', [])]
test_env['LD_LIBRARY_PATH'] = ':'.join(ld_library_path + os_library_path)
env.AlwaysBuild(env.Alias('check', TEST_EXES, test_app, ENV=test_env))
testall_files = [
'test/test_all.c',
'test/CuTest.c',
'test/test_util.c',
'test/test_context.c',
'test/test_buckets.c',
'test/test_auth.c',
'test/test_internal.c',
'test/test_server.c',
'test/mock_buckets.c',
'test/mock_sock_buckets.c',
'test/test_ssl.c',
]
# We link the programs explicitly against the static libraries, to allow
# access to private functions
mocklib = mockhttpinc[0].rfile().abspath
serflib = lib_static[0].rfile().abspath
for proggie in TEST_EXES:
if 'test_all' in proggie:
tenv.Program(proggie, testall_files + [mocklib, serflib])
else:
tenv.Program(target=proggie,
source=[proggie.replace('.exe','') + '.c', serflib])
# HANDLE CLEANING
if env.GetOption('clean'):
# When we're cleaning, we want the dependency tree to include "everything"
# that could be built. Thus, include all of the tests.
env.Default('check')