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 os
28import os.path
29import platform
30import subprocess
31import sys
32
33root_dir = os.path.dirname(File('SConstruct').rfile().abspath)
34sys.path.insert(0, os.path.join(root_dir, 'tools'))
35import util
36
37
38Help('''
39Build system for the VIXL project.
40See README.md for documentation and details about the build system.
41Some common build targets are:
42    scons            # Build the VIXL library and test utility.
43    scons examples   # Build all the examples.
44    scons all        # Build everything.
45
46''')
47
48
49# Global configuration.
50PROJ_SRC_DIR   = 'src'
51PROJ_SRC_FILES = '''
52src/vixl/a64/assembler-a64.cc
53src/vixl/a64/cpu-a64.cc
54src/vixl/a64/debugger-a64.cc
55src/vixl/a64/decoder-a64.cc
56src/vixl/a64/disasm-a64.cc
57src/vixl/a64/instructions-a64.cc
58src/vixl/a64/instrument-a64.cc
59src/vixl/a64/logic-a64.cc
60src/vixl/a64/macro-assembler-a64.cc
61src/vixl/a64/simulator-a64.cc
62src/vixl/code-buffer.cc
63src/vixl/compiler-intrinsics.cc
64src/vixl/utils.cc
65'''.split()
66PROJ_EXAMPLES_DIR = 'examples'
67PROJ_EXAMPLES_SRC_FILES = '''
68examples/abs.cc
69examples/add2-vectors.cc
70examples/add3-double.cc
71examples/add4-double.cc
72examples/check-bounds.cc
73examples/crc-checksums.cc
74examples/custom-disassembler.cc
75examples/debugger.cc
76examples/factorial-rec.cc
77examples/factorial.cc
78examples/neon-matrix-multiply.cc
79examples/getting-started.cc
80examples/non-const-visitor.cc
81examples/sum-array.cc
82examples/swap-int32.cc
83examples/swap4.cc
84'''.split()
85# List target specific files.
86# Target names are used as dictionary entries.
87TARGET_SRC_DIR = {
88  'test': 'test',
89  'bench-dataop': 'benchmarks',
90  'bench-branch': 'benchmarks',
91  'bench-branch-link': 'benchmarks',
92  'bench-branch-masm': 'benchmarks',
93  'bench-branch-link-masm': 'benchmarks',
94  'examples': 'examples'
95}
96TARGET_SRC_FILES = {
97  'test': '''
98    test/test-runner.cc
99    test/examples/test-examples.cc
100    test/test-assembler-a64.cc
101    test/test-disasm-a64.cc
102    test/test-fuzz-a64.cc
103    test/test-invalset.cc
104    test/test-simulator-a64.cc
105    test/test-utils-a64.cc
106    '''.split(),
107  'bench-dataop': '''
108    benchmarks/bench-dataop.cc
109    '''.split(),
110  'bench-branch': '''
111    benchmarks/bench-branch.cc
112    '''.split(),
113  'bench-branch-link': '''
114    benchmarks/bench-branch-link.cc
115    '''.split(),
116  'bench-branch-masm': '''
117    benchmarks/bench-branch-masm.cc
118    '''.split(),
119  'bench-branch-link-masm': '''
120    benchmarks/bench-branch-link-masm.cc
121    '''.split()
122}
123OBJ_DIR  = 'obj'
124
125# Helper functions.
126def abort(message):
127  print('ABORTING: ' + message)
128  sys.exit(1)
129
130
131def list_target(obj_dir, src_files):
132  return map(lambda x: os.path.join(obj_dir, x), src_files)
133
134
135def is_compiler(compiler):
136  return env['CXX'].find(compiler) == 0
137
138
139def create_variant(obj_dir, targets_dir):
140  VariantDir(os.path.join(obj_dir, PROJ_SRC_DIR), PROJ_SRC_DIR)
141  for directory in targets_dir.itervalues():
142    VariantDir(os.path.join(obj_dir, directory), directory)
143
144
145# Build arguments.
146args = Variables()
147args.Add(EnumVariable('mode', 'Build mode', 'release',
148                      allowed_values = ['release', 'debug']))
149sim_default = 'off' if platform.machine() == 'aarch64' else 'on'
150args.Add(EnumVariable('simulator', 'build for the simulator', sim_default,
151                      allowed_values = ['on', 'off']))
152args.Add('std', 'c++ standard')
153
154# Configure the environment.
155env = Environment(variables=args)
156
157# Commandline help.
158Help(args.GenerateHelpText(env) + '\n')
159
160# Abort if any invalid argument was passed.
161# This check must happened after an environment is created.
162unknown_arg = args.UnknownVariables()
163if unknown_arg:
164  abort('Unknown variable(s): ' + str(unknown_arg.keys()))
165
166# Setup tools.
167# This is necessary for cross-compilation.
168env['CXX'] = os.environ.get('CXX', env.get('CXX'))
169env['AR'] = os.environ.get('AR', env.get('AR'))
170env['RANLIB'] = os.environ.get('RANLIB', env.get('RANLIB'))
171env['CC'] = os.environ.get('CC', env.get('CC'))
172env['LD'] = os.environ.get('LD', env.get('LD'))
173
174if os.environ.get('CPPFLAGS'):
175  env.Append(CPPFLAGS = os.environ.get('CPPFLAGS').split())
176if os.environ.get('LINKFLAGS'):
177  env.Append(LINKFLAGS = os.environ.get('LINKFLAGS').split())
178
179# Always look in 'src' for include files.
180# TODO: Restore the '-Wunreachable-code' flag. This flag breaks builds for clang
181# 3.4 with std=c++98. So we need to re-enable this conditionally when clang is at
182# version 3.5 or later.
183env.Append(CPPPATH = [PROJ_SRC_DIR])
184env.Append(CPPFLAGS = ['-Wall',
185                       '-Werror',
186                       '-fdiagnostics-show-option',
187                       '-Wextra',
188                       '-Wredundant-decls',
189                       '-pedantic',
190                       # Explicitly enable the write-strings warning. VIXL uses
191                       # const correctly when handling string constants.
192                       '-Wwrite-strings'])
193
194build_suffix = ''
195std_path = 'default-std'
196
197if 'std' in env:
198  env.Append(CPPFLAGS = ['-std=' + env['std']])
199  std_path = env['std']
200
201if is_compiler('clang++'):
202  # This warning only works for Clang, when compiling the code base as C++11
203  # or newer. The compiler does not complain if the option is passed when
204  # compiling earlier C++ standards.
205  env.Append(CPPFLAGS = ['-Wimplicit-fallthrough'])
206
207if env['simulator'] == 'on':
208  env.Append(CPPFLAGS = ['-DUSE_SIMULATOR'])
209  build_suffix += '_sim'
210
211if env['mode'] == 'debug':
212  env.Append(CPPFLAGS = ['-g', '-DVIXL_DEBUG'])
213  # Append the debug mode suffix to the executable name.
214  build_suffix += '_g'
215else:
216  # Release mode.
217  env.Append(CPPFLAGS = ['-O3'])
218  process = subprocess.Popen(env['CXX'] + ' --version | grep "gnu.*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    # GCC 4.8 has a bug which produces a warning saying that an anonymous
225    # Operand object might be used uninitialized:
226    #   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57045
227    # The bug does not seem to appear in GCC 4.7, or in debug builds with
228    # GCC 4.8.
229    env.Append(CPPFLAGS = ['-Wno-maybe-uninitialized'])
230
231# Configure build directory
232build_dir = os.path.join(OBJ_DIR, env['mode'], env['CXX'], std_path, '')
233create_variant(build_dir, TARGET_SRC_DIR)
234
235# The lists of available targets and target names.
236targets = []
237target_alias_names = []
238# Helper to create aliases.
239def create_alias(name, target):
240  env.Alias(name, target)
241  targets.append(target)
242  target_alias_names.append(name)
243
244
245# The vixl library.
246libvixl = env.Library(build_dir + 'vixl' + build_suffix,
247                      list_target(build_dir, PROJ_SRC_FILES))
248create_alias('libvixl', libvixl)
249
250
251# The test executable.
252# The test requires building the example files with specific options, so we
253# create a separate variant dir for the example objects built this way.
254test_ex_vdir = os.path.join(build_dir, 'test_examples')
255VariantDir(test_ex_vdir, '.')
256test_ex_obj = env.Object(list_target(test_ex_vdir, PROJ_EXAMPLES_SRC_FILES),
257                         CPPFLAGS = env['CPPFLAGS'] + ['-DTEST_EXAMPLES'])
258test = env.Program(build_dir + 'test-runner' + build_suffix,
259                   list_target(build_dir, TARGET_SRC_FILES['test']) +
260                   test_ex_obj + libvixl,
261                   CPPPATH = env['CPPPATH'] + [PROJ_EXAMPLES_DIR])
262create_alias('test', test)
263
264# The benchmarks.
265benchmarks = ['bench-dataop', 'bench-branch', 'bench-branch-link',
266              'bench-branch-masm', 'bench-branch-link-masm']
267for bench in benchmarks:
268  prog = env.Program(build_dir + bench + build_suffix,
269                     list_target(build_dir, TARGET_SRC_FILES[bench]) + libvixl)
270  create_alias(bench, prog)
271# Alias to build all benchmarks.
272create_alias('benchmarks', benchmarks)
273
274# The examples.
275examples = []
276for example in PROJ_EXAMPLES_SRC_FILES:
277  example_name = "example-" + os.path.splitext(os.path.basename(example))[0]
278  prog = env.Program(build_dir + example_name,
279                     [os.path.join(build_dir, example)] + libvixl,
280                     CPPPATH = env['CPPPATH'] + [PROJ_EXAMPLES_DIR])
281  create_alias(example_name, prog)
282  examples.append(prog)
283# Alias to build all examples.
284create_alias('examples', examples)
285
286
287# Create a simple alias to build everything with the current options.
288create_alias('all', targets)
289
290Help('Available top level targets:\n' + '\t' + '\n\t'.join(target_alias_names) + '\n')
291
292# By default, only build the tests.
293Default(libvixl, test)
294