blob: 5d7011d8565973802ba9fca2d9e1c8b81270518a [file] [log] [blame]
Brian Silvermana29ebf92014-04-23 13:08:49 -05001#!/usr/bin/python3
2
3import argparse
4import sys
5import subprocess
6import re
7import os
8import os.path
9import string
10import shutil
11import errno
12
Brian Silvermanbe6cfe22014-04-27 08:06:27 -050013def aos_path():
14 return os.path.join(os.path.dirname(__file__), '..')
15
16def get_ip(device):
17 FILENAME = os.path.normpath(os.path.join(aos_path(), '..', 'output', 'ip_base.txt'))
18 if not os.access(FILENAME, os.R_OK):
19 os.makedirs(os.path.dirname(FILENAME), exist_ok=True)
20 with open(FILENAME, 'w') as f:
21 f.write('10.9.71')
22 with open(FILENAME, 'r') as f:
23 base = f.readline()
24 if device == 'prime':
25 return base + '.179'
26 elif device == 'robot':
27 return base + '.2'
28 else:
29 raise Exception('Unknown device %s to get an IP address for.' % device)
30
Brian Silvermana9b1e5c2014-04-30 18:08:04 -070031def user_output(message):
32 print('build.py: ' + message, file=sys.stderr)
33
Brian Silvermana29ebf92014-04-23 13:08:49 -050034class Processor(object):
35 class UnknownPlatform(Exception):
36 def __init__(self, message):
37 self.message = message
38
Brian Silvermanb3d50542014-04-23 14:28:55 -050039 class Platform(object):
40 def outdir(self):
41 return os.path.join(
Brian Silvermanbe6cfe22014-04-27 08:06:27 -050042 aos_path(), '..', 'output', self.outname())
Brian Silvermanb3d50542014-04-23 14:28:55 -050043 def build_ninja(self):
44 return os.path.join(self.outdir(), 'build.ninja')
45
Brian Silvermanbe6cfe22014-04-27 08:06:27 -050046 def do_deploy(self, dry_run, command):
47 real_command = (('echo',) + command) if dry_run else command
48 subprocess.check_call(real_command, stdin=open(os.devnull, 'r'))
Brian Silvermana29ebf92014-04-23 13:08:49 -050049
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -070050 # TODO(brians): Verify that this (and its callers) catch everything from a
51 # fresh install.
52 def do_check_installed(self, other_packages):
53 all_packages = () + other_packages
54 try:
55 result = subprocess.check_output(
56 ('dpkg-query', '--show') + all_packages,
57 stdin=open(os.devnull, 'r'),
58 stderr=subprocess.STDOUT)
59 except subprocess.CalledProcessError as e:
60 user_output('Some packages not installed:\n'
61 + e.output.decode('utf-8').rstrip())
62 exit(1)
63
Brian Silvermana29ebf92014-04-23 13:08:49 -050064class CRIOProcessor(Processor):
Brian Silvermanb3d50542014-04-23 14:28:55 -050065 class Platform(Processor.Platform):
Brian Silvermana4aff562014-05-02 17:43:50 -070066 def __init__(self, debug, wind_base):
Brian Silvermanb3d50542014-04-23 14:28:55 -050067 super(CRIOProcessor.Platform, self).__init__()
68
69 self.debug = debug
Brian Silvermana4aff562014-05-02 17:43:50 -070070 self.wind_base = wind_base
Brian Silvermanb3d50542014-04-23 14:28:55 -050071
72 def __repr__(self):
73 return 'CRIOProcessor.Platform(debug=%s)' % self.debug
74 def __str__(self):
75 return 'crio%s' % ('-debug' if self.debug else '')
76
77 def outname(self):
78 return 'crio-debug' if self.debug else 'crio'
79 def os(self):
80 return 'vxworks'
81 def gyp_platform(self):
82 return 'crio'
83 def architecture(self):
84 return 'ppc'
85 def compiler(self):
86 return 'gcc'
87
Brian Silvermane48c09a2014-04-30 18:04:58 -070088 # TODO(brians): test this
Brian Silvermanbe6cfe22014-04-27 08:06:27 -050089 def deploy(self, dry_run):
90 self.do_deploy(dry_run,
91 ('ncftpput', get_ip('robot'), '/',
92 os.path.join(self.outdir(), 'lib', 'FRC_UserProgram.out')))
93
Brian Silvermana4aff562014-05-02 17:43:50 -070094 def build_env(self):
95 return {'WIND_BASE': self.wind_base}
96
Brian Silvermana29ebf92014-04-23 13:08:49 -050097 def __init__(self):
98 super(CRIOProcessor, self).__init__()
99
100 if 'WIND_BASE' in os.environ:
101 self.wind_base = os.environ['WIND_BASE']
102 else:
103 self.wind_base = '/usr/local/powerpc-wrs-vxworks/wind_base'
104
105 def parse_platforms(self, string):
Brian Silvermanb3d50542014-04-23 14:28:55 -0500106 if string is None or string == 'crio':
Brian Silvermana4aff562014-05-02 17:43:50 -0700107 return (CRIOProcessor.Platform(False, self.wind_base),)
108 elif string == 'crio-debug' or string == 'debug':
109 return (CRIOProcessor.Platform(True, self.wind_base),)
Brian Silvermanb3d50542014-04-23 14:28:55 -0500110 else:
111 raise Processor.UnknownPlatform('Unknown cRIO platform "%s".' % string)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500112
Brian Silvermanb3d50542014-04-23 14:28:55 -0500113 def extra_gyp_flags(self):
114 return ('-DWIND_BASE=%s' % self.wind_base,)
115
Brian Silvermana29ebf92014-04-23 13:08:49 -0500116 def is_crio(self): return True
117
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700118 def check_installed(self):
119 # TODO(brians): Add powerpc-wrs-vxworks (a new enough version too).
120 self.do_check_installed(
121 ('ncftp',))
122
Brian Silvermana29ebf92014-04-23 13:08:49 -0500123class PrimeProcessor(Processor):
Brian Silvermanb3d50542014-04-23 14:28:55 -0500124 class Platform(Processor.Platform):
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700125 def __init__(self, architecture, compiler, debug, sanitizer):
Brian Silvermanb3d50542014-04-23 14:28:55 -0500126 super(PrimeProcessor.Platform, self).__init__()
127
Brian Silvermana29ebf92014-04-23 13:08:49 -0500128 self.architecture = architecture
129 self.compiler = compiler
130 self.debug = debug
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700131 self.sanitizer = sanitizer
Brian Silvermana29ebf92014-04-23 13:08:49 -0500132
133 def __repr__(self):
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700134 return 'PrimeProcessor.Platform(architecture=%s, compiler=%s, debug=%s' \
135 ', sanitizer=%s)' \
136 % (self.architecture, self.compiler, self.debug, self.sanitizer)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500137 def __str__(self):
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700138 return '%s-%s%s-%s' % (self.architecture, self.compiler,
139 '-debug' if self.debug else '', self.sanitizer)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500140
141 def os(self):
142 return 'linux'
143 def gyp_platform(self):
144 return '%s-%s-%s' % (self.os(), self.architecture, self.compiler)
145
Brian Silvermana29ebf92014-04-23 13:08:49 -0500146 def outname(self):
147 return str(self)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500148
Brian Silvermane48c09a2014-04-30 18:04:58 -0700149 # TODO(brians): test this
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500150 def deploy(self, dry_run):
151 """Downloads code to the prime in a way that avoids clashing too badly with starter
152 """
153 SUM = 'md5sum'
154 TARGET_DIR = '/home/driver/robot_code/bin'
155 TEMP_DIR = '/tmp/aos_downloader'
156 TARGET = 'driver@' + get_ip('prime')
157
158 from_dir = os.path.join(self.outdir(), 'outputs')
159 sums = subprocess.check_output((SUM,) + tuple(os.listdir(from_dir)),
160 stdin=open(os.devnull, 'r'),
161 cwd=from_dir)
162 to_download = subprocess.check_output(
163 ('ssh', TARGET,
164 """rm -rf {TMPDIR} && mkdir {TMPDIR} && cd {TO_DIR}
165 && echo '{SUMS}' | {SUM} --check --quiet
166 |& grep -F FAILED | sed 's/^\\(.*\\): FAILED.*"'$'"/\\1/g'""".format(
167 TMPDIR=TEMP_DIR, TO_DIR=TARGET_DIR, SUMS=sums, SUM=SUM)))
168 if not to_download:
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700169 user_output("Nothing to download")
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500170 return
171 self.do_deploy(
172 dry_run,
173 ('scp', '-o', 'Compression yes') + to_download
174 + (('%s:%s' % (TARGET, TEMP_DIR)),))
175 if not dry_run:
176 subprocess.check_call(
177 ('ssh', TARGET,
178 """mv {TMPDIR}/* {TO_DIR}
179 && echo 'Done moving new executables into place'
180 && ionice -c 3 bash -c 'sync && sync && sync'""".format(
181 TMPDIR=TEMP_DIR, TO_DIR=TARGET_DIR)))
182
Brian Silvermana4aff562014-05-02 17:43:50 -0700183 def build_env(self):
184 r = {}
Brian Silverman47cd6f62014-05-03 10:35:52 -0700185 if self.compiler == 'clang' or self.compiler == 'gcc_4.8':
Brian Silvermana4aff562014-05-02 17:43:50 -0700186 r['LD_LIBRARY_PATH'] = '/opt/clang-3.5/lib64'
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700187 if self.sanitizer == 'address':
Brian Silvermana4aff562014-05-02 17:43:50 -0700188 r['ASAN_SYMBOLIZER_PATH'] = '/opt/clang-3.5/bin/llvm-symbolizer'
189 r['ASAN_OPTIONS'] = 'detect_leaks=1:check_initialization_order=1:strict_init_order=1'
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700190 elif self.sanitizer == 'memory':
191 r['MSAN_SYMBOLIZER_PATH'] = '/opt/clang-3.5/bin/llvm-symbolizer'
Brian Silverman266811f2014-05-03 22:48:04 -0700192 elif self.sanitizer == 'thread':
193 r['TSAN_OPTIONS'] = 'external_symbolizer_path=/opt/clang-3.5/bin/llvm-symbolizer'
Brian Silvermand3fac732014-05-03 16:03:46 -0700194
195 r['CCACHE_COMPRESS'] = 'yes'
196 r['CCACHE_DIR'] = \
197 os.path.abspath(os.path.join(aos_path(), '..', 'output', 'ccache_dir'))
198 r['CCACHE_HASHDIR'] = 'yes'
199 if self.compiler == 'clang':
200 # clang doesn't like being run directly on the preprocessed files.
201 r['CCACHE_CPP2'] = 'yes'
202 # Without this, ccache slows down because of the generated header files.
203 # The race condition that this opens up isn't a problem because the build
204 # system finishes modifying header files before compiling anything that
205 # uses them.
206 r['CCACHE_SLOPPINESS'] = 'include_file_mtime'
Brian Silvermand3fac732014-05-03 16:03:46 -0700207
208 if self.architecture == 'amd64':
209 r['PATH'] = os.path.join(aos_path(), 'build', 'bin-ld.gold') + \
210 ':' + os.environ['PATH']
211
Brian Silvermana4aff562014-05-02 17:43:50 -0700212 return r
213
Brian Silverman47cd6f62014-05-03 10:35:52 -0700214 ARCHITECTURES = ('arm', 'amd64')
215 COMPILERS = ('clang', 'gcc', 'gcc_4.8')
216 SANITIZERS = ('address', 'undefined', 'integer', 'memory', 'thread', 'none')
217 SANITIZER_TEST_WARNINGS = {
218 'memory': (True,
219"""We don't have all of the libraries instrumented which leads to lots of false
220errors with msan (especially stdlibc++).
221TODO(brians): Figure out a way to deal with it."""),
222 'undefined': (False,
223"""There are several warnings in other people's code that ubsan catches.
224The following have been verified non-interesting:
225 include/c++/4.8.2/array:*: runtime error: reference binding to null pointer of type 'int'
226 This happens with ::std::array<T, 0> and it doesn't seem to cause any issues.
227 output/downloaded/eigen-3.2.1/Eigen/src/Core/util/Memory.h:782:*: runtime error: load of misaligned address 0x* for type 'const int', which requires 4 byte alignment
228 That's in the CPUID detection code which only runs on x86."""),
229 }
Brian Silvermana5301e32014-05-03 10:51:49 -0700230 PIE_SANITIZERS = ('memory', 'thread')
Brian Silvermana29ebf92014-04-23 13:08:49 -0500231
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500232 def __init__(self, is_test, is_deploy):
Brian Silvermana29ebf92014-04-23 13:08:49 -0500233 super(Processor, self).__init__()
234
235 platforms = []
236 for architecture in PrimeProcessor.ARCHITECTURES:
237 for compiler in PrimeProcessor.COMPILERS:
238 for debug in [True, False]:
Brian Silverman47cd6f62014-05-03 10:35:52 -0700239 if architecture == 'arm' and compiler == 'gcc_4.8':
240 # We don't have a compiler to use here.
241 continue
Brian Silvermana29ebf92014-04-23 13:08:49 -0500242 platforms.append(
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700243 PrimeProcessor.Platform(architecture, compiler, debug, 'none'))
244 for sanitizer in PrimeProcessor.SANITIZERS:
Brian Silverman47cd6f62014-05-03 10:35:52 -0700245 for compiler in ('gcc_4.8', 'clang'):
246 if compiler == 'gcc_4.8' and (sanitizer == 'undefined' or
247 sanitizer == 'integer' or
248 sanitizer == 'memory'):
249 # GCC 4.8 doesn't support these sanitizers.
250 continue
251 # We already added sanitizer == 'none' above.
252 if sanitizer != 'none':
253 platforms.append(
254 PrimeProcessor.Platform('amd64', compiler, True, sanitizer))
Brian Silvermana29ebf92014-04-23 13:08:49 -0500255 self.platforms = frozenset(platforms)
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500256 if is_test:
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700257 self.default_platforms = self.select_platforms(architecture='amd64',
258 debug=True)
Brian Silverman47cd6f62014-05-03 10:35:52 -0700259 for sanitizer in PrimeProcessor.SANITIZER_TEST_WARNINGS:
260 self.default_platforms -= self.select_platforms(sanitizer=sanitizer)
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500261 elif is_deploy:
262 # TODO(brians): Switch to deploying the code built with clang.
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700263 self.default_platforms = self.select_platforms(architecture='arm',
264 compiler='gcc',
265 debug=False)
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500266 else:
267 self.default_platforms = self.select_platforms(debug=False)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500268
Brian Silvermanb3d50542014-04-23 14:28:55 -0500269 def extra_gyp_flags(self):
270 return ()
Brian Silvermana29ebf92014-04-23 13:08:49 -0500271 def is_crio(self): return False
272
273 def parse_platforms(self, string):
274 if string is None:
275 return self.default_platforms
276 r = self.default_platforms
277 for part in string.split(','):
278 if part[0] == '+':
279 r = r | self.select_platforms_string(part[1:])
280 elif part[0] == '-':
281 r = r - self.select_platforms_string(part[1:])
282 elif part[0] == '=':
283 r = self.select_platforms_string(part[1:])
284 else:
Brian Silverman7cd5ad42014-04-27 08:11:30 -0500285 selected = self.select_platforms_string(part)
286 r = r - (self.platforms - selected)
287 if not r:
288 r = selected
Brian Silvermana29ebf92014-04-23 13:08:49 -0500289 return r
290
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700291 def select_platforms(self, architecture=None, compiler=None, debug=None, sanitizer=None):
Brian Silvermana29ebf92014-04-23 13:08:49 -0500292 r = []
293 for platform in self.platforms:
294 if architecture is None or platform.architecture == architecture:
295 if compiler is None or platform.compiler == compiler:
296 if debug is None or platform.debug == debug:
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700297 if sanitizer is None or platform.sanitizer == sanitizer:
298 r.append(platform)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500299 return set(r)
300
301 def select_platforms_string(self, string):
302 r = []
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700303 architecture, compiler, debug, sanitizer = None, None, None, None
Brian Silvermana29ebf92014-04-23 13:08:49 -0500304 for part in string.split('-'):
305 if part in PrimeProcessor.ARCHITECTURES:
306 architecture = part
307 elif part in PrimeProcessor.COMPILERS:
308 compiler = part
309 elif part in ['debug', 'dbg']:
310 debug = True
311 elif part in ['release', 'nodebug', 'ndb']:
312 debug = False
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700313 elif part in PrimeProcessor.SANITIZERS:
314 sanitizer = part
Brian Silvermana29ebf92014-04-23 13:08:49 -0500315 else:
316 raise Processor.UnknownPlatform('Unknown platform string component "%s".' % part)
317 return self.select_platforms(
318 architecture=architecture,
319 compiler=compiler,
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700320 debug=debug,
321 sanitizer=sanitizer)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500322
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700323 def check_installed(self):
324 self.do_check_installed(
325 ('clang-3.5', 'gcc-4.7-arm-linux-gnueabihf',
326 'g++-4.7-arm-linux-gnueabihf', 'openssh-client'))
327
Brian Silvermana29ebf92014-04-23 13:08:49 -0500328def main():
329 class TryParsingAgain(Exception):
330 pass
331
332 class TryAgainArgumentParser(argparse.ArgumentParser):
333 def __init__(self, **kwargs):
334 super(TryAgainArgumentParser, self).__init__(**kwargs)
335
336 def error(self, message):
337 raise TryParsingAgain
338
339 def SetUpParser(parser, args):
340 def AddBuildArgs(parser):
341 parser.add_argument(
342 'target',
343 help='target to build',
344 nargs='*')
345 def AddCommonArgs(parser):
346 parser.add_argument(
347 'platforms',
348 help='platform(s) to act on',
349 nargs='?')
350
351 parser.add_argument('--processor', required=True, help='prime or crio')
352 parser.add_argument('--main_gyp', required=True, help='main .gyp file')
353 subparsers = parser.add_subparsers(dest='action_name')
354
355 build_parser = subparsers.add_parser(
356 'build',
357 help='build the code (default)')
358 AddCommonArgs(build_parser)
359 AddBuildArgs(build_parser)
360
361 clean_parser = subparsers.add_parser(
362 'clean',
363 help='remove all output directories')
364 AddCommonArgs(clean_parser)
365
366 deploy_parser = subparsers.add_parser(
367 'deploy',
368 help='build and download the code')
369 AddCommonArgs(deploy_parser)
370 AddBuildArgs(deploy_parser)
371 deploy_parser.add_argument(
372 '-n', '--dry-run',
373 help="don't actually download anything",
374 action='store_true')
375
Brian Silvermane48c09a2014-04-30 18:04:58 -0700376 tests_parser = subparsers.add_parser(
377 'tests',
378 help='run tests')
379 AddCommonArgs(tests_parser)
380 AddBuildArgs(tests_parser)
381
Brian Silvermana29ebf92014-04-23 13:08:49 -0500382 return parser.parse_args(args)
383
384 try:
385 parser = TryAgainArgumentParser()
386 args = SetUpParser(parser, sys.argv[1:])
387 except TryParsingAgain:
388 parser = argparse.ArgumentParser()
389 REQUIRED_ARGS_END = 5
390 args = SetUpParser(parser, sys.argv[1:REQUIRED_ARGS_END] + ['build'] +
391 sys.argv[(REQUIRED_ARGS_END):])
392
393 if args.processor == 'crio':
394 processor = CRIOProcessor()
395 elif args.processor == 'prime':
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500396 processor = PrimeProcessor(args.action_name == 'tests',
397 args.action_name == 'deploy')
Brian Silvermana29ebf92014-04-23 13:08:49 -0500398 else:
399 parser.exit(status=1, message='Unknown processor "%s".' % args.processor)
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700400 processor.check_installed()
Brian Silvermana29ebf92014-04-23 13:08:49 -0500401
402 if 'target' in args:
403 targets = args.target[:]
404 else:
405 targets = []
406 unknown_platform_error = None
407 try:
408 platforms = processor.parse_platforms(args.platforms)
409 except Processor.UnknownPlatform as e:
410 unknown_platform_error = e.message
411 targets.append(args.platforms)
412 platforms = processor.parse_platforms(None)
413 if not platforms:
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700414 user_output("No platforms selected!")
Brian Silvermana29ebf92014-04-23 13:08:49 -0500415 exit(1)
416
417 def download_externals(argument):
418 subprocess.check_call(
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500419 (os.path.join(aos_path(), 'build', 'download_externals.sh'),
Brian Silvermana29ebf92014-04-23 13:08:49 -0500420 argument),
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500421 stdin=open(os.devnull, 'r'))
Brian Silvermana29ebf92014-04-23 13:08:49 -0500422
423 if processor.is_crio():
424 download_externals('crio')
425 else:
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700426 to_download = set()
Brian Silvermana29ebf92014-04-23 13:08:49 -0500427 for architecture in PrimeProcessor.ARCHITECTURES:
Brian Silvermana5301e32014-05-03 10:51:49 -0700428 for sanitizer in PrimeProcessor.PIE_SANITIZERS:
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700429 if platforms & processor.select_platforms(architecture=architecture,
430 sanitizer=sanitizer):
Brian Silvermana5301e32014-05-03 10:51:49 -0700431 to_download.add(architecture + '-fPIE')
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700432 if platforms & processor.select_platforms(architecture=architecture,
433 sanitizer='none'):
434 to_download.add(architecture)
435 for download_target in to_download:
436 download_externals(download_target)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500437
438 class ToolsConfig(object):
439 def __init__(self):
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500440 self.variables = {'AOS': aos_path()}
441 with open(os.path.join(aos_path(), 'build', 'tools_config'), 'r') as f:
Brian Silvermana29ebf92014-04-23 13:08:49 -0500442 for line in f:
443 if line[0] == '#':
444 pass
445 elif line.isspace():
446 pass
447 else:
448 new_name, new_value = line.rstrip().split('=')
449 for name, value in self.variables.items():
450 new_value = new_value.replace('${%s}' % name, value)
451 self.variables[new_name] = new_value
452 def __getitem__(self, key):
453 return self.variables[key]
454
455 tools_config = ToolsConfig()
456
457 def handle_clean_error(function, path, excinfo):
458 if issubclass(OSError, excinfo[0]):
459 if excinfo[1].errno == errno.ENOENT:
460 # Who cares if the file we're deleting isn't there?
461 return
462 raise excinfo[1]
463
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700464 def need_to_run_gyp(platform):
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700465 if not os.path.exists(platform.build_ninja()):
466 return True
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700467 dirs = os.listdir(os.path.join(aos_path(), '..'))
Brian Silvermana4aff562014-05-02 17:43:50 -0700468 # Looking through these folders takes a long time and isn't useful.
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700469 dirs.remove('output')
Brian Silvermana4aff562014-05-02 17:43:50 -0700470 dirs.remove('.git')
471 return not not subprocess.check_output(
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700472 ('find',) + tuple(os.path.join(aos_path(), '..', d) for d in dirs)
473 + ('-newer', platform.build_ninja(),
474 '(', '-name', '*.gyp', '-or', '-name', '*.gypi', ')'),
Brian Silvermana4aff562014-05-02 17:43:50 -0700475 stdin=open(os.devnull, 'r'))
476
477 def env(platform):
478 build_env = dict(platform.build_env())
Brian Silvermand3fac732014-05-03 16:03:46 -0700479 if not 'TERM' in build_env:
480 build_env['TERM'] = os.environ['TERM']
481 if not 'PATH' in build_env:
482 build_env['PATH'] = os.environ['PATH']
Brian Silvermana4aff562014-05-02 17:43:50 -0700483 return build_env
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700484
Brian Silverman47cd6f62014-05-03 10:35:52 -0700485 to_build = []
486 for platform in platforms:
487 to_build.append(str(platform))
488 if len(to_build) > 1:
489 to_build[-1] = 'and ' + to_build[-1]
490 user_output('Building %s...' % ', '.join(to_build))
491
492 if args.action_name == 'tests':
493 for sanitizer, warning in PrimeProcessor.SANITIZER_TEST_WARNINGS.items():
494 warned_about = platforms & processor.select_platforms(sanitizer=sanitizer)
495 if warned_about:
496 user_output(warning[1])
497 if warning[0]:
498 # TODO(brians): Add a --force flag or something?
499 user_output('Refusing to run tests for sanitizer %s.' % sanitizer)
500 exit(1)
501
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700502 num = 1
Brian Silvermana29ebf92014-04-23 13:08:49 -0500503 for platform in platforms:
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700504 user_output('Building %s (%d/%d)...' % (platform, num, len(platforms)))
Brian Silvermana29ebf92014-04-23 13:08:49 -0500505 if args.action_name == 'clean':
506 shutil.rmtree(platform.outdir(), onerror=handle_clean_error)
507 else:
508 if need_to_run_gyp(platform):
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700509 user_output('Running gyp...')
Brian Silvermana29ebf92014-04-23 13:08:49 -0500510 gyp = subprocess.Popen(
511 (tools_config['GYP'],
512 '--check',
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500513 '--depth=%s' % os.path.join(aos_path(), '..'),
Brian Silvermana29ebf92014-04-23 13:08:49 -0500514 '--no-circular-check',
515 '-f', 'ninja',
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500516 '-I%s' % os.path.join(aos_path(), 'build', 'aos.gypi'),
Brian Silvermana29ebf92014-04-23 13:08:49 -0500517 '-I/dev/stdin', '-Goutput_dir=output',
518 '-DOS=%s' % platform.os(),
519 '-DPLATFORM=%s' % platform.gyp_platform(),
520 '-DARCHITECTURE=%s' % platform.architecture,
Brian Silverman47cd6f62014-05-03 10:35:52 -0700521 '-DCOMPILER=%s' % platform.compiler.split('_')[0],
522 '-DFULL_COMPILER=%s' % platform.compiler,
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700523 '-DDEBUG=%s' % ('yes' if platform.debug else 'no'),
524 '-DSANITIZER=%s' % platform.sanitizer,
Brian Silvermana5301e32014-05-03 10:51:49 -0700525 '-DSANITIZER_FPIE=%s' % ('-fPIE'
526 if platform.sanitizer in PrimeProcessor.PIE_SANITIZERS
Brian Silvermanf0d3c782014-05-02 23:56:32 -0700527 else '')) +
Brian Silvermanb3d50542014-04-23 14:28:55 -0500528 processor.extra_gyp_flags() + (args.main_gyp,),
Brian Silvermana29ebf92014-04-23 13:08:49 -0500529 stdin=subprocess.PIPE)
530 gyp.communicate(("""
531{
532 'target_defaults': {
533 'configurations': {
534 '%s': {}
535 }
536 }
537}""" % platform.outname()).encode())
538 if gyp.returncode:
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700539 user_output("Running gyp failed!")
Brian Silvermana29ebf92014-04-23 13:08:49 -0500540 exit(1)
Brian Silvermanb3d50542014-04-23 14:28:55 -0500541 if processor.is_crio():
542 subprocess.check_call(
543 ('sed', '-i',
544 's/nm -gD/nm/g', platform.build_ninja()),
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500545 stdin=open(os.devnull, 'r'))
Brian Silverman47cd6f62014-05-03 10:35:52 -0700546 user_output('Done running gyp')
Brian Silvermana29ebf92014-04-23 13:08:49 -0500547 else:
Brian Silverman47cd6f62014-05-03 10:35:52 -0700548 user_output("Not running gyp")
Brian Silvermana29ebf92014-04-23 13:08:49 -0500549
550 try:
551 subprocess.check_call(
552 (tools_config['NINJA'],
553 '-C', platform.outdir()) + tuple(targets),
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500554 stdin=open(os.devnull, 'r'),
Brian Silvermana4aff562014-05-02 17:43:50 -0700555 env=env(platform))
Brian Silvermana29ebf92014-04-23 13:08:49 -0500556 except subprocess.CalledProcessError as e:
557 if unknown_platform_error is not None:
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700558 user_output(unknown_platform_error)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500559 raise e
560
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500561 if args.action_name == 'deploy':
562 platform.deploy(args.dry_run)
Brian Silvermane48c09a2014-04-30 18:04:58 -0700563 elif args.action_name == 'tests':
564 dirname = os.path.join(platform.outdir(), 'tests')
565 for f in targets or os.listdir(dirname):
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700566 user_output('Running test %s...' % f)
Brian Silvermana4aff562014-05-02 17:43:50 -0700567 subprocess.check_call(
568 os.path.join(dirname, f),
569 env=env(platform))
Brian Silvermana9b1e5c2014-04-30 18:08:04 -0700570 user_output('Test %s succeeded' % f)
Brian Silvermanbe6cfe22014-04-27 08:06:27 -0500571
Brian Silvermanc2d8e5a2014-05-01 18:33:12 -0700572 user_output('Done building %s (%d/%d)' % (platform, num, len(platforms)))
573 num += 1
Brian Silvermana29ebf92014-04-23 13:08:49 -0500574
575if __name__ == '__main__':
576 main()