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