]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/serf/SConstruct
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / contrib / serf / SConstruct
1 # -*- python -*-
2 #
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
11
12 #      http://www.apache.org/licenses/LICENSE-2.0
13
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
19 #    under the License.
20 # ====================================================================
21 #
22
23 import sys
24 import os
25 import re
26
27 EnsureSConsVersion(2,3,0)
28
29 HEADER_FILES = ['serf.h',
30                 'serf_bucket_types.h',
31                 'serf_bucket_util.h',
32                 ]
33
34 # where we save the configuration variables
35 SAVED_CONFIG = '.saved_config'
36
37 # Variable class that does no validation on the input
38 def _converter(val):
39     """
40     """
41     if val == 'none':
42       val = []
43     else:
44       val = val.split(' ')
45     return val
46
47 def RawListVariable(key, help, default):
48     """
49     The input parameters describe a 'raw string list' option. This class
50     accepts a space-separated string and converts it to a list.
51     """
52     return (key, '%s' % (help), default, None, lambda val: _converter(val))
53
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)
61     else:
62       return PathVariable.PathAccept(key, val, env)
63   return my_validator
64
65 # default directories
66 if sys.platform == 'win32':
67   default_incdir='..'
68   default_libdir='..'
69   default_prefix='Debug'
70 else:
71   default_incdir='/usr'
72   default_libdir='$PREFIX/lib'
73   default_prefix='/usr/local'
74
75 opts = Variables(files=[SAVED_CONFIG])
76 opts.AddVariables(
77   PathVariable('PREFIX',
78                'Directory to install under',
79                default_prefix,
80                createPathIsDirCreateWithTarget('install')),
81   PathVariable('LIBDIR',
82                'Directory to install architecture dependent libraries under',
83                default_libdir,
84                createPathIsDirCreateWithTarget('install')),
85   PathVariable('APR',
86                "Path to apr-1-config, or to APR's install area",
87                default_incdir,
88                PathVariable.PathAccept),
89   PathVariable('APU',
90                "Path to apu-1-config, or to APR's install area",
91                default_incdir,
92                PathVariable.PathAccept),
93   PathVariable('OPENSSL',
94                "Path to OpenSSL's install area",
95                default_incdir,
96                PathVariable.PathIsDir),
97   PathVariable('ZLIB',
98                "Path to zlib's install area",
99                default_incdir,
100                PathVariable.PathIsDir),
101   PathVariable('GSSAPI',
102                "Path to GSSAPI's install area",
103                None,
104                None),
105   BoolVariable('DEBUG',
106                "Enable debugging info and strict compile warnings",
107                False),
108   BoolVariable('APR_STATIC',
109                "Enable using a static compiled APR",
110                False),
111   RawListVariable('CC', "Command name or path of the C compiler", None),
112   RawListVariable('CFLAGS', "Extra flags for the C compiler (space-separated)",
113                   None),
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)",
117                   None),
118   RawListVariable('CPPFLAGS', "Extra flags for the C preprocessor "
119                   "(space separated)", None), 
120   )
121
122 if sys.platform == 'win32':
123   opts.AddVariables(
124     # By default SCons builds for the host platform on Windows, when using
125     # a supported compiler (E.g. VS2010/VS2012). Allow overriding
126
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)",
131                  'x86',
132                  allowed_values=('x86', 'x86_64', 'ia64'),
133                  map={'X86'  : 'x86',
134                       'win32': 'x86',
135                       'Win32': 'x86',
136                       'x64'  : 'x86_64',
137                       'X64'  : 'x86_64'
138                      }),
139
140     EnumVariable('MSVC_VERSION',
141                  "Visual C++ to use for building (E.g. 11.0, 9.0)",
142                  None,
143                  allowed_values=('14.0', '12.0',
144                                  '11.0', '10.0', '9.0', '8.0', '6.0')
145                 ),
146
147     # We always documented that we handle an install layout, but in fact we
148     # hardcoded source layouts. Allow disabling this behavior.
149     # ### Fix default?
150     BoolVariable('SOURCE_LAYOUT',
151                  "Assume a source layout instead of install layout",
152                  True),
153     )
154
155 env = Environment(variables=opts,
156                   tools=('default', 'textfile',),
157                   CPPPATH=['.', ],
158                   )
159
160 env.Append(BUILDERS = {
161     'GenDef' : 
162       Builder(action = sys.executable + ' build/gen_def.py $SOURCES > $TARGET',
163               suffix='.def', src_suffix='.h')
164   })
165
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(),
170                   re.DOTALL)
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))
175
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
178 # they are)
179 CALLOUT_OKAY = not (env.GetOption('clean') or env.GetOption('help'))
180
181
182 # HANDLING OF OPTION VARIABLES
183
184 unknown = opts.UnknownVariables()
185 if unknown:
186   print 'Warning: Used unknown variables:', ', '.join(unknown.keys())
187
188 apr = str(env['APR'])
189 apu = str(env['APU'])
190 zlib = str(env['ZLIB'])
191 gssapi = env.get('GSSAPI', None)
192
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):
196     gssapi = krb5_config
197     env['GSSAPI'] = krb5_config
198
199 debug = env.get('DEBUG', None)
200 aprstatic = env.get('APR_STATIC', None)
201
202 Help(opts.GenerateHelpText(env))
203 opts.Save(SAVED_CONFIG, env)
204
205
206 # PLATFORM-SPECIFIC BUILD TWEAKS
207
208 thisdir = os.getcwd()
209 libdir = '$LIBDIR'
210 incdir = '$PREFIX/include/serf-$MAJOR'
211
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
214 #
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)
220
221 LIBNAME = 'libserf-%d' % (MAJOR,)
222 if sys.platform != 'win32':
223   LIBNAMESTATIC = LIBNAME
224 else:
225   LIBNAMESTATIC = 'serf-${MAJOR}'
226
227 env.Append(RPATH=libdir,
228            PDB='${TARGET.filebase}.pdb')
229
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,)])
233
234 if sys.platform != 'win32':
235   def CheckGnuCC(context):
236     src = '''
237     #ifndef __GNUC__
238     oh noes!
239     #endif
240     '''
241     context.Message('Checking for GNU-compatible C compiler...')
242     result = context.TryCompile(src, '.c')
243     context.Result(result)
244     return result
245
246   conf = Configure(env, custom_tests = dict(CheckGnuCC=CheckGnuCC))
247   have_gcc = conf.CheckGnuCC()
248   env = conf.Finish()
249
250   if have_gcc:
251     env.Append(CFLAGS=['-std=c89'])
252     env.Append(CCFLAGS=['-Wdeclaration-after-statement',
253                         '-Wmissing-prototypes',
254                         '-Wall'])
255
256   if debug:
257     env.Append(CCFLAGS=['-g'])
258     env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
259   else:
260     env.Append(CCFLAGS=['-O2'])
261     env.Append(CPPDEFINES=['NDEBUG'])
262
263   ### works for Mac OS. probably needs to change
264   env.Append(LIBS=['ssl', 'crypto', 'z', ])
265
266   if sys.platform == 'sunos5':
267     env.Append(LIBS=['m'])
268     env.Append(PLATFORM='posix')
269 else:
270   # Warning level 4, no unused argument warnings
271   env.Append(CCFLAGS=['/W4', '/wd4100'])
272
273   # Choose runtime and optimization
274   if debug:
275     # Disable optimizations for debugging, use debug DLL runtime
276     env.Append(CCFLAGS=['/Od', '/MDd'])
277     env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
278   else:
279     # Optimize for speed, use DLL runtime
280     env.Append(CCFLAGS=['/O2', '/MD'])
281     env.Append(CPPDEFINES=['NDEBUG'])
282     env.Append(LINKFLAGS=['/RELEASE'])
283
284 # PLAN THE BUILD
285 SHARED_SOURCES = []
286 if sys.platform == 'win32':
287   env.GenDef(['serf.h','serf_bucket_types.h', 'serf_bucket_util.h'])
288   SHARED_SOURCES.append(['serf.def'])
289
290 SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c')
291
292 lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES)
293 lib_shared = env.SharedLibrary(LIBNAME, SOURCES + SHARED_SOURCES)
294
295 if aprstatic:
296   env.Append(CPPDEFINES=['APR_DECLARE_STATIC', 'APU_DECLARE_STATIC'])
297
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'])
301
302   # Get apr/apu information into our build
303   env.Append(CPPDEFINES=['WIN32','WIN32_LEAN_AND_MEAN','NOUSER',
304                          'NOGDI', 'NONLS','NOCRYPT'])
305
306   if env.get('TARGET_ARCH', None) == 'x86_64':
307     env.Append(CPPDEFINES=['WIN64'])
308
309   if aprstatic:
310     apr_libs='apr-1.lib'
311     apu_libs='aprutil-1.lib'
312     env.Append(LIBS=['shell32.lib', 'xml.lib'])
313   else:
314     apr_libs='libapr-1.lib'
315     apu_libs='libaprutil-1.lib'
316
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'])
321   elif aprstatic:
322     env.Append(LIBPATH=['$APR/LibR','$APU/LibR'],
323                CPPPATH=['$APR/include', '$APU/include'])
324   else:
325     env.Append(LIBPATH=['$APR/Release','$APU/Release'],
326                CPPPATH=['$APR/include', '$APU/include'])
327
328   # zlib
329   env.Append(LIBS=['zlib.lib'])
330   if not env.get('SOURCE_LAYOUT', None):
331     env.Append(CPPPATH=['$ZLIB/include'],
332                LIBPATH=['$ZLIB/lib'])
333   else:
334     env.Append(CPPPATH=['$ZLIB'],
335                LIBPATH=['$ZLIB'])
336
337   # openssl
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'])
345   else:
346     env.Append(CPPPATH=['$OPENSSL/inc32'],
347                LIBPATH=['$OPENSSL/out32dll'])
348 else:
349   if os.path.isdir(apr):
350     apr = os.path.join(apr, 'bin', 'apr-1-config')
351     env['APR'] = apr
352   if os.path.isdir(apu):
353     apu = os.path.join(apu, 'bin', 'apu-1-config')
354     env['APU'] = apu
355
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
359   if CALLOUT_OKAY:
360     env.ParseConfig('$APR --cflags --cppflags --ldflags --includes'
361                     ' --link-ld --libs')
362     env.ParseConfig('$APU --ldflags --includes --link-ld --libs')
363
364   ### there is probably a better way to run/capture output.
365   ### env.ParseConfig() may be handy for getting this stuff into the build
366   if CALLOUT_OKAY:
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()
369   else:
370     apr_libs = ''
371     apu_libs = ''
372
373   env.Append(CPPPATH=['$OPENSSL/include'])
374   env.Append(LIBPATH=['$OPENSSL/lib'])
375
376
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'])
387
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)
393
394 # Set up the construction of serf-*.pc
395 pkgconfig = env.Textfile('serf-%d.pc' % (MAJOR,),
396                          env.File('build/serf.pc.in'),
397                          SUBST_DICT = {
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', '')),
405                            })
406
407 env.Default(lib_static, lib_shared, pkgconfig)
408
409 if CALLOUT_OKAY:
410   conf = Configure(env)
411
412   ### some configuration stuffs
413
414   env = conf.Finish()
415
416
417 # INSTALLATION STUFF
418
419 install_static = env.Install(libdir, lib_static)
420 install_shared = env.InstallVersionedLib(libdir, lib_shared)
421
422 if sys.platform == 'darwin':
423   # Change the shared library install name (id) to its final name and location.
424   # Notes:
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.
430
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)))
436
437 env.Alias('install-lib', [install_static, install_shared,
438                           ])
439 env.Alias('install-inc', env.Install(incdir, HEADER_FILES))
440 env.Alias('install-pc', env.Install(os.path.join(libdir, 'pkgconfig'),
441                                     pkgconfig))
442 env.Alias('install', ['install-lib', 'install-inc', 'install-pc', ])
443
444
445 # TESTS
446 ### make move to a separate scons file in the test/ subdir?
447
448 tenv = env.Clone()
449
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])
453
454 tenv.Append(CPPDEFINES=['MOCKHTTP_OPENSSL'])
455
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 ]
460 else:
461   TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ]
462
463 # Find the (dynamic) library in this directory
464 tenv.Replace(RPATH=thisdir)
465 tenv.Prepend(LIBS=[LIBNAMESTATIC, ],
466              LIBPATH=[thisdir, ])
467
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')
472
473 # Set the library search path for the test programs
474 test_env = {'PATH' : os.environ['PATH'],
475             'srcdir' : src_dir}
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))
479
480 testall_files = [
481         'test/test_all.c',
482         'test/CuTest.c',
483         'test/test_util.c',
484         'test/test_context.c',
485         'test/test_buckets.c',
486         'test/test_auth.c',
487         'test/mock_buckets.c',
488         'test/test_ssl.c',
489         'test/server/test_server.c',
490         'test/server/test_sslserver.c',
491         ]
492
493 for proggie in TEST_EXES:
494   if 'test_all' in proggie:
495     tenv.Program(proggie, testall_files )
496   else:
497     tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c'])
498
499
500 # HANDLE CLEANING
501
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.
505   env.Default('check')