From fea154a058112d7d022d1b8e6ba51835f38d8848 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 4 Jul 2014 10:55:32 -0700 Subject: [PATCH 01/40] So much clean up... Brought in the SConscript from Late Fish, which is where a lot of these changes were made. Cleaned up a bunch of stuff. Moved repeated code to functions. Implemented dynamically building libraries in lib/. --- SConstruct | 110 ++++++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/SConstruct b/SConstruct index 5b7d355..3568877 100644 --- a/SConstruct +++ b/SConstruct @@ -16,11 +16,9 @@ CC = None CXX = None AS = None -LINK = None +LINK = 'clang++' -# Same as above but for default CFLAGS. These are appended to both CFLAGS and -# CXXFLAGS. -CFLAGS = '-Wall -Wextra -pedantic' +CCFLAGS = ['-Wall', '-Wextra', '-pedantic'] # @@ -29,6 +27,7 @@ CFLAGS = '-Wall -Wextra -pedantic' import os import os.path +import sys import SCons.Errors @@ -75,14 +74,6 @@ 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) -common_env.Append(CFLAGS='{} -std=c99'.format(CFLAGS)) -common_env.Append(CXXFLAGS='{} -std=c++11'.format(CFLAGS)) - -# Add color error messages for clang -if 'clang' in common_env['CC']: - common_env.Append(CFLAGS=' -fcolor-diagnostics') -if 'clang' in common_env['CXX']: - common_env.Append(CXXFLAGS=' -fcolor-diagnostics') BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) if not BUILD_CMDS: @@ -99,25 +90,32 @@ if not BUILD_CMDS: common_env['SHCXXCOMSTR'] = generate_comstr('Building (C++, Shared)') common_env['SHLINKCOMSTR'] = generate_comstr('Linking (Shared)') -build_dir = Dir('#build') -lib_dir = Dir('#lib') -src_dir = Dir('#src') -test_dir = Dir('#test') +# 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') -def create_env(name, src_dirs, appends=None): - output_dir = build_dir.Dir(name) +def create_env(name, appends=None): + output_dir = BUILD_DIR.Dir(name) env = common_env.Clone() + # Standard env extensions. + env.Append(CPPPATH=[SRC_DIR]) + # Custom env stuff. env['__name'] = name - env['__build_dir'] = output_dir - env['__src_dirs'] = [] - env['__output_dirs'] = [] - for d in src_dirs: - out_dir = output_dir.Dir(d.path) - env['__src_dirs'].append(d) - env['__output_dirs'].append(out_dir) - env.VariantDir(out_dir, d.path, duplicate=0) - env.Clean('.', out_dir) if appends: for k, v in appends.iteritems(): if k.startswith('='): @@ -127,35 +125,28 @@ def create_env(name, src_dirs, appends=None): return env -debug_cflags = ' -O0 -g' -debug_env = create_env('debug', [src_dir], { - 'CPPDEFINES': ['DEBUG'], - 'CFLAGS': debug_cflags, - 'CXXFLAGS': debug_cflags, +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) + + +debug_env = create_env('debug', { + 'CPPDEFINES': ['NDEBUG'], + 'CCFLAGS': ['-O0', '-g'], }) -release_cflags = ' -O2' -release_env = create_env('release', [src_dir], { - 'CPPDEFINES': ['RELEASE'], - 'CFLAGS': release_cflags, - 'CXXFLAGS': release_cflags, -}) - -test_gtest_dir = Dir('#lib/gtest') -test_cpppath = test_gtest_dir.Dir('include') -test_env = create_env('test', [src_dir, test_dir, test_gtest_dir], { - 'CPPDEFINES': ['DEBUG'], - 'CPPPATH': [test_cpppath], - 'LIBPATH': [test_gtest_dir], - 'CFLAGS': debug_cflags, - 'CXXFLAGS': debug_cflags, +release_env = create_env('release', { + 'CPPDEFINES': ['NRELEASE'], + 'CCFLAGS': ['-O2'] }) modes = { 'debug': debug_env, 'release': release_env, - 'test': test_env, } mode = ARGUMENTS.get('MODE', None) @@ -164,12 +155,29 @@ if mode: # If MODE=foo is specified, build only that mode. build_modes.append(mode) else: - build_modes = modes.keys() + build_modes = ['debug'] for mode in build_modes: try: env = modes[mode] except KeyError: print 'Skipping invalid mode: {}'.format(mode) - for d in env['__output_dirs']: - env.SConscript(d.File('SConscript'), {'env': env}) + 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')) From fb0f090892d1d64cef326491cfd78b3df2bb99d6 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 4 Jul 2014 11:09:39 -0700 Subject: [PATCH 02/40] Add succinct build commands, but don't use 'em yet --- SConstruct | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 3568877..31a8e2e 100644 --- a/SConstruct +++ b/SConstruct @@ -75,8 +75,8 @@ set_toolchain_binary(common_env, 'CXX', CXX, ('clang++', 'g++')) set_toolchain_binary(common_env, 'AS', AS) set_toolchain_binary(common_env, 'LINK', LINK) -BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) -if not BUILD_CMDS: + +def verbose_build_cmds(): def generate_comstr(action): return '{:>25}: $TARGET'.format(action) common_env['ARCOMSTR'] = generate_comstr('Archiving') @@ -90,6 +90,26 @@ if not BUILD_CMDS: common_env['SHCXXCOMSTR'] = generate_comstr('Building (C++, Shared)') common_env['SHLINKCOMSTR'] = generate_comstr('Linking (Shared)') + +def succinct_build_cmds(): + def generate_comstr(action): + return ' [{:^6}] $TARGET'.format(action) + common_env['ARCOMSTR'] = generate_comstr('AR') + common_env['ASCOMSTR'] = generate_comstr('AS') + common_env['ASPPCOMSTR'] = generate_comstr('AS') + common_env['CCCOMSTR'] = generate_comstr('CC') + common_env['CXXCOMSTR'] = generate_comstr('CXX') + common_env['LINKCOMSTR'] = generate_comstr('LINK') + common_env['RANLIBCOMSTR'] = generate_comstr('RANLIB') + common_env['SHCCCOMSTR'] = generate_comstr('SHCC') + common_env['SHCXXCOMSTR'] = generate_comstr('SHCXX') + common_env['SHLINKCOMSTR'] = generate_comstr('SHLINK') + + +BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) +if not BUILD_CMDS: + verbose_build_cmds() + # Separate environment for building libraries because they often don't use the # same CCFLAGS I do. lib_env = common_env.Clone() From a2dea2a35fbc3e7769ea6c90ae025952f9a45a03 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Jul 2014 07:42:00 -0700 Subject: [PATCH 03/40] Add beta environment -- debug, with optimization --- SConstruct | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SConstruct b/SConstruct index 31a8e2e..396d50c 100644 --- a/SConstruct +++ b/SConstruct @@ -158,6 +158,11 @@ debug_env = create_env('debug', { 'CCFLAGS': ['-O0', '-g'], }) +beta_env = create_env('beta', { + 'CPPDEFINES': ['NDEBUG'], + 'CCFLAGS': ['-O3', '-g'], +}) + release_env = create_env('release', { 'CPPDEFINES': ['NRELEASE'], 'CCFLAGS': ['-O2'] @@ -166,6 +171,7 @@ release_env = create_env('release', { modes = { 'debug': debug_env, + 'beta': beta_env, 'release': release_env, } From 2b87d5add61be79fa9232af2c8841801b922e551 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Jul 2014 09:10:41 -0700 Subject: [PATCH 04/40] Use -O3 for production --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 396d50c..8d00119 100644 --- a/SConstruct +++ b/SConstruct @@ -165,7 +165,7 @@ beta_env = create_env('beta', { release_env = create_env('release', { 'CPPDEFINES': ['NRELEASE'], - 'CCFLAGS': ['-O2'] + 'CCFLAGS': ['-O3'] }) From 882f5614aed89a967a6eb359e709851252b3ccf7 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Jul 2014 09:12:08 -0700 Subject: [PATCH 05/40] Add build_env (as env) to sconscript environment --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 8d00119..02dc2b7 100644 --- a/SConstruct +++ b/SConstruct @@ -149,7 +149,7 @@ 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}, + {'env': build_env, 'build_env': env}, variant_dir=out_dir) From 9b00e03b2d0204e022035174a85eaa047d73ef67 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Jul 2014 09:21:11 -0700 Subject: [PATCH 06/40] Get rid of broken check for lib dir existence --- SConstruct | 2 -- 1 file changed, 2 deletions(-) diff --git a/SConstruct b/SConstruct index 02dc2b7..203cfb1 100644 --- a/SConstruct +++ b/SConstruct @@ -195,8 +195,6 @@ for mode in build_modes: # 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]) From a0edcab26d30d0822386931e2d09003994da2bb8 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Jul 2014 09:22:53 -0700 Subject: [PATCH 07/40] Comment about swapping env and build_env in do_sconscript() --- SConstruct | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SConstruct b/SConstruct index 203cfb1..140ab6a 100644 --- a/SConstruct +++ b/SConstruct @@ -148,6 +148,9 @@ def create_env(name, appends=None): def do_sconscript(env, build_env, src_dir, out_dir): sconscript = src_dir.File('SConscript') print 'Reading {}'.format(sconscript) + # Swapping env and build_env here is a bit wacky. Doing so means that env is + # always the Environment that the SConscript should be building with, while + # build_env is the Environment we're using to put everything together. env.SConscript(sconscript, {'env': build_env, 'build_env': env}, variant_dir=out_dir) From b9a582caf400ac2c8c0d256414152858f1809d9d Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Thu, 17 Sep 2015 19:26:59 -0700 Subject: [PATCH 08/40] Basic test runner --- lib/gtest/SConscript | 5 ++++- test/SConscript | 4 ++++ test/main.cc | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/main.cc diff --git a/lib/gtest/SConscript b/lib/gtest/SConscript index 42a81b0..814a72e 100644 --- a/lib/gtest/SConscript +++ b/lib/gtest/SConscript @@ -6,6 +6,7 @@ import os.path Import('env') +Import('build_env') files = [ @@ -23,6 +24,8 @@ objs = [] for f in files: objs.append(env.Object(f)) -env.Append(CPPPATH=[Dir('include').srcnode()]) +include_dir = Dir('include').srcnode() +env.Append(CPPPATH=[include_dir]) +build_env.Append(CPPPATH=[include_dir]) gtest = env.Library('gtest', objs) diff --git a/test/SConscript b/test/SConscript index f0639e5..0f6a24b 100644 --- a/test/SConscript +++ b/test/SConscript @@ -18,8 +18,12 @@ for d in subdirs: files = [ # TODO: Put files here. + 'main.cc', ] objs = [] for f in files: objs.append(env.Object(f)) + +# TODO: Add the library target for the package to test to LIBS. +env.Program('test', objs, LIBS=['gtest']) diff --git a/test/main.cc b/test/main.cc new file mode 100644 index 0000000..76cfc21 --- /dev/null +++ b/test/main.cc @@ -0,0 +1,16 @@ +/* main.cc + * Eryn Wells > + */ + +/** Basic driver for unit tests. */ + +#include "gtest/gtest.h" + + +int +main(int argc, + char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 2f1c86a7935b66cb0b4b2e59976a29bc1d4344a0 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Tue, 22 Sep 2015 14:44:30 -0700 Subject: [PATCH 09/40] Move environment set up to site_scons --- SConstruct | 176 +++------------------------------------- site_scons/.gitignore | 1 + site_scons/erw.py | 84 +++++++++++++++++++ site_scons/paths.py | 30 +++++++ site_scons/site_init.py | 20 +++++ 5 files changed, 148 insertions(+), 163 deletions(-) create mode 100644 site_scons/.gitignore create mode 100644 site_scons/erw.py create mode 100644 site_scons/paths.py create mode 100644 site_scons/site_init.py diff --git a/SConstruct b/SConstruct index 140ab6a..3f336e3 100644 --- a/SConstruct +++ b/SConstruct @@ -6,122 +6,10 @@ # # Eryn Wells - -# -# DEFAULT CONFIGURATION VALUES -# - -# Settings for the default toolchain binaries. Setting these overrides the -# defaults, provided your the given binary exists. -CC = None -CXX = None -AS = None -LINK = 'clang++' - -CCFLAGS = ['-Wall', '-Wextra', '-pedantic'] - - -# -# BUILD STUFF BELOW HERE -# - import os -import os.path -import sys -import SCons.Errors +import erw -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 - - -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 - -common_env = Environment() -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) - - -def verbose_build_cmds(): - def generate_comstr(action): - return '{:>25}: $TARGET'.format(action) - common_env['ARCOMSTR'] = generate_comstr('Archiving') - common_env['ASCOMSTR'] = generate_comstr('Assembling') - common_env['ASPPCOMSTR'] = generate_comstr('Assembling') - common_env['CCCOMSTR'] = generate_comstr('Building (C)') - common_env['CXXCOMSTR'] = generate_comstr('Building (C++)') - common_env['LINKCOMSTR'] = generate_comstr('Linking') - common_env['RANLIBCOMSTR'] = generate_comstr('Indexing') - common_env['SHCCCOMSTR'] = generate_comstr('Building (C, Shared)') - common_env['SHCXXCOMSTR'] = generate_comstr('Building (C++, Shared)') - common_env['SHLINKCOMSTR'] = generate_comstr('Linking (Shared)') - - -def succinct_build_cmds(): - def generate_comstr(action): - return ' [{:^6}] $TARGET'.format(action) - common_env['ARCOMSTR'] = generate_comstr('AR') - common_env['ASCOMSTR'] = generate_comstr('AS') - common_env['ASPPCOMSTR'] = generate_comstr('AS') - common_env['CCCOMSTR'] = generate_comstr('CC') - common_env['CXXCOMSTR'] = generate_comstr('CXX') - common_env['LINKCOMSTR'] = generate_comstr('LINK') - common_env['RANLIBCOMSTR'] = generate_comstr('RANLIB') - common_env['SHCCCOMSTR'] = generate_comstr('SHCC') - common_env['SHCXXCOMSTR'] = generate_comstr('SHCXX') - common_env['SHLINKCOMSTR'] = generate_comstr('SHLINK') - - -BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) -if not BUILD_CMDS: - verbose_build_cmds() - -# 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') @@ -129,82 +17,44 @@ SRC_DIR = Dir('#src') TEST_DIR = Dir('#test') -def create_env(name, appends=None): - output_dir = BUILD_DIR.Dir(name) - env = common_env.Clone() - # Standard env extensions. - env.Append(CPPPATH=[SRC_DIR]) - # Custom env stuff. - env['__name'] = name - if appends: - for k, v in appends.iteritems(): - if k.startswith('='): - env[k[1:]] = v - else: - env.Append(**{k: v}) - return env - - -def do_sconscript(env, build_env, src_dir, out_dir): +def do_sconscript(env, src_dir, out_dir): sconscript = src_dir.File('SConscript') print 'Reading {}'.format(sconscript) # Swapping env and build_env here is a bit wacky. Doing so means that env is # always the Environment that the SConscript should be building with, while # build_env is the Environment we're using to put everything together. env.SConscript(sconscript, - {'env': build_env, 'build_env': env}, + {'env': env.Clone(), 'build_env': env}, variant_dir=out_dir) - -debug_env = create_env('debug', { - 'CPPDEFINES': ['NDEBUG'], - 'CCFLAGS': ['-O0', '-g'], -}) - -beta_env = create_env('beta', { - 'CPPDEFINES': ['NDEBUG'], - 'CCFLAGS': ['-O3', '-g'], -}) - -release_env = create_env('release', { - 'CPPDEFINES': ['NRELEASE'], - 'CCFLAGS': ['-O3'] -}) - +BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) +MODE = ARGUMENTS.get('MODE', None) modes = { - 'debug': debug_env, - 'beta': beta_env, - 'release': release_env, + 'debug': erw.DebugEnvironment(succinct=not BUILD_CMDS), + 'beta': erw.BetaEnvironment(succinct=not BUILD_CMDS), + 'release': erw.ReleaseEnvironment(succinct=not BUILD_CMDS), } -mode = ARGUMENTS.get('MODE', None) -build_modes = [] -if mode: - # If MODE=foo is specified, build only that mode. - build_modes.append(mode) -else: - build_modes = ['debug'] - -for mode in build_modes: +for mode in (MODE.split(',') if MODE else ['debug']): try: env = modes[mode] except KeyError: print 'Skipping invalid mode: {}'.format(mode) break - out_dir = BUILD_DIR.Dir(env['__name']) + 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) - do_sconscript(env, lib_env, LIB_DIR.Dir(lib), lib_out_dir) + do_sconscript(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) + do_sconscript(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')) + do_sconscript(env, TEST_DIR, out_dir.Dir('test')) diff --git a/site_scons/.gitignore b/site_scons/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/site_scons/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/site_scons/erw.py b/site_scons/erw.py new file mode 100644 index 0000000..4742080 --- /dev/null +++ b/site_scons/erw.py @@ -0,0 +1,84 @@ +# erw.py +# Eryn Wells + +import sys + +import SCons.Environment + +import paths + +# +# Environments +# + +class Environment(SCons.Environment.Environment): + ''' + Default SCons environment for building things. + ''' + + def __init__(self, name, modern=True, paranoid=True, colorful=True, succinct=True, **kwargs): + # Use clang if its available. + kwargs.setdefault('CC', self._toolchain_binary(('clang', 'gcc'))) + kwargs.setdefault('CXX', self._toolchain_binary(('clang++', 'g++'))) + kwargs.setdefault('LINK', self._toolchain_binary(('clang++'))) + + super(Environment, self).__init__(**kwargs) + + self['_name'] = name + + # Modern C/C++ + if modern: + self.Append(CFLAGS=['-std=c99']) + self.Append(CXXFLAGS=['-std=c++11']) + + # Paranoid C/C++ + if paranoid: + self.Append(CCFLAGS=['-Wall', '-Wextra', '-pedantic']) + + # Colorful C/C++ + if colorful and sys.stdout.isatty(): + if 'clang' in self['CC'] or 'clang' in self['CXX']: + self.Append(CCFLAGS=['-fcolor-diagnostics']) + + self['ARCOMSTR'] = self._comstr('Archiving', succinct) + self['ASCOMSTR'] = self._comstr('Assembling', succinct) + self['ASPPCOMSTR'] = self._comstr('Assembling', succinct) + self['CCCOMSTR'] = self._comstr('Building (C)', succinct) + self['CXXCOMSTR'] = self._comstr('Building (C++)', succinct) + self['LINKCOMSTR'] = self._comstr('Linking', succinct) + self['RANLIBCOMSTR'] = self._comstr('Indexing', succinct) + self['SHCCCOMSTR'] = self._comstr('Building (C, Shared)', succinct) + self['SHCXXCOMSTR'] = self._comstr('Building (C++, Shared)', succinct) + self['SHLINKCOMSTR'] = self._comstr('Linking (Shared)', succinct) + + def _toolchain_binary(self, binaries): + for b in binaries: + if b and paths.which(b): + return b + + def _comstr(self, action, succinct=True): + if succinct: + return '{:>25}: $TARGET'.format(action) + else: + return ' [{:^6}] $TARGET'.format(action) + + +class DebugEnvironment(Environment): + def __init__(self, name='debug', **kwargs): + super(DebugEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['NDEBUG']) + self.Append(CCFLAGS=['-O0', '-g']) + + +class BetaEnvironment(Environment): + def __init__(self, name='beta', **kwargs): + super(BetaEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['NDEBUG']) + self.Append(CCFLAGS=['-O3', '-g']) + + +class ReleaseEnvironment(Environment): + def __init__(self, name='release', **kwargs): + super(ReleaseEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['NRELEASE']) + self.Append(CCFLAGS=['-O3']) diff --git a/site_scons/paths.py b/site_scons/paths.py new file mode 100644 index 0000000..c2b37b9 --- /dev/null +++ b/site_scons/paths.py @@ -0,0 +1,30 @@ +# paths.py +# Eryn Wells + +import os +import os.path + + +def is_executable(path): + return os.path.exists(path) and os.access(path, os.X_OK) + + +def which(program): + ''' + Look for `program` in system path and return the full path to that binary if + it is found. Otherwise, return `None`. + ''' + 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 + diff --git a/site_scons/site_init.py b/site_scons/site_init.py new file mode 100644 index 0000000..1c64046 --- /dev/null +++ b/site_scons/site_init.py @@ -0,0 +1,20 @@ +# site_init.py +# Eryn Wells + +import erw + +# +# Argument utils +# + +def get_bool_argument(arg): + ''' + Convert the given argument value to a bool. True values are any integer that + is considered true by Python, and any string value that isn't a + capitalization variant of the word "false". + ''' + try: + return bool(int(arg)) + except ValueError: + pass + return str(arg).lower() != 'false' From 53897b2ed32e3724a278483a6b3f5d05a84b67e8 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Thu, 24 Sep 2015 16:48:49 -0700 Subject: [PATCH 10/40] WIP osxapp bundle tool --- site_scons/site_tools/osxapp.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 site_scons/site_tools/osxapp.py diff --git a/site_scons/site_tools/osxapp.py b/site_scons/site_tools/osxapp.py new file mode 100644 index 0000000..ae7865a --- /dev/null +++ b/site_scons/site_tools/osxapp.py @@ -0,0 +1,33 @@ +# osxapp.py +# Eryn Wells + +_BUNDLE_SUFFIX = '.app' + + +def _assemble_app_bundle(env, bundle_name, binary, info_plist, resources_dir): + # TODO: Create directory structure, copy binary, plist, and resources. Sign the binary? app bundle? + if not bundle_name.endswith('.app'): + bundle_name += '.app' + + bundle_dir = env.Dir(bundle_name) + contents_dir = bundle_dir.Dir('Contents') + macos_dir = contents_dir.Dir('MacOS') + resources_dir = contents_dir.Dir('Resources') + + actions = [] + contents_mkdir = env.Mkdir(contents_dir) + + return actions + + +_app_builder = Builder(action=_assemble_app_bundle, + suffix=_BUNDLE_SUFFIX) + + +def generate(env): + env['OSXAPPBUNDLESUFFIX'] = _BUNDLE_SUFFIX + env['BUILDERS']['OSXApp'] = _app_builder + + +def exists(env): + return True From 1fb32c204de8360d9f70c38fd670e4bc8216f2bd Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 25 Sep 2015 16:57:51 -0700 Subject: [PATCH 11/40] WIP --- site_scons/site_tools/osxapp.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/site_scons/site_tools/osxapp.py b/site_scons/site_tools/osxapp.py index ae7865a..e56a795 100644 --- a/site_scons/site_tools/osxapp.py +++ b/site_scons/site_tools/osxapp.py @@ -1,11 +1,14 @@ # osxapp.py # Eryn Wells +import os.path + _BUNDLE_SUFFIX = '.app' def _assemble_app_bundle(env, bundle_name, binary, info_plist, resources_dir): # TODO: Create directory structure, copy binary, plist, and resources. Sign the binary? app bundle? + if not bundle_name.endswith('.app'): bundle_name += '.app' @@ -14,10 +17,10 @@ def _assemble_app_bundle(env, bundle_name, binary, info_plist, resources_dir): macos_dir = contents_dir.Dir('MacOS') resources_dir = contents_dir.Dir('Resources') - actions = [] - contents_mkdir = env.Mkdir(contents_dir) - - return actions + return [env.Mkdir(contents_dir), + env.Copy(contents_dir.File('Info.plist'), info_list), + env.Mkdir(macos_dir), + env.Copy(macos_dir.File(bundle_name), binary) _app_builder = Builder(action=_assemble_app_bundle, From 8c83b6aa3ba0c47e4db9d1c0b4961f2c6ef8f9d1 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:36:17 -0700 Subject: [PATCH 12/40] Move Scons arguments up --- SConstruct | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index 3f336e3..39dbb5f 100644 --- a/SConstruct +++ b/SConstruct @@ -11,6 +11,9 @@ import os import erw +BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) +MODE = ARGUMENTS.get('MODE', None) + BUILD_DIR = Dir('#build') LIB_DIR = Dir('#lib') SRC_DIR = Dir('#src') @@ -27,9 +30,6 @@ def do_sconscript(env, src_dir, out_dir): {'env': env.Clone(), 'build_env': env}, variant_dir=out_dir) -BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) -MODE = ARGUMENTS.get('MODE', None) - modes = { 'debug': erw.DebugEnvironment(succinct=not BUILD_CMDS), 'beta': erw.BetaEnvironment(succinct=not BUILD_CMDS), From 857516b6ed64dd217c99c669d16511627df89d25 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:37:23 -0700 Subject: [PATCH 13/40] Add SRC_DIR to CPPPATH --- SConstruct | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 39dbb5f..fafc38a 100644 --- a/SConstruct +++ b/SConstruct @@ -54,7 +54,8 @@ for mode in (MODE.split(',') if MODE else ['debug']): # Get source files. src_out_dir = out_dir.Dir('src') do_sconscript(env, SRC_DIR, src_out_dir) - env.Append(LIBPATH=[src_out_dir]) + env.Append(LIBPATH=[src_out_dir], + CPPPATH=[SRC_DIR]) # Get test binaries. do_sconscript(env, TEST_DIR, out_dir.Dir('test')) From dcad55050058bba3c52589c76b7c314105956560 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:39:03 -0700 Subject: [PATCH 14/40] Move do_sconscript to site_init --- SConstruct | 10 ---------- site_scons/site_init.py | 9 ++++++++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/SConstruct b/SConstruct index fafc38a..a06af1b 100644 --- a/SConstruct +++ b/SConstruct @@ -20,16 +20,6 @@ SRC_DIR = Dir('#src') TEST_DIR = Dir('#test') -def do_sconscript(env, src_dir, out_dir): - sconscript = src_dir.File('SConscript') - print 'Reading {}'.format(sconscript) - # Swapping env and build_env here is a bit wacky. Doing so means that env is - # always the Environment that the SConscript should be building with, while - # build_env is the Environment we're using to put everything together. - env.SConscript(sconscript, - {'env': env.Clone(), 'build_env': env}, - variant_dir=out_dir) - modes = { 'debug': erw.DebugEnvironment(succinct=not BUILD_CMDS), 'beta': erw.BetaEnvironment(succinct=not BUILD_CMDS), diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 1c64046..cfe68e7 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,7 +1,14 @@ # site_init.py # Eryn Wells -import erw + +def do_sconscript(env, src_dir, out_dir): + ''' + Look for a SConscript file in `src_dir` and run it. Return any result. + ''' + sconscript = src_dir.File('SConscript') + print 'Reading {}'.format(sconscript) + return env.SConscript(sconscript, {'env': env}, variant_dir=out_dir) # # Argument utils From a408005eb1f67a3d9d20098923ce505a2e705b65 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:40:07 -0700 Subject: [PATCH 15/40] Add same directory incldues --- SConstruct | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SConstruct b/SConstruct index a06af1b..106f7f5 100644 --- a/SConstruct +++ b/SConstruct @@ -35,6 +35,9 @@ for mode in (MODE.split(',') if MODE else ['debug']): out_dir = BUILD_DIR.Dir(env['_name']) + # Allow same directory includes. + env.Append(CPPPATH=['.']) + # Process all lib dirs. for lib in os.listdir(LIB_DIR.abspath): lib_out_dir = out_dir.Dir('lib').Dir(lib) From b1f11e9732de6289231ab2ed900902475e3c043e Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:41:15 -0700 Subject: [PATCH 16/40] env['NAME'] instead of env['_name'] --- SConstruct | 2 +- site_scons/erw.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 106f7f5..d44a6d5 100644 --- a/SConstruct +++ b/SConstruct @@ -33,7 +33,7 @@ for mode in (MODE.split(',') if MODE else ['debug']): print 'Skipping invalid mode: {}'.format(mode) break - out_dir = BUILD_DIR.Dir(env['_name']) + out_dir = BUILD_DIR.Dir(env['NAME']) # Allow same directory includes. env.Append(CPPPATH=['.']) diff --git a/site_scons/erw.py b/site_scons/erw.py index 4742080..9ac6aa3 100644 --- a/site_scons/erw.py +++ b/site_scons/erw.py @@ -24,7 +24,7 @@ class Environment(SCons.Environment.Environment): super(Environment, self).__init__(**kwargs) - self['_name'] = name + self['NAME'] = name # Modern C/C++ if modern: From b745019f3978a8eeaf0dca8bb67f206d69c5d80a Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:42:25 -0700 Subject: [PATCH 17/40] Fix up how CC, CXX, and LINK are set in environments --- site_scons/erw.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/site_scons/erw.py b/site_scons/erw.py index 9ac6aa3..b5fbe0e 100644 --- a/site_scons/erw.py +++ b/site_scons/erw.py @@ -17,15 +17,17 @@ class Environment(SCons.Environment.Environment): ''' def __init__(self, name, modern=True, paranoid=True, colorful=True, succinct=True, **kwargs): - # Use clang if its available. - kwargs.setdefault('CC', self._toolchain_binary(('clang', 'gcc'))) - kwargs.setdefault('CXX', self._toolchain_binary(('clang++', 'g++'))) - kwargs.setdefault('LINK', self._toolchain_binary(('clang++'))) - super(Environment, self).__init__(**kwargs) self['NAME'] = name + if 'CC' not in kwargs: + self['CC'] = self.Detect(['clang', 'gcc']) + if 'CXX' not in kwargs: + self['CXX'] = self.Detect(['clang++', 'g++']) + if 'LINK' not in kwargs: + self['LINK'] = self.Detect(['clang++', 'clang', 'ld']) + # Modern C/C++ if modern: self.Append(CFLAGS=['-std=c99']) @@ -51,11 +53,6 @@ class Environment(SCons.Environment.Environment): self['SHCXXCOMSTR'] = self._comstr('Building (C++, Shared)', succinct) self['SHLINKCOMSTR'] = self._comstr('Linking (Shared)', succinct) - def _toolchain_binary(self, binaries): - for b in binaries: - if b and paths.which(b): - return b - def _comstr(self, action, succinct=True): if succinct: return '{:>25}: $TARGET'.format(action) From 164d2944f608f94b05b0568e96a8098abf934b2a Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:43:18 -0700 Subject: [PATCH 18/40] Revamp how libraries are done --- SConstruct | 10 +++++++++- lib/gtest/SConscript | 18 +++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/SConstruct b/SConstruct index d44a6d5..841f14c 100644 --- a/SConstruct +++ b/SConstruct @@ -39,9 +39,17 @@ for mode in (MODE.split(',') if MODE else ['debug']): env.Append(CPPPATH=['.']) # Process all lib dirs. + env['AVAILABLE_LIBS'] = {} for lib in os.listdir(LIB_DIR.abspath): + lib_dir = LIB_DIR.Dir(lib) + if not lib_dir.isdir(): + print 'Skipping {} in lib directory: is not a directory'.format(lib) + continue lib_out_dir = out_dir.Dir('lib').Dir(lib) - do_sconscript(env, LIB_DIR.Dir(lib), lib_out_dir) + output = do_sconscript(env, LIB_DIR.Dir(lib), lib_out_dir) + if not output: + print "Lib {} didn't return any object".format(lib) + env['AVAILABLE_LIBS'][lib] = output env.Append(LIBPATH=[lib_out_dir]) # Get source files. diff --git a/lib/gtest/SConscript b/lib/gtest/SConscript index 814a72e..07126f5 100644 --- a/lib/gtest/SConscript +++ b/lib/gtest/SConscript @@ -1,13 +1,16 @@ # SConscript # vim: set ft=python: # +# Build file for the gtest library. This file also serves as an example for how +# to build libraries for inclusion with my SCons environment. Libraries should +# return a build object. +# # Eryn Wells import os.path Import('env') -Import('build_env') - +env.Append(CPPPATH=[Dir('include').srcnode()]) files = [ 'gtest-all.cc', @@ -20,12 +23,5 @@ files = [ 'gtest.cc', ] -objs = [] -for f in files: - objs.append(env.Object(f)) - -include_dir = Dir('include').srcnode() -env.Append(CPPPATH=[include_dir]) -build_env.Append(CPPPATH=[include_dir]) - -gtest = env.Library('gtest', objs) +gtest = env.Library('gtest', files) +Return('gtest') From 6087354bc896a13f463ba8461ed7f6315e10094b Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 10:49:47 -0700 Subject: [PATCH 19/40] out_dir for do_sconscript is optional --- site_scons/site_init.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index cfe68e7..d1412f0 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -2,13 +2,16 @@ # Eryn Wells -def do_sconscript(env, src_dir, out_dir): +def do_sconscript(env, src_dir, out_dir=None): ''' Look for a SConscript file in `src_dir` and run it. Return any result. ''' sconscript = src_dir.File('SConscript') print 'Reading {}'.format(sconscript) - return env.SConscript(sconscript, {'env': env}, variant_dir=out_dir) + kwargs = {} + if out_dir: + kwargs['variant_dir'] = out_dir + return env.SConscript(sconscript, {'env': env}, **kwargs) # # Argument utils From a472d5aae5743e91738f2edd9012dea4f1db6f0e Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 19:30:44 -0700 Subject: [PATCH 20/40] Return and run tests after building --- SConstruct | 4 +++- test/SConscript | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 841f14c..4de5056 100644 --- a/SConstruct +++ b/SConstruct @@ -59,4 +59,6 @@ for mode in (MODE.split(',') if MODE else ['debug']): CPPPATH=[SRC_DIR]) # Get test binaries. - do_sconscript(env, TEST_DIR, out_dir.Dir('test')) + test = do_sconscript(env, TEST_DIR, out_dir.Dir('test')) + run_tests = env.Alias('test', [test], test[0].path) + env.AlwaysBuild(run_tests) diff --git a/test/SConscript b/test/SConscript index 0f6a24b..2f3afcc 100644 --- a/test/SConscript +++ b/test/SConscript @@ -26,4 +26,5 @@ for f in files: objs.append(env.Object(f)) # TODO: Add the library target for the package to test to LIBS. -env.Program('test', objs, LIBS=['gtest']) +test = env.Program('test', objs, LIBS=['gtest']) +Return('test') From d78abd911c85374e9c806abd92f9a0e190afe74f Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Oct 2015 19:40:53 -0700 Subject: [PATCH 21/40] gtest color for autorun tests --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 4de5056..bbb2775 100644 --- a/SConstruct +++ b/SConstruct @@ -60,5 +60,5 @@ for mode in (MODE.split(',') if MODE else ['debug']): # Get test binaries. test = do_sconscript(env, TEST_DIR, out_dir.Dir('test')) - run_tests = env.Alias('test', [test], test[0].path) + run_tests = env.Alias('test', [test], '{} --gtest_color=yes'.format(test[0].path)) env.AlwaysBuild(run_tests) From 10b650c9a44aa16e3a9269392f8cb56f6694cd69 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 10 Oct 2015 23:07:32 -0700 Subject: [PATCH 22/40] Make the defines right --- site_scons/erw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site_scons/erw.py b/site_scons/erw.py index b5fbe0e..224e4d6 100644 --- a/site_scons/erw.py +++ b/site_scons/erw.py @@ -63,19 +63,19 @@ class Environment(SCons.Environment.Environment): class DebugEnvironment(Environment): def __init__(self, name='debug', **kwargs): super(DebugEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['NDEBUG']) + self.Append(CPPDEFINES=['DEBUG']) self.Append(CCFLAGS=['-O0', '-g']) class BetaEnvironment(Environment): def __init__(self, name='beta', **kwargs): super(BetaEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['NDEBUG']) + self.Append(CPPDEFINES=['DEBUG']) self.Append(CCFLAGS=['-O3', '-g']) class ReleaseEnvironment(Environment): def __init__(self, name='release', **kwargs): super(ReleaseEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['NRELEASE']) + self.Append(CPPDEFINES=['NDEBUG', 'RELEASE']) self.Append(CCFLAGS=['-O3']) From c56cc557f8f4aa031356dc645ec89df5182f37ef Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Oct 2015 23:11:49 -0700 Subject: [PATCH 23/40] Redo the _whooollleee_ build system Following the tutorials on http://www.ostricher.com/tag/scons/, I rebuilt the build system so that SConscript files are *much* easier to write. --- SConstruct | 56 +-------- lib/gtest/SConscript | 18 +-- site_scons/erw.py | 81 ------------- site_scons/site_init.py | 216 ++++++++++++++++++++++++++++++++++ site_scons/site_tools/lib.py | 59 ++++++++++ site_scons/site_tools/test.py | 51 ++++++++ 6 files changed, 335 insertions(+), 146 deletions(-) delete mode 100644 site_scons/erw.py create mode 100644 site_scons/site_tools/lib.py create mode 100644 site_scons/site_tools/test.py diff --git a/SConstruct b/SConstruct index bbb2775..4f4d44c 100644 --- a/SConstruct +++ b/SConstruct @@ -6,59 +6,15 @@ # # Eryn Wells -import os - -import erw +import logging +setup_logging() BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False)) MODE = ARGUMENTS.get('MODE', None) -BUILD_DIR = Dir('#build') -LIB_DIR = Dir('#lib') -SRC_DIR = Dir('#src') -TEST_DIR = Dir('#test') - - -modes = { - 'debug': erw.DebugEnvironment(succinct=not BUILD_CMDS), - 'beta': erw.BetaEnvironment(succinct=not BUILD_CMDS), - 'release': erw.ReleaseEnvironment(succinct=not BUILD_CMDS), -} - for mode in (MODE.split(',') if MODE else ['debug']): - try: - env = modes[mode] - except KeyError: - print 'Skipping invalid mode: {}'.format(mode) - break - - out_dir = BUILD_DIR.Dir(env['NAME']) - - # Allow same directory includes. - env.Append(CPPPATH=['.']) - - # Process all lib dirs. - env['AVAILABLE_LIBS'] = {} - for lib in os.listdir(LIB_DIR.abspath): - lib_dir = LIB_DIR.Dir(lib) - if not lib_dir.isdir(): - print 'Skipping {} in lib directory: is not a directory'.format(lib) - continue - lib_out_dir = out_dir.Dir('lib').Dir(lib) - output = do_sconscript(env, LIB_DIR.Dir(lib), lib_out_dir) - if not output: - print "Lib {} didn't return any object".format(lib) - env['AVAILABLE_LIBS'][lib] = output - env.Append(LIBPATH=[lib_out_dir]) - - # Get source files. - src_out_dir = out_dir.Dir('src') - do_sconscript(env, SRC_DIR, src_out_dir) - env.Append(LIBPATH=[src_out_dir], - CPPPATH=[SRC_DIR]) - - # Get test binaries. - test = do_sconscript(env, TEST_DIR, out_dir.Dir('test')) - run_tests = env.Alias('test', [test], '{} --gtest_color=yes'.format(test[0].path)) - env.AlwaysBuild(run_tests) + env = Environment.for_mode(mode) + env.process_lib_dirs() + env.process_src() + env.process_tests() diff --git a/lib/gtest/SConscript b/lib/gtest/SConscript index 07126f5..5d2e1c7 100644 --- a/lib/gtest/SConscript +++ b/lib/gtest/SConscript @@ -1,18 +1,8 @@ # SConscript # vim: set ft=python: -# -# Build file for the gtest library. This file also serves as an example for how -# to build libraries for inclusion with my SCons environment. Libraries should -# return a build object. -# # Eryn Wells -import os.path - -Import('env') -env.Append(CPPPATH=[Dir('include').srcnode()]) - -files = [ +Library('gtest', [ 'gtest-all.cc', 'gtest-death-test.cc', 'gtest-filepath.cc', @@ -21,7 +11,5 @@ files = [ 'gtest-test-part.cc', 'gtest-typed-test.cc', 'gtest.cc', -] - -gtest = env.Library('gtest', files) -Return('gtest') + 'gtest_main.cc' +]) diff --git a/site_scons/erw.py b/site_scons/erw.py deleted file mode 100644 index 224e4d6..0000000 --- a/site_scons/erw.py +++ /dev/null @@ -1,81 +0,0 @@ -# erw.py -# Eryn Wells - -import sys - -import SCons.Environment - -import paths - -# -# Environments -# - -class Environment(SCons.Environment.Environment): - ''' - Default SCons environment for building things. - ''' - - def __init__(self, name, modern=True, paranoid=True, colorful=True, succinct=True, **kwargs): - super(Environment, self).__init__(**kwargs) - - self['NAME'] = name - - if 'CC' not in kwargs: - self['CC'] = self.Detect(['clang', 'gcc']) - if 'CXX' not in kwargs: - self['CXX'] = self.Detect(['clang++', 'g++']) - if 'LINK' not in kwargs: - self['LINK'] = self.Detect(['clang++', 'clang', 'ld']) - - # Modern C/C++ - if modern: - self.Append(CFLAGS=['-std=c99']) - self.Append(CXXFLAGS=['-std=c++11']) - - # Paranoid C/C++ - if paranoid: - self.Append(CCFLAGS=['-Wall', '-Wextra', '-pedantic']) - - # Colorful C/C++ - if colorful and sys.stdout.isatty(): - if 'clang' in self['CC'] or 'clang' in self['CXX']: - self.Append(CCFLAGS=['-fcolor-diagnostics']) - - self['ARCOMSTR'] = self._comstr('Archiving', succinct) - self['ASCOMSTR'] = self._comstr('Assembling', succinct) - self['ASPPCOMSTR'] = self._comstr('Assembling', succinct) - self['CCCOMSTR'] = self._comstr('Building (C)', succinct) - self['CXXCOMSTR'] = self._comstr('Building (C++)', succinct) - self['LINKCOMSTR'] = self._comstr('Linking', succinct) - self['RANLIBCOMSTR'] = self._comstr('Indexing', succinct) - self['SHCCCOMSTR'] = self._comstr('Building (C, Shared)', succinct) - self['SHCXXCOMSTR'] = self._comstr('Building (C++, Shared)', succinct) - self['SHLINKCOMSTR'] = self._comstr('Linking (Shared)', succinct) - - def _comstr(self, action, succinct=True): - if succinct: - return '{:>25}: $TARGET'.format(action) - else: - return ' [{:^6}] $TARGET'.format(action) - - -class DebugEnvironment(Environment): - def __init__(self, name='debug', **kwargs): - super(DebugEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['DEBUG']) - self.Append(CCFLAGS=['-O0', '-g']) - - -class BetaEnvironment(Environment): - def __init__(self, name='beta', **kwargs): - super(BetaEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['DEBUG']) - self.Append(CCFLAGS=['-O3', '-g']) - - -class ReleaseEnvironment(Environment): - def __init__(self, name='release', **kwargs): - super(ReleaseEnvironment, self).__init__(name, **kwargs) - self.Append(CPPDEFINES=['NDEBUG', 'RELEASE']) - self.Append(CCFLAGS=['-O3']) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index d1412f0..e451523 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,6 +1,14 @@ # site_init.py # Eryn Wells +import logging +import os +import sys + +import SCons.Environment +import SCons.Errors + +import paths def do_sconscript(env, src_dir, out_dir=None): ''' @@ -13,6 +21,13 @@ def do_sconscript(env, src_dir, out_dir=None): kwargs['variant_dir'] = out_dir return env.SConscript(sconscript, {'env': env}, **kwargs) + +def setup_logging(level=logging.DEBUG): + '''Configure global logging for the SCons system.''' + root = logging.getLogger() + root.setLevel(logging.DEBUG) + root.addHandler(logging.StreamHandler()) + # # Argument utils # @@ -28,3 +43,204 @@ def get_bool_argument(arg): except ValueError: pass return str(arg).lower() != 'false' + +# +# Builders +# + +def program_builder(env): + original_builder = env.Program + def builder(env, prog_name, sources, *args, **kwargs): + return original_builder(prog_name, sources, *args, **kwargs) + return builder + +# +# Environments +# + +class Environment(SCons.Environment.Environment): + ''' + Default SCons environment for building things. + ''' + + @classmethod + def _comstr(cls, action, succinct=True): + if succinct: + return '{:>25}: $TARGET'.format(action) + # Use the default *COMSTR. + return None + + @classmethod + def for_mode(cls, mode, name=None, succinct=True): + kwargs = {'succinct': succinct} + if name: + kwargs['name'] = name + mode_lower = mode.lower() + if mode_lower == 'debug': + return DebugEnvironment(**kwargs) + elif mode_lower == 'beta': + return BetaEnvironment(**kwargs) + elif mode_lower == 'release': + return ReleaseEnvironment(**kwargs) + raise SCons.Errors.UserError('Invalid mode: {}'.format(mode)) + + def __init__(self, name, modern=True, paranoid=True, colorful=True, succinct=True, **kwargs): + super(Environment, self).__init__(**self._append_custom_tools(kwargs)) + + self.SetDefault(NAME=name) + + self.SetDefault(LOGGER=logging.getLogger(name)) + + self.SetDefault(BUILD_ROOT=self.Dir('#build')) + self.SetDefault(LIB_ROOT=self.Dir('#lib')) + self.SetDefault(SRC_ROOT=self.Dir('#src')) + self.SetDefault(TEST_ROOT=self.Dir('#test')) + self.SetDefault(INCLUDE_ROOT=self.Dir('#include')) + + # Allow same directory includes. + self.AppendUnique(CPPPATH=['.']) + + self.SetDefault(CC=self.Detect(['clang', 'gcc'])) + self.SetDefault(CXX=self.Detect(['clang++', 'g++'])) + self.SetDefault(LINK=self.Detect(['clang++', 'clang', 'ld'])) + + # Modern C/C++ + if modern: + self.AppendUnique(CFLAGS=['-std=c99']) + self.AppendUnique(CXXFLAGS=['-std=c++11']) + + # Paranoid C/C++ + if paranoid: + self.AppendUnique(CCFLAGS=['-Wall', '-Wextra', '-pedantic']) + + # Colorful C/C++ + if colorful and sys.stdout.isatty(): + if 'clang' in self['CC'] or 'clang' in self['CXX']: + self.AppendUnique(CCFLAGS=['-fcolor-diagnostics']) + + # Pretty printing + self.SetDefault(ARCOMSTR=Environment._comstr('Archiving', succinct)) + self.SetDefault(ASCOMSTR=Environment._comstr('Assembling', succinct)) + self.SetDefault(ASPPCOMSTR=Environment._comstr('Assembling', succinct)) + self.SetDefault(CCCOMSTR=Environment._comstr('Building (C)', succinct)) + self.SetDefault(CXXCOMSTR=Environment._comstr('Building (C++)', succinct)) + self.SetDefault(LINKCOMSTR=Environment._comstr('Linking', succinct)) + self.SetDefault(RANLIBCOMSTR=Environment._comstr('Indexing', succinct)) + self.SetDefault(SHCCCOMSTR=Environment._comstr('Building (C, Shared)', succinct)) + self.SetDefault(SHCXXCOMSTR=Environment._comstr('Building (C++, Shared)', succinct)) + self.SetDefault(SHLINKCOMSTR=Environment._comstr('Linking (Shared)', succinct)) + + @property + def build_root(self): + '''Return the build output directory for this environment.''' + return self['BUILD_ROOT'].Dir(self['NAME']) + + @property + def lib_root(self): + '''Return the root directory for libraries for this environment.''' + return self['LIB_ROOT'] + + @property + def src_root(self): + '''Return the main source root directory for this environment.''' + return self['SRC_ROOT'] + + @property + def lib_dirs(self): + for lib in os.listdir(self['LIB_ROOT'].abspath): + lib_dir = self['LIB_ROOT'].Dir(lib) + if not lib_dir.isdir(): + continue + yield (lib, lib_dir) + + # + # Library processing + # + + def process_lib_dirs(self): + self.log('Processing libs in #{} ...'.format(self.lib_root.path)) + for name, lib in self.lib_dirs: + self.log(' - {}'.format(name)) + self.LibDir(name) + + def process_src(self): + out_dir = self.build_root.Dir('src') + # TODO: Do the thing. + # do_sconscript(env, env.source_root, src_out_dir) + self.Append(CPPPATH=[self.src_root]) + + def lib(self, name): + return self['LIBS'].get(name) + + def register_lib(self, name, lib): + if name in self['LIBS']: + self.log_error('Library has already been built: {}'.format(name)) + self['LIBS'][name] = lib + + # + # Test processing + # + + def test_objects(self, name): + self._ensure_test_structure(name) + return self['TESTS'][name]['objects'] + + def test_program(self, name): + self._ensure_test_structure(name) + return self['TESTS'][name]['program'] + + def register_test_program(self, name, prog): + self._ensure_test_structure(name) + self['TESTS'][name]['program'] = prog + + def process_tests(self): + for test, struct in self['TESTS'].iteritems(): + if not struct['program']: + continue + self.TestRun(test) + + def _ensure_test_structure(self, name): + self['TESTS'].setdefault(name, {'program': None, 'objects': []}) + + # + # Logging + # + + def log(self, msg, *args, **kwargs): + self['LOGGER'].info(msg, *args, **kwargs) + + def log_debug(self, msg, *args, **kwargs): + self['LOGGER'].debug(msg, *args, **kwargs) + + def log_error(self, msg, *args, **kwargs): + self['LOGGER'].error(msg, *args, **kwargs) + + def _append_custom_tools(self, kwargs): + '''Add custom tools to the `kwargs`.''' + tools = kwargs.setdefault('tools', ['default']) + for tool in ['lib', 'test']: + if tool in tools: + continue + tools.append(tool) + return kwargs + + +class DebugEnvironment(Environment): + def __init__(self, name='debug', **kwargs): + super(DebugEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['DEBUG']) + self.Append(CCFLAGS=['-O0', '-g']) + + +class BetaEnvironment(Environment): + def __init__(self, name='beta', **kwargs): + super(BetaEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['DEBUG']) + self.Append(CCFLAGS=['-O3', '-g']) + + +class ReleaseEnvironment(Environment): + def __init__(self, name='release', **kwargs): + super(ReleaseEnvironment, self).__init__(name, **kwargs) + self.Append(CPPDEFINES=['NDEBUG', 'RELEASE']) + self.Append(CCFLAGS=['-O3']) diff --git a/site_scons/site_tools/lib.py b/site_scons/site_tools/lib.py new file mode 100644 index 0000000..212bd6b --- /dev/null +++ b/site_scons/site_tools/lib.py @@ -0,0 +1,59 @@ +# lib.py +# Eryn Wells + +''' +SCons builder for a lib directory. +''' + +import SCons.Errors +import SCons.Script + + +def _process_lib_dir(env, lib, src_dir=None, out_dir=None, inc_dir=None): + if not src_dir: + src_dir = env.lib_root.Dir(lib) + if not src_dir.isdir(): + err = 'Invalid library source directory: {}'.format(src_dir) + env.log_error(err) + raise SCons.Errors.UserError(err) + if not out_dir: + out_dir = env.build_root.Dir('lib').Dir(lib) + if not inc_dir: + include_dir = src_dir.Dir('include') + if include_dir.isdir(): + inc_dir = [include_dir] + env.Append(CPPPATH=inc_dir) + exports = {'Library': env.Library, + 'StaticLibrary': env.StaticLibrary, + 'SharedLibrary': env.SharedLibrary} + SCons.Script._SConscript.GlobalDict.update(exports) + out = env.SConscript(src_dir.File('SConscript'), + {'env': env.Clone()}, + variant_dir=out_dir, + exports=exports) + return out + + +def _build_library(env, lib_func): + original_builder = lib_func + + def builder(env, lib_name, sources, *args, **kwargs): + lib = original_builder(lib_name, sources, *args, **kwargs) + env.register_lib(lib_name, lib) + return lib + + return builder + +# +# SCons tool interface +# + +def generate(env): + env.SetDefault(LIBS={}) + env.AddMethod(_process_lib_dir, 'LibDir') + env.AddMethod(_build_library(env, env.Library), 'Library') + env.AddMethod(_build_library(env, env.StaticLibrary), 'StaticLibrary') + env.AddMethod(_build_library(env, env.SharedLibrary), 'SharedLibrary') + +def exists(env): + return True diff --git a/site_scons/site_tools/test.py b/site_scons/site_tools/test.py new file mode 100644 index 0000000..df8e924 --- /dev/null +++ b/site_scons/site_tools/test.py @@ -0,0 +1,51 @@ +# test.py +# Eryn Wells + +''' +Test builder for SCons. Test files are compiled to objects and stored in the +environment. +''' + +def _process_test_dir(env, dir, program=None): + # TODO: Builder for test directories? + pass + + +def _build_test_object(env, source, program=None): + obj = env.Object(source) + if not program: + program = 'test' + try: + env.test_objects(program).extend(obj) + except TypeError: + env.test_objects(program).append(obj) + return obj + + +def _build_test_program(env, name=None): + if not name: + name = 'test' + prog = env.Program(name, env.test_objects(name), LIBS=[env.lib('gtest')]) + env.register_test_program(name, prog) + return + + +def _run_tests(env, name=None): + if not name: + name = 'test' + cmd = env.Command(env.test_program(name), None, '$SOURCE --gtest_color=yes') + env.AlwaysBuild(cmd) + return cmd + +# +# SCons tool interface +# + +def generate(env): + env.SetDefault(TESTS={}) + env.AddMethod(_build_test_object, 'Test') + env.AddMethod(_build_test_program, 'TestProgram') + env.AddMethod(_run_tests, 'TestRun') + +def exists(env): + return 'Object' in env From de2d8602e1e8b7831e3508f40e5ca54da7d16fba Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Oct 2015 23:14:56 -0700 Subject: [PATCH 24/40] Remove test directory --- test/SConscript | 30 ------------------------------ test/main.cc | 16 ---------------- 2 files changed, 46 deletions(-) delete mode 100644 test/SConscript delete mode 100644 test/main.cc diff --git a/test/SConscript b/test/SConscript deleted file mode 100644 index 2f3afcc..0000000 --- a/test/SConscript +++ /dev/null @@ -1,30 +0,0 @@ -# SConscript -# vim: set ft=python: -# -# Eryn Wells - -import os.path - -Import('env') - - -subdirs = [ - # TODO: Put subdirectories here. -] - -for d in subdirs: - env.SConscript(os.path.join(d, 'SConscript'), {'env': env}) - - -files = [ - # TODO: Put files here. - 'main.cc', -] - -objs = [] -for f in files: - objs.append(env.Object(f)) - -# TODO: Add the library target for the package to test to LIBS. -test = env.Program('test', objs, LIBS=['gtest']) -Return('test') diff --git a/test/main.cc b/test/main.cc deleted file mode 100644 index 76cfc21..0000000 --- a/test/main.cc +++ /dev/null @@ -1,16 +0,0 @@ -/* main.cc - * Eryn Wells > - */ - -/** Basic driver for unit tests. */ - -#include "gtest/gtest.h" - - -int -main(int argc, - char *argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} From 9031c61ad77ef71fc4897106565ffee28dd24b92 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Oct 2015 23:50:07 -0700 Subject: [PATCH 25/40] Fix color diagnostics stuff --- site_scons/site_init.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index e451523..c0e207d 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -10,6 +10,7 @@ import SCons.Errors import paths + def do_sconscript(env, src_dir, out_dir=None): ''' Look for a SConscript file in `src_dir` and run it. Return any result. @@ -100,9 +101,9 @@ class Environment(SCons.Environment.Environment): # Allow same directory includes. self.AppendUnique(CPPPATH=['.']) - self.SetDefault(CC=self.Detect(['clang', 'gcc'])) - self.SetDefault(CXX=self.Detect(['clang++', 'g++'])) - self.SetDefault(LINK=self.Detect(['clang++', 'clang', 'ld'])) + self['CC'] = self.Detect(['clang', 'gcc']) + self['CXX'] = self.Detect(['clang++', 'g++']) + self['LINK'] = self.Detect(['clang++', 'clang', 'ld']) # Modern C/C++ if modern: From c9e28a2bff78f9cdd4d0c287bd6e27eb9d32ba13 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Oct 2015 23:50:48 -0700 Subject: [PATCH 26/40] Move lib environment methods to the lib tool --- site_scons/site_init.py | 12 ------------ site_scons/site_tools/lib.py | 12 +++++++++++- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index c0e207d..881bd6d 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -154,10 +154,6 @@ class Environment(SCons.Environment.Environment): continue yield (lib, lib_dir) - # - # Library processing - # - def process_lib_dirs(self): self.log('Processing libs in #{} ...'.format(self.lib_root.path)) for name, lib in self.lib_dirs: @@ -170,14 +166,6 @@ class Environment(SCons.Environment.Environment): # do_sconscript(env, env.source_root, src_out_dir) self.Append(CPPPATH=[self.src_root]) - def lib(self, name): - return self['LIBS'].get(name) - - def register_lib(self, name, lib): - if name in self['LIBS']: - self.log_error('Library has already been built: {}'.format(name)) - self['LIBS'][name] = lib - # # Test processing # diff --git a/site_scons/site_tools/lib.py b/site_scons/site_tools/lib.py index 212bd6b..2e01526 100644 --- a/site_scons/site_tools/lib.py +++ b/site_scons/site_tools/lib.py @@ -8,6 +8,15 @@ SCons builder for a lib directory. import SCons.Errors import SCons.Script +def _lib(env, name): + return env['LIBS'].get(name) + + +def _register_lib(env, name, lib): + if name in env['LIBS']: + env.log_error('Library has already been built: {}'.format(name)) + env['LIBS'][name] = lib + def _process_lib_dir(env, lib, src_dir=None, out_dir=None, inc_dir=None): if not src_dir: @@ -39,7 +48,7 @@ def _build_library(env, lib_func): def builder(env, lib_name, sources, *args, **kwargs): lib = original_builder(lib_name, sources, *args, **kwargs) - env.register_lib(lib_name, lib) + _register_lib(env, lib_name, lib) return lib return builder @@ -54,6 +63,7 @@ def generate(env): env.AddMethod(_build_library(env, env.Library), 'Library') env.AddMethod(_build_library(env, env.StaticLibrary), 'StaticLibrary') env.AddMethod(_build_library(env, env.SharedLibrary), 'SharedLibrary') + env.AddMethod(_lib, 'lib') def exists(env): return True From e0f3f0a2b4855747125083f8576752ab83507e13 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Oct 2015 23:51:14 -0700 Subject: [PATCH 27/40] Run all tests as part of the test target --- site_scons/site_init.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 881bd6d..085f671 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -183,10 +183,12 @@ class Environment(SCons.Environment.Environment): self['TESTS'][name]['program'] = prog def process_tests(self): + tests = [] for test, struct in self['TESTS'].iteritems(): if not struct['program']: continue - self.TestRun(test) + tests.append(self.TestRun(test)) + self.Alias('test', tests) def _ensure_test_structure(self, name): self['TESTS'].setdefault(name, {'program': None, 'objects': []}) From bd7a79c9e13dd62a85a3d5423fc0bc7f2d960beb Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:13:11 -0700 Subject: [PATCH 28/40] Move more things to the lib tool --- site_scons/site_init.py | 15 --------------- site_scons/site_tools/lib.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 085f671..e3a4894 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -2,7 +2,6 @@ # Eryn Wells import logging -import os import sys import SCons.Environment @@ -146,20 +145,6 @@ class Environment(SCons.Environment.Environment): '''Return the main source root directory for this environment.''' return self['SRC_ROOT'] - @property - def lib_dirs(self): - for lib in os.listdir(self['LIB_ROOT'].abspath): - lib_dir = self['LIB_ROOT'].Dir(lib) - if not lib_dir.isdir(): - continue - yield (lib, lib_dir) - - def process_lib_dirs(self): - self.log('Processing libs in #{} ...'.format(self.lib_root.path)) - for name, lib in self.lib_dirs: - self.log(' - {}'.format(name)) - self.LibDir(name) - def process_src(self): out_dir = self.build_root.Dir('src') # TODO: Do the thing. diff --git a/site_scons/site_tools/lib.py b/site_scons/site_tools/lib.py index 2e01526..b610440 100644 --- a/site_scons/site_tools/lib.py +++ b/site_scons/site_tools/lib.py @@ -5,9 +5,12 @@ SCons builder for a lib directory. ''' +import os + import SCons.Errors import SCons.Script + def _lib(env, name): return env['LIBS'].get(name) @@ -18,6 +21,19 @@ def _register_lib(env, name, lib): env['LIBS'][name] = lib +def _lib_dirs(env): + for lib in os.listdir(env.lib_root.abspath): + lib_dir = env.lib_root.Dir(lib) + if not lib_dir.isdir(): + continue + yield (lib, lib_dir) + + +def _process_lib_dirs(env): + for name, _ in _lib_dirs(env): + env.LibDir(name) + + def _process_lib_dir(env, lib, src_dir=None, out_dir=None, inc_dir=None): if not src_dir: src_dir = env.lib_root.Dir(lib) @@ -64,6 +80,7 @@ def generate(env): env.AddMethod(_build_library(env, env.StaticLibrary), 'StaticLibrary') env.AddMethod(_build_library(env, env.SharedLibrary), 'SharedLibrary') env.AddMethod(_lib, 'lib') + env.AddMethod(_process_lib_dirs, 'process_lib_dirs') def exists(env): return True From e1c4e0c4b71990fc1bbdd88711784007add57e59 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:13:49 -0700 Subject: [PATCH 29/40] Add program and sconscript tools --- site_scons/site_init.py | 4 ++- site_scons/site_tools/lib.py | 9 ++---- site_scons/site_tools/program.py | 46 +++++++++++++++++++++++++++++ site_scons/site_tools/sconscript.py | 36 ++++++++++++++++++++++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 site_scons/site_tools/program.py create mode 100644 site_scons/site_tools/sconscript.py diff --git a/site_scons/site_init.py b/site_scons/site_init.py index e3a4894..9eee045 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -149,6 +149,8 @@ class Environment(SCons.Environment.Environment): out_dir = self.build_root.Dir('src') # TODO: Do the thing. # do_sconscript(env, env.source_root, src_out_dir) + self.SConscript(self.src_root.File('SConscript'), + variant_dir=out_dir) self.Append(CPPPATH=[self.src_root]) # @@ -194,7 +196,7 @@ class Environment(SCons.Environment.Environment): def _append_custom_tools(self, kwargs): '''Add custom tools to the `kwargs`.''' tools = kwargs.setdefault('tools', ['default']) - for tool in ['lib', 'test']: + for tool in ['lib', 'test', 'program', 'sconscript']: if tool in tools: continue tools.append(tool) diff --git a/site_scons/site_tools/lib.py b/site_scons/site_tools/lib.py index b610440..f5c59cb 100644 --- a/site_scons/site_tools/lib.py +++ b/site_scons/site_tools/lib.py @@ -48,14 +48,9 @@ def _process_lib_dir(env, lib, src_dir=None, out_dir=None, inc_dir=None): if include_dir.isdir(): inc_dir = [include_dir] env.Append(CPPPATH=inc_dir) - exports = {'Library': env.Library, - 'StaticLibrary': env.StaticLibrary, - 'SharedLibrary': env.SharedLibrary} - SCons.Script._SConscript.GlobalDict.update(exports) out = env.SConscript(src_dir.File('SConscript'), - {'env': env.Clone()}, - variant_dir=out_dir, - exports=exports) + clone=True, + variant_dir=out_dir) return out diff --git a/site_scons/site_tools/program.py b/site_scons/site_tools/program.py new file mode 100644 index 0000000..edb1186 --- /dev/null +++ b/site_scons/site_tools/program.py @@ -0,0 +1,46 @@ +# program.py +# Eryn Wells + +''' +SCons tool for working with Programs. +''' + +def _program(env, name): + return env['PROGRAMS'].get(name) + + +def _register_program(env, name, program): + env['PROGRAMS'][name] = program + + +def _build_program(env): + original_builder = env.Program + + def builder(env, program, sources, local_libs=None, *args, **kwargs): + # local_libs is an array of names of libs built in the local project. + # These will be looked up in the environment and added to the LIBS + # array, if present. +# if local_libs: +# local_libs = map(lambda lib: env.lib(lib), local_libs) +# try: +# kwargs['LIBS'].extend(local_libs) +# except KeyError: +# kwargs['LIBS'] = local_libs + prog = original_builder(program, sources, *args, **kwargs) + register_program(prog) + return prog + + return builder + +# +# SCons tool interface +# + +def generate(env): + env.SetDefault(PROGRAMS={}) + env.AddMethod(_build_program(env), 'Program') + env.AddMethod(_program, 'program') + + +def exists(env): + return True diff --git a/site_scons/site_tools/sconscript.py b/site_scons/site_tools/sconscript.py new file mode 100644 index 0000000..b898481 --- /dev/null +++ b/site_scons/site_tools/sconscript.py @@ -0,0 +1,36 @@ +# sconscript.py +# Eryn Wells + +''' +SCons tool for working with SConscripts. +''' + +import SCons.Script + +def _do_sconscript(env): + original_sconscript = env.SConscript + + def sconscript(env, sconscript, clone=False, *args, **kwargs): + exports = {'Library': env.Library, + 'StaticLibrary': env.StaticLibrary, + 'SharedLibrary': env.SharedLibrary, + 'Program': env.Program} + SCons.Script._SConscript.GlobalDict.update(exports) + env.log('Reading {}'.format(sconscript)) + return original_sconscript(sconscript, + {'env': env.Clone() if clone else env}, + *args, + **kwargs) + + return sconscript + +# +# SCons tool interface +# + +def generate(env): + env.AddMethod(_do_sconscript(env), 'SConscript') + + +def exists(env): + return True From 26eeb0874e37e273fde5e4476e88b87d715a1a04 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:15:20 -0700 Subject: [PATCH 30/40] Remove global do_sconscript function --- site_scons/site_init.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 9eee045..cb25ecf 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -10,18 +10,6 @@ import SCons.Errors import paths -def do_sconscript(env, src_dir, out_dir=None): - ''' - Look for a SConscript file in `src_dir` and run it. Return any result. - ''' - sconscript = src_dir.File('SConscript') - print 'Reading {}'.format(sconscript) - kwargs = {} - if out_dir: - kwargs['variant_dir'] = out_dir - return env.SConscript(sconscript, {'env': env}, **kwargs) - - def setup_logging(level=logging.DEBUG): '''Configure global logging for the SCons system.''' root = logging.getLogger() From 67ab71ee8da5b455229a31e00c967850c4ad50ba Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:24:19 -0700 Subject: [PATCH 31/40] Use LOCAL_LIBS instead of LIBS -- fewer conflicts --- site_scons/site_tools/lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site_scons/site_tools/lib.py b/site_scons/site_tools/lib.py index f5c59cb..dd47f61 100644 --- a/site_scons/site_tools/lib.py +++ b/site_scons/site_tools/lib.py @@ -12,13 +12,13 @@ import SCons.Script def _lib(env, name): - return env['LIBS'].get(name) + return env['LOCAL_LIBS'].get(name) def _register_lib(env, name, lib): - if name in env['LIBS']: + if name in env['LOCAL_LIBS']: env.log_error('Library has already been built: {}'.format(name)) - env['LIBS'][name] = lib + env['LOCAL_LIBS'][name] = lib def _lib_dirs(env): @@ -69,7 +69,7 @@ def _build_library(env, lib_func): # def generate(env): - env.SetDefault(LIBS={}) + env.SetDefault(LOCAL_LIBS={}) env.AddMethod(_process_lib_dir, 'LibDir') env.AddMethod(_build_library(env, env.Library), 'Library') env.AddMethod(_build_library(env, env.StaticLibrary), 'StaticLibrary') From 8a00e0a00da73960b8046edaadd1c8a19d13101c Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:24:38 -0700 Subject: [PATCH 32/40] Program: name instead of program argument --- site_scons/site_tools/program.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site_scons/site_tools/program.py b/site_scons/site_tools/program.py index edb1186..21bbddf 100644 --- a/site_scons/site_tools/program.py +++ b/site_scons/site_tools/program.py @@ -16,7 +16,7 @@ def _register_program(env, name, program): def _build_program(env): original_builder = env.Program - def builder(env, program, sources, local_libs=None, *args, **kwargs): + def builder(env, name, sources, local_libs=None, *args, **kwargs): # local_libs is an array of names of libs built in the local project. # These will be looked up in the environment and added to the LIBS # array, if present. @@ -26,8 +26,8 @@ def _build_program(env): # kwargs['LIBS'].extend(local_libs) # except KeyError: # kwargs['LIBS'] = local_libs - prog = original_builder(program, sources, *args, **kwargs) - register_program(prog) + prog = original_builder(name, sources, *args, **kwargs) + _register_program(env, name, prog) return prog return builder From 7266c248963f5ffa34d94666f5286025ed72761b Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 17 Oct 2015 00:24:54 -0700 Subject: [PATCH 33/40] Rewrite the src SConscript --- src/SConscript | 22 +--------------------- src/hello.cc | 8 ++++++++ 2 files changed, 9 insertions(+), 21 deletions(-) create mode 100644 src/hello.cc diff --git a/src/SConscript b/src/SConscript index f0639e5..5d9a22e 100644 --- a/src/SConscript +++ b/src/SConscript @@ -1,25 +1,5 @@ # SConscript # vim: set ft=python: -# # Eryn Wells -import os.path - -Import('env') - - -subdirs = [ - # TODO: Put subdirectories here. -] - -for d in subdirs: - env.SConscript(os.path.join(d, 'SConscript'), {'env': env}) - - -files = [ - # TODO: Put files here. -] - -objs = [] -for f in files: - objs.append(env.Object(f)) +Program('hello', ['hello.cc']) diff --git a/src/hello.cc b/src/hello.cc new file mode 100644 index 0000000..b02d2e0 --- /dev/null +++ b/src/hello.cc @@ -0,0 +1,8 @@ +#include + +int +main() +{ + std::cout << "Hello world!" << std::endl; + return 0; +} From c669ad98f1e83bec1d4affd44bdef075d97b1280 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 5 Dec 2015 09:52:52 -0800 Subject: [PATCH 34/40] Doc comment for SConstruct --- SConstruct | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SConstruct b/SConstruct index 4f4d44c..48b0d84 100644 --- a/SConstruct +++ b/SConstruct @@ -1,11 +1,12 @@ # SConstruct # vim: set ft=python: -# -# Toplevel Scons build script. This should be mostly complete and generic enough -# for most builds. -# # Eryn Wells +''' +Toplevel Scons build script. This should be mostly complete and generic enough +for most builds. +''' + import logging setup_logging() From fd2e6ae69916b901f6304536f34242eb119a1d64 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 5 Dec 2015 09:53:32 -0800 Subject: [PATCH 35/40] Add swiftc tool --- site_scons/site_init.py | 2 +- site_scons/site_tools/swiftc.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 site_scons/site_tools/swiftc.py diff --git a/site_scons/site_init.py b/site_scons/site_init.py index cb25ecf..12b7b3c 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -184,7 +184,7 @@ class Environment(SCons.Environment.Environment): def _append_custom_tools(self, kwargs): '''Add custom tools to the `kwargs`.''' tools = kwargs.setdefault('tools', ['default']) - for tool in ['lib', 'test', 'program', 'sconscript']: + for tool in ['lib', 'test', 'program', 'sconscript', 'swiftc']: if tool in tools: continue tools.append(tool) diff --git a/site_scons/site_tools/swiftc.py b/site_scons/site_tools/swiftc.py new file mode 100644 index 0000000..5d2766d --- /dev/null +++ b/site_scons/site_tools/swiftc.py @@ -0,0 +1,33 @@ +# swiftc.py +# vim: set ft=python: +# Eryn Wells + +''' +SCons plugin for building Swift files with swiftc. +''' + +import SCons.Action +import SCons.Tool +import SCons.Util + +SwiftSuffix = '.swift' +SwiftAction = SCons.Action.Action("$SWIFTCCOM", "$SWIFTCCOMSTR") + +compilers = ['swiftc'] + +def generate(env): + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + static_obj.add_action(SwiftSuffix, SwiftAction) + static_obj.add_emitter(SwiftSuffix, SCons.Defaults.SharedObjectEmitter) + shared_obj.add_action(SwiftSuffix, SwiftAction) + shared_obj.add_emitter(SwiftSuffix, SCons.Defaults.SharedObjectEmitter) + + if 'SWIFTC' not in env: + compiler = env.Detect(compilers) + env['SWIFTC'] = compiler if compiler else compilers[0] + env['SWIFTFLAGS'] = SCons.Util.CLVar('') + env['SWIFTCCOM'] = '$SWIFTC -o $TARGET -c $SWIFTFLAGS $SOURCES' + env['SWIFTFILESUFFIX'] = SwiftSuffix + +def exists(env): + return env.Detect(compilers) From 517a083694ed6444a1dff235a72879b16d6f78a9 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 27 Feb 2016 08:55:09 -0800 Subject: [PATCH 36/40] Add Append and Replace to the env --- site_scons/site_tools/sconscript.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/site_scons/site_tools/sconscript.py b/site_scons/site_tools/sconscript.py index b898481..12c0015 100644 --- a/site_scons/site_tools/sconscript.py +++ b/site_scons/site_tools/sconscript.py @@ -9,12 +9,15 @@ import SCons.Script def _do_sconscript(env): original_sconscript = env.SConscript - + def sconscript(env, sconscript, clone=False, *args, **kwargs): exports = {'Library': env.Library, 'StaticLibrary': env.StaticLibrary, 'SharedLibrary': env.SharedLibrary, - 'Program': env.Program} + 'Program': env.Program, + + 'Append': env.Append, + 'Replace': env.Replace} SCons.Script._SConscript.GlobalDict.update(exports) env.log('Reading {}'.format(sconscript)) return original_sconscript(sconscript, From 2ab67a8138010dc6b2ef9ae8ed57b6520149e524 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 27 Feb 2016 09:11:38 -0800 Subject: [PATCH 37/40] Add color diagnostics switch for gcc --- site_scons/site_init.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 12b7b3c..e4398ca 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -105,6 +105,9 @@ class Environment(SCons.Environment.Environment): if colorful and sys.stdout.isatty(): if 'clang' in self['CC'] or 'clang' in self['CXX']: self.AppendUnique(CCFLAGS=['-fcolor-diagnostics']) + elif 'gcc' in self['CC'] or 'g++' in self['CXX']: + # TODO: Also set a GCC_COLORS variable in the system environment? + self.AppendUnique(CCFLAGS=['-fdiagnostics-color=always']) # Pretty printing self.SetDefault(ARCOMSTR=Environment._comstr('Archiving', succinct)) From 84b5595490fa52da13c42be008692242d76d1fdd Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 28 Feb 2016 09:22:34 -0800 Subject: [PATCH 38/40] Remove TODO --- site_scons/site_init.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index e4398ca..2d3788b 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -138,8 +138,6 @@ class Environment(SCons.Environment.Environment): def process_src(self): out_dir = self.build_root.Dir('src') - # TODO: Do the thing. - # do_sconscript(env, env.source_root, src_out_dir) self.SConscript(self.src_root.File('SConscript'), variant_dir=out_dir) self.Append(CPPPATH=[self.src_root]) From 146b177ca99f133b6391afc1acbbdd602360a2d0 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 28 Feb 2016 09:22:46 -0800 Subject: [PATCH 39/40] Property export env --- site_scons/site_tools/sconscript.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/site_scons/site_tools/sconscript.py b/site_scons/site_tools/sconscript.py index 12c0015..3e9b597 100644 --- a/site_scons/site_tools/sconscript.py +++ b/site_scons/site_tools/sconscript.py @@ -11,19 +11,18 @@ def _do_sconscript(env): original_sconscript = env.SConscript def sconscript(env, sconscript, clone=False, *args, **kwargs): - exports = {'Library': env.Library, - 'StaticLibrary': env.StaticLibrary, - 'SharedLibrary': env.SharedLibrary, - 'Program': env.Program, - - 'Append': env.Append, - 'Replace': env.Replace} + exports = { + 'Library': env.Library, + 'Object': env.Object, + 'SharedObject': env.SharedObject, + 'StaticLibrary': env.StaticLibrary, + 'SharedLibrary': env.SharedLibrary, + 'Program': env.Program, + 'env': env.Clone() if clone else env, + } SCons.Script._SConscript.GlobalDict.update(exports) env.log('Reading {}'.format(sconscript)) - return original_sconscript(sconscript, - {'env': env.Clone() if clone else env}, - *args, - **kwargs) + return original_sconscript(sconscript, {}, *args, **kwargs) return sconscript From 50177d96b8b243f739d9a2adfe71734dc39f89b0 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 28 Feb 2016 09:24:20 -0800 Subject: [PATCH 40/40] Remove paths module --- site_scons/paths.py | 30 ------------------------------ site_scons/site_init.py | 3 --- 2 files changed, 33 deletions(-) delete mode 100644 site_scons/paths.py diff --git a/site_scons/paths.py b/site_scons/paths.py deleted file mode 100644 index c2b37b9..0000000 --- a/site_scons/paths.py +++ /dev/null @@ -1,30 +0,0 @@ -# paths.py -# Eryn Wells - -import os -import os.path - - -def is_executable(path): - return os.path.exists(path) and os.access(path, os.X_OK) - - -def which(program): - ''' - Look for `program` in system path and return the full path to that binary if - it is found. Otherwise, return `None`. - ''' - 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 - diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 2d3788b..0c2f2ba 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -3,12 +3,9 @@ import logging import sys - import SCons.Environment import SCons.Errors -import paths - def setup_logging(level=logging.DEBUG): '''Configure global logging for the SCons system.'''