SConstruct revision 262339
111894Speter# -*- python -*- 211894Speter# 311894Speter# Copyright 2011-2012 Justin Erenkrantz and Greg Stein 411894Speter# 59Sjkh# Licensed under the Apache License, Version 2.0 (the "License"); 69Sjkh# you may not use this file except in compliance with the License. 79Sjkh# You may obtain a copy of the License at 89Sjkh# 99Sjkh# http://www.apache.org/licenses/LICENSE-2.0 109Sjkh# 119Sjkh# Unless required by applicable law or agreed to in writing, software 129Sjkh# distributed under the License is distributed on an "AS IS" BASIS, 139Sjkh# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 149Sjkh# See the License for the specific language governing permissions and 159Sjkh# limitations under the License. 169Sjkh# 179Sjkh 189Sjkhimport sys 199Sjkhimport os 2011894Speterimport re 2111894Speter 2211894SpeterEnsureSConsVersion(2,3,0) 239Sjkh 249SjkhHEADER_FILES = ['serf.h', 259Sjkh 'serf_bucket_types.h', 269Sjkh 'serf_bucket_util.h', 279Sjkh ] 289Sjkh 299Sjkh# where we save the configuration variables 309SjkhSAVED_CONFIG = '.saved_config' 3111894Speter 3211894Speter# Variable class that does no validation on the input 338858Srgrimesdef _converter(val): 3411894Speter """ 3511894Speter """ 3611894Speter if val == 'none': 3711894Speter val = [] 3811894Speter else: 3911894Speter val = val.split(' ') 4011894Speter return val 4111894Speter 4211894Speterdef RawListVariable(key, help, default): 4311894Speter """ 4411894Speter The input parameters describe a 'raw string list' option. This class 4511894Speter accepts a space-separated string and converts it to a list. 4611894Speter """ 4711894Speter return (key, '%s' % (help), default, None, lambda val: _converter(val)) 4811894Speter 4911894Speter# Custom path validator, creates directory when a specified option is set. 509Sjkh# To be used to ensure a PREFIX directory is only created when installing. 519Sjkhdef createPathIsDirCreateWithTarget(target): 529Sjkh def my_validator(key, val, env): 539Sjkh build_targets = (map(str, BUILD_TARGETS)) 549Sjkh if target in build_targets: 559Sjkh return PathVariable.PathIsDirCreate(key, val, env) 569Sjkh else: 579Sjkh return PathVariable.PathAccept(key, val, env) 589Sjkh return my_validator 599Sjkh 609Sjkh# default directories 619Sjkhif sys.platform == 'win32': 629Sjkh default_incdir='..' 639Sjkh default_libdir='..' 649Sjkh default_prefix='Debug' 659Sjkhelse: 668858Srgrimes default_incdir='/usr' 679Sjkh default_libdir='$PREFIX/lib' 689Sjkh default_prefix='/usr/local' 698858Srgrimes 709Sjkhopts = Variables(files=[SAVED_CONFIG]) 719Sjkhopts.AddVariables( 729Sjkh PathVariable('PREFIX', 738858Srgrimes 'Directory to install under', 749Sjkh default_prefix, 759Sjkh createPathIsDirCreateWithTarget('install')), 769Sjkh PathVariable('LIBDIR', 779Sjkh 'Directory to install architecture dependent libraries under', 788858Srgrimes default_libdir, 799Sjkh createPathIsDirCreateWithTarget('install')), 809Sjkh PathVariable('APR', 818858Srgrimes "Path to apr-1-config, or to APR's install area", 829Sjkh default_incdir, 839Sjkh PathVariable.PathAccept), 849Sjkh PathVariable('APU', 858858Srgrimes "Path to apu-1-config, or to APR's install area", 869Sjkh default_incdir, 879Sjkh PathVariable.PathAccept), 889Sjkh PathVariable('OPENSSL', 899Sjkh "Path to OpenSSL's install area", 909Sjkh default_incdir, 919Sjkh PathVariable.PathIsDir), 929Sjkh PathVariable('ZLIB', 939Sjkh "Path to zlib's install area", 949Sjkh default_incdir, 959Sjkh PathVariable.PathIsDir), 969Sjkh PathVariable('GSSAPI', 979Sjkh "Path to GSSAPI's install area", 989Sjkh None, 999Sjkh None), 1009Sjkh BoolVariable('DEBUG', 1019Sjkh "Enable debugging info and strict compile warnings", 10211894Speter False), 10311894Speter BoolVariable('APR_STATIC', 1049Sjkh "Enable using a static compiled APR", 10550472Speter False), 1069Sjkh RawListVariable('CC', "Command name or path of the C compiler", None), 10711894Speter RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)", 1089Sjkh None), 1099Sjkh RawListVariable('LIBS', "Extra libraries passed to the linker, " 1109Sjkh "e.g. \"-l<library1> -l<library2>\" (space separated)", None), 1119Sjkh RawListVariable('LINKFLAGS', "Extra flags for the linker (space-separated)", 11211894Speter None), 1139Sjkh RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor " 11411894Speter "(space separated)", None), 1159Sjkh ) 11611894Speter 11711894Speterif sys.platform == 'win32': 11811894Speter opts.AddVariables( 11911894Speter # By default SCons builds for the host platform on Windows, when using 12011894Speter # a supported compiler (E.g. VS2010/VS2012). Allow overriding 12111894Speter 1229Sjkh # Note that Scons 1.3 only supports this on Windows and only when 12311894Speter # constructing Environment(). Later changes to TARGET_ARCH are ignored 12411894Speter EnumVariable('TARGET_ARCH', 12511894Speter "Platform to build for (x86|x64|win32|x86_64)", 12611894Speter 'x86', 1279Sjkh allowed_values=('x86', 'x86_64', 'ia64'), 12811894Speter map={'X86' : 'x86', 12911894Speter 'win32': 'x86', 13011894Speter 'Win32': 'x86', 13111894Speter 'x64' : 'x86_64', 13211894Speter 'X64' : 'x86_64' 13311894Speter }), 13411894Speter 13511894Speter EnumVariable('MSVC_VERSION', 13611894Speter "Visual C++ to use for building (E.g. 11.0, 9.0)", 13711894Speter None, 13811894Speter allowed_values=('12.0', '11.0', '10.0', '9.0', '8.0', '6.0') 13911894Speter ), 14011894Speter 14111894Speter # We always documented that we handle an install layout, but in fact we 14211894Speter # hardcoded source layouts. Allow disabling this behavior. 14311894Speter # ### Fix default? 14411894Speter BoolVariable('SOURCE_LAYOUT', 14511894Speter "Assume a source layout instead of install layout", 14611894Speter True), 14711894Speter ) 14811894Speter 14911894Speterenv = Environment(variables=opts, 15011894Speter tools=('default', 'textfile',), 1519Sjkh CPPPATH=['.', ], 15211894Speter ) 1539Sjkh 1549Sjkhenv.Append(BUILDERS = { 1559Sjkh 'GenDef' : 1569Sjkh Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET', 1579Sjkh suffix='.def', src_suffix='.h') 15811894Speter }) 15911894Speter 1609Sjkhmatch = re.search('SERF_MAJOR_VERSION ([0-9]+).*' 16111894Speter 'SERF_MINOR_VERSION ([0-9]+).*' 16211894Speter 'SERF_PATCH_VERSION ([0-9]+)', 16311894Speter env.File('serf.h').get_contents(), 16411894Speter re.DOTALL) 16511894SpeterMAJOR, MINOR, PATCH = [int(x) for x in match.groups()] 1669Sjkhenv.Append(MAJOR=str(MAJOR)) 16711894Speterenv.Append(MINOR=str(MINOR)) 16811894Speterenv.Append(PATCH=str(PATCH)) 16911894Speter 17011894Speter# Calling external programs is okay if we're not cleaning or printing help. 17111894Speter# (cleaning: no sense in fetching information; help: we may not know where 17211894Speter# they are) 17311894SpeterCALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help')) 17411894Speter 17511894Speter 1769Sjkh# HANDLING OF OPTION VARIABLES 17711894Speter 17811894Speterunknown = opts.UnknownVariables() 1799Sjkhif unknown: 1809Sjkh print 'Unknown variables:', ', '.join(unknown.keys()) 1819Sjkh Exit(1) 1829Sjkh 1839Sjkhapr = str(env['APR']) 18411894Speterapu = str(env['APU']) 1859Sjkhzlib = str(env['ZLIB']) 1869Sjkhgssapi = env.get('GSSAPI', None) 1879Sjkh 1889Sjkhif gssapi and os.path.isdir(gssapi): 18911894Speter krb5_config = os.path.join(gssapi, 'bin', 'krb5-config') 1909Sjkh if os.path.isfile(krb5_config): 19111894Speter gssapi = krb5_config 19211894Speter env['GSSAPI'] = krb5_config 19311894Speter 19411894Speterdebug = env.get('DEBUG', None) 1959Sjkhaprstatic = env.get('APR_STATIC', None) 19611894Speter 1979SjkhHelp(opts.GenerateHelpText(env)) 1989Sjkhopts.Save(SAVED_CONFIG, env) 1999Sjkh 20011894Speter 20111894Speter# PLATFORM-SPECIFIC BUILD TWEAKS 2029Sjkh 2039Sjkhthisdir = os.getcwd() 2049Sjkhlibdir = '$LIBDIR' 2059Sjkhincdir = '$PREFIX/include/serf-$MAJOR' 20611894Speter 20711894Speter# This version string is used in the dynamic library name, and for Mac OS X also 20811894Speter# for the current_version and compatibility_version options in the .dylib 20911894Speter# 21011894Speter# Unfortunately we can't set the .dylib compatibility_version option separately 21111894Speter# from current_version, so don't use the PATCH level to avoid that build and 21211894Speter# runtime patch levels have to be identical. 21311894Speterenv['SHLIBVERSION'] = '%d.%d.%d' % (MAJOR, MINOR, 0) 21411894Speter 21511894SpeterLIBNAME = 'libserf-%d' % (MAJOR,) 2169Sjkhif sys.platform != 'win32': 2179Sjkh LIBNAMESTATIC = LIBNAME 21811894Speterelse: 2199Sjkh LIBNAMESTATIC = 'serf-${MAJOR}' 2209Sjkh 2219Sjkhenv.Append(RPATH=libdir, 2229Sjkh PDB='${TARGET.filebase}.pdb') 2239Sjkh 2249Sjkhif sys.platform == 'darwin': 2259Sjkh# linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,)) 2269Sjkh env.Append(LINKFLAGS='-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,)) 2279Sjkh 2289Sjkhif sys.platform != 'win32': 2299Sjkh ### gcc only. figure out appropriate test / better way to check these 2309Sjkh ### flags, and check for gcc. 2319Sjkh env.Append(CFLAGS='-std=c89') 2329Sjkh 23311894Speter ### These warnings are not available on Solaris 2349Sjkh if sys.platform != 'sunos5': 2359Sjkh env.Append(CCFLAGS=['-Wdeclaration-after-statement', 23687625Speter '-Wmissing-prototypes', 2379Sjkh '-Wall']) 2389Sjkh 2399Sjkh if debug: 2409Sjkh env.Append(CCFLAGS='-g') 2419Sjkh env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 2429Sjkh else: 2439Sjkh env.Append(CCFLAGS='-O2') 2449Sjkh env.Append(CPPDEFINES='NDEBUG') 2459Sjkh 2469Sjkh ### works for Mac OS. probably needs to change 2479Sjkh env.Append(LIBS=['ssl', 'crypto', 'z', ]) 2489Sjkh 2499Sjkh if sys.platform == 'sunos5': 2509Sjkh env.Append(LIBS='m') 2519Sjkhelse: 25211894Speter # Warning level 4, no unused argument warnings 2539Sjkh env.Append(CCFLAGS=['/W4', '/wd4100']) 2549Sjkh 2559Sjkh # Choose runtime and optimization 2569Sjkh if debug: 2579Sjkh # Disable optimizations for debugging, use debug DLL runtime 2589Sjkh env.Append(CCFLAGS=['/Od', '/MDd']) 2599Sjkh env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 2609Sjkh else: 2619Sjkh # Optimize for speed, use DLL runtime 2629Sjkh env.Append(CCFLAGS=['/O2', '/MD']) 2639Sjkh env.Append(CPPDEFINES='NDEBUG') 2649Sjkh env.Append(LINKFLAGS='/RELEASE') 2659Sjkh 2669Sjkh# PLAN THE BUILD 2679SjkhSHARED_SOURCES = [] 26811894Speterif sys.platform == 'win32': 2699Sjkh env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h']) 2709Sjkh SHARED_SOURCES.append(['serf.def']) 271 272SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c') 273 274lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES) 275lib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES) 276 277if aprstatic: 278 env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC']) 279 280if sys.platform == 'win32': 281 env.Append(LIBS=['user32.lib', 'advapi32.lib', 'gdi32.lib', 'ws2_32.lib', 282 'crypt32.lib', 'mswsock.lib', 'rpcrt4.lib', 'secur32.lib']) 283 284 # Get apr/apu information into our build 285 env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER', 286 'NOGDI', 'NONLS','NOCRYPT']) 287 288 if env.get('TARGET_ARCH', None) == 'x86_64': 289 env.Append(CPPDEFINES=['WIN64']) 290 291 if aprstatic: 292 apr_libs='apr-1.lib' 293 apu_libs='aprutil-1.lib' 294 else: 295 apr_libs='libapr-1.lib' 296 apu_libs='libaprutil-1.lib' 297 298 env.Append(LIBS=[apr_libs, apu_libs]) 299 if not env.get('SOURCE_LAYOUT', None): 300 env.Append(LIBPATH=['$APR/lib', '$APU/lib'], 301 CPPPATH=['$APR/include/apr-1', '$APU/include/apr-1']) 302 elif aprstatic: 303 env.Append(LIBPATH=['$APR/LibR','$APU/LibR'], 304 CPPPATH=['$APR/include', '$APU/include']) 305 else: 306 env.Append(LIBPATH=['$APR/Release','$APU/Release'], 307 CPPPATH=['$APR/include', '$APU/include']) 308 309 # zlib 310 env.Append(LIBS='zlib.lib') 311 if not env.get('SOURCE_LAYOUT', None): 312 env.Append(CPPPATH='$ZLIB/include', 313 LIBPATH='$ZLIB/lib') 314 else: 315 env.Append(CPPPATH='$ZLIB', 316 LIBPATH='$ZLIB') 317 318 # openssl 319 env.Append(LIBS=['libeay32.lib', 'ssleay32.lib']) 320 if not env.get('SOURCE_LAYOUT', None): 321 env.Append(CPPPATH='$OPENSSL/include/openssl', 322 LIBPATH='$OPENSSL/lib') 323 elif 0: # opensslstatic: 324 env.Append(CPPPATH='$OPENSSL/inc32', 325 LIBPATH='$OPENSSL/out32') 326 else: 327 env.Append(CPPPATH='$OPENSSL/inc32', 328 LIBPATH='$OPENSSL/out32dll') 329else: 330 if os.path.isdir(apr): 331 apr = os.path.join(apr, 'bin', 'apr-1-config') 332 env['APR'] = apr 333 if os.path.isdir(apu): 334 apu = os.path.join(apu, 'bin', 'apu-1-config') 335 env['APU'] = apu 336 337 ### we should use --cc, but that is giving some scons error about an implict 338 ### dependency upon gcc. probably ParseConfig doesn't know what to do with 339 ### the apr-1-config output 340 if CALLOUT_OKAY: 341 env.ParseConfig('$APR --cflags --cppflags --ldflags --includes' 342 ' --link-ld --libs') 343 env.ParseConfig('$APU --ldflags --includes --link-ld --libs') 344 345 ### there is probably a better way to run/capture output. 346 ### env.ParseConfig() may be handy for getting this stuff into the build 347 if CALLOUT_OKAY: 348 apr_libs = os.popen(env.subst('$APR --link-libtool --libs')).read().strip() 349 apu_libs = os.popen(env.subst('$APU --link-libtool --libs')).read().strip() 350 else: 351 apr_libs = '' 352 apu_libs = '' 353 354 env.Append(CPPPATH='$OPENSSL/include') 355 env.Append(LIBPATH='$OPENSSL/lib') 356 357 358# If build with gssapi, get its information and define SERF_HAVE_GSSAPI 359if gssapi and CALLOUT_OKAY: 360 env.ParseConfig('$GSSAPI --cflags gssapi') 361 def parse_libs(env, cmd, unique=1): 362 env['GSSAPI_LIBS'] = cmd.strip() 363 return env.MergeFlags(cmd, unique) 364 env.ParseConfig('$GSSAPI --libs gssapi', parse_libs) 365 env.Append(CPPDEFINES='SERF_HAVE_GSSAPI') 366if sys.platform == 'win32': 367 env.Append(CPPDEFINES=['SERF_HAVE_SSPI']) 368 369# On some systems, the -R values that APR describes never make it into actual 370# RPATH flags. We'll manually map all directories in LIBPATH into new 371# flags to set RPATH values. 372for d in env['LIBPATH']: 373 env.Append(RPATH=':'+d) 374 375# Set up the construction of serf-*.pc 376pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,), 377 env.File('build/serf.pc.in'), 378 SUBST_DICT = { 379 '@MAJOR@': str(MAJOR), 380 '@PREFIX@': '$PREFIX', 381 '@LIBDIR@': '$LIBDIR', 382 '@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,), 383 '@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH), 384 '@LIBS@': '%s %s %s -lz' % (apu_libs, apr_libs, 385 env.get('GSSAPI_LIBS', '')), 386 }) 387 388env.Default(lib_static, lib_shared, pkgconfig) 389 390if CALLOUT_OKAY: 391 conf = Configure(env) 392 393 ### some configuration stuffs 394 395 env = conf.Finish() 396 397 398# INSTALLATION STUFF 399 400install_static = env.Install(libdir, lib_static) 401install_shared = env.InstallVersionedLib(libdir, lib_shared) 402 403if sys.platform == 'darwin': 404 # Change the shared library install name (id) to its final name and location. 405 # Notes: 406 # If --install-sandbox=<path> is specified, install_shared_path will point 407 # to a path in the sandbox. We can't use that path because the sandbox is 408 # only a temporary location. The id should be the final target path. 409 # Also, we shouldn't use the complete version number for id, as that'll 410 # make applications depend on the exact major.minor.patch version of serf. 411 412 install_shared_path = install_shared[0].abspath 413 target_install_shared_path = os.path.join(libdir, '%s.dylib' % LIBNAME) 414 env.AddPostAction(install_shared, ('install_name_tool -id %s %s' 415 % (target_install_shared_path, 416 install_shared_path))) 417 418env.Alias('install-lib', [install_static, install_shared, 419 ]) 420env.Alias('install-inc', env.Install(incdir, HEADER_FILES)) 421env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'), 422 pkgconfig)) 423env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ]) 424 425 426# TESTS 427### make move to a separate scons file in the test/ subdir? 428 429tenv = env.Clone() 430 431TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider', 432 'test_all', 'serf_bwtp' ] 433if sys.platform == 'win32': 434 TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in TEST_PROGRAMS ] 435else: 436 TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ] 437 438env.AlwaysBuild(env.Alias('check', TEST_EXES, sys.executable + ' build/check.py', 439 ENV={'PATH' : os.environ['PATH']})) 440 441# Find the (dynamic) library in this directory 442tenv.Replace(RPATH=thisdir) 443tenv.Prepend(LIBS=[LIBNAMESTATIC, ], 444 LIBPATH=[thisdir, ]) 445 446testall_files = [ 447 'test/test_all.c', 448 'test/CuTest.c', 449 'test/test_util.c', 450 'test/test_context.c', 451 'test/test_buckets.c', 452 'test/test_auth.c', 453 'test/mock_buckets.c', 454 'test/test_ssl.c', 455 'test/server/test_server.c', 456 'test/server/test_sslserver.c', 457 ] 458 459for proggie in TEST_EXES: 460 if 'test_all' in proggie: 461 tenv.Program(proggie, testall_files ) 462 else: 463 tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c']) 464 465 466# HANDLE CLEANING 467 468if env.GetOption('clean'): 469 # When we're cleaning, we want the dependency tree to include "everything" 470 # that could be built. Thus, include all of the tests. 471 env.Default('check') 472