1#!/usr/bin/env python 2 3import ninja_syntax 4import os, sys, glob, re 5import itertools 6import argparse 7 8# utilities 9def flags(*args): 10 return ' '.join(itertools.chain(*args)) 11 12def includes(l): 13 return ['-I"{}"'.format(x) for x in l] 14 15def library_includes(l): 16 return ['-L"{}"'.format(x) for x in l] 17 18def libraries(l): 19 return ['-l{}'.format(x) for x in l] 20 21def dependencies(l): 22 return ['-isystem"{}"'.format(x) for x in l] 23 24def object_file(f): 25 (root, ext) = os.path.splitext(f) 26 return os.path.join(objdir, root + '.o') 27 28def replace_extension(f, e): 29 (root, ext) = os.path.splitext(f) 30 return root + e 31 32# Default install dir 33install_dir = os.path.join('/usr', 'include') if 'linux' in sys.platform else 'include' 34 35# Compiler: Read from environment or defaulted 36cxx = os.environ.get('CXX', "g++") 37 38# command line stuff 39parser = argparse.ArgumentParser() 40parser.add_argument('--debug', action='store_true', help='compile with debug flags') 41parser.add_argument('--cxx', metavar='<compiler>', help='compiler name to use (default: env.CXX=%s)' % cxx, default=cxx) 42parser.add_argument('--cxx-flags', help='additional flags passed to the compiler', default='') 43parser.add_argument('--ci', action='store_true', help=argparse.SUPPRESS) 44parser.add_argument('--testing', action='store_true', help=argparse.SUPPRESS) 45parser.add_argument('--lua-version', help='Lua version, e.g. lua53', default='lua53') 46parser.add_argument('--lua-lib', help='lua library name (without the lib on *nix).', default='lua') 47parser.add_argument('--lua-dir', metavar='<dir>', help='directory lua is in with include and lib subdirectories') 48parser.add_argument('--install-dir', metavar='<dir>', help='directory to install the headers to', default=install_dir); 49parser.epilog = """In order to install sol, administrative privileges might be required. 50Note that installation is done through the 'ninja install' command. To uninstall, the 51command used is 'ninja uninstall'. The default installation directory for this 52system is {}""".format(install_dir) 53 54args = parser.parse_args() 55 56# general variables 57include = [ '.', './include' ] 58depends = [os.path.join('Catch', 'include')] 59cxxflags = [ '-Wall', '-Wextra', '-Wpedantic', '-pedantic', '-pedantic-errors', '-std=c++14', '-ftemplate-depth=1024' ] 60cxxflags.extend([p for p in re.split("( |\\\".*?\\\"|'.*?')", args.cxx_flags) if p.strip()]) 61example_cxxflags = [ '-Wall', '-Wextra', '-Wpedantic', '-pedantic', '-pedantic-errors', '-std=c++14', '-ftemplate-depth=1024' ] 62example_cxxflags.extend([p for p in re.split("( |\\\".*?\\\"|'.*?')", args.cxx_flags) if p.strip()]) 63ldflags = [] 64script_dir = os.path.dirname(os.path.realpath(sys.argv[0])) 65sol_dir = os.path.join(script_dir, 'sol') 66sol_file = os.path.join(script_dir, 'sol.hpp') 67 68copy_command = 'cp -rf {} $in && cp -f {} $in'.format(sol_dir, sol_file) 69remove_command = 'rm -rf {} && rm -f {}'.format(os.path.join(args.install_dir, 'sol'), os.path.join(args.install_dir, 'sol.hpp')) 70if sys.platform == 'win32': 71 copy_command = 'robocopy /COPYALL /E {} $in && robocopy /COPYALL {} $in'.format(sol_dir, sol_file) 72 remove_command = 'rmdir /S /Q {} && erase /F /S /Q /A {}'.format(os.path.join(args.install_dir, 'sol'), 73 os.path.join(args.install_dir, 'sol.hpp')) 74 75if not args.lua_lib: 76 args.lua_lib = 'lua' 77 78if args.debug: 79 cxxflags.extend(['-g', '-O0']) 80else: 81 cxxflags.extend(['-DNDEBUG', '-O3']) 82example_cxxflags.extend(['-g', '-O0']) 83 84if args.lua_dir: 85 include.extend([os.path.join(args.lua_dir, 'include')]) 86 ldflags.extend(library_includes([os.path.join(args.lua_dir, 'lib')])) 87 88if 'linux' in sys.platform: 89 lua_version = os.environ.get('LUA_VERSION', args.lua_version) 90 if re.match(r'lua5[1-3]', lua_version): 91 # Using normal lua 92 lua_lib = lua_version[:-1] + '.' + lua_version[-1] 93 lua_incl = lua_lib 94 elif re.match(r'luajit5[1-3]', lua_version): 95 # luajit 96 lua_incl = 'luajit-2.0' # I don't get this.. 97 lua_lib = lua_version[:-2] + '-' + lua_version[-2] + '.' + lua_version[-1] 98 include.extend(['/usr/include/luajit-2.0/', '/usr/local/include/luajit-2.0/']) 99 else: 100 raise Exception('Unknown lua_version={}' % lua_version) 101 102 103 include.extend(['/usr/include/' + lua_incl, '/usr/local/include/' + lua_incl]) 104 ldflags.extend(library_includes(['/usr/local/lib'])) 105 ldflags.extend(libraries([lua_lib])) 106elif 'darwin' in sys.platform: 107 # OSX 108 lua_version = os.environ.get('LUA_VERSION', args.lua_version) 109 if re.match(r'lua5[1-3]', lua_version): 110 # Using normal lua 111 lua_incl = lua_version[:-1] + '.' + lua_version[-1] 112 lua_lib = lua_version[:-2] + '.' + lua_version[-2] + '.' + lua_version[-1] 113 elif re.match(r'luajit', lua_version): 114 # luajit 115 lua_incl = 'luajit-2.0' 116 lua_lib = 'luajit' 117 ldflags.extend(['-pagezero_size 10000', '-image_base 100000000']) 118 elif re.match(r'luajit5[1-3]', lua_version): 119 # luajit 120 lua_incl = 'luajit-2.0' 121 lua_lib = lua_version[:-2] + '-' + lua_version[-2] + '.' + lua_version[-1] 122 ldflags.extend(['-pagezero_size 10000', '-image_base 100000000']) 123 else: 124 raise Exception('Unknown lua_version={}' % lua_version) 125 126 depends.extend(['/usr/include/' + lua_incl, '/usr/local/include/' + lua_incl]) 127 ldflags.extend(library_includes(['/usr/local/lib'])) 128 ldflags.extend(libraries([lua_lib])) 129else: 130 ldflags.extend(libraries([args.lua_lib])) 131 132if args.testing: 133 cxxflags.append('-Wmissing-declarations') 134 135if 'linux' in sys.platform: 136 ldflags.extend(libraries(['dl'])) 137 138builddir = 'bin' 139objdir = 'obj' 140if 'win32' in sys.platform: 141 tests = os.path.join(builddir, 'tests.exe') 142else: 143 tests = os.path.join(builddir, 'tests') 144 145tests_inputs = [] 146tests_object_files = [] 147for f in glob.glob('test*.cpp'): 148 obj = object_file(f) 149 tests_inputs.append(f) 150 tests_object_files.append(obj) 151 152examples = [] 153examples_input = [] 154for f in glob.glob('examples/*.cpp'): 155 if 'win32' in sys.platform: 156 example = os.path.join(builddir, replace_extension(f, '.exe')) 157 else: 158 example = os.path.join(builddir, replace_extension(f, '')) 159 examples_input.append(f) 160 examples.append(example) 161 162 163# ninja file 164ninja = ninja_syntax.Writer(open('build.ninja', 'w')) 165 166# variables 167ninja.variable('ninja_required_version', '1.3') 168ninja.variable('builddir', 'bin') 169ninja.variable('cxx', args.cxx) 170ninja.variable('cxxflags', flags(cxxflags + includes(include) + dependencies(depends))) 171ninja.variable('example_cxxflags', flags(example_cxxflags + includes(include) + dependencies(depends))) 172ninja.variable('ldflags', flags(ldflags)) 173ninja.newline() 174 175# rules 176ninja.rule('bootstrap', command = ' '.join(['python'] + sys.argv), generator = True) 177ninja.rule('compile', command = '$cxx -MMD -MF $out.d -c $cxxflags -Werror $in -o $out', 178 deps = 'gcc', depfile = '$out.d', 179 description = 'compiling $in to $out') 180ninja.rule('link', command = '$cxx $cxxflags $in -o $out $ldflags', description = 'creating $out') 181ninja.rule('tests_runner', command = tests) 182ninja.rule('examples_runner', command = 'cmd /c ' + (' && '.join(examples)) if 'win32' in sys.platform else ' && '.join(examples) ) 183ninja.rule('example', command = '$cxx $example_cxxflags -MMD -MF $out.d $in -o $out $ldflags', 184 deps = 'gcc', depfile = '$out.d', 185 description = 'compiling example $in to $out') 186ninja.rule('installer', command = copy_command) 187ninja.rule('uninstaller', command = remove_command) 188ninja.newline() 189 190# builds 191ninja.build('build.ninja', 'bootstrap', implicit = sys.argv[0]) 192 193for obj, f in zip(tests_object_files, tests_inputs): 194 ninja.build(obj, 'compile', inputs = f) 195 196for example, f in zip(examples, examples_input): 197 ninja.build(example, 'example', inputs = f) 198 199ninja.build(tests, 'link', inputs = tests_object_files) 200ninja.build('tests', 'phony', inputs = tests) 201ninja.build('examples', 'phony', inputs = examples) 202ninja.build('install', 'installer', inputs = args.install_dir) 203ninja.build('uninstall', 'uninstaller') 204ninja.build('run', 'tests_runner', implicit = 'tests') 205ninja.build('run_examples', 'examples_runner', implicit = 'examples') 206ninja.default('run run_examples') 207