| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | # SConstruct | 
					
						
							|  |  |  | # vim: set ft=python: | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2014-01-30 15:42:25 -08:00
										 |  |  | # Toplevel Scons build script. This should be mostly complete and generic enough | 
					
						
							|  |  |  | # for most builds. | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | # | 
					
						
							|  |  |  | # Eryn Wells <eryn@erynwells.me> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # DEFAULT CONFIGURATION VALUES | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 20:43:09 -08:00
										 |  |  | # Settings for the default toolchain binaries. Setting these overrides the | 
					
						
							|  |  |  | # defaults, provided your the given binary exists. | 
					
						
							| 
									
										
										
										
											2014-01-30 20:36:38 -08:00
										 |  |  | CC = None | 
					
						
							|  |  |  | CXX = None | 
					
						
							|  |  |  | AS = None | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | LINK = 'clang++' | 
					
						
							| 
									
										
										
										
											2014-01-30 20:36:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | CCFLAGS = ['-Wall', '-Wextra', '-pedantic'] | 
					
						
							| 
									
										
										
										
											2014-01-30 20:36:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # BUILD STUFF BELOW HERE | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 15:09:28 -08:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | import os.path | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2014-01-30 21:01:20 -08:00
										 |  |  | import SCons.Errors | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 15:09:28 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | def which(program): | 
					
						
							|  |  |  |     def is_executable(path): | 
					
						
							|  |  |  |         return os.path.exists(path) and os.access(path, os.X_OK) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     path, name = os.path.split(program) | 
					
						
							|  |  |  |     if path: | 
					
						
							|  |  |  |         if is_executable(program): | 
					
						
							|  |  |  |             return program | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         pathext = [''] + os.environ.get('PATHEXT', '').split(os.pathsep) | 
					
						
							|  |  |  |         for path in os.environ.get('PATH', '').split(os.pathsep): | 
					
						
							|  |  |  |             exe = os.path.join(path, program) | 
					
						
							|  |  |  |             for ext in pathext: | 
					
						
							|  |  |  |                 candidate = exe + ext | 
					
						
							|  |  |  |                 if is_executable(candidate): | 
					
						
							|  |  |  |                     return candidate | 
					
						
							|  |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_bool_argument(arg): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         return bool(int(arg)) | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     if arg in ('False', 'FALSE', 'false', ''): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 21:01:20 -08:00
										 |  |  | def set_toolchain_binary(env, var, user_binary, binaries=()): | 
					
						
							|  |  |  |     if user_binary and which(user_binary): | 
					
						
							|  |  |  |         env[var] = user_binary | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     for c in binaries: | 
					
						
							|  |  |  |         if which(c): | 
					
						
							|  |  |  |             env[var] = c | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2014-01-30 20:36:38 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | common_env = Environment() | 
					
						
							| 
									
										
										
										
											2014-01-30 21:01:20 -08:00
										 |  |  | set_toolchain_binary(common_env, 'CC', CC, ('clang', 'gcc')) | 
					
						
							|  |  |  | set_toolchain_binary(common_env, 'CXX', CXX, ('clang++', 'g++')) | 
					
						
							|  |  |  | set_toolchain_binary(common_env, 'AS', AS) | 
					
						
							|  |  |  | set_toolchain_binary(common_env, 'LINK', LINK) | 
					
						
							| 
									
										
										
										
											2014-02-27 22:20:29 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  | BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | if not BUILD_CMDS: | 
					
						
							|  |  |  |     def generate_comstr(action): | 
					
						
							| 
									
										
										
										
											2014-02-27 21:57:20 -08:00
										 |  |  |         return '{:>25}: $TARGET'.format(action) | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |     common_env['ARCOMSTR'] = generate_comstr('Archiving') | 
					
						
							| 
									
										
										
										
											2014-01-30 15:42:25 -08:00
										 |  |  |     common_env['ASCOMSTR'] = generate_comstr('Assembling') | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |     common_env['ASPPCOMSTR'] = generate_comstr('Assembling') | 
					
						
							| 
									
										
										
										
											2014-01-30 15:42:25 -08:00
										 |  |  |     common_env['CCCOMSTR'] = generate_comstr('Building (C)') | 
					
						
							|  |  |  |     common_env['CXXCOMSTR'] = generate_comstr('Building (C++)') | 
					
						
							|  |  |  |     common_env['LINKCOMSTR'] = generate_comstr('Linking') | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  |     common_env['RANLIBCOMSTR'] = generate_comstr('Indexing') | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |     common_env['SHCCCOMSTR'] = generate_comstr('Building (C, Shared)') | 
					
						
							|  |  |  |     common_env['SHCXXCOMSTR'] = generate_comstr('Building (C++, Shared)') | 
					
						
							|  |  |  |     common_env['SHLINKCOMSTR'] = generate_comstr('Linking (Shared)') | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | # Separate environment for building libraries because they often don't use the | 
					
						
							|  |  |  | # same CCFLAGS I do. | 
					
						
							|  |  |  | lib_env = common_env.Clone() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | common_env.Append(CCFLAGS=CCFLAGS) | 
					
						
							|  |  |  | common_env.Append(CFLAGS=['-std=c99']) | 
					
						
							|  |  |  | common_env.Append(CXXFLAGS=['-std=c++11']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Add color error messages for clang | 
					
						
							|  |  |  | if sys.stdout.isatty(): | 
					
						
							|  |  |  |     if 'clang' in common_env['CC'] or 'clang' in common_env['CXX']: | 
					
						
							|  |  |  |         common_env.Append(CCFLAGS=['-fcolor-diagnostics']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BUILD_DIR = Dir('#build') | 
					
						
							|  |  |  | LIB_DIR = Dir('#lib') | 
					
						
							|  |  |  | SRC_DIR = Dir('#src') | 
					
						
							|  |  |  | TEST_DIR = Dir('#test') | 
					
						
							| 
									
										
										
										
											2014-01-30 15:09:28 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-31 08:42:41 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | def create_env(name, appends=None): | 
					
						
							|  |  |  |     output_dir = BUILD_DIR.Dir(name) | 
					
						
							| 
									
										
										
										
											2014-01-31 08:42:41 -08:00
										 |  |  |     env = common_env.Clone() | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  |     # Standard env extensions. | 
					
						
							|  |  |  |     env.Append(CPPPATH=[SRC_DIR]) | 
					
						
							|  |  |  |     # Custom env stuff. | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |     env['__name'] = name | 
					
						
							|  |  |  |     if appends: | 
					
						
							|  |  |  |         for k, v in appends.iteritems(): | 
					
						
							|  |  |  |             if k.startswith('='): | 
					
						
							|  |  |  |                 env[k[1:]] = v | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 env.Append(**{k: v}) | 
					
						
							| 
									
										
										
										
											2014-01-31 08:42:41 -08:00
										 |  |  |     return env | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | def do_sconscript(env, build_env, src_dir, out_dir): | 
					
						
							|  |  |  |     sconscript = src_dir.File('SConscript') | 
					
						
							|  |  |  |     print 'Reading {}'.format(sconscript) | 
					
						
							|  |  |  |     env.SConscript(sconscript, | 
					
						
							|  |  |  |                    {'env': build_env}, | 
					
						
							|  |  |  |                    variant_dir=out_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | debug_env = create_env('debug', { | 
					
						
							|  |  |  |     'CPPDEFINES': ['NDEBUG'], | 
					
						
							|  |  |  |     'CCFLAGS': ['-O0', '-g'], | 
					
						
							| 
									
										
										
										
											2014-01-31 08:42:41 -08:00
										 |  |  | }) | 
					
						
							| 
									
										
										
										
											2014-01-30 14:08:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  | release_env = create_env('release', { | 
					
						
							|  |  |  |     'CPPDEFINES': ['NRELEASE'], | 
					
						
							|  |  |  |     'CCFLAGS': ['-O2'] | 
					
						
							| 
									
										
										
										
											2014-02-28 17:40:49 -08:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-31 08:27:37 -08:00
										 |  |  | modes = { | 
					
						
							|  |  |  |     'debug': debug_env, | 
					
						
							|  |  |  |     'release': release_env, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mode = ARGUMENTS.get('MODE', None) | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  | build_modes = [] | 
					
						
							| 
									
										
										
										
											2014-01-31 08:27:37 -08:00
										 |  |  | if mode: | 
					
						
							|  |  |  |     # If MODE=foo is specified, build only that mode. | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |     build_modes.append(mode) | 
					
						
							|  |  |  | else: | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  |     build_modes = ['debug'] | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | for mode in build_modes: | 
					
						
							| 
									
										
										
										
											2014-01-31 08:27:37 -08:00
										 |  |  |     try: | 
					
						
							|  |  |  |         env = modes[mode] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							| 
									
										
										
										
											2014-02-28 17:39:41 -08:00
										 |  |  |         print 'Skipping invalid mode: {}'.format(mode) | 
					
						
							| 
									
										
										
										
											2014-07-04 10:55:32 -07:00
										 |  |  |         break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     out_dir = BUILD_DIR.Dir(env['__name']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Process all lib dirs. | 
					
						
							|  |  |  |     for lib in os.listdir(LIB_DIR.abspath): | 
					
						
							|  |  |  |         lib_out_dir = out_dir.Dir('lib').Dir(lib) | 
					
						
							|  |  |  |         if not os.path.isdir(lib_out_dir.abspath): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         do_sconscript(env, lib_env, LIB_DIR.Dir(lib), lib_out_dir) | 
					
						
							|  |  |  |         env.Append(LIBPATH=[lib_out_dir]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get source files. | 
					
						
							|  |  |  |     src_out_dir = out_dir.Dir('src') | 
					
						
							|  |  |  |     do_sconscript(env, env, SRC_DIR, src_out_dir) | 
					
						
							|  |  |  |     env.Append(LIBPATH=[src_out_dir]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get test binaries. | 
					
						
							|  |  |  |     do_sconscript(env, env, TEST_DIR, out_dir.Dir('test')) |