3 # ====================================================================
4 # Licensed to the Apache Software Foundation (ASF) under one
5 # or more contributor license agreements. See the NOTICE file
6 # distributed with this work for additional information
7 # regarding copyright ownership. The ASF licenses this file
8 # to you under the Apache License, Version 2.0 (the
9 # "License"); you may not use this file except in compliance
10 # with the License. You may obtain a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing,
15 # software distributed under the License is distributed on an
16 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 # KIND, either express or implied. See the License for the
18 # specific language governing permissions and limitations
20 # ====================================================================
27 EnsureSConsVersion(2,3,0)
29 HEADER_FILES = ['serf.h',
30 'serf_bucket_types.h',
34 # where we save the configuration variables
35 SAVED_CONFIG = '.saved_config'
37 # Variable class that does no validation on the input
47 def RawListVariable(key, help, default):
49 The input parameters describe a 'raw string list' option. This class
50 accepts a space-separated string and converts it to a list.
52 return (key, '%s' % (help), default, None, lambda val: _converter(val))
54 # Custom path validator, creates directory when a specified option is set.
55 # To be used to ensure a PREFIX directory is only created when installing.
56 def createPathIsDirCreateWithTarget(target):
57 def my_validator(key, val, env):
58 build_targets = (map(str, BUILD_TARGETS))
59 if target in build_targets:
60 return PathVariable.PathIsDirCreate(key, val, env)
62 return PathVariable.PathAccept(key, val, env)
66 if sys.platform == 'win32':
69 default_prefix='Debug'
72 default_libdir='$PREFIX/lib'
73 default_prefix='/usr/local'
75 opts = Variables(files=[SAVED_CONFIG])
77 PathVariable('PREFIX',
78 'Directory to install under',
80 createPathIsDirCreateWithTarget('install')),
81 PathVariable('LIBDIR',
82 'Directory to install architecture dependent libraries under',
84 createPathIsDirCreateWithTarget('install')),
86 "Path to apr-1-config, or to APR's install area",
88 PathVariable.PathAccept),
90 "Path to apu-1-config, or to APR's install area",
92 PathVariable.PathAccept),
93 PathVariable('OPENSSL',
94 "Path to OpenSSL's install area",
96 PathVariable.PathIsDir),
98 "Path to zlib's install area",
100 PathVariable.PathIsDir),
101 PathVariable('GSSAPI',
102 "Path to GSSAPI's install area",
105 BoolVariable('DEBUG',
106 "Enable debugging info and strict compile warnings",
108 BoolVariable('APR_STATIC',
109 "Enable using a static compiled APR",
111 RawListVariable('CC', "Command name or path of the C compiler", None),
112 RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)",
114 RawListVariable('LIBS', "Extra libraries passed to the linker, "
115 "e.g. \"-l<library1> -l<library2>\" (space separated)", None),
116 RawListVariable('LINKFLAGS', "Extra flags for the linker (space-separated)",
118 RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor "
119 "(space separated)", None),
122 if sys.platform == 'win32':
124 # By default SCons builds for the host platform on Windows, when using
125 # a supported compiler (E.g. VS2010/VS2012). Allow overriding
127 # Note that Scons 1.3 only supports this on Windows and only when
128 # constructing Environment(). Later changes to TARGET_ARCH are ignored
129 EnumVariable('TARGET_ARCH',
130 "Platform to build for (x86|x64|win32|x86_64)",
132 allowed_values=('x86', 'x86_64', 'ia64'),
140 EnumVariable('MSVC_VERSION',
141 "Visual C++ to use for building (E.g. 11.0, 9.0)",
143 allowed_values=('14.0', '12.0',
144 '11.0', '10.0', '9.0', '8.0', '6.0')
147 # We always documented that we handle an install layout, but in fact we
148 # hardcoded source layouts. Allow disabling this behavior.
150 BoolVariable('SOURCE_LAYOUT',
151 "Assume a source layout instead of install layout",
155 env = Environment(variables=opts,
156 tools=('default', 'textfile',),
160 env.Append(BUILDERS = {
162 Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET',
163 suffix='.def', src_suffix='.h')
166 match = re.search('SERF_MAJOR_VERSION ([0-9]+).*'
167 'SERF_MINOR_VERSION ([0-9]+).*'
168 'SERF_PATCH_VERSION ([0-9]+)',
169 env.File('serf.h').get_contents(),
171 MAJOR, MINOR, PATCH = [int(x) for x in match.groups()]
172 env.Append(MAJOR=str(MAJOR))
173 env.Append(MINOR=str(MINOR))
174 env.Append(PATCH=str(PATCH))
176 # Calling external programs is okay if we're not cleaning or printing help.
177 # (cleaning: no sense in fetching information; help: we may not know where
179 CALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help'))
182 # HANDLING OF OPTION VARIABLES
184 unknown = opts.UnknownVariables()
186 print 'Warning: Used unknown variables:', ', '.join(unknown.keys())
188 apr = str(env['APR'])
189 apu = str(env['APU'])
190 zlib = str(env['ZLIB'])
191 gssapi = env.get('GSSAPI', None)
193 if gssapi and os.path.isdir(gssapi):
194 krb5_config = os.path.join(gssapi, 'bin', 'krb5-config')
195 if os.path.isfile(krb5_config):
197 env['GSSAPI'] = krb5_config
199 debug = env.get('DEBUG', None)
200 aprstatic = env.get('APR_STATIC', None)
202 Help(opts.GenerateHelpText(env))
203 opts.Save(SAVED_CONFIG, env)
206 # PLATFORM-SPECIFIC BUILD TWEAKS
208 thisdir = os.getcwd()
210 incdir = '$PREFIX/include/serf-$MAJOR'
212 # This version string is used in the dynamic library name, and for Mac OS X also
213 # for the current_version and compatibility_version options in the .dylib
215 # Unfortunately we can't set the .dylib compatibility_version option separately
216 # from current_version, so don't use the PATCH level to avoid that build and
217 # runtime patch levels have to be identical.
218 if sys.platform != 'sunos5':
219 env['SHLIBVERSION'] = '%d.%d.%d' % (MAJOR, MINOR, 0)
221 LIBNAME = 'libserf-%d' % (MAJOR,)
222 if sys.platform != 'win32':
223 LIBNAMESTATIC = LIBNAME
225 LIBNAMESTATIC = 'serf-${MAJOR}'
227 env.Append(RPATH=libdir,
228 PDB='${TARGET.filebase}.pdb')
230 if sys.platform == 'darwin':
231 # linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,))
232 env.Append(LINKFLAGS=['-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,)])
234 if sys.platform != 'win32':
235 def CheckGnuCC(context):
241 context.Message('Checking for GNU-compatible C compiler...')
242 result = context.TryCompile(src, '.c')
243 context.Result(result)
246 conf = Configure(env, custom_tests = dict(CheckGnuCC=CheckGnuCC))
247 have_gcc = conf.CheckGnuCC()
251 env.Append(CFLAGS=['-std=c89'])
252 env.Append(CCFLAGS=['-Wdeclaration-after-statement',
253 '-Wmissing-prototypes',
257 env.Append(CCFLAGS=['-g'])
258 env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
260 env.Append(CCFLAGS=['-O2'])
261 env.Append(CPPDEFINES=['NDEBUG'])
263 ### works for Mac OS. probably needs to change
264 env.Append(LIBS=['ssl', 'crypto', 'z', ])
266 if sys.platform == 'sunos5':
267 env.Append(LIBS=['m'])
268 env.Append(PLATFORM='posix')
270 # Warning level 4, no unused argument warnings
271 env.Append(CCFLAGS=['/W4', '/wd4100'])
273 # Choose runtime and optimization
275 # Disable optimizations for debugging, use debug DLL runtime
276 env.Append(CCFLAGS=['/Od', '/MDd'])
277 env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
279 # Optimize for speed, use DLL runtime
280 env.Append(CCFLAGS=['/O2', '/MD'])
281 env.Append(CPPDEFINES=['NDEBUG'])
282 env.Append(LINKFLAGS=['/RELEASE'])
286 if sys.platform == 'win32':
287 env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h'])
288 SHARED_SOURCES.append(['serf.def'])
290 SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c')
292 lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES)
293 lib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES)
296 env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC'])
298 if sys.platform == 'win32':
299 env.Append(LIBS=['user32.lib', 'advapi32.lib', 'gdi32.lib', 'ws2_32.lib',
300 'crypt32.lib', 'mswsock.lib', 'rpcrt4.lib', 'secur32.lib'])
302 # Get apr/apu information into our build
303 env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER',
304 'NOGDI', 'NONLS','NOCRYPT'])
306 if env.get('TARGET_ARCH', None) == 'x86_64':
307 env.Append(CPPDEFINES=['WIN64'])
311 apu_libs='aprutil-1.lib'
312 env.Append(LIBS=['shell32.lib', 'xml.lib'])
314 apr_libs='libapr-1.lib'
315 apu_libs='libaprutil-1.lib'
317 env.Append(LIBS=[apr_libs, apu_libs])
318 if not env.get('SOURCE_LAYOUT', None):
319 env.Append(LIBPATH=['$APR/lib', '$APU/lib'],
320 CPPPATH=['$APR/include/apr-1', '$APU/include/apr-1'])
322 env.Append(LIBPATH=['$APR/LibR','$APU/LibR'],
323 CPPPATH=['$APR/include', '$APU/include'])
325 env.Append(LIBPATH=['$APR/Release','$APU/Release'],
326 CPPPATH=['$APR/include', '$APU/include'])
329 env.Append(LIBS=['zlib.lib'])
330 if not env.get('SOURCE_LAYOUT', None):
331 env.Append(CPPPATH=['$ZLIB/include'],
332 LIBPATH=['$ZLIB/lib'])
334 env.Append(CPPPATH=['$ZLIB'],
338 env.Append(LIBS=['libeay32.lib', 'ssleay32.lib'])
339 if not env.get('SOURCE_LAYOUT', None):
340 env.Append(CPPPATH=['$OPENSSL/include/openssl'],
341 LIBPATH=['$OPENSSL/lib'])
342 elif 0: # opensslstatic:
343 env.Append(CPPPATH=['$OPENSSL/inc32'],
344 LIBPATH=['$OPENSSL/out32'])
346 env.Append(CPPPATH=['$OPENSSL/inc32'],
347 LIBPATH=['$OPENSSL/out32dll'])
349 if os.path.isdir(apr):
350 apr = os.path.join(apr, 'bin', 'apr-1-config')
352 if os.path.isdir(apu):
353 apu = os.path.join(apu, 'bin', 'apu-1-config')
356 ### we should use --cc, but that is giving some scons error about an implict
357 ### dependency upon gcc. probably ParseConfig doesn't know what to do with
358 ### the apr-1-config output
360 env.ParseConfig('$APR --cflags --cppflags --ldflags --includes'
362 env.ParseConfig('$APU --ldflags --includes --link-ld --libs')
364 ### there is probably a better way to run/capture output.
365 ### env.ParseConfig() may be handy for getting this stuff into the build
367 apr_libs = os.popen(env.subst('$APR --link-libtool --libs')).read().strip()
368 apu_libs = os.popen(env.subst('$APU --link-libtool --libs')).read().strip()
373 env.Append(CPPPATH=['$OPENSSL/include'])
374 env.Append(LIBPATH=['$OPENSSL/lib'])
377 # If build with gssapi, get its information and define SERF_HAVE_GSSAPI
378 if gssapi and CALLOUT_OKAY:
379 env.ParseConfig('$GSSAPI --cflags gssapi')
380 def parse_libs(env, cmd, unique=1):
381 env['GSSAPI_LIBS'] = cmd.strip()
382 return env.MergeFlags(cmd, unique)
383 env.ParseConfig('$GSSAPI --libs gssapi', parse_libs)
384 env.Append(CPPDEFINES=['SERF_HAVE_GSSAPI'])
385 if sys.platform == 'win32':
386 env.Append(CPPDEFINES=['SERF_HAVE_SSPI'])
388 # On some systems, the -R values that APR describes never make it into actual
389 # RPATH flags. We'll manually map all directories in LIBPATH into new
390 # flags to set RPATH values.
391 for d in env['LIBPATH']:
392 env.Append(RPATH=':'+d)
394 # Set up the construction of serf-*.pc
395 pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,),
396 env.File('build/serf.pc.in'),
398 '@MAJOR@': str(MAJOR),
399 '@PREFIX@': '$PREFIX',
400 '@LIBDIR@': '$LIBDIR',
401 '@INCLUDE_SUBDIR@': 'serf-%d' % (MAJOR,),
402 '@VERSION@': '%d.%d.%d' % (MAJOR, MINOR, PATCH),
403 '@LIBS@': '%s %s %s -lz' % (apu_libs, apr_libs,
404 env.get('GSSAPI_LIBS', '')),
407 env.Default(lib_static, lib_shared, pkgconfig)
410 conf = Configure(env)
412 ### some configuration stuffs
419 install_static = env.Install(libdir, lib_static)
420 install_shared = env.InstallVersionedLib(libdir, lib_shared)
422 if sys.platform == 'darwin':
423 # Change the shared library install name (id) to its final name and location.
425 # If --install-sandbox=<path> is specified, install_shared_path will point
426 # to a path in the sandbox. We can't use that path because the sandbox is
427 # only a temporary location. The id should be the final target path.
428 # Also, we shouldn't use the complete version number for id, as that'll
429 # make applications depend on the exact major.minor.patch version of serf.
431 install_shared_path = install_shared[0].abspath
432 target_install_shared_path = os.path.join(libdir, '%s.dylib' % LIBNAME)
433 env.AddPostAction(install_shared, ('install_name_tool -id %s %s'
434 % (target_install_shared_path,
435 install_shared_path)))
437 env.Alias('install-lib', [install_static, install_shared,
439 env.Alias('install-inc', env.Install(incdir, HEADER_FILES))
440 env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'),
442 env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ])
446 ### make move to a separate scons file in the test/ subdir?
450 # MockHTTP requires C99 standard, so use it for the test suite.
451 cflags = tenv['CFLAGS']
452 tenv.Replace(CFLAGS = [f.replace('-std=c89', '-std=c99') for f in cflags])
454 tenv.Append(CPPDEFINES=['MOCKHTTP_OPENSSL'])
456 TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider',
457 'test_all', 'serf_bwtp' ]
458 if sys.platform == 'win32':
459 TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in TEST_PROGRAMS ]
461 TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ]
463 # Find the (dynamic) library in this directory
464 tenv.Replace(RPATH=thisdir)
465 tenv.Prepend(LIBS=[LIBNAMESTATIC, ],
468 check_script = env.File('build/check.py').rstr()
469 test_dir = env.File('test/test_all.c').rfile().get_dir()
470 src_dir = env.File('serf.h').rfile().get_dir()
471 test_app = ("%s %s %s %s") % (sys.executable, check_script, test_dir, 'test')
473 # Set the library search path for the test programs
474 test_env = {'PATH' : os.environ['PATH'],
476 if sys.platform != 'win32':
477 test_env['LD_LIBRARY_PATH'] = ':'.join(tenv.get('LIBPATH', []))
478 env.AlwaysBuild(env.Alias('check', TEST_EXES, test_app, ENV=test_env))
484 'test/test_context.c',
485 'test/test_buckets.c',
487 'test/mock_buckets.c',
489 'test/server/test_server.c',
490 'test/server/test_sslserver.c',
493 for proggie in TEST_EXES:
494 if 'test_all' in proggie:
495 tenv.Program(proggie, testall_files )
497 tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c'])
502 if env.GetOption('clean'):
503 # When we're cleaning, we want the dependency tree to include "everything"
504 # that could be built. Thus, include all of the tests.