# -*- python -*- # # Copyright 2011-2012 Justin Erenkrantz and Greg Stein # # Licensed 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 import platform HEADER_FILES = ['serf.h', 'serf_bucket_types.h', 'serf_bucket_util.h', ] # where we save the configuration variables SAVED_CONFIG = '.saved_config' opts = Variables(files=[SAVED_CONFIG]) opts.AddVariables( PathVariable('PREFIX', 'Directory to install under', '/usr/local', PathVariable.PathIsDir), PathVariable('APR', "Path to apr-1-config, or to APR's install area", '/usr', PathVariable.PathAccept), PathVariable('APU', "Path to apu-1-config, or to APR's install area", '/usr', PathVariable.PathAccept), PathVariable('ZLIB', "Path to zlib's install area", '/usr', PathVariable.PathIsDir), PathVariable('GSSAPI', "Path to GSSAPI's install area", None, None), BoolVariable('DEBUG', "Enable debugging info and strict compile warnings", False), ) if sys.platform == 'darwin': opts.AddVariables( PathVariable('OPENSSL', "Path to OpenSSL's install area", None, None)) else: opts.AddVariables( PathVariable('OPENSSL', "Path to OpenSSL's install area", '/usr', PathVariable.PathIsDir)) env = Environment(variables=opts, tools=('default', 'textfile',), CPPPATH=['.', ], ) match = re.search('SERF_MAJOR_VERSION ([0-9]+).*' 'SERF_MINOR_VERSION ([0-9]+).*' 'SERF_PATCH_VERSION ([0-9]+)', env.File('serf.h').get_contents(), re.DOTALL) MAJOR, MINOR, PATCH = [int(x) for x in match.groups()] env.Append(MAJOR=str(MAJOR)) # 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 'Unknown variables:', ', '.join(unknown.keys()) Exit(1) apr = str(env['APR']) apu = str(env['APU']) zlib = str(env['ZLIB']) gssapi = env.get('GSSAPI', 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 openssl = env.get('OPENSSL', None) if openssl and os.path.isdir(str(openssl)): env.Append(CPPPATH='$OPENSSL/include') env.Append(LIBPATH='$OPENSSL/lib') env.Append(CFLAGS='-DSERF_HAVE_OPENSSL') debug = env.get('DEBUG', None) Help(opts.GenerateHelpText(env)) opts.Save(SAVED_CONFIG, env) # PLATFORM-SPECIFIC BUILD TWEAKS def link_rpath(d): if sys.platform == 'sunos5': return '-Wl,-R,%s' % (d,) return '-Wl,-rpath,%s' % (d,) thisdir = os.getcwd() libdir = '$PREFIX/lib' incdir = '$PREFIX/include/serf-$MAJOR' LIBNAME = 'libserf-${MAJOR}' if sys.platform != 'win32': LIBNAMESTATIC = LIBNAME else: LIBNAMESTATIC = 'serf-${MAJOR}' linkflags = [] if sys.platform != 'win32': linkflags.append(link_rpath(libdir)) else: linkflags.append(['/nologo']) if sys.platform == 'darwin': # linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,)) linkflags.append('-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,)) # 'man ld' says positive non-zero for the first number, so we add one. # Mac's interpretation of compatibility is the same as our MINOR version. linkflags.append('-Wl,-compatibility_version,%d' % (MINOR+1,)) linkflags.append('-Wl,-current_version,%d.%d' % (MINOR+1, PATCH,)) # enable macosxssl_buckets only on Mac OS X 10.7+ macosxssl = False ver, _, _ = platform.mac_ver() ver = float('.'.join(ver.split('.')[:2])) if ver >= 10.7: macosxssl = True env.Append(CFLAGS='-DSERF_HAVE_MACOSXSSL') if sys.platform == 'win32': ### we should create serf.def for Windows DLLs and add it into the link ### step somehow. pass ccflags = [ ] if sys.platform != 'win32': ### gcc only. figure out appropriate test / better way to check these ### flags, and check for gcc. ccflags = ['-std=c89', '-Wdeclaration-after-statement', '-Wmissing-prototypes', ] ### -Wall is not available on Solaris if sys.platform != 'sunos5': ccflags.append(['-Wall', ]) if debug: ccflags.append(['-g']) else: ccflags.append('-O2') else: ccflags.append(['/nologo', '/W4', '/Zi']) if debug: ccflags.append(['/Od']) else: ccflags.append(['/O2']) libs = [ ] if sys.platform != 'win32': ### works for Mac OS. probably needs to change libs = ['ssl', 'crypto', 'z', ] if macosxssl: env.Append(FRAMEWORKS=['Security', 'SecurityInterface', 'CoreFoundation', 'AppKit']) libs.append('objc') if sys.platform == 'sunos5': libs.append('m') env.Replace(LINKFLAGS=linkflags, CCFLAGS=ccflags, LIBS=libs, ) # PLAN THE BUILD SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c') if macosxssl: SOURCES += ['buckets/macosxssl_helper.m'] lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES) lib_shared = env.SharedLibrary(LIBNAME, SOURCES) if sys.platform == 'win32': if debug: env.Append(CFLAGS='/MDd') else: env.Append(CFLAGS='/MD') env.Append(LIBS=['user32.lib', 'advapi32.lib', 'gdi32.lib', 'ws2_32.lib', 'crypt32.lib', 'mswsock.lib', 'rpcrt4.lib', 'secur32.lib']) # Get apr/apu information into our build env.Append(CFLAGS='-DWIN32 ' + \ '/I "$APR/include" /I "$APU/include" ' + \ '-DWIN32 -DWIN32_LEAN_AND_MEAN -DNOUSER' + \ '-DNOGDI -DNONLS -DNOCRYPT') env.Append(LIBPATH=['$APR/Release','$APU/Release'], LIBS=['libapr-1.lib', 'libaprutil-1.lib']) apr_libs='libapr-1.lib' apu_libs='libaprutil-1.lib' # zlib env.Append(CFLAGS='/I "$ZLIB"') env.Append(LIBPATH='$ZLIB', LIBS='zlib.lib') # openssl env.Append(CPPPATH='$OPENSSL/inc32') env.Append(LIBPATH='$OPENSSL/out32dll', LIBS=['libeay32.lib', 'ssleay32.lib']) else: if os.path.isdir(apr): apr = os.path.join(apr, 'bin', 'apr-1-config') env['APR'] = apr 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 implict ### dependency upon gcc. probably ParseConfig doesn't know what to do with ### the apr-1-config output if CALLOUT_OKAY: env.ParseConfig('$APR --cflags --cppflags --ldflags --includes' ' --link-ld --libs') env.ParseConfig('$APU --ldflags --includes --link-ld --libs') ### there is probably a better way to run/capture output. ### env.ParseConfig() may be handy for getting this stuff into the build if CALLOUT_OKAY: apr_libs = os.popen(env.subst('$APR --link-libtool --libs')).read().strip() apu_libs = os.popen(env.subst('$APU --link-libtool --libs')).read().strip() else: apr_libs = '' apu_libs = '' env.Append(CPPPATH='$OPENSSL/include') env.Append(LIBPATH='$OPENSSL/lib') # If build with gssapi, get its information and define SERF_HAVE_GSSAPI if gssapi and CALLOUT_OKAY: env.ParseConfig('$GSSAPI --libs gssapi') env.Append(CFLAGS='-DSERF_HAVE_GSSAPI') if sys.platform == 'win32': env.Append(CFLAGS='-DSERF_HAVE_SPNEGO -DSERF_HAVE_SSPI') # On Solaris, 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. if sys.platform == 'sunos5': for d in env['LIBPATH']: env.Append(LINKFLAGS=link_rpath(d)) # Set up the construction of serf-*.pc # TODO: add gssapi libs pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,), env.File('build/serf.pc.in'), SUBST_DICT = { '@MAJOR@': str(MAJOR), '@PREFIX@': '$PREFIX', '@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,), '@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH), '@LIBS@': '%s %s -lz' % (apu_libs, apr_libs), }) env.Default(lib_static, lib_shared, pkgconfig) if CALLOUT_OKAY: conf = Configure(env) ### some configuration stuffs env = conf.Finish() # INSTALLATION STUFF install_static = env.Install(libdir, lib_static) install_shared = env.Install(libdir, lib_shared) if sys.platform == 'darwin': install_shared_path = install_shared[0].abspath env.AddPostAction(install_shared, ('install_name_tool -id %s %s' % (install_shared_path, install_shared_path))) ### construct shared lib symlinks. this also means install the lib ### as libserf-2.1.0.0.dylib, then add the symlinks. ### note: see InstallAs env.Alias('install-lib', [install_static, install_shared, ]) env.Alias('install-inc', env.Install(incdir, HEADER_FILES)) env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'), 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() TEST_PROGRAMS = [ 'test/serf_get', 'test/serf_response', 'test/serf_request', 'test/serf_spider', 'test/test_all', 'test/serf_bwtp', ] env.AlwaysBuild(env.Alias('check', TEST_PROGRAMS, 'build/check.sh')) # Find the (dynamic) library in this directory linkflags = [link_rpath(thisdir,), ] tenv.Replace(LINKFLAGS=linkflags) tenv.Prepend(LIBS=['libserf-2', ], LIBPATH=[thisdir, ]) for proggie in TEST_PROGRAMS: if proggie.endswith('test_all'): tenv.Program('test/test_all', [ 'test/test_all.c', 'test/CuTest.c', 'test/test_util.c', 'test/test_context.c', 'test/test_buckets.c', 'test/test_auth.c', 'test/mock_buckets.c', 'test/test_ssl.c', 'test/server/test_server.c', 'test/server/test_sslserver.c', ]) else: tenv.Program(proggie, [proggie + '.c']) # 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')