1253893Speter# -*- python -*- 2253893Speter# 3253893Speter# Copyright 2011-2012 Justin Erenkrantz and Greg Stein 4253893Speter# 5253893Speter# Licensed under the Apache License, Version 2.0 (the "License"); 6253893Speter# you may not use this file except in compliance with the License. 7253893Speter# You may obtain a copy of the License at 8253893Speter# 9253893Speter# http://www.apache.org/licenses/LICENSE-2.0 10253893Speter# 11253893Speter# Unless required by applicable law or agreed to in writing, software 12253893Speter# distributed under the License is distributed on an "AS IS" BASIS, 13253893Speter# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14253893Speter# See the License for the specific language governing permissions and 15253893Speter# limitations under the License. 16253893Speter# 17253893Speter 18253893Speterimport sys 19253893Speterimport os 20253893Speterimport re 21253893Speter 22262339SpeterEnsureSConsVersion(2,3,0) 23262339Speter 24253893SpeterHEADER_FILES = ['serf.h', 25253893Speter 'serf_bucket_types.h', 26253893Speter 'serf_bucket_util.h', 27253893Speter ] 28253893Speter 29253893Speter# where we save the configuration variables 30253893SpeterSAVED_CONFIG = '.saved_config' 31253893Speter 32253893Speter# Variable class that does no validation on the input 33253893Speterdef _converter(val): 34253893Speter """ 35253893Speter """ 36253893Speter if val == 'none': 37253893Speter val = [] 38253893Speter else: 39262339Speter val = val.split(' ') 40253893Speter return val 41253893Speter 42253893Speterdef RawListVariable(key, help, default): 43253893Speter """ 44253893Speter The input parameters describe a 'raw string list' option. This class 45262339Speter accepts a space-separated string and converts it to a list. 46253893Speter """ 47253893Speter return (key, '%s' % (help), default, None, lambda val: _converter(val)) 48253893Speter 49262339Speter# Custom path validator, creates directory when a specified option is set. 50262339Speter# To be used to ensure a PREFIX directory is only created when installing. 51262339Speterdef createPathIsDirCreateWithTarget(target): 52262339Speter def my_validator(key, val, env): 53262339Speter build_targets = (map(str, BUILD_TARGETS)) 54262339Speter if target in build_targets: 55262339Speter return PathVariable.PathIsDirCreate(key, val, env) 56262339Speter else: 57262339Speter return PathVariable.PathAccept(key, val, env) 58262339Speter return my_validator 59262339Speter 60253893Speter# default directories 61253893Speterif sys.platform == 'win32': 62262339Speter default_incdir='..' 63253893Speter default_libdir='..' 64253893Speter default_prefix='Debug' 65253893Speterelse: 66262339Speter default_incdir='/usr' 67262339Speter default_libdir='$PREFIX/lib' 68253893Speter default_prefix='/usr/local' 69253893Speter 70253893Speteropts = Variables(files=[SAVED_CONFIG]) 71253893Speteropts.AddVariables( 72253893Speter PathVariable('PREFIX', 73253893Speter 'Directory to install under', 74253893Speter default_prefix, 75262339Speter createPathIsDirCreateWithTarget('install')), 76262339Speter PathVariable('LIBDIR', 77262339Speter 'Directory to install architecture dependent libraries under', 78262339Speter default_libdir, 79262339Speter createPathIsDirCreateWithTarget('install')), 80253893Speter PathVariable('APR', 81253893Speter "Path to apr-1-config, or to APR's install area", 82262339Speter default_incdir, 83253893Speter PathVariable.PathAccept), 84253893Speter PathVariable('APU', 85253893Speter "Path to apu-1-config, or to APR's install area", 86262339Speter default_incdir, 87253893Speter PathVariable.PathAccept), 88253893Speter PathVariable('OPENSSL', 89253893Speter "Path to OpenSSL's install area", 90262339Speter default_incdir, 91253893Speter PathVariable.PathIsDir), 92253893Speter PathVariable('ZLIB', 93253893Speter "Path to zlib's install area", 94262339Speter default_incdir, 95253893Speter PathVariable.PathIsDir), 96253893Speter PathVariable('GSSAPI', 97253893Speter "Path to GSSAPI's install area", 98253893Speter None, 99253893Speter None), 100253893Speter BoolVariable('DEBUG', 101253893Speter "Enable debugging info and strict compile warnings", 102253893Speter False), 103253893Speter BoolVariable('APR_STATIC', 104253893Speter "Enable using a static compiled APR", 105253893Speter False), 106253893Speter RawListVariable('CC', "Command name or path of the C compiler", None), 107262339Speter RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)", 108253893Speter None), 109253893Speter RawListVariable('LIBS', "Extra libraries passed to the linker, " 110262339Speter "e.g. \"-l<library1> -l<library2>\" (space separated)", None), 111262339Speter RawListVariable('LINKFLAGS', "Extra flags for the linker (space-separated)", 112253893Speter None), 113253893Speter RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor " 114262339Speter "(space separated)", None), 115253893Speter ) 116253893Speter 117253893Speterif sys.platform == 'win32': 118253893Speter opts.AddVariables( 119253893Speter # By default SCons builds for the host platform on Windows, when using 120253893Speter # a supported compiler (E.g. VS2010/VS2012). Allow overriding 121253893Speter 122253893Speter # Note that Scons 1.3 only supports this on Windows and only when 123253893Speter # constructing Environment(). Later changes to TARGET_ARCH are ignored 124253893Speter EnumVariable('TARGET_ARCH', 125253893Speter "Platform to build for (x86|x64|win32|x86_64)", 126253893Speter 'x86', 127253893Speter allowed_values=('x86', 'x86_64', 'ia64'), 128253893Speter map={'X86' : 'x86', 129253893Speter 'win32': 'x86', 130253893Speter 'Win32': 'x86', 131253893Speter 'x64' : 'x86_64', 132253893Speter 'X64' : 'x86_64' 133253893Speter }), 134253893Speter 135253893Speter EnumVariable('MSVC_VERSION', 136253893Speter "Visual C++ to use for building (E.g. 11.0, 9.0)", 137253893Speter None, 138253893Speter allowed_values=('12.0', '11.0', '10.0', '9.0', '8.0', '6.0') 139253893Speter ), 140253893Speter 141253893Speter # We always documented that we handle an install layout, but in fact we 142253893Speter # hardcoded source layouts. Allow disabling this behavior. 143253893Speter # ### Fix default? 144253893Speter BoolVariable('SOURCE_LAYOUT', 145253893Speter "Assume a source layout instead of install layout", 146253893Speter True), 147253893Speter ) 148253893Speter 149253893Speterenv = Environment(variables=opts, 150253893Speter tools=('default', 'textfile',), 151253893Speter CPPPATH=['.', ], 152253893Speter ) 153253893Speter 154253893Speterenv.Append(BUILDERS = { 155253893Speter 'GenDef' : 156253893Speter Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET', 157253893Speter suffix='.def', src_suffix='.h') 158253893Speter }) 159253893Speter 160253893Spetermatch = re.search('SERF_MAJOR_VERSION ([0-9]+).*' 161253893Speter 'SERF_MINOR_VERSION ([0-9]+).*' 162253893Speter 'SERF_PATCH_VERSION ([0-9]+)', 163253893Speter env.File('serf.h').get_contents(), 164253893Speter re.DOTALL) 165253893SpeterMAJOR, MINOR, PATCH = [int(x) for x in match.groups()] 166253893Speterenv.Append(MAJOR=str(MAJOR)) 167262339Speterenv.Append(MINOR=str(MINOR)) 168262339Speterenv.Append(PATCH=str(PATCH)) 169253893Speter 170253893Speter# Calling external programs is okay if we're not cleaning or printing help. 171253893Speter# (cleaning: no sense in fetching information; help: we may not know where 172253893Speter# they are) 173253893SpeterCALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help')) 174253893Speter 175253893Speter 176253893Speter# HANDLING OF OPTION VARIABLES 177253893Speter 178253893Speterunknown = opts.UnknownVariables() 179253893Speterif unknown: 180253893Speter print 'Unknown variables:', ', '.join(unknown.keys()) 181253893Speter Exit(1) 182253893Speter 183253893Speterapr = str(env['APR']) 184253893Speterapu = str(env['APU']) 185253893Speterzlib = str(env['ZLIB']) 186253893Spetergssapi = env.get('GSSAPI', None) 187253893Speter 188253893Speterif gssapi and os.path.isdir(gssapi): 189253893Speter krb5_config = os.path.join(gssapi, 'bin', 'krb5-config') 190253893Speter if os.path.isfile(krb5_config): 191253893Speter gssapi = krb5_config 192253893Speter env['GSSAPI'] = krb5_config 193253893Speter 194253893Speterdebug = env.get('DEBUG', None) 195253893Speteraprstatic = env.get('APR_STATIC', None) 196253893Speter 197253893SpeterHelp(opts.GenerateHelpText(env)) 198253893Speteropts.Save(SAVED_CONFIG, env) 199253893Speter 200253893Speter 201253893Speter# PLATFORM-SPECIFIC BUILD TWEAKS 202253893Speter 203253893Speterthisdir = os.getcwd() 204262339Speterlibdir = '$LIBDIR' 205253893Speterincdir = '$PREFIX/include/serf-$MAJOR' 206253893Speter 207262339Speter# This version string is used in the dynamic library name, and for Mac OS X also 208262339Speter# for the current_version and compatibility_version options in the .dylib 209262339Speter# 210262339Speter# Unfortunately we can't set the .dylib compatibility_version option separately 211262339Speter# from current_version, so don't use the PATCH level to avoid that build and 212262339Speter# runtime patch levels have to be identical. 213262339Speterenv['SHLIBVERSION'] = '%d.%d.%d' % (MAJOR, MINOR, 0) 214262339Speter 215262339SpeterLIBNAME = 'libserf-%d' % (MAJOR,) 216253893Speterif sys.platform != 'win32': 217253893Speter LIBNAMESTATIC = LIBNAME 218253893Speterelse: 219253893Speter LIBNAMESTATIC = 'serf-${MAJOR}' 220253893Speter 221253893Speterenv.Append(RPATH=libdir, 222253893Speter PDB='${TARGET.filebase}.pdb') 223253893Speter 224253893Speterif sys.platform == 'darwin': 225253893Speter# linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,)) 226253893Speter env.Append(LINKFLAGS='-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,)) 227253893Speter 228253893Speterif sys.platform != 'win32': 229253893Speter ### gcc only. figure out appropriate test / better way to check these 230253893Speter ### flags, and check for gcc. 231253893Speter env.Append(CFLAGS='-std=c89') 232253893Speter 233262339Speter ### These warnings are not available on Solaris 234253893Speter if sys.platform != 'sunos5': 235262339Speter env.Append(CCFLAGS=['-Wdeclaration-after-statement', 236262339Speter '-Wmissing-prototypes', 237262339Speter '-Wall']) 238253893Speter 239253893Speter if debug: 240253893Speter env.Append(CCFLAGS='-g') 241253893Speter env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 242253893Speter else: 243253893Speter env.Append(CCFLAGS='-O2') 244253893Speter env.Append(CPPDEFINES='NDEBUG') 245253893Speter 246253893Speter ### works for Mac OS. probably needs to change 247253893Speter env.Append(LIBS=['ssl', 'crypto', 'z', ]) 248253893Speter 249253893Speter if sys.platform == 'sunos5': 250253893Speter env.Append(LIBS='m') 251253893Speterelse: 252253893Speter # Warning level 4, no unused argument warnings 253253893Speter env.Append(CCFLAGS=['/W4', '/wd4100']) 254253893Speter 255253893Speter # Choose runtime and optimization 256253893Speter if debug: 257253893Speter # Disable optimizations for debugging, use debug DLL runtime 258253893Speter env.Append(CCFLAGS=['/Od', '/MDd']) 259253893Speter env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 260253893Speter else: 261253893Speter # Optimize for speed, use DLL runtime 262253893Speter env.Append(CCFLAGS=['/O2', '/MD']) 263253893Speter env.Append(CPPDEFINES='NDEBUG') 264262339Speter env.Append(LINKFLAGS='/RELEASE') 265253893Speter 266253893Speter# PLAN THE BUILD 267253893SpeterSHARED_SOURCES = [] 268253893Speterif sys.platform == 'win32': 269253893Speter env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h']) 270253893Speter SHARED_SOURCES.append(['serf.def']) 271253893Speter 272253893SpeterSOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c') 273253893Speter 274253893Speterlib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES) 275253893Speterlib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES) 276253893Speter 277253893Speterif aprstatic: 278253893Speter env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC']) 279253893Speter 280253893Speterif sys.platform == 'win32': 281253893Speter env.Append(LIBS=['user32.lib', 'advapi32.lib', 'gdi32.lib', 'ws2_32.lib', 282253893Speter 'crypt32.lib', 'mswsock.lib', 'rpcrt4.lib', 'secur32.lib']) 283253893Speter 284253893Speter # Get apr/apu information into our build 285253893Speter env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER', 286253893Speter 'NOGDI', 'NONLS','NOCRYPT']) 287253893Speter 288253893Speter if env.get('TARGET_ARCH', None) == 'x86_64': 289253893Speter env.Append(CPPDEFINES=['WIN64']) 290253893Speter 291253893Speter if aprstatic: 292253893Speter apr_libs='apr-1.lib' 293253893Speter apu_libs='aprutil-1.lib' 294253893Speter else: 295253893Speter apr_libs='libapr-1.lib' 296253893Speter apu_libs='libaprutil-1.lib' 297253893Speter 298253893Speter env.Append(LIBS=[apr_libs, apu_libs]) 299253893Speter if not env.get('SOURCE_LAYOUT', None): 300253893Speter env.Append(LIBPATH=['$APR/lib', '$APU/lib'], 301253893Speter CPPPATH=['$APR/include/apr-1', '$APU/include/apr-1']) 302253893Speter elif aprstatic: 303253893Speter env.Append(LIBPATH=['$APR/LibR','$APU/LibR'], 304253893Speter CPPPATH=['$APR/include', '$APU/include']) 305253893Speter else: 306253893Speter env.Append(LIBPATH=['$APR/Release','$APU/Release'], 307253893Speter CPPPATH=['$APR/include', '$APU/include']) 308253893Speter 309253893Speter # zlib 310253893Speter env.Append(LIBS='zlib.lib') 311253893Speter if not env.get('SOURCE_LAYOUT', None): 312253893Speter env.Append(CPPPATH='$ZLIB/include', 313253893Speter LIBPATH='$ZLIB/lib') 314253893Speter else: 315253893Speter env.Append(CPPPATH='$ZLIB', 316253893Speter LIBPATH='$ZLIB') 317253893Speter 318253893Speter # openssl 319253893Speter env.Append(LIBS=['libeay32.lib', 'ssleay32.lib']) 320253893Speter if not env.get('SOURCE_LAYOUT', None): 321253893Speter env.Append(CPPPATH='$OPENSSL/include/openssl', 322253893Speter LIBPATH='$OPENSSL/lib') 323253893Speter elif 0: # opensslstatic: 324253893Speter env.Append(CPPPATH='$OPENSSL/inc32', 325253893Speter LIBPATH='$OPENSSL/out32') 326253893Speter else: 327253893Speter env.Append(CPPPATH='$OPENSSL/inc32', 328253893Speter LIBPATH='$OPENSSL/out32dll') 329253893Speterelse: 330253893Speter if os.path.isdir(apr): 331253893Speter apr = os.path.join(apr, 'bin', 'apr-1-config') 332253893Speter env['APR'] = apr 333253893Speter if os.path.isdir(apu): 334253893Speter apu = os.path.join(apu, 'bin', 'apu-1-config') 335253893Speter env['APU'] = apu 336253893Speter 337253893Speter ### we should use --cc, but that is giving some scons error about an implict 338253893Speter ### dependency upon gcc. probably ParseConfig doesn't know what to do with 339253893Speter ### the apr-1-config output 340253893Speter if CALLOUT_OKAY: 341253893Speter env.ParseConfig('$APR --cflags --cppflags --ldflags --includes' 342253893Speter ' --link-ld --libs') 343253893Speter env.ParseConfig('$APU --ldflags --includes --link-ld --libs') 344253893Speter 345253893Speter ### there is probably a better way to run/capture output. 346253893Speter ### env.ParseConfig() may be handy for getting this stuff into the build 347253893Speter if CALLOUT_OKAY: 348253893Speter apr_libs = os.popen(env.subst('$APR --link-libtool --libs')).read().strip() 349253893Speter apu_libs = os.popen(env.subst('$APU --link-libtool --libs')).read().strip() 350253893Speter else: 351253893Speter apr_libs = '' 352253893Speter apu_libs = '' 353253893Speter 354253893Speter env.Append(CPPPATH='$OPENSSL/include') 355253893Speter env.Append(LIBPATH='$OPENSSL/lib') 356253893Speter 357253893Speter 358253893Speter# If build with gssapi, get its information and define SERF_HAVE_GSSAPI 359253893Speterif gssapi and CALLOUT_OKAY: 360262339Speter env.ParseConfig('$GSSAPI --cflags gssapi') 361262339Speter def parse_libs(env, cmd, unique=1): 362262339Speter env['GSSAPI_LIBS'] = cmd.strip() 363262339Speter return env.MergeFlags(cmd, unique) 364262339Speter env.ParseConfig('$GSSAPI --libs gssapi', parse_libs) 365253893Speter env.Append(CPPDEFINES='SERF_HAVE_GSSAPI') 366253893Speterif sys.platform == 'win32': 367253893Speter env.Append(CPPDEFINES=['SERF_HAVE_SSPI']) 368253893Speter 369262339Speter# On some systems, the -R values that APR describes never make it into actual 370253893Speter# RPATH flags. We'll manually map all directories in LIBPATH into new 371253893Speter# flags to set RPATH values. 372262339Speterfor d in env['LIBPATH']: 373262339Speter env.Append(RPATH=':'+d) 374253893Speter 375253893Speter# Set up the construction of serf-*.pc 376253893Speterpkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,), 377253893Speter env.File('build/serf.pc.in'), 378253893Speter SUBST_DICT = { 379253893Speter '@MAJOR@': str(MAJOR), 380253893Speter '@PREFIX@': '$PREFIX', 381262339Speter '@LIBDIR@': '$LIBDIR', 382253893Speter '@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,), 383253893Speter '@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH), 384262339Speter '@LIBS@': '%s %s %s -lz' % (apu_libs, apr_libs, 385262339Speter env.get('GSSAPI_LIBS', '')), 386253893Speter }) 387253893Speter 388253893Speterenv.Default(lib_static, lib_shared, pkgconfig) 389253893Speter 390253893Speterif CALLOUT_OKAY: 391253893Speter conf = Configure(env) 392253893Speter 393253893Speter ### some configuration stuffs 394253893Speter 395253893Speter env = conf.Finish() 396253893Speter 397253893Speter 398253893Speter# INSTALLATION STUFF 399253893Speter 400253893Speterinstall_static = env.Install(libdir, lib_static) 401262339Speterinstall_shared = env.InstallVersionedLib(libdir, lib_shared) 402253893Speter 403253893Speterif sys.platform == 'darwin': 404262339Speter # Change the shared library install name (id) to its final name and location. 405262339Speter # Notes: 406262339Speter # If --install-sandbox=<path> is specified, install_shared_path will point 407262339Speter # to a path in the sandbox. We can't use that path because the sandbox is 408262339Speter # only a temporary location. The id should be the final target path. 409262339Speter # Also, we shouldn't use the complete version number for id, as that'll 410262339Speter # make applications depend on the exact major.minor.patch version of serf. 411262339Speter 412253893Speter install_shared_path = install_shared[0].abspath 413262339Speter target_install_shared_path = os.path.join(libdir, '%s.dylib' % LIBNAME) 414253893Speter env.AddPostAction(install_shared, ('install_name_tool -id %s %s' 415262339Speter % (target_install_shared_path, 416253893Speter install_shared_path))) 417253893Speter 418253893Speterenv.Alias('install-lib', [install_static, install_shared, 419253893Speter ]) 420253893Speterenv.Alias('install-inc', env.Install(incdir, HEADER_FILES)) 421253893Speterenv.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'), 422253893Speter pkgconfig)) 423253893Speterenv.Alias('install', ['install-lib', 'install-inc', 'install-pc', ]) 424253893Speter 425253893Speter 426253893Speter# TESTS 427253893Speter### make move to a separate scons file in the test/ subdir? 428253893Speter 429253893Spetertenv = env.Clone() 430253893Speter 431253893SpeterTEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider', 432253893Speter 'test_all', 'serf_bwtp' ] 433253893Speterif sys.platform == 'win32': 434253893Speter TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in TEST_PROGRAMS ] 435253893Speterelse: 436253893Speter TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ] 437253893Speter 438253893Speterenv.AlwaysBuild(env.Alias('check', TEST_EXES, sys.executable + ' build/check.py', 439253893Speter ENV={'PATH' : os.environ['PATH']})) 440253893Speter 441253893Speter# Find the (dynamic) library in this directory 442253893Spetertenv.Replace(RPATH=thisdir) 443253893Spetertenv.Prepend(LIBS=[LIBNAMESTATIC, ], 444253893Speter LIBPATH=[thisdir, ]) 445253893Speter 446253893Spetertestall_files = [ 447253893Speter 'test/test_all.c', 448253893Speter 'test/CuTest.c', 449253893Speter 'test/test_util.c', 450253893Speter 'test/test_context.c', 451253893Speter 'test/test_buckets.c', 452253893Speter 'test/test_auth.c', 453253893Speter 'test/mock_buckets.c', 454253893Speter 'test/test_ssl.c', 455253893Speter 'test/server/test_server.c', 456253893Speter 'test/server/test_sslserver.c', 457253893Speter ] 458253893Speter 459253893Speterfor proggie in TEST_EXES: 460253893Speter if 'test_all' in proggie: 461253893Speter tenv.Program(proggie, testall_files ) 462253893Speter else: 463253893Speter tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c']) 464253893Speter 465253893Speter 466253893Speter# HANDLE CLEANING 467253893Speter 468253893Speterif env.GetOption('clean'): 469253893Speter # When we're cleaning, we want the dependency tree to include "everything" 470253893Speter # that could be built. Thus, include all of the tests. 471253893Speter env.Default('check') 472