3 # Copyright 2011-2012 Justin Erenkrantz and Greg Stein
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
22 EnsureSConsVersion(2,3,0)
24 HEADER_FILES = ['serf.h',
25 'serf_bucket_types.h',
29 # where we save the configuration variables
30 SAVED_CONFIG = '.saved_config'
32 # Variable class that does no validation on the input
42 def RawListVariable(key, help, default):
44 The input parameters describe a 'raw string list' option. This class
45 accepts a space-separated string and converts it to a list.
47 return (key, '%s' % (help), default, None, lambda val: _converter(val))
49 # Custom path validator, creates directory when a specified option is set.
50 # To be used to ensure a PREFIX directory is only created when installing.
51 def createPathIsDirCreateWithTarget(target):
52 def my_validator(key, val, env):
53 build_targets = (map(str, BUILD_TARGETS))
54 if target in build_targets:
55 return PathVariable.PathIsDirCreate(key, val, env)
57 return PathVariable.PathAccept(key, val, env)
61 if sys.platform == 'win32':
64 default_prefix='Debug'
67 default_libdir='$PREFIX/lib'
68 default_prefix='/usr/local'
70 opts = Variables(files=[SAVED_CONFIG])
72 PathVariable('PREFIX',
73 'Directory to install under',
75 createPathIsDirCreateWithTarget('install')),
76 PathVariable('LIBDIR',
77 'Directory to install architecture dependent libraries under',
79 createPathIsDirCreateWithTarget('install')),
81 "Path to apr-1-config, or to APR's install area",
83 PathVariable.PathAccept),
85 "Path to apu-1-config, or to APR's install area",
87 PathVariable.PathAccept),
88 PathVariable('OPENSSL',
89 "Path to OpenSSL's install area",
91 PathVariable.PathIsDir),
93 "Path to zlib's install area",
95 PathVariable.PathIsDir),
96 PathVariable('GSSAPI',
97 "Path to GSSAPI's install area",
100 BoolVariable('DEBUG',
101 "Enable debugging info and strict compile warnings",
103 BoolVariable('APR_STATIC',
104 "Enable using a static compiled APR",
106 RawListVariable('CC', "Command name or path of the C compiler", None),
107 RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)",
109 RawListVariable('LIBS', "Extra libraries passed to the linker, "
110 "e.g. \"-l<library1> -l<library2>\" (space separated)", None),
111 RawListVariable('LINKFLAGS', "Extra flags for the linker (space-separated)",
113 RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor "
114 "(space separated)", None),
117 if sys.platform == 'win32':
119 # By default SCons builds for the host platform on Windows, when using
120 # a supported compiler (E.g. VS2010/VS2012). Allow overriding
122 # Note that Scons 1.3 only supports this on Windows and only when
123 # constructing Environment(). Later changes to TARGET_ARCH are ignored
124 EnumVariable('TARGET_ARCH',
125 "Platform to build for (x86|x64|win32|x86_64)",
127 allowed_values=('x86', 'x86_64', 'ia64'),
135 EnumVariable('MSVC_VERSION',
136 "Visual C++ to use for building (E.g. 11.0, 9.0)",
138 allowed_values=('12.0', '11.0', '10.0', '9.0', '8.0', '6.0')
141 # We always documented that we handle an install layout, but in fact we
142 # hardcoded source layouts. Allow disabling this behavior.
144 BoolVariable('SOURCE_LAYOUT',
145 "Assume a source layout instead of install layout",
149 env = Environment(variables=opts,
150 tools=('default', 'textfile',),
154 env.Append(BUILDERS = {
156 Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET',
157 suffix='.def', src_suffix='.h')
160 match = re.search('SERF_MAJOR_VERSION ([0-9]+).*'
161 'SERF_MINOR_VERSION ([0-9]+).*'
162 'SERF_PATCH_VERSION ([0-9]+)',
163 env.File('serf.h').get_contents(),
165 MAJOR, MINOR, PATCH = [int(x) for x in match.groups()]
166 env.Append(MAJOR=str(MAJOR))
167 env.Append(MINOR=str(MINOR))
168 env.Append(PATCH=str(PATCH))
170 # Calling external programs is okay if we're not cleaning or printing help.
171 # (cleaning: no sense in fetching information; help: we may not know where
173 CALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help'))
176 # HANDLING OF OPTION VARIABLES
178 unknown = opts.UnknownVariables()
180 print 'Unknown variables:', ', '.join(unknown.keys())
183 apr = str(env['APR'])
184 apu = str(env['APU'])
185 zlib = str(env['ZLIB'])
186 gssapi = env.get('GSSAPI', None)
188 if gssapi and os.path.isdir(gssapi):
189 krb5_config = os.path.join(gssapi, 'bin', 'krb5-config')
190 if os.path.isfile(krb5_config):
192 env['GSSAPI'] = krb5_config
194 debug = env.get('DEBUG', None)
195 aprstatic = env.get('APR_STATIC', None)
197 Help(opts.GenerateHelpText(env))
198 opts.Save(SAVED_CONFIG, env)
201 # PLATFORM-SPECIFIC BUILD TWEAKS
203 thisdir = os.getcwd()
205 incdir = '$PREFIX/include/serf-$MAJOR'
207 # This version string is used in the dynamic library name, and for Mac OS X also
208 # for the current_version and compatibility_version options in the .dylib
210 # Unfortunately we can't set the .dylib compatibility_version option separately
211 # from current_version, so don't use the PATCH level to avoid that build and
212 # runtime patch levels have to be identical.
213 env['SHLIBVERSION'] = '%d.%d.%d' % (MAJOR, MINOR, 0)
215 LIBNAME = 'libserf-%d' % (MAJOR,)
216 if sys.platform != 'win32':
217 LIBNAMESTATIC = LIBNAME
219 LIBNAMESTATIC = 'serf-${MAJOR}'
221 env.Append(RPATH=libdir,
222 PDB='${TARGET.filebase}.pdb')
224 if sys.platform == 'darwin':
225 # linkflags.append('-Wl,-install_name,@executable_path/%s.dylib' % (LIBNAME,))
226 env.Append(LINKFLAGS='-Wl,-install_name,%s/%s.dylib' % (thisdir, LIBNAME,))
228 if sys.platform != 'win32':
229 ### gcc only. figure out appropriate test / better way to check these
230 ### flags, and check for gcc.
231 env.Append(CFLAGS='-std=c89')
233 ### These warnings are not available on Solaris
234 if sys.platform != 'sunos5':
235 env.Append(CCFLAGS=['-Wdeclaration-after-statement',
236 '-Wmissing-prototypes',
240 env.Append(CCFLAGS='-g')
241 env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
243 env.Append(CCFLAGS='-O2')
244 env.Append(CPPDEFINES='NDEBUG')
246 ### works for Mac OS. probably needs to change
247 env.Append(LIBS=['ssl', 'crypto', 'z', ])
249 if sys.platform == 'sunos5':
252 # Warning level 4, no unused argument warnings
253 env.Append(CCFLAGS=['/W4', '/wd4100'])
255 # Choose runtime and optimization
257 # Disable optimizations for debugging, use debug DLL runtime
258 env.Append(CCFLAGS=['/Od', '/MDd'])
259 env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
261 # Optimize for speed, use DLL runtime
262 env.Append(CCFLAGS=['/O2', '/MD'])
263 env.Append(CPPDEFINES='NDEBUG')
264 env.Append(LINKFLAGS='/RELEASE')
268 if sys.platform == 'win32':
269 env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h'])
270 SHARED_SOURCES.append(['serf.def'])
272 SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c')
274 lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES)
275 lib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES)
278 env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC'])
280 if 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'])
284 # Get apr/apu information into our build
285 env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER',
286 'NOGDI', 'NONLS','NOCRYPT'])
288 if env.get('TARGET_ARCH', None) == 'x86_64':
289 env.Append(CPPDEFINES=['WIN64'])
293 apu_libs='aprutil-1.lib'
295 apr_libs='libapr-1.lib'
296 apu_libs='libaprutil-1.lib'
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'])
303 env.Append(LIBPATH=['$APR/LibR','$APU/LibR'],
304 CPPPATH=['$APR/include', '$APU/include'])
306 env.Append(LIBPATH=['$APR/Release','$APU/Release'],
307 CPPPATH=['$APR/include', '$APU/include'])
310 env.Append(LIBS='zlib.lib')
311 if not env.get('SOURCE_LAYOUT', None):
312 env.Append(CPPPATH='$ZLIB/include',
315 env.Append(CPPPATH='$ZLIB',
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')
327 env.Append(CPPPATH='$OPENSSL/inc32',
328 LIBPATH='$OPENSSL/out32dll')
330 if os.path.isdir(apr):
331 apr = os.path.join(apr, 'bin', 'apr-1-config')
333 if os.path.isdir(apu):
334 apu = os.path.join(apu, 'bin', 'apu-1-config')
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
341 env.ParseConfig('$APR --cflags --cppflags --ldflags --includes'
343 env.ParseConfig('$APU --ldflags --includes --link-ld --libs')
345 ### there is probably a better way to run/capture output.
346 ### env.ParseConfig() may be handy for getting this stuff into the build
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()
354 env.Append(CPPPATH='$OPENSSL/include')
355 env.Append(LIBPATH='$OPENSSL/lib')
358 # If build with gssapi, get its information and define SERF_HAVE_GSSAPI
359 if 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')
366 if sys.platform == 'win32':
367 env.Append(CPPDEFINES=['SERF_HAVE_SSPI'])
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.
372 for d in env['LIBPATH']:
373 env.Append(RPATH=':'+d)
375 # Set up the construction of serf-*.pc
376 pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,),
377 env.File('build/serf.pc.in'),
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', '')),
388 env.Default(lib_static, lib_shared, pkgconfig)
391 conf = Configure(env)
393 ### some configuration stuffs
400 install_static = env.Install(libdir, lib_static)
401 install_shared = env.InstallVersionedLib(libdir, lib_shared)
403 if sys.platform == 'darwin':
404 # Change the shared library install name (id) to its final name and location.
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.
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)))
418 env.Alias('install-lib', [install_static, install_shared,
420 env.Alias('install-inc', env.Install(incdir, HEADER_FILES))
421 env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'),
423 env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ])
427 ### make move to a separate scons file in the test/ subdir?
431 TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider',
432 'test_all', 'serf_bwtp' ]
433 if sys.platform == 'win32':
434 TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in TEST_PROGRAMS ]
436 TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ]
438 env.AlwaysBuild(env.Alias('check', TEST_EXES, sys.executable + ' build/check.py',
439 ENV={'PATH' : os.environ['PATH']}))
441 # Find the (dynamic) library in this directory
442 tenv.Replace(RPATH=thisdir)
443 tenv.Prepend(LIBS=[LIBNAMESTATIC, ],
450 'test/test_context.c',
451 'test/test_buckets.c',
453 'test/mock_buckets.c',
455 'test/server/test_server.c',
456 'test/server/test_sslserver.c',
459 for proggie in TEST_EXES:
460 if 'test_all' in proggie:
461 tenv.Program(proggie, testall_files )
463 tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c'])
468 if 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.