• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015, VIXL authors
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 itertools
29import os
30from os.path import join
31import platform
32import subprocess
33import sys
34from collections import OrderedDict
35
36root_dir = os.path.dirname(File('SConstruct').rfile().abspath)
37sys.path.insert(0, join(root_dir, 'tools'))
38import config
39import util
40
41from SCons.Errors import UserError
42
43
44Help('''
45Build system for the VIXL project.
46See README.md for documentation and details about the build system.
47''')
48
49
50# We track top-level targets to automatically generate help and alias them.
51class VIXLTargets:
52  def __init__(self):
53    self.targets = []
54    self.help_messages = []
55  def Add(self, target, help_message):
56    self.targets.append(target)
57    self.help_messages.append(help_message)
58  def Help(self):
59    res = ""
60    for i in range(len(self.targets)):
61      res += '\t{0:<{1}}{2:<{3}}\n'.format(
62        'scons ' + self.targets[i],
63        len('scons ') + max(map(len, self.targets)),
64        ' : ' + self.help_messages[i],
65        len(' : ') + max(map(len, self.help_messages)))
66    return res
67
68top_level_targets = VIXLTargets()
69
70
71
72# Build options ----------------------------------------------------------------
73
74# Store all the options in a dictionary.
75# The SConstruct will check the build variables and construct the build
76# environment as appropriate.
77options = {
78    'all' : { # Unconditionally processed.
79      'CCFLAGS' : ['-Wall',
80                   '-Werror',
81                   '-fdiagnostics-show-option',
82                   '-Wextra',
83                   '-Wredundant-decls',
84                   '-pedantic',
85                   '-Wwrite-strings',
86                   '-Wunused'],
87      'CPPPATH' : [config.dir_src_vixl]
88      },
89#   'build_option:value' : {
90#     'environment_key' : 'values to append'
91#     },
92    'mode:debug' : {
93      'CCFLAGS' : ['-DVIXL_DEBUG', '-O0']
94      },
95    'mode:release' : {
96      'CCFLAGS' : ['-O3'],
97      },
98    'simulator:aarch64' : {
99      'CCFLAGS' : ['-DVIXL_INCLUDE_SIMULATOR_AARCH64'],
100      },
101    'symbols:on' : {
102      'CCFLAGS' : ['-g'],
103      'LINKFLAGS' : ['-g']
104      },
105    'negative_testing:on' : {
106      'CCFLAGS' : ['-DVIXL_NEGATIVE_TESTING']
107      },
108    'code_buffer_allocator:mmap' : {
109      'CCFLAGS' : ['-DVIXL_CODE_BUFFER_MMAP']
110      },
111    'code_buffer_allocator:malloc' : {
112      'CCFLAGS' : ['-DVIXL_CODE_BUFFER_MALLOC']
113      },
114    'ubsan:on' : {
115      'CCFLAGS': ['-fsanitize=undefined'],
116      'LINKFLAGS': ['-fsanitize=undefined']
117      }
118    }
119
120
121# A `DefaultVariable` has a default value that depends on elements not known
122# when variables are first evaluated.
123# Each `DefaultVariable` has a handler that will compute the default value for
124# the given environment.
125def modifiable_flags_handler(env):
126  env['modifiable_flags'] = \
127      'on' if 'mode' in env and env['mode'] == 'debug' else 'off'
128
129
130def symbols_handler(env):
131  env['symbols'] = 'on' if 'mode' in env and env['mode'] == 'debug' else 'off'
132
133def Is32BitHost(env):
134  return env['host_arch'] in ['aarch32', 'i386']
135
136def IsAArch64Host(env):
137  return env['host_arch'] == 'aarch64'
138
139def CanTargetA32(env):
140  return 'a32' in env['target']
141
142def CanTargetT32(env):
143  return 't32' in env['target']
144
145def CanTargetAArch32(env):
146  return CanTargetA32(env) or CanTargetT32(env)
147
148def CanTargetA64(env):
149  return 'a64' in env['target']
150
151def CanTargetAArch64(env):
152  return CanTargetA64(env)
153
154
155# By default, include the simulator only if AArch64 is targeted and we are not
156# building VIXL natively for AArch64.
157def simulator_handler(env):
158  if not IsAArch64Host(env) and CanTargetAArch64(env):
159    env['simulator'] = 'aarch64'
160  else:
161    env['simulator'] = 'none'
162
163
164# 'mmap' is required for use with 'mprotect', which is needed for the tests
165# (when running natively), so we use it by default where we can.
166def code_buffer_allocator_handler(env):
167  directives = util.GetCompilerDirectives(env)
168  if '__linux__' in directives:
169    env['code_buffer_allocator'] = 'mmap'
170  else:
171    env['code_buffer_allocator'] = 'malloc'
172
173# A validator checks the consistency of provided options against the environment.
174def default_validator(env):
175  pass
176
177
178def simulator_validator(env):
179  if env['simulator'] == 'aarch64' and not CanTargetAArch64(env):
180    raise UserError('Building an AArch64 simulator implies that VIXL targets '
181                    'AArch64. Set `target` to include `aarch64` or `a64`.')
182
183
184# Default variables may depend on each other, therefore we need this dictionnary
185# to be ordered.
186vars_default_handlers = OrderedDict({
187    # variable_name    : [ 'default val', 'handler', 'validator']
188    'symbols'          : [ 'mode==debug', symbols_handler, default_validator ],
189    'modifiable_flags' : [ 'mode==debug', modifiable_flags_handler, default_validator],
190    'simulator'        : [ 'on if the target architectures include AArch64 but '
191                           'the host is not AArch64, else off',
192                           simulator_handler, simulator_validator ],
193    'code_buffer_allocator' : [ 'mmap with __linux__, malloc otherwise',
194                                code_buffer_allocator_handler, default_validator ]
195    })
196
197
198def DefaultVariable(name, help, allowed_values):
199  help = '%s (%s)' % (help, '|'.join(allowed_values))
200  default_value = vars_default_handlers[name][0]
201  def validator(name, value, env):
202    if value != default_value and value not in allowed_values:
203        raise UserError('Invalid value for option {name}: {value}.  '
204                        'Valid values are: {allowed_values}'.format(
205                            name, value, allowed_values))
206  return (name, help, default_value, validator)
207
208
209def AliasedListVariable(name, help, default_value, allowed_values, aliasing):
210  help = '%s (all|auto|comma-separated list) (any combination from [%s])' % \
211         (help, ', '.join(allowed_values))
212
213  def validator(name, value, env):
214    # Here list has been converted to space separated strings.
215    if value == '': return  # auto
216    for v in value.split():
217      if v not in allowed_values:
218        raise UserError('Invalid value for %s: %s' % (name, value))
219
220  def converter(value):
221    if value == 'auto': return []
222    if value == 'all':
223      translated = [aliasing[v] for v in allowed_values]
224      return list(set(itertools.chain.from_iterable(translated)))
225    # The validator is run later hence the get.
226    translated = [aliasing.get(v, v) for v in value.split(',')]
227    return list(set(itertools.chain.from_iterable(translated)))
228
229  return (name, help, default_value, validator, converter)
230
231
232vars = Variables()
233# Define command line build options.
234vars.AddVariables(
235    AliasedListVariable('target', 'Target ISA/Architecture', 'auto',
236                        ['aarch32', 'a32', 't32', 'aarch64', 'a64'],
237                        {'aarch32' : ['a32', 't32'],
238                         'a32' : ['a32'], 't32' : ['t32'],
239                         'aarch64' : ['a64'], 'a64' : ['a64']}),
240    EnumVariable('mode', 'Build mode',
241                 'release', allowed_values=config.build_options_modes),
242    EnumVariable('ubsan', 'Enable undefined behavior checks',
243                 'off', allowed_values=['on', 'off']),
244    EnumVariable('negative_testing',
245                  'Enable negative testing (needs exceptions)',
246                 'off', allowed_values=['on', 'off']),
247    DefaultVariable('symbols', 'Include debugging symbols in the binaries',
248                    ['on', 'off']),
249    DefaultVariable('simulator', 'Simulators to include', ['aarch64', 'none']),
250    DefaultVariable('code_buffer_allocator',
251                    'Configure the allocation mechanism in the CodeBuffer',
252                    ['malloc', 'mmap']),
253    ('std', 'C++ standard. The standards tested are: %s.' % \
254                                         ', '.join(config.tested_cpp_standards))
255    )
256
257# We use 'variant directories' to avoid recompiling multiple times when build
258# options are changed, different build paths are used depending on the options
259# set. These are the options that should be reflected in the build directory
260# path.
261options_influencing_build_path = [
262  'target', 'mode', 'symbols', 'CXX', 'std', 'simulator', 'negative_testing',
263  'code_buffer_allocator'
264]
265
266
267
268# Build helpers ----------------------------------------------------------------
269
270def RetrieveEnvironmentVariables(env):
271  for key in ['CC', 'CXX', 'AR', 'RANLIB', 'LD']:
272    if os.getenv(key): env[key] = os.getenv(key)
273  if os.getenv('LD_LIBRARY_PATH'): env['LIBPATH'] = os.getenv('LD_LIBRARY_PATH')
274  if os.getenv('CCFLAGS'):
275    env.Append(CCFLAGS = os.getenv('CCFLAGS').split())
276  if os.getenv('CXXFLAGS'):
277    env.Append(CXXFLAGS = os.getenv('CXXFLAGS').split())
278  if os.getenv('LINKFLAGS'):
279    env.Append(LINKFLAGS = os.getenv('LINKFLAGS').split())
280  # This allows colors to be displayed when using with clang.
281  env['ENV']['TERM'] = os.getenv('TERM')
282
283
284# The architecture targeted by default will depend on the compiler being
285# used. 'host_arch' is extracted from the compiler while 'target' can be
286# set by the user.
287# By default, we target both AArch32 and AArch64 unless the compiler targets a
288# 32-bit architecture. At the moment, we cannot build VIXL's AArch64 support on
289# a 32-bit platform.
290# TODO: Port VIXL to build on a 32-bit platform.
291def target_handler(env):
292  # Auto detect
293  if Is32BitHost(env):
294    # We use list(set(...)) to keep the same order as if it was specify as
295    # an option.
296    env['target'] = list(set(['a32', 't32']))
297  else:
298    env['target'] = list(set(['a64', 'a32', 't32']))
299
300
301def target_validator(env):
302  # TODO: Port VIXL64 to work on a 32-bit platform.
303  if Is32BitHost(env) and CanTargetAArch64(env):
304    raise UserError('Building VIXL for AArch64 in 32-bit is not supported. Set '
305                    '`target` to `aarch32`')
306
307
308# The target option is handled differently from the rest.
309def ProcessTargetOption(env):
310  if env['target'] == []: target_handler(env)
311
312  if 'a32' in env['target']: env['CCFLAGS'] += ['-DVIXL_INCLUDE_TARGET_A32']
313  if 't32' in env['target']: env['CCFLAGS'] += ['-DVIXL_INCLUDE_TARGET_T32']
314  if 'a64' in env['target']: env['CCFLAGS'] += ['-DVIXL_INCLUDE_TARGET_A64']
315
316  target_validator(env)
317
318
319def ProcessBuildOptions(env):
320  # 'all' is unconditionally processed.
321  if 'all' in options:
322    for var in options['all']:
323      if var in env and env[var]:
324        env[var] += options['all'][var]
325      else:
326        env[var] = options['all'][var]
327
328  # The target option *must* be processed before the options defined in
329  # vars_default_handlers.
330  ProcessTargetOption(env)
331
332  # Other build options must match 'option:value'
333  env_dict = env.Dictionary()
334
335  # First apply the default variables handlers in order.
336  for key, value in vars_default_handlers.items():
337    default = value[0]
338    handler = value[1]
339    if env_dict.get(key) == default:
340      handler(env_dict)
341
342  # Second, run the series of validators, to check for errors.
343  for _, value in vars_default_handlers.items():
344    validator = value[2]
345    validator(env)
346
347  for key in env_dict.keys():
348    # Then update the environment according to the value of the variable.
349    key_val_couple = key + ':%s' % env_dict[key]
350    if key_val_couple in options:
351      for var in options[key_val_couple]:
352        env[var] += options[key_val_couple][var]
353
354
355def ConfigureEnvironmentForCompiler(env):
356  if CanTargetA32(env) and CanTargetT32(env):
357    # When building for only one aarch32 isa, fixing the no-return is not worth
358    # the effort.
359    env.Append(CPPFLAGS = ['-Wmissing-noreturn'])
360
361  compiler = util.CompilerInformation(env)
362  if compiler == 'clang':
363    # These warnings only work for Clang.
364    # -Wimplicit-fallthrough only works when compiling the code base as C++11 or
365    # newer. The compiler does not complain if the option is passed when
366    # compiling earlier C++ standards.
367    env.Append(CPPFLAGS = ['-Wimplicit-fallthrough', '-Wshorten-64-to-32'])
368
369    # The '-Wunreachable-code' flag breaks builds for clang 3.4.
370    if compiler != 'clang-3.4':
371      env.Append(CPPFLAGS = ['-Wunreachable-code'])
372
373    if env['ubsan'] == 'on':
374      env.Append(LINKFLAGS = ['-fuse-ld=lld'])
375
376  # GCC 4.8 has a bug which produces a warning saying that an anonymous Operand
377  # object might be used uninitialized:
378  #   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57045
379  # The bug does not seem to appear in GCC 4.7, or in debug builds with GCC 4.8.
380  if env['mode'] == 'release':
381    if compiler == 'gcc-4.8':
382      env.Append(CPPFLAGS = ['-Wno-maybe-uninitialized'])
383
384  # GCC 6 and higher is able to detect throwing from inside a destructor and
385  # reports a warning. However, if negative testing is enabled then assertions
386  # will throw exceptions.
387  if env['negative_testing'] == 'on' and env['mode'] == 'debug' \
388      and compiler >= 'gcc-6':
389    env.Append(CPPFLAGS = ['-Wno-terminate'])
390    # The C++11 compatibility warning will also be triggered for this case, as
391    # the behavior of throwing from desctructors has changed.
392    if 'std' in env and env['std'] == 'c++98':
393      env.Append(CPPFLAGS = ['-Wno-c++11-compat'])
394
395  # When compiling with c++98 (the default), allow long long constants.
396  if 'std' not in env or env['std'] == 'c++98':
397    env.Append(CPPFLAGS = ['-Wno-long-long'])
398    env.Append(CPPFLAGS = ['-Wno-variadic-macros'])
399  # When compiling with c++11, suggest missing override keywords on methods.
400  if 'std' in env and env['std'] in ['c++11', 'c++14']:
401    if compiler >= 'gcc-5':
402      env.Append(CPPFLAGS = ['-Wsuggest-override'])
403    elif compiler >= 'clang-3.6':
404      env.Append(CPPFLAGS = ['-Winconsistent-missing-override'])
405
406
407def ConfigureEnvironment(env):
408  RetrieveEnvironmentVariables(env)
409  env['host_arch'] = util.GetHostArch(env)
410  ProcessBuildOptions(env)
411  if 'std' in env:
412    env.Append(CPPFLAGS = ['-std=' + env['std']])
413    std_path = env['std']
414  ConfigureEnvironmentForCompiler(env)
415
416
417def TargetBuildDir(env):
418  # Build-time option values are embedded in the build path to avoid requiring a
419  # full build when an option changes.
420  build_dir = config.dir_build
421  for option in options_influencing_build_path:
422    option_value = ''.join(env[option]) if option in env else ''
423    build_dir = join(build_dir, option + '_'+ option_value)
424  return build_dir
425
426
427def PrepareVariantDir(location, build_dir):
428  location_build_dir = join(build_dir, location)
429  VariantDir(location_build_dir, location)
430  return location_build_dir
431
432
433def VIXLLibraryTarget(env):
434  build_dir = TargetBuildDir(env)
435  # Create a link to the latest build directory.
436  # Use `-r` to avoid failure when `latest` exists and is a directory.
437  subprocess.check_call(["rm", "-rf", config.dir_build_latest])
438  util.ensure_dir(build_dir)
439  subprocess.check_call(["ln", "-s", build_dir, config.dir_build_latest])
440  # Source files are in `src` and in `src/aarch64/`.
441  variant_dir_vixl = PrepareVariantDir(join('src'), build_dir)
442  sources = [Glob(join(variant_dir_vixl, '*.cc'))]
443  if CanTargetAArch32(env):
444    variant_dir_aarch32 = PrepareVariantDir(join('src', 'aarch32'), build_dir)
445    sources.append(Glob(join(variant_dir_aarch32, '*.cc')))
446  if CanTargetAArch64(env):
447    variant_dir_aarch64 = PrepareVariantDir(join('src', 'aarch64'), build_dir)
448    sources.append(Glob(join(variant_dir_aarch64, '*.cc')))
449  return env.Library(join(build_dir, 'vixl'), sources)
450
451
452
453# Build ------------------------------------------------------------------------
454
455# The VIXL library, built by default.
456env = Environment(variables = vars,
457                  BUILDERS = {
458                      'Markdown': Builder(action = 'markdown $SOURCE > $TARGET',
459                                          suffix = '.html')
460                  })
461# Abort the build if any command line option is unknown or invalid.
462unknown_build_options = vars.UnknownVariables()
463if unknown_build_options:
464  print 'Unknown build options:',  unknown_build_options.keys()
465  Exit(1)
466
467ConfigureEnvironment(env)
468Help(vars.GenerateHelpText(env))
469libvixl = VIXLLibraryTarget(env)
470Default(libvixl)
471env.Alias('libvixl', libvixl)
472top_level_targets.Add('', 'Build the VIXL library.')
473
474
475# Common test code.
476test_build_dir = PrepareVariantDir('test', TargetBuildDir(env))
477test_objects = [env.Object(Glob(join(test_build_dir, '*.cc')))]
478
479# AArch32 support
480if CanTargetAArch32(env):
481  # The examples.
482  aarch32_example_names = util.ListCCFilesWithoutExt(config.dir_aarch32_examples)
483  aarch32_examples_build_dir = PrepareVariantDir('examples/aarch32', TargetBuildDir(env))
484  aarch32_example_targets = []
485  for example in aarch32_example_names:
486    prog = env.Program(join(aarch32_examples_build_dir, example),
487                       join(aarch32_examples_build_dir, example + '.cc'),
488                       LIBS=[libvixl])
489    aarch32_example_targets.append(prog)
490  env.Alias('aarch32_examples', aarch32_example_targets)
491  top_level_targets.Add('aarch32_examples', 'Build the examples for AArch32.')
492
493  # The benchmarks
494  aarch32_benchmark_names = util.ListCCFilesWithoutExt(config.dir_aarch32_benchmarks)
495  aarch32_benchmarks_build_dir = PrepareVariantDir('benchmarks/aarch32', TargetBuildDir(env))
496  aarch32_benchmark_targets = []
497  for bench in aarch32_benchmark_names:
498    prog = env.Program(join(aarch32_benchmarks_build_dir, bench),
499                       join(aarch32_benchmarks_build_dir, bench + '.cc'),
500                       LIBS=[libvixl])
501    aarch32_benchmark_targets.append(prog)
502  env.Alias('aarch32_benchmarks', aarch32_benchmark_targets)
503  top_level_targets.Add('aarch32_benchmarks', 'Build the benchmarks for AArch32.')
504
505  # The tests.
506  test_aarch32_build_dir = PrepareVariantDir(join('test', 'aarch32'), TargetBuildDir(env))
507  test_objects.append(env.Object(
508      Glob(join(test_aarch32_build_dir, '*.cc')),
509      CPPPATH = env['CPPPATH'] + [config.dir_tests]))
510
511# AArch64 support
512if CanTargetAArch64(env):
513  # The benchmarks.
514  aarch64_benchmark_names = util.ListCCFilesWithoutExt(config.dir_aarch64_benchmarks)
515  aarch64_benchmarks_build_dir = PrepareVariantDir('benchmarks/aarch64', TargetBuildDir(env))
516  aarch64_benchmark_targets = []
517  for bench in aarch64_benchmark_names:
518    prog = env.Program(join(aarch64_benchmarks_build_dir, bench),
519                       join(aarch64_benchmarks_build_dir, bench + '.cc'),
520                       LIBS=[libvixl])
521    aarch64_benchmark_targets.append(prog)
522  env.Alias('aarch64_benchmarks', aarch64_benchmark_targets)
523  top_level_targets.Add('aarch64_benchmarks', 'Build the benchmarks for AArch64.')
524
525  # The examples.
526  aarch64_example_names = util.ListCCFilesWithoutExt(config.dir_aarch64_examples)
527  aarch64_examples_build_dir = PrepareVariantDir('examples/aarch64', TargetBuildDir(env))
528  aarch64_example_targets = []
529  for example in aarch64_example_names:
530    prog = env.Program(join(aarch64_examples_build_dir, example),
531                       join(aarch64_examples_build_dir, example + '.cc'),
532                       LIBS=[libvixl])
533    aarch64_example_targets.append(prog)
534  env.Alias('aarch64_examples', aarch64_example_targets)
535  top_level_targets.Add('aarch64_examples', 'Build the examples for AArch64.')
536
537  # The tests.
538  test_aarch64_build_dir = PrepareVariantDir(join('test', 'aarch64'), TargetBuildDir(env))
539  test_objects.append(env.Object(
540      Glob(join(test_aarch64_build_dir, '*.cc')),
541      CPPPATH = env['CPPPATH'] + [config.dir_tests]))
542
543  # The test requires building the example files with specific options, so we
544  # create a separate variant dir for the example objects built this way.
545  test_aarch64_examples_vdir = join(TargetBuildDir(env), 'test', 'aarch64', 'test_examples')
546  VariantDir(test_aarch64_examples_vdir, '.')
547  test_aarch64_examples_obj = env.Object(
548      [Glob(join(test_aarch64_examples_vdir, join('test', 'aarch64', 'examples', '*.cc'))),
549       Glob(join(test_aarch64_examples_vdir, join('examples/aarch64', '*.cc')))],
550      CCFLAGS = env['CCFLAGS'] + ['-DTEST_EXAMPLES'],
551      CPPPATH = env['CPPPATH'] + [config.dir_aarch64_examples] + [config.dir_tests])
552  test_objects.append(test_aarch64_examples_obj)
553
554test = env.Program(join(test_build_dir, 'test-runner'), test_objects,
555                   LIBS=[libvixl])
556env.Alias('tests', test)
557top_level_targets.Add('tests', 'Build the tests.')
558
559
560env.Alias('all', top_level_targets.targets)
561top_level_targets.Add('all', 'Build all the targets above.')
562
563Help('\n\nAvailable top level targets:\n' + top_level_targets.Help())
564
565extra_targets = VIXLTargets()
566
567# Build documentation
568doc = [
569    env.Markdown('README.md'),
570    env.Markdown('doc/changelog.md'),
571    env.Markdown('doc/aarch32/getting-started-aarch32.md'),
572    env.Markdown('doc/aarch32/design/code-generation-aarch32.md'),
573    env.Markdown('doc/aarch32/design/literal-pool-aarch32.md'),
574    env.Markdown('doc/aarch64/supported-instructions-aarch64.md'),
575    env.Markdown('doc/aarch64/getting-started-aarch64.md'),
576    env.Markdown('doc/aarch64/topics/ycm.md'),
577    env.Markdown('doc/aarch64/topics/extending-the-disassembler.md'),
578    env.Markdown('doc/aarch64/topics/index.md'),
579]
580env.Alias('doc', doc)
581extra_targets.Add('doc', 'Convert documentation to HTML (requires the '
582                         '`markdown` program).')
583
584Help('\nAvailable extra targets:\n' + extra_targets.Help())
585