• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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