1# Copyright 2015, ARM Limited 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# * Redistributions of source code must retain the above copyright notice, 8# this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# * Neither the name of ARM Limited nor the names of its contributors may be 13# used to endorse or promote products derived from this software without 14# specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27import glob 28import os 29from os.path import join 30import platform 31import subprocess 32import sys 33 34root_dir = os.path.dirname(File('SConstruct').rfile().abspath) 35sys.path.insert(0, join(root_dir, 'tools')) 36import config 37import util 38 39 40Help(''' 41Build system for the VIXL project. 42See README.md for documentation and details about the build system. 43''') 44 45 46# We track top-level targets to automatically generate help and alias them. 47class TopLevelTargets: 48 def __init__(self): 49 self.targets = [] 50 self.help_messages = [] 51 def Add(self, target, help_message): 52 self.targets.append(target) 53 self.help_messages.append(help_message) 54 def Help(self): 55 res = "" 56 for i in range(len(self.targets)): 57 res += '\t{0:<{1}}{2:<{3}}\n'.format( 58 'scons ' + self.targets[i], 59 len('scons ') + max(map(len, self.targets)), 60 ' : ' + self.help_messages[i], 61 len(' : ') + max(map(len, self.help_messages))) 62 return res 63 64top_level_targets = TopLevelTargets() 65 66 67 68# Build options ---------------------------------------------------------------- 69 70# Store all the options in a dictionary. 71# The SConstruct will check the build variables and construct the build 72# environment as appropriate. 73options = { 74 'all' : { # Unconditionally processed. 75 'CCFLAGS' : ['-Wall', 76 '-Werror', 77 '-fdiagnostics-show-option', 78 '-Wextra', 79 '-Wredundant-decls', 80 '-pedantic', 81 '-Wmissing-noreturn', 82 '-Wwrite-strings'], 83 'CPPPATH' : [config.dir_src_vixl] 84 }, 85# 'build_option:value' : { 86# 'environment_key' : 'values to append' 87# }, 88 'mode:debug' : { 89 'CCFLAGS' : ['-DVIXL_DEBUG', '-O0'] 90 }, 91 'mode:release' : { 92 'CCFLAGS' : ['-O3'] 93 }, 94 'simulator:on' : { 95 'CCFLAGS' : ['-DVIXL_INCLUDE_SIMULATOR'], 96 }, 97 'symbols:on' : { 98 'CCFLAGS' : ['-g'], 99 'LINKFLAGS' : ['-g'] 100 }, 101 } 102 103 104# A `DefaultVariable` has a default value that depends on elements not known 105# when variables are first evaluated. 106# Each `DefaultVariable` has a handler that will compute the default value for 107# the given environment. 108def modifiable_flags_handler(env): 109 env['modifiable_flags'] = \ 110 'on' if 'mode' in env and env['mode'] == 'debug' else 'off' 111 112 113def symbols_handler(env): 114 env['symbols'] = 'on' if 'mode' in env and env['mode'] == 'debug' else 'off' 115 116 117vars_default_handlers = { 118 # variable_name : [ 'default val', 'handler' ] 119 'symbols' : [ 'mode==debug', symbols_handler ], 120 'modifiable_flags' : [ 'mode==debug', modifiable_flags_handler ] 121 } 122 123 124def DefaultVariable(name, help, allowed): 125 default_value = vars_default_handlers[name][0] 126 allowed.append(default_value) 127 return EnumVariable(name, help, default_value, allowed) 128 129 130vars = Variables() 131# Define command line build options. 132sim_default = 'off' if platform.machine() == 'aarch64' else 'on' 133vars.AddVariables( 134 EnumVariable('mode', 'Build mode', 135 'release', allowed_values=config.build_options_modes), 136 DefaultVariable('symbols', 'Include debugging symbols in the binaries', 137 ['on', 'off']), 138 EnumVariable('simulator', 'Build for the simulator', 139 sim_default, allowed_values=['on', 'off']), 140 ('std', 'C++ standard. The standards tested are: %s.' % \ 141 ', '.join(config.tested_cpp_standards)) 142 ) 143 144# Abort the build if any command line option is unknown or invalid. 145unknown_build_options = vars.UnknownVariables() 146if unknown_build_options: 147 print 'Unknown build options:', unknown_build_options.keys() 148 Exit(1) 149 150# We use 'variant directories' to avoid recompiling multiple times when build 151# options are changed, different build paths are used depending on the options 152# set. These are the options that should be reflected in the build directory 153# path. 154options_influencing_build_path = ['mode', 'symbols', 'CXX', 'std', 'simulator'] 155 156 157 158# Build helpers ---------------------------------------------------------------- 159 160def RetrieveEnvironmentVariables(env): 161 for key in ['CC', 'CXX', 'CCFLAGS', 'CXXFLAGS', 'AR', 'RANLIB', 'LD']: 162 if os.getenv(key): env[key] = os.getenv(key) 163 if os.getenv('LD_LIBRARY_PATH'): env['LIBPATH'] = os.getenv('LD_LIBRARY_PATH') 164 if os.getenv('CXXFLAGS'): 165 env.Append(CXXFLAGS = os.getenv('CXXFLAGS').split()) 166 if os.getenv('LINKFLAGS'): 167 env.Append(LINKFLAGS = os.getenv('LINKFLAGS').split()) 168 # This allows colors to be displayed when using with clang. 169 env['ENV']['TERM'] = os.getenv('TERM') 170 171 172def ProcessBuildOptions(env): 173 # 'all' is unconditionally processed. 174 if 'all' in options: 175 for var in options['all']: 176 if var in env and env[var]: 177 env[var] += options['all'][var] 178 else: 179 env[var] = options['all'][var] 180 # Other build options must match 'option:value' 181 env_dict = env.Dictionary() 182 for key in env_dict.keys(): 183 # First apply the default variables handlers. 184 if key in vars_default_handlers and \ 185 env_dict[key] == vars_default_handlers[key][0]: 186 vars_default_handlers[key][1](env_dict) 187 # Then update the environment according to the value of the variable. 188 key_val_couple = key + ':%s' % env_dict[key] 189 if key_val_couple in options: 190 for var in options[key_val_couple]: 191 env[var] += options[key_val_couple][var] 192 193 194def ConfigureEnvironmentForCompiler(env): 195 def is_compiler(compiler): 196 return env['CXX'].find(compiler) == 0 197 if is_compiler('clang++'): 198 # These warnings only work for Clang. 199 # -Wimplicit-fallthrough only works when compiling the code base as C++11 or 200 # newer. The compiler does not complain if the option is passed when 201 # compiling earlier C++ standards. 202 env.Append(CPPFLAGS = ['-Wimplicit-fallthrough', '-Wshorten-64-to-32']) 203 204 # The '-Wunreachable-code' flag breaks builds for clang 3.4. 205 process = subprocess.Popen(env['CXX'] + ' --version | grep "clang.*3\.4"', 206 shell = True, 207 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 208 stdout, stderr = process.communicate() 209 using_clang3_4 = stdout != '' 210 if not using_clang3_4: 211 env.Append(CPPFLAGS = ['-Wunreachable-code']) 212 213 # GCC 4.8 has a bug which produces a warning saying that an anonymous Operand 214 # object might be used uninitialized: 215 # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57045 216 # The bug does not seem to appear in GCC 4.7, or in debug builds with GCC 4.8. 217 if env['mode'] == 'release': 218 process = subprocess.Popen(env['CXX'] + ' --version | grep "g++.*4\.8"', 219 shell = True, 220 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 221 stdout, stderr = process.communicate() 222 using_gcc48 = stdout != '' 223 if using_gcc48: 224 env.Append(CPPFLAGS = ['-Wno-maybe-uninitialized']) 225 226 227def ConfigureEnvironment(env): 228 RetrieveEnvironmentVariables(env) 229 ProcessBuildOptions(env) 230 if 'std' in env: 231 env.Append(CPPFLAGS = ['-std=' + env['std']]) 232 std_path = env['std'] 233 ConfigureEnvironmentForCompiler(env) 234 235 236def TargetBuildDir(env): 237 # Build-time option values are embedded in the build path to avoid requiring a 238 # full build when an option changes. 239 build_dir = config.dir_build 240 for option in options_influencing_build_path: 241 option_value = env[option] if option in env else '' 242 build_dir = join(build_dir, option + '_'+ option_value) 243 return build_dir 244 245 246def PrepareVariantDir(location, build_dir): 247 location_build_dir = join(build_dir, location) 248 VariantDir(location_build_dir, location) 249 return location_build_dir 250 251 252def VIXLLibraryTarget(env): 253 build_dir = TargetBuildDir(env) 254 # Create a link to the latest build directory. 255 subprocess.check_call(["rm", "-f", config.dir_build_latest]) 256 util.ensure_dir(build_dir) 257 subprocess.check_call(["ln", "-s", build_dir, config.dir_build_latest]) 258 # Source files are in `src/vixl` and in `src/vixl/a64/`. 259 variant_dir_vixl = PrepareVariantDir(join('src', 'vixl'), build_dir) 260 variant_dir_a64 = PrepareVariantDir(join('src', 'vixl', 'a64'), build_dir) 261 sources = [Glob(join(variant_dir_vixl, '*.cc')), 262 Glob(join(variant_dir_a64, '*.cc'))] 263 return env.Library(join(build_dir, 'vixl'), sources) 264 265 266 267# Build ------------------------------------------------------------------------ 268 269# The VIXL library, built by default. 270env = Environment(variables = vars) 271ConfigureEnvironment(env) 272Help(vars.GenerateHelpText(env)) 273libvixl = VIXLLibraryTarget(env) 274Default(libvixl) 275env.Alias('libvixl', libvixl) 276top_level_targets.Add('', 'Build the VIXL library.') 277 278 279# The benchmarks. 280benchmark_names = util.ListCCFilesWithoutExt(config.dir_benchmarks) 281benchmarks_build_dir = PrepareVariantDir('benchmarks', TargetBuildDir(env)) 282benchmark_targets = [] 283for bench in benchmark_names: 284 prog = env.Program(join(benchmarks_build_dir, bench), 285 join(benchmarks_build_dir, bench + '.cc'), 286 LIBS=[libvixl]) 287 benchmark_targets.append(prog) 288env.Alias('benchmarks', benchmark_targets) 289top_level_targets.Add('benchmarks', 'Build the benchmarks.') 290 291 292# The examples. 293example_names = util.ListCCFilesWithoutExt(config.dir_examples) 294examples_build_dir = PrepareVariantDir('examples', TargetBuildDir(env)) 295example_targets = [] 296for example in example_names: 297 prog = env.Program(join(examples_build_dir, example), 298 join(examples_build_dir, example + '.cc'), 299 LIBS=[libvixl]) 300 example_targets.append(prog) 301env.Alias('examples', example_targets) 302top_level_targets.Add('examples', 'Build the examples.') 303 304 305# The tests. 306test_build_dir = PrepareVariantDir('test', TargetBuildDir(env)) 307# The test requires building the example files with specific options, so we 308# create a separate variant dir for the example objects built this way. 309test_examples_vdir = join(TargetBuildDir(env), 'test', 'test_examples') 310VariantDir(test_examples_vdir, '.') 311test_examples_obj = env.Object( 312 [Glob(join(test_examples_vdir, join('test', 'examples', '*.cc'))), 313 Glob(join(test_examples_vdir, join('examples', '*.cc')))], 314 CCFLAGS = env['CCFLAGS'] + ['-DTEST_EXAMPLES'], 315 CPPPATH = env['CPPPATH'] + [config.dir_examples]) 316test = env.Program(join(test_build_dir, 'test-runner'), 317 [Glob(join(test_build_dir, '*.cc')), test_examples_obj], 318 CPPPATH = env['CPPPATH'] + [config.dir_examples], 319 LIBS=[libvixl]) 320env.Alias('tests', test) 321top_level_targets.Add('tests', 'Build the tests.') 322 323 324env.Alias('all', top_level_targets.targets) 325top_level_targets.Add('all', 'Build all the targets above.') 326 327Help('\n\nAvailable top level targets:\n' + top_level_targets.Help()) 328