blob: b1e02e3fe998ae9ca4be9073520d3644518ca22b [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
13class Processor(object):
14 class UnknownPlatform(Exception):
15 def __init__(self, message):
16 self.message = message
17
Brian Silvermanb3d50542014-04-23 14:28:55 -050018 class Platform(object):
19 def outdir(self):
20 return os.path.join(
21 Processor.aos_path(), '..', 'output', self.outname())
22 def build_ninja(self):
23 return os.path.join(self.outdir(), 'build.ninja')
24
Brian Silvermana29ebf92014-04-23 13:08:49 -050025 def aos_path():
26 return os.path.join(os.path.dirname(__file__), '..')
27
28class CRIOProcessor(Processor):
Brian Silvermanb3d50542014-04-23 14:28:55 -050029 class Platform(Processor.Platform):
30 def __init__(self, debug):
31 super(CRIOProcessor.Platform, self).__init__()
32
33 self.debug = debug
34
35 def __repr__(self):
36 return 'CRIOProcessor.Platform(debug=%s)' % self.debug
37 def __str__(self):
38 return 'crio%s' % ('-debug' if self.debug else '')
39
40 def outname(self):
41 return 'crio-debug' if self.debug else 'crio'
42 def os(self):
43 return 'vxworks'
44 def gyp_platform(self):
45 return 'crio'
46 def architecture(self):
47 return 'ppc'
48 def compiler(self):
49 return 'gcc'
50
Brian Silvermana29ebf92014-04-23 13:08:49 -050051 def __init__(self):
52 super(CRIOProcessor, self).__init__()
53
54 if 'WIND_BASE' in os.environ:
55 self.wind_base = os.environ['WIND_BASE']
56 else:
57 self.wind_base = '/usr/local/powerpc-wrs-vxworks/wind_base'
58
59 def parse_platforms(self, string):
Brian Silvermanb3d50542014-04-23 14:28:55 -050060 if string is None or string == 'crio':
61 return (CRIOProcessor.Platform(False),)
62 elif string == 'crio-debug':
63 return (CRIOProcessor.Platform(True),)
64 else:
65 raise Processor.UnknownPlatform('Unknown cRIO platform "%s".' % string)
Brian Silvermana29ebf92014-04-23 13:08:49 -050066
67 def build_env(self):
68 return {'WIND_BASE': self.wind_base}
Brian Silvermanb3d50542014-04-23 14:28:55 -050069 def extra_gyp_flags(self):
70 return ('-DWIND_BASE=%s' % self.wind_base,)
71
Brian Silvermana29ebf92014-04-23 13:08:49 -050072 def is_crio(self): return True
73
74class PrimeProcessor(Processor):
Brian Silvermanb3d50542014-04-23 14:28:55 -050075 class Platform(Processor.Platform):
Brian Silvermana29ebf92014-04-23 13:08:49 -050076 def __init__(self, architecture, compiler, debug):
Brian Silvermanb3d50542014-04-23 14:28:55 -050077 super(PrimeProcessor.Platform, self).__init__()
78
Brian Silvermana29ebf92014-04-23 13:08:49 -050079 self.architecture = architecture
80 self.compiler = compiler
81 self.debug = debug
82
83 def __repr__(self):
84 return 'PrimeProcessor.Platform(architecture=%s, compiler=%s, debug=%s)' \
85 % (self.architecture, self.compiler, self.debug)
86 def __str__(self):
87 return '%s-%s%s' % (self.architecture, self.compiler,
88 '-debug' if self.debug else '')
89
90 def os(self):
91 return 'linux'
92 def gyp_platform(self):
93 return '%s-%s-%s' % (self.os(), self.architecture, self.compiler)
94
Brian Silvermana29ebf92014-04-23 13:08:49 -050095 def outname(self):
96 return str(self)
Brian Silvermana29ebf92014-04-23 13:08:49 -050097
98 ARCHITECTURES = ['arm', 'amd64']
99 COMPILERS = ['clang', 'gcc']
100
101 def __init__(self):
102 super(Processor, self).__init__()
103
104 platforms = []
105 for architecture in PrimeProcessor.ARCHITECTURES:
106 for compiler in PrimeProcessor.COMPILERS:
107 for debug in [True, False]:
108 platforms.append(
109 PrimeProcessor.Platform(architecture, compiler, debug))
110 self.platforms = frozenset(platforms)
111 self.default_platforms = self.select_platforms(debug=False)
112
113 def build_env(self):
114 return {}
Brian Silvermanb3d50542014-04-23 14:28:55 -0500115 def extra_gyp_flags(self):
116 return ()
Brian Silvermana29ebf92014-04-23 13:08:49 -0500117 def is_crio(self): return False
118
119 def parse_platforms(self, string):
120 if string is None:
121 return self.default_platforms
122 r = self.default_platforms
123 for part in string.split(','):
124 if part[0] == '+':
125 r = r | self.select_platforms_string(part[1:])
126 elif part[0] == '-':
127 r = r - self.select_platforms_string(part[1:])
128 elif part[0] == '=':
129 r = self.select_platforms_string(part[1:])
130 else:
131 r = r - (self.platforms - self.select_platforms_string(part))
132 return r
133
134 def select_platforms(self, architecture=None, compiler=None, debug=None):
135 r = []
136 for platform in self.platforms:
137 if architecture is None or platform.architecture == architecture:
138 if compiler is None or platform.compiler == compiler:
139 if debug is None or platform.debug == debug:
140 r.append(platform)
141 return set(r)
142
143 def select_platforms_string(self, string):
144 r = []
145 architecture, compiler, debug = None, None, None
146 for part in string.split('-'):
147 if part in PrimeProcessor.ARCHITECTURES:
148 architecture = part
149 elif part in PrimeProcessor.COMPILERS:
150 compiler = part
151 elif part in ['debug', 'dbg']:
152 debug = True
153 elif part in ['release', 'nodebug', 'ndb']:
154 debug = False
155 else:
156 raise Processor.UnknownPlatform('Unknown platform string component "%s".' % part)
157 return self.select_platforms(
158 architecture=architecture,
159 compiler=compiler,
160 debug=debug)
161
162def main():
163 class TryParsingAgain(Exception):
164 pass
165
166 class TryAgainArgumentParser(argparse.ArgumentParser):
167 def __init__(self, **kwargs):
168 super(TryAgainArgumentParser, self).__init__(**kwargs)
169
170 def error(self, message):
171 raise TryParsingAgain
172
173 def SetUpParser(parser, args):
174 def AddBuildArgs(parser):
175 parser.add_argument(
176 'target',
177 help='target to build',
178 nargs='*')
179 def AddCommonArgs(parser):
180 parser.add_argument(
181 'platforms',
182 help='platform(s) to act on',
183 nargs='?')
184
185 parser.add_argument('--processor', required=True, help='prime or crio')
186 parser.add_argument('--main_gyp', required=True, help='main .gyp file')
187 subparsers = parser.add_subparsers(dest='action_name')
188
189 build_parser = subparsers.add_parser(
190 'build',
191 help='build the code (default)')
192 AddCommonArgs(build_parser)
193 AddBuildArgs(build_parser)
194
195 clean_parser = subparsers.add_parser(
196 'clean',
197 help='remove all output directories')
198 AddCommonArgs(clean_parser)
199
200 deploy_parser = subparsers.add_parser(
201 'deploy',
202 help='build and download the code')
203 AddCommonArgs(deploy_parser)
204 AddBuildArgs(deploy_parser)
205 deploy_parser.add_argument(
206 '-n', '--dry-run',
207 help="don't actually download anything",
208 action='store_true')
209
210 return parser.parse_args(args)
211
212 try:
213 parser = TryAgainArgumentParser()
214 args = SetUpParser(parser, sys.argv[1:])
215 except TryParsingAgain:
216 parser = argparse.ArgumentParser()
217 REQUIRED_ARGS_END = 5
218 args = SetUpParser(parser, sys.argv[1:REQUIRED_ARGS_END] + ['build'] +
219 sys.argv[(REQUIRED_ARGS_END):])
220
221 if args.processor == 'crio':
222 processor = CRIOProcessor()
223 elif args.processor == 'prime':
224 processor = PrimeProcessor()
225 else:
226 parser.exit(status=1, message='Unknown processor "%s".' % args.processor)
227
228 if 'target' in args:
229 targets = args.target[:]
230 else:
231 targets = []
232 unknown_platform_error = None
233 try:
234 platforms = processor.parse_platforms(args.platforms)
235 except Processor.UnknownPlatform as e:
236 unknown_platform_error = e.message
237 targets.append(args.platforms)
238 platforms = processor.parse_platforms(None)
239 if not platforms:
240 print("No platforms selected!", file=sys.stderr)
241 exit(1)
242
243 def download_externals(argument):
244 subprocess.check_call(
245 (os.path.join(Processor.aos_path(), 'build', 'download_externals.sh'),
246 argument),
247 stdin=open('/dev/null', 'r'))
248
249 if processor.is_crio():
250 download_externals('crio')
251 else:
252 for architecture in PrimeProcessor.ARCHITECTURES:
253 if platforms & processor.select_platforms(architecture=architecture):
254 download_externals(architecture)
255
256 class ToolsConfig(object):
257 def __init__(self):
258 self.variables = {'AOS': Processor.aos_path()}
259 with open(os.path.join(Processor.aos_path(), 'build', 'tools_config'), 'r') as f:
260 for line in f:
261 if line[0] == '#':
262 pass
263 elif line.isspace():
264 pass
265 else:
266 new_name, new_value = line.rstrip().split('=')
267 for name, value in self.variables.items():
268 new_value = new_value.replace('${%s}' % name, value)
269 self.variables[new_name] = new_value
270 def __getitem__(self, key):
271 return self.variables[key]
272
273 tools_config = ToolsConfig()
274
275 def handle_clean_error(function, path, excinfo):
276 if issubclass(OSError, excinfo[0]):
277 if excinfo[1].errno == errno.ENOENT:
278 # Who cares if the file we're deleting isn't there?
279 return
280 raise excinfo[1]
281
282 def need_to_run_gyp(platform):
283 try:
284 build_mtime = os.stat(platform.build_ninja()).st_mtime
285 except OSError as e:
286 if e.errno == errno.ENOENT:
287 return True
288 else:
289 raise e
290 pattern = re.compile('.*\.gyp[i]$')
291 for dirname, _, files in os.walk(os.path.join(Processor.aos_path(), '..')):
292 for f in [f for f in files if pattern.match(f)]:
293 if (os.stat(os.path.join(dirname, f)).st_mtime > build_mtime):
294 return True
295 return False
296
297 for platform in platforms:
298 print('Building %s...' % platform, file=sys.stderr)
299 if args.action_name == 'clean':
300 shutil.rmtree(platform.outdir(), onerror=handle_clean_error)
301 else:
302 if need_to_run_gyp(platform):
303 print('Running gyp...', file=sys.stderr)
304 gyp = subprocess.Popen(
305 (tools_config['GYP'],
306 '--check',
307 '--depth=%s' % os.path.join(Processor.aos_path(), '..'),
308 '--no-circular-check',
309 '-f', 'ninja',
310 '-I%s' % os.path.join(Processor.aos_path(), 'build', 'aos.gypi'),
311 '-I/dev/stdin', '-Goutput_dir=output',
312 '-DOS=%s' % platform.os(),
313 '-DPLATFORM=%s' % platform.gyp_platform(),
314 '-DARCHITECTURE=%s' % platform.architecture,
315 '-DCOMPILER=%s' % platform.compiler,
Brian Silvermanb3d50542014-04-23 14:28:55 -0500316 '-DDEBUG=%s' % ('yes' if platform.debug else 'no')) +
317 processor.extra_gyp_flags() + (args.main_gyp,),
Brian Silvermana29ebf92014-04-23 13:08:49 -0500318 stdin=subprocess.PIPE)
319 gyp.communicate(("""
320{
321 'target_defaults': {
322 'configurations': {
323 '%s': {}
324 }
325 }
326}""" % platform.outname()).encode())
327 if gyp.returncode:
328 print("Running gyp failed!", file=sys.stderr)
329 exit(1)
Brian Silvermanb3d50542014-04-23 14:28:55 -0500330 if processor.is_crio():
331 subprocess.check_call(
332 ('sed', '-i',
333 's/nm -gD/nm/g', platform.build_ninja()),
334 stdin=open('/dev/null', 'r'))
Brian Silvermana29ebf92014-04-23 13:08:49 -0500335 print('Done running gyp.', file=sys.stderr)
336 else:
337 print("Not running gyp.", file=sys.stderr)
338
339 try:
Brian Silvermanb3d50542014-04-23 14:28:55 -0500340 build_env = dict(processor.build_env())
341 build_env['TERM'] = os.environ['TERM']
342 build_env['PATH'] = os.environ['PATH']
Brian Silvermana29ebf92014-04-23 13:08:49 -0500343 subprocess.check_call(
344 (tools_config['NINJA'],
345 '-C', platform.outdir()) + tuple(targets),
Brian Silvermanb3d50542014-04-23 14:28:55 -0500346 stdin=open('/dev/null', 'r'),
347 env=build_env)
Brian Silvermana29ebf92014-04-23 13:08:49 -0500348 except subprocess.CalledProcessError as e:
349 if unknown_platform_error is not None:
350 print(unknown_platform_error, file=sys.stderr)
351 raise e
352
353 # TODO(brians): deploy and tests
354 print('Done building %s...' % platform, file=sys.stderr)
355
356if __name__ == '__main__':
357 main()