SConstruct revision 253895
1# -*- python -*- 2# 3# Copyright 2011-2012 Justin Erenkrantz and Greg Stein 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import sys 19import os 20import re 21 22HEADER_FILES = ['serf.h', 23 'serf_bucket_types.h', 24 'serf_bucket_util.h', 25 ] 26 27# where we save the configuration variables 28SAVED_CONFIG = '.saved_config' 29 30# Variable class that does no validation on the input 31def _converter(val): 32 """ 33 """ 34 if val == 'none': 35 val = [] 36 else: 37 val = val.split(',') 38 return val 39 40def RawListVariable(key, help, default): 41 """ 42 The input parameters describe a 'raw string list' option. This class 43 accepts a comma separated list and converts it to a space separated 44 list. 45 """ 46 return (key, '%s' % (help), default, None, lambda val: _converter(val)) 47 48# default directories 49if sys.platform == 'win32': 50 default_libdir='..' 51 default_prefix='Debug' 52else: 53 default_libdir='/usr' 54 default_prefix='/usr/local' 55 56opts = Variables(files=[SAVED_CONFIG]) 57opts.AddVariables( 58 PathVariable('PREFIX', 59 'Directory to install under', 60 default_prefix, 61 PathVariable.PathIsDir), 62 PathVariable('APR', 63 "Path to apr-1-config, or to APR's install area", 64 default_libdir, 65 PathVariable.PathAccept), 66 PathVariable('APU', 67 "Path to apu-1-config, or to APR's install area", 68 default_libdir, 69 PathVariable.PathAccept), 70 PathVariable('OPENSSL', 71 "Path to OpenSSL's install area", 72 default_libdir, 73 PathVariable.PathIsDir), 74 PathVariable('ZLIB', 75 "Path to zlib's install area", 76 default_libdir, 77 PathVariable.PathIsDir), 78 PathVariable('GSSAPI', 79 "Path to GSSAPI's install area", 80 None, 81 None), 82 BoolVariable('DEBUG', 83 "Enable debugging info and strict compile warnings", 84 False), 85 BoolVariable('APR_STATIC', 86 "Enable using a static compiled APR", 87 False), 88 RawListVariable('CC', "Command name or path of the C compiler", None), 89 RawListVariable('CFLAGS', "Extra flags for the C compiler (comma separated)", 90 None), 91 RawListVariable('LIBS', "Extra libraries passed to the linker, " 92 "e.g. -l<library> (comma separated)", None), 93 RawListVariable('LINKFLAGS', "Extra flags for the linker (comma separated)", 94 None), 95 RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor " 96 "(comma separated)", None), 97 ) 98 99if sys.platform == 'win32': 100 opts.AddVariables( 101 # By default SCons builds for the host platform on Windows, when using 102 # a supported compiler (E.g. VS2010/VS2012). Allow overriding 103 104 # Note that Scons 1.3 only supports this on Windows and only when 105 # constructing Environment(). Later changes to TARGET_ARCH are ignored 106 EnumVariable('TARGET_ARCH', 107 "Platform to build for (x86|x64|win32|x86_64)", 108 'x86', 109 allowed_values=('x86', 'x86_64', 'ia64'), 110 map={'X86' : 'x86', 111 'win32': 'x86', 112 'Win32': 'x86', 113 'x64' : 'x86_64', 114 'X64' : 'x86_64' 115 }), 116 117 EnumVariable('MSVC_VERSION', 118 "Visual C++ to use for building (E.g. 11.0, 9.0)", 119 None, 120 allowed_values=('12.0', '11.0', '10.0', '9.0', '8.0', '6.0') 121 ), 122 123 # We always documented that we handle an install layout, but in fact we 124 # hardcoded source layouts. Allow disabling this behavior. 125 # ### Fix default? 126 BoolVariable('SOURCE_LAYOUT', 127 "Assume a source layout instead of install layout", 128 True), 129 ) 130 131env = Environment(variables=opts, 132 tools=('default', 'textfile',), 133 CPPPATH=['.', ], 134 ) 135 136env.Append(BUILDERS = { 137 'GenDef' : 138 Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET', 139 suffix='.def', src_suffix='.h') 140 }) 141 142match = re.search('SERF_MAJOR_VERSION ([0-9]+).*' 143 'SERF_MINOR_VERSION ([0-9]+).*' 144 'SERF_PATCH_VERSION ([0-9]+)', 145 env.File('serf.h').get_contents(), 146 re.DOTALL) 147MAJOR, MINOR, PATCH = [int(x) for x in match.groups()] 148env.Append(MAJOR=str(MAJOR)) 149 150# Calling external programs is okay if we're not cleaning or printing help. 151# (cleaning: no sense in fetching information; help: we may not know where 152# they are) 153CALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help')) 154 155 156# HANDLING OF OPTION VARIABLES 157 158unknown = opts.UnknownVariables() 159if unknown: 160 print 'Unknown variables:', ', '.join(unknown.keys()) 161 Exit(1) 162 163apr = str(env['APR']) 164apu = str(env['APU']) 165zlib = str(env['ZLIB']) 166gssapi = env.get('GSSAPI', None) 167 168if gssapi and os.path.isdir(gssapi): 169 krb5_config = os.path.join(gssapi, 'bin', 'krb5-config') 170 if os.path.isfile(krb5_config): 171 gssapi = krb5_config 172 env['GSSAPI'] = krb5_config 173 174debug = env.get('DEBUG', None) 175aprstatic = env.get('APR_STATIC', None) 176 177Help(opts.GenerateHelpText(env)) 178opts.Save(SAVED_CONFIG, env) 179 180 181# PLATFORM-SPECIFIC BUILD TWEAKS 182 183thisdir = os.getcwd() 184libdir = '$PREFIX/lib' 185incdir = '$PREFIX/include/serf-$MAJOR' 186 187LIBNAME = 'libserf-${MAJOR}' 188if sys.platform != 'win32': 189 LIBNAMESTATIC = LIBNAME 190else: 191 LIBNAMESTATIC = 'serf-${MAJOR}' 192 193env.Append(RPATH=libdir, 194 PDB='${TARGET.filebase}.pdb') 195 196if sys.platform == 'darwin': 197# linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,)) 198 env.Append(LINKFLAGS='-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,)) 199 # 'man ld' says positive non-zero for the first number, so we add one. 200 # Mac's interpretation of compatibility is the same as our MINOR version. 201 env.Append(LINKFLAGS='-Wl,-compatibility_version,%d' % (MINOR+1,)) 202 env.Append(LINKFLAGS='-Wl,-current_version,%d.%d' % (MINOR+1, PATCH,)) 203 204if sys.platform != 'win32': 205 ### gcc only. figure out appropriate test / better way to check these 206 ### flags, and check for gcc. 207 env.Append(CFLAGS='-std=c89') 208 env.Append(CCFLAGS=[ 209 '-Wdeclaration-after-statement', 210 '-Wmissing-prototypes', 211 ]) 212 213 ### -Wall is not available on Solaris 214 if sys.platform != 'sunos5': 215 env.Append(CCFLAGS='-Wall') 216 217 if debug: 218 env.Append(CCFLAGS='-g') 219 env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 220 else: 221 env.Append(CCFLAGS='-O2') 222 env.Append(CPPDEFINES='NDEBUG') 223 224 ### works for Mac OS. probably needs to change 225 env.Append(LIBS=['ssl', 'crypto', 'z', ]) 226 227 if sys.platform == 'sunos5': 228 env.Append(LIBS='m') 229else: 230 # Warning level 4, no unused argument warnings 231 env.Append(CCFLAGS=['/W4', '/wd4100']) 232 233 # Choose runtime and optimization 234 if debug: 235 # Disable optimizations for debugging, use debug DLL runtime 236 env.Append(CCFLAGS=['/Od', '/MDd']) 237 env.Append(CPPDEFINES=['DEBUG', '_DEBUG']) 238 else: 239 # Optimize for speed, use DLL runtime 240 env.Append(CCFLAGS=['/O2', '/MD']) 241 env.Append(CPPDEFINES='NDEBUG') 242 243# PLAN THE BUILD 244SHARED_SOURCES = [] 245if sys.platform == 'win32': 246 env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h']) 247 SHARED_SOURCES.append(['serf.def']) 248 249SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c') 250 251lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES) 252lib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES) 253 254if aprstatic: 255 env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC']) 256 257if sys.platform == 'win32': 258 env.Append(LIBS=['user32.lib', 'advapi32.lib', 'gdi32.lib', 'ws2_32.lib', 259 'crypt32.lib', 'mswsock.lib', 'rpcrt4.lib', 'secur32.lib']) 260 261 # Get apr/apu information into our build 262 env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER', 263 'NOGDI', 'NONLS','NOCRYPT']) 264 265 if env.get('TARGET_ARCH', None) == 'x86_64': 266 env.Append(CPPDEFINES=['WIN64']) 267 268 if aprstatic: 269 apr_libs='apr-1.lib' 270 apu_libs='aprutil-1.lib' 271 else: 272 apr_libs='libapr-1.lib' 273 apu_libs='libaprutil-1.lib' 274 275 env.Append(LIBS=[apr_libs, apu_libs]) 276 if not env.get('SOURCE_LAYOUT', None): 277 env.Append(LIBPATH=['$APR/lib', '$APU/lib'], 278 CPPPATH=['$APR/include/apr-1', '$APU/include/apr-1']) 279 elif aprstatic: 280 env.Append(LIBPATH=['$APR/LibR','$APU/LibR'], 281 CPPPATH=['$APR/include', '$APU/include']) 282 else: 283 env.Append(LIBPATH=['$APR/Release','$APU/Release'], 284 CPPPATH=['$APR/include', '$APU/include']) 285 286 # zlib 287 env.Append(LIBS='zlib.lib') 288 if not env.get('SOURCE_LAYOUT', None): 289 env.Append(CPPPATH='$ZLIB/include', 290 LIBPATH='$ZLIB/lib') 291 else: 292 env.Append(CPPPATH='$ZLIB', 293 LIBPATH='$ZLIB') 294 295 # openssl 296 env.Append(LIBS=['libeay32.lib', 'ssleay32.lib']) 297 if not env.get('SOURCE_LAYOUT', None): 298 env.Append(CPPPATH='$OPENSSL/include/openssl', 299 LIBPATH='$OPENSSL/lib') 300 elif 0: # opensslstatic: 301 env.Append(CPPPATH='$OPENSSL/inc32', 302 LIBPATH='$OPENSSL/out32') 303 else: 304 env.Append(CPPPATH='$OPENSSL/inc32', 305 LIBPATH='$OPENSSL/out32dll') 306else: 307 if os.path.isdir(apr): 308 apr = os.path.join(apr, 'bin', 'apr-1-config') 309 env['APR'] = apr 310 if os.path.isdir(apu): 311 apu = os.path.join(apu, 'bin', 'apu-1-config') 312 env['APU'] = apu 313 314 ### we should use --cc, but that is giving some scons error about an implict 315 ### dependency upon gcc. probably ParseConfig doesn't know what to do with 316 ### the apr-1-config output 317 if CALLOUT_OKAY: 318 env.ParseConfig('$APR --cflags --cppflags --ldflags --includes' 319 ' --link-ld --libs') 320 env.ParseConfig('$APU --ldflags --includes --link-ld --libs') 321 322 ### there is probably a better way to run/capture output. 323 ### env.ParseConfig() may be handy for getting this stuff into the build 324 if CALLOUT_OKAY: 325 apr_libs = os.popen(env.subst('$APR --link-libtool --libs')).read().strip() 326 apu_libs = os.popen(env.subst('$APU --link-libtool --libs')).read().strip() 327 else: 328 apr_libs = '' 329 apu_libs = '' 330 331 env.Append(CPPPATH='$OPENSSL/include') 332 env.Append(LIBPATH='$OPENSSL/lib') 333 334 335# If build with gssapi, get its information and define SERF_HAVE_GSSAPI 336if gssapi and CALLOUT_OKAY: 337 env.ParseConfig('$GSSAPI --libs gssapi') 338 env.Append(CPPDEFINES='SERF_HAVE_GSSAPI') 339if sys.platform == 'win32': 340 env.Append(CPPDEFINES=['SERF_HAVE_SSPI']) 341 342# On Solaris, the -R values that APR describes never make it into actual 343# RPATH flags. We'll manually map all directories in LIBPATH into new 344# flags to set RPATH values. 345if sys.platform == 'sunos5': 346 for d in env['LIBPATH']: 347 env.Append(RPATH=d) 348 349# Set up the construction of serf-*.pc 350# TODO: add gssapi libs 351pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,), 352 env.File('build/serf.pc.in'), 353 SUBST_DICT = { 354 '@MAJOR@': str(MAJOR), 355 '@PREFIX@': '$PREFIX', 356 '@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,), 357 '@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH), 358 '@LIBS@': '%s %s -lz' % (apu_libs, apr_libs), 359 }) 360 361env.Default(lib_static, lib_shared, pkgconfig) 362 363if CALLOUT_OKAY: 364 conf = Configure(env) 365 366 ### some configuration stuffs 367 368 env = conf.Finish() 369 370 371# INSTALLATION STUFF 372 373install_static = env.Install(libdir, lib_static) 374install_shared = env.Install(libdir, lib_shared) 375 376if sys.platform == 'darwin': 377 install_shared_path = install_shared[0].abspath 378 env.AddPostAction(install_shared, ('install_name_tool -id %s %s' 379 % (install_shared_path, 380 install_shared_path))) 381 ### construct shared lib symlinks. this also means install the lib 382 ### as libserf-2.1.0.0.dylib, then add the symlinks. 383 ### note: see InstallAs 384 385env.Alias('install-lib', [install_static, install_shared, 386 ]) 387env.Alias('install-inc', env.Install(incdir, HEADER_FILES)) 388env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'), 389 pkgconfig)) 390env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ]) 391 392 393# TESTS 394### make move to a separate scons file in the test/ subdir? 395 396tenv = env.Clone() 397 398TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider', 399 'test_all', 'serf_bwtp' ] 400if sys.platform == 'win32': 401 TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in TEST_PROGRAMS ] 402else: 403 TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ] 404 405env.AlwaysBuild(env.Alias('check', TEST_EXES, sys.executable + ' build/check.py', 406 ENV={'PATH' : os.environ['PATH']})) 407 408# Find the (dynamic) library in this directory 409tenv.Replace(RPATH=thisdir) 410tenv.Prepend(LIBS=[LIBNAMESTATIC, ], 411 LIBPATH=[thisdir, ]) 412 413testall_files = [ 414 'test/test_all.c', 415 'test/CuTest.c', 416 'test/test_util.c', 417 'test/test_context.c', 418 'test/test_buckets.c', 419 'test/test_auth.c', 420 'test/mock_buckets.c', 421 'test/test_ssl.c', 422 'test/server/test_server.c', 423 'test/server/test_sslserver.c', 424 ] 425 426for proggie in TEST_EXES: 427 if 'test_all' in proggie: 428 tenv.Program(proggie, testall_files ) 429 else: 430 tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c']) 431 432 433# HANDLE CLEANING 434 435if env.GetOption('clean'): 436 # When we're cleaning, we want the dependency tree to include "everything" 437 # that could be built. Thus, include all of the tests. 438 env.Default('check') 439