1 | # |
---|
2 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
3 | # Copyright 2016-2018 Chris Johns (chrisj@rtems.org) |
---|
4 | # All rights reserved. |
---|
5 | # |
---|
6 | # This file is part of the RTEMS Tools package in 'rtems-tools'. |
---|
7 | # |
---|
8 | # Redistribution and use in source and binary forms, with or without |
---|
9 | # modification, are permitted provided that the following conditions are met: |
---|
10 | # |
---|
11 | # 1. Redistributions of source code must retain the above copyright notice, |
---|
12 | # this list of conditions and the following disclaimer. |
---|
13 | # |
---|
14 | # 2. Redistributions in binary form must reproduce the above copyright notice, |
---|
15 | # this list of conditions and the following disclaimer in the documentation |
---|
16 | # and/or other materials provided with the distribution. |
---|
17 | # |
---|
18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
21 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
---|
22 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
23 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
24 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
26 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
27 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
28 | # POSSIBILITY OF SUCH DAMAGE. |
---|
29 | # |
---|
30 | |
---|
31 | from __future__ import print_function |
---|
32 | |
---|
33 | import copy |
---|
34 | import os |
---|
35 | import re |
---|
36 | import sys |
---|
37 | import textwrap |
---|
38 | |
---|
39 | from rtemstoolkit import configuration as configuration_ |
---|
40 | from rtemstoolkit import error |
---|
41 | from rtemstoolkit import path |
---|
42 | from rtemstoolkit import textbox |
---|
43 | |
---|
44 | # |
---|
45 | # The default path we install RTEMS under |
---|
46 | # |
---|
47 | _prefix_path = '/opt/rtems' |
---|
48 | |
---|
49 | def default_prefix(): |
---|
50 | from rtemstoolkit import version |
---|
51 | return path.join(_prefix_path, version.version()) |
---|
52 | |
---|
53 | def clean_windows_path(): |
---|
54 | '''On Windows MSYS2 prepends a path to itself to the environment path. This |
---|
55 | means the RTEMS specific automake is not found and which breaks the |
---|
56 | bootstrap. We need to remove the prepended path. Also remove any ACLOCAL |
---|
57 | paths from the environment. |
---|
58 | |
---|
59 | ''' |
---|
60 | if os.name == 'nt': |
---|
61 | cspath = os.environ['PATH'].split(os.pathsep) |
---|
62 | if 'msys' in cspath[0] and cspath[0].endswith('bin'): |
---|
63 | os.environ['PATH'] = os.pathsep.join(cspath[1:]) |
---|
64 | |
---|
65 | def configuration_path(): |
---|
66 | '''Return the path the configuration data path for RTEMS. The path is relative |
---|
67 | to the installed executable. Mangage the installed package and the in source |
---|
68 | tree when running from within the rtems-tools repo. |
---|
69 | |
---|
70 | ''' |
---|
71 | exec_name = os.path.abspath(sys.argv[0]) |
---|
72 | for top in [os.path.dirname(exec_name), |
---|
73 | os.path.dirname(os.path.dirname(exec_name))]: |
---|
74 | config_path = path.join(top, 'share', 'rtems', 'config') |
---|
75 | if path.exists(config_path): |
---|
76 | break |
---|
77 | config_path = path.join(top, 'config') |
---|
78 | if path.exists(config_path): |
---|
79 | break |
---|
80 | config_path = None |
---|
81 | return config_path |
---|
82 | |
---|
83 | def configuration_file(config): |
---|
84 | '''Return the path to a configuration file for RTEMS. The path is relative to |
---|
85 | the installed executable or we are testing and running from within the |
---|
86 | rtems-tools repo. |
---|
87 | |
---|
88 | ''' |
---|
89 | return path.join(configuration_path(), config) |
---|
90 | |
---|
91 | def bsp_configuration_file(): |
---|
92 | '''Return the path to the BSP configuration file for RTEMS. The path is |
---|
93 | relative to the installed executable or we are testing and running from |
---|
94 | within the rtems-tools repo. |
---|
95 | |
---|
96 | ''' |
---|
97 | return configuration_file('rtems-bsps.ini') |
---|
98 | |
---|
99 | class configuration: |
---|
100 | |
---|
101 | def __init__(self): |
---|
102 | self.config = configuration_.configuration() |
---|
103 | self.archs = { } |
---|
104 | self.profiles = { } |
---|
105 | |
---|
106 | def __str__(self): |
---|
107 | s = self.name + os.linesep |
---|
108 | s += 'Archs:' + os.linesep + \ |
---|
109 | pprint.pformat(self.archs, indent = 1, width = 80) + os.linesep |
---|
110 | s += 'Profiles:' + os.linesep + \ |
---|
111 | pprint.pformat(self.profiles, indent = 1, width = 80) + os.linesep |
---|
112 | return s |
---|
113 | |
---|
114 | def _build_options(self, build, nesting = 0): |
---|
115 | if ':' in build: |
---|
116 | section, name = build.split(':', 1) |
---|
117 | opts = [self.config.get_item(section, name)] |
---|
118 | return opts |
---|
119 | builds = self.builds_['builds'] |
---|
120 | if build not in builds: |
---|
121 | raise error.general('build %s not found' % (build)) |
---|
122 | if nesting > 20: |
---|
123 | raise error.general('nesting build %s' % (build)) |
---|
124 | options = [] |
---|
125 | for option in self.builds_['builds'][build]: |
---|
126 | if ':' in option: |
---|
127 | section, name = option.split(':', 1) |
---|
128 | opts = [self.config.get_item(section, name)] |
---|
129 | else: |
---|
130 | opts = self._build_options(option, nesting + 1) |
---|
131 | for opt in opts: |
---|
132 | if opt not in options: |
---|
133 | options += [opt] |
---|
134 | return options |
---|
135 | |
---|
136 | def load(self, name, build): |
---|
137 | self.config.load(name) |
---|
138 | archs = [] |
---|
139 | self.profiles['profiles'] = \ |
---|
140 | self.config.comma_list('profiles', 'profiles', err = False) |
---|
141 | if len(self.profiles['profiles']) == 0: |
---|
142 | self.profiles['profiles'] = ['tier-%d' % (t) for t in range(1,4)] |
---|
143 | for p in self.profiles['profiles']: |
---|
144 | profile = {} |
---|
145 | profile['name'] = p |
---|
146 | profile['archs'] = self.config.comma_list(profile['name'], 'archs', err = False) |
---|
147 | archs += profile['archs'] |
---|
148 | for arch in profile['archs']: |
---|
149 | bsps = 'bsps_%s' % (arch) |
---|
150 | profile[bsps] = self.config.comma_list(profile['name'], bsps) |
---|
151 | self.profiles[profile['name']] = profile |
---|
152 | invalid_chars = re.compile(r'[^a-zA-Z0-9_-]') |
---|
153 | for a in set(archs): |
---|
154 | if len(invalid_chars.findall(a)) != 0: |
---|
155 | raise error.general('invalid character(s) in arch name: %s' % (a)) |
---|
156 | arch = {} |
---|
157 | arch['excludes'] = {} |
---|
158 | for exclude in self.config.comma_list(a, 'exclude', err = False): |
---|
159 | arch['excludes'][exclude] = ['all'] |
---|
160 | for i in self.config.get_items(a, False): |
---|
161 | if i[0].startswith('exclude-'): |
---|
162 | exclude = i[0][len('exclude-'):] |
---|
163 | if exclude not in arch['excludes']: |
---|
164 | arch['excludes'][exclude] = [] |
---|
165 | arch['excludes'][exclude] += \ |
---|
166 | sorted(set([b.strip() for b in i[1].split(',')])) |
---|
167 | arch['bsps'] = self.config.comma_list(a, 'bsps', err = False) |
---|
168 | for b in arch['bsps']: |
---|
169 | if len(invalid_chars.findall(b)) != 0: |
---|
170 | raise error.general('invalid character(s) in BSP name: %s' % (b)) |
---|
171 | arch[b] = {} |
---|
172 | arch[b]['bspopts'] = \ |
---|
173 | self.config.comma_list(a, 'bspopts_%s' % (b), err = False) |
---|
174 | self.archs[a] = arch |
---|
175 | builds = {} |
---|
176 | builds['default'] = self.config.get_item('builds', 'default') |
---|
177 | if build is None: |
---|
178 | build = builds['default'] |
---|
179 | builds['config'] = { } |
---|
180 | for config in self.config.get_items('config'): |
---|
181 | builds['config'][config[0]] = config[1] |
---|
182 | builds['build'] = build |
---|
183 | builds_ = self.config.get_item_names('builds') |
---|
184 | builds['builds'] = {} |
---|
185 | for build in builds_: |
---|
186 | build_builds = self.config.comma_list('builds', build) |
---|
187 | has_config = False |
---|
188 | has_build = False |
---|
189 | for b in build_builds: |
---|
190 | if ':' in b: |
---|
191 | if has_build: |
---|
192 | raise error.general('config and build in build: %s' % (build)) |
---|
193 | has_config = True |
---|
194 | else: |
---|
195 | if has_config: |
---|
196 | raise error.general('config and build in build: %s' % (build)) |
---|
197 | has_build = True |
---|
198 | builds['builds'][build] = build_builds |
---|
199 | self.builds_ = builds |
---|
200 | |
---|
201 | def configs(self): |
---|
202 | return sorted(list(self.builds_['config'].keys())) |
---|
203 | |
---|
204 | def config_flags(self, config): |
---|
205 | if config not in self.builds_['config']: |
---|
206 | raise error.general('config entry not found: %s' % (config)) |
---|
207 | return self.builds_['config'][config] |
---|
208 | |
---|
209 | def build(self): |
---|
210 | return self.builds_['build'] |
---|
211 | |
---|
212 | def builds(self): |
---|
213 | if self.builds_['build'] in self.builds_['builds']: |
---|
214 | build = copy.copy(self.builds_['builds'][self.builds_['build']]) |
---|
215 | if ':' in build[0]: |
---|
216 | return [self.builds_['build']] |
---|
217 | return build |
---|
218 | return None |
---|
219 | |
---|
220 | def build_options(self, build): |
---|
221 | return ' '.join(self._build_options(build)) |
---|
222 | |
---|
223 | def excludes(self, arch, bsp): |
---|
224 | return list(set(self.arch_excludes(arch) + self.bsp_excludes(arch, bsp))) |
---|
225 | |
---|
226 | def exclude_options(self, arch, bsp): |
---|
227 | return ' '.join([self.config_flags('no-' + e) for e in self.excludes(arch, bsp)]) |
---|
228 | |
---|
229 | def archs(self): |
---|
230 | return sorted(self.archs.keys()) |
---|
231 | |
---|
232 | def arch_present(self, arch): |
---|
233 | return arch in self.archs |
---|
234 | |
---|
235 | def arch_excludes(self, arch): |
---|
236 | excludes = self.archs[arch]['excludes'].keys() |
---|
237 | for exclude in self.archs[arch]['excludes']: |
---|
238 | if 'all' not in self.archs[arch]['excludes'][exclude]: |
---|
239 | excludes.remove(exclude) |
---|
240 | return sorted(excludes) |
---|
241 | |
---|
242 | def arch_bsps(self, arch): |
---|
243 | return sorted(self.archs[arch]['bsps']) |
---|
244 | |
---|
245 | def bsp_present(self, arch, bsp): |
---|
246 | return bsp in self.archs[arch]['bsps'] |
---|
247 | |
---|
248 | def bsp_excludes(self, arch, bsp): |
---|
249 | excludes = self.archs[arch]['excludes'].keys() |
---|
250 | for exclude in self.archs[arch]['excludes']: |
---|
251 | if 'all' not in self.archs[arch]['excludes'][exclude] and \ |
---|
252 | bsp not in self.archs[arch]['excludes'][exclude]: |
---|
253 | excludes.remove(exclude) |
---|
254 | return sorted(excludes) |
---|
255 | |
---|
256 | def bspopts(self, arch, bsp): |
---|
257 | if arch not in self.archs: |
---|
258 | raise error.general('invalid architecture: %s' % (arch)) |
---|
259 | if bsp not in self.archs[arch]: |
---|
260 | raise error.general('invalid BSP: %s' % (bsp)) |
---|
261 | return self.archs[arch][bsp]['bspopts'] |
---|
262 | |
---|
263 | def profile_present(self, profile): |
---|
264 | return profile in self.profiles |
---|
265 | |
---|
266 | def profile_archs(self, profile): |
---|
267 | if profile not in self.profiles: |
---|
268 | raise error.general('invalid profile: %s' % (profile)) |
---|
269 | return self.profiles[profile]['archs'] |
---|
270 | |
---|
271 | def profile_arch_bsps(self, profile, arch): |
---|
272 | if profile not in self.profiles: |
---|
273 | raise error.general('invalid profile: %s' % (profile)) |
---|
274 | if 'bsps_%s' % (arch) not in self.profiles[profile]: |
---|
275 | raise error.general('invalid profile arch: %s' % (arch)) |
---|
276 | return ['%s/%s' % (arch, bsp) for bsp in self.profiles[profile]['bsps_%s' % (arch)]] |
---|
277 | |
---|
278 | def report(self, profiles = True, builds = True, architectures = True): |
---|
279 | width = 70 |
---|
280 | cols_1 = [width] |
---|
281 | cols_2 = [10, width - 10] |
---|
282 | s = textbox.line(cols_1, line = '=', marker = '+', indent = 1) |
---|
283 | s1 = ' File(s)' |
---|
284 | for f in self.config.files(): |
---|
285 | colon = ':' |
---|
286 | for l in textwrap.wrap(f, width = cols_2[1] - 3): |
---|
287 | s += textbox.row(cols_2, [s1, ' ' + l], marker = colon, indent = 1) |
---|
288 | colon = ' ' |
---|
289 | s1 = ' ' * len(s1) |
---|
290 | s += textbox.line(cols_1, marker = '+', indent = 1) |
---|
291 | s += os.linesep |
---|
292 | if profiles: |
---|
293 | s += textbox.line(cols_1, line = '=', marker = '+', indent = 1) |
---|
294 | profiles = sorted(self.profiles['profiles']) |
---|
295 | archs = [] |
---|
296 | bsps = [] |
---|
297 | for profile in profiles: |
---|
298 | archs += self.profiles[profile]['archs'] |
---|
299 | for arch in sorted(self.profiles[profile]['archs']): |
---|
300 | bsps += self.profiles[profile]['bsps_%s' % (arch)] |
---|
301 | archs = len(set(archs)) |
---|
302 | bsps = len(set(bsps)) |
---|
303 | s += textbox.row(cols_1, |
---|
304 | [' Profiles : %d (archs:%d, bsps:%d)' % \ |
---|
305 | (len(profiles), archs, bsps)], |
---|
306 | indent = 1) |
---|
307 | for profile in profiles: |
---|
308 | textbox.row(cols_2, |
---|
309 | [profile, self.profiles[profile]['name']], |
---|
310 | indent = 1) |
---|
311 | s += textbox.line(cols_1, marker = '+', indent = 1) |
---|
312 | for profile in profiles: |
---|
313 | s += textbox.row(cols_1, [' %s' % (profile)], indent = 1) |
---|
314 | profile = self.profiles[profile] |
---|
315 | archs = sorted(profile['archs']) |
---|
316 | for arch in archs: |
---|
317 | arch_bsps = ', '.join(profile['bsps_%s' % (arch)]) |
---|
318 | if len(arch_bsps) > 0: |
---|
319 | s += textbox.line(cols_2, marker = '+', indent = 1) |
---|
320 | s1 = ' ' + arch |
---|
321 | for l in textwrap.wrap(arch_bsps, |
---|
322 | width = cols_2[1] - 3): |
---|
323 | s += textbox.row(cols_2, [s1, ' ' + l], indent = 1) |
---|
324 | s1 = ' ' * len(s1) |
---|
325 | s += textbox.line(cols_2, marker = '+', indent = 1) |
---|
326 | s += os.linesep |
---|
327 | if builds: |
---|
328 | s += textbox.line(cols_1, line = '=', marker = '+', indent = 1) |
---|
329 | s += textbox.row(cols_1, |
---|
330 | [' Builds: %s (default)' % (self.builds_['default'])], |
---|
331 | indent = 1) |
---|
332 | builds = self.builds_['builds'] |
---|
333 | bsize = 0 |
---|
334 | for build in builds: |
---|
335 | if len(build) > bsize: |
---|
336 | bsize = len(build) |
---|
337 | cols_b = [bsize + 2, width - bsize - 2] |
---|
338 | s += textbox.line(cols_b, marker = '+', indent = 1) |
---|
339 | for build in builds: |
---|
340 | s1 = ' ' + build |
---|
341 | for l in textwrap.wrap(', '.join(builds[build]), |
---|
342 | width = cols_b[1] - 3): |
---|
343 | s += textbox.row(cols_b, [s1, ' ' + l], indent = 1) |
---|
344 | s1 = ' ' * len(s1) |
---|
345 | s += textbox.line(cols_b, marker = '+', indent = 1) |
---|
346 | configs = self.builds_['config'] |
---|
347 | s += textbox.row(cols_1, |
---|
348 | [' Configure Options: %d' % (len(configs))], |
---|
349 | indent = 1) |
---|
350 | csize = 0 |
---|
351 | for config in configs: |
---|
352 | if len(config) > csize: |
---|
353 | csize = len(config) |
---|
354 | cols_c = [csize + 3, width - csize - 3] |
---|
355 | s += textbox.line(cols_c, marker = '+', indent = 1) |
---|
356 | for config in configs: |
---|
357 | s1 = ' ' + config |
---|
358 | for l in textwrap.wrap(configs[config], width = cols_c[1] - 3): |
---|
359 | s += textbox.row(cols_c, [s1, ' ' + l], indent = 1) |
---|
360 | s1 = ' ' * len(s1) |
---|
361 | s += textbox.line(cols_c, marker = '+', indent = 1) |
---|
362 | s += os.linesep |
---|
363 | if architectures: |
---|
364 | s += textbox.line(cols_1, line = '=', marker = '+', indent = 1) |
---|
365 | archs = sorted(self.archs.keys()) |
---|
366 | bsps = 0 |
---|
367 | asize = 0 |
---|
368 | for arch in archs: |
---|
369 | if len(arch) > asize: |
---|
370 | asize = len(arch) |
---|
371 | bsps += len(self.archs[arch]['bsps']) |
---|
372 | s += textbox.row(cols_1, |
---|
373 | [' Architectures : %d (bsps: %d)' % (len(archs), bsps)], |
---|
374 | indent = 1) |
---|
375 | cols_a = [asize + 2, width - asize - 2] |
---|
376 | s += textbox.line(cols_a, marker = '+', indent = 1) |
---|
377 | for arch in archs: |
---|
378 | s += textbox.row(cols_a, |
---|
379 | [' ' + arch, ' %d' % (len(self.archs[arch]['bsps']))], |
---|
380 | indent = 1) |
---|
381 | s += textbox.line(cols_a, marker = '+', indent = 1) |
---|
382 | for archn in archs: |
---|
383 | arch = self.archs[archn] |
---|
384 | if len(arch['bsps']) > 0: |
---|
385 | bsize = 0 |
---|
386 | for bsp in arch['bsps']: |
---|
387 | if len(bsp) > bsize: |
---|
388 | bsize = len(bsp) |
---|
389 | cols_b = [bsize + 3, width - bsize - 3] |
---|
390 | s += textbox.row(cols_1, [' ' + archn + ':'], indent = 1) |
---|
391 | s += textbox.line(cols_b, marker = '+', indent = 1) |
---|
392 | for bsp in arch['bsps']: |
---|
393 | s1 = ' ' + bsp |
---|
394 | bspopts = ' '.join(arch[bsp]['bspopts']) |
---|
395 | if len(bspopts): |
---|
396 | for l in textwrap.wrap('bopt: ' + bspopts, |
---|
397 | width = cols_b[1] - 3): |
---|
398 | s += textbox.row(cols_b, [s1, ' ' + l], indent = 1) |
---|
399 | s1 = ' ' * len(s1) |
---|
400 | excludes = [] |
---|
401 | for exclude in arch['excludes']: |
---|
402 | if 'all' in arch['excludes'][exclude] or \ |
---|
403 | bsp in arch['excludes'][exclude]: |
---|
404 | excludes += [exclude] |
---|
405 | excludes = ', '.join(excludes) |
---|
406 | if len(excludes): |
---|
407 | for l in textwrap.wrap('ex: ' + excludes, |
---|
408 | width = cols_b[1] - 3): |
---|
409 | s += textbox.row(cols_b, [s1, ' ' + l], indent = 1) |
---|
410 | s1 = ' ' * len(s1) |
---|
411 | if len(bspopts) == 0 and len(excludes) == 0: |
---|
412 | s += textbox.row(cols_b, [s1, ' '], indent = 1) |
---|
413 | s += textbox.line(cols_b, marker = '+', indent = 1) |
---|
414 | s += os.linesep |
---|
415 | return s |
---|