source: rtems-tools/rtemstoolkit/rtems.py @ e058db0

Last change on this file since e058db0 was e058db0, checked in by Chris Johns <chrisj@…>, on Nov 7, 2018 at 3:55:20 AM

python: Provide support to select a valid python version.

  • Update imports after wrapping the code.
  • Fix python3 issues.
  • Fix config path issues for in repo and install runs.

Closes #3537

  • Property mode set to 100755
File size: 17.6 KB
Line 
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
31from __future__ import print_function
32
33import copy
34import os
35import re
36import sys
37import textwrap
38
39from rtemstoolkit import configuration as configuration_
40from rtemstoolkit import error
41from rtemstoolkit import path
42from rtemstoolkit import textbox
43
44#
45# The default path we install RTEMS under
46#
47_prefix_path = '/opt/rtems'
48
49def default_prefix():
50    from rtemstoolkit import version
51    return path.join(_prefix_path, version.version())
52
53def 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
65def configuration_path(prog = None):
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    Note:
70     1. This code assumes the executable is wrapped and not using 'env'.
71     2. Ok to directly call os.path.
72    '''
73    if prog is None:
74        exec_name = sys.argv[1]
75    else:
76        exec_name = prog
77    exec_name = os.path.abspath(exec_name)
78    for top in [os.path.dirname(os.path.dirname(exec_name)),
79                os.path.dirname(exec_name)]:
80        config_path = path.join(top, 'share', 'rtems', 'config')
81        if path.exists(config_path):
82            break
83        config_path = path.join(top, 'config')
84        if path.exists(config_path):
85            break
86        config_path = None
87    return config_path
88
89def configuration_file(config, prog = None):
90    '''Return the path to a configuration file for RTEMS. The path is relative to
91    the installed executable or we are testing and running from within the
92    rtems-tools repo.
93
94    '''
95    return path.join(configuration_path(prog = prog), config)
96
97def bsp_configuration_file(prog = None):
98    '''Return the path to the BSP configuration file for RTEMS. The path is
99    relative to the installed executable or we are testing and running from
100    within the rtems-tools repo.
101
102    '''
103    return configuration_file('rtems-bsps.ini', prog = prog)
104
105class configuration:
106
107    def __init__(self):
108        self.config = configuration_.configuration()
109        self.archs = { }
110        self.profiles = { }
111
112    def __str__(self):
113        s = self.name + os.linesep
114        s += 'Archs:' + os.linesep + \
115             pprint.pformat(self.archs, indent = 1, width = 80) + os.linesep
116        s += 'Profiles:' + os.linesep + \
117             pprint.pformat(self.profiles, indent = 1, width = 80) + os.linesep
118        return s
119
120    def _build_options(self, build, nesting = 0):
121        if ':' in build:
122            section, name = build.split(':', 1)
123            opts = [self.config.get_item(section, name)]
124            return opts
125        builds = self.builds_['builds']
126        if build not in builds:
127            raise error.general('build %s not found' % (build))
128        if nesting > 20:
129            raise error.general('nesting build %s' % (build))
130        options = []
131        for option in self.builds_['builds'][build]:
132            if ':' in option:
133                section, name = option.split(':', 1)
134                opts = [self.config.get_item(section, name)]
135            else:
136                opts = self._build_options(option, nesting + 1)
137            for opt in opts:
138                if opt not in options:
139                    options += [opt]
140        return options
141
142    def load(self, name, build):
143        self.config.load(name)
144        archs = []
145        self.profiles['profiles'] = \
146            self.config.comma_list('profiles', 'profiles', err = False)
147        if len(self.profiles['profiles']) == 0:
148            self.profiles['profiles'] = ['tier-%d' % (t) for t in range(1,4)]
149        for p in self.profiles['profiles']:
150            profile = {}
151            profile['name'] = p
152            profile['archs'] = self.config.comma_list(profile['name'], 'archs', err = False)
153            archs += profile['archs']
154            for arch in profile['archs']:
155                bsps = 'bsps_%s' % (arch)
156                profile[bsps] = self.config.comma_list(profile['name'], bsps)
157            self.profiles[profile['name']] = profile
158        invalid_chars = re.compile(r'[^a-zA-Z0-9_-]')
159        for a in set(archs):
160            if len(invalid_chars.findall(a)) != 0:
161                raise error.general('invalid character(s) in arch name: %s' % (a))
162            arch = {}
163            arch['excludes'] = {}
164            for exclude in self.config.comma_list(a, 'exclude', err = False):
165                arch['excludes'][exclude] = ['all']
166            for i in self.config.get_items(a, False):
167                if i[0].startswith('exclude-'):
168                    exclude = i[0][len('exclude-'):]
169                    if exclude not in arch['excludes']:
170                        arch['excludes'][exclude] = []
171                    arch['excludes'][exclude] += \
172                        sorted(set([b.strip() for b in i[1].split(',')]))
173            arch['bsps'] = self.config.comma_list(a, 'bsps', err = False)
174            for b in arch['bsps']:
175                if len(invalid_chars.findall(b)) != 0:
176                    raise error.general('invalid character(s) in BSP name: %s' % (b))
177                arch[b] = {}
178                arch[b]['bspopts'] = \
179                    self.config.comma_list(a, 'bspopts_%s' % (b), err = False)
180            self.archs[a] = arch
181        builds = {}
182        builds['default'] = self.config.get_item('builds', 'default')
183        if build is None:
184            build = builds['default']
185        builds['config'] = { }
186        for config in self.config.get_items('config'):
187            builds['config'][config[0]] = config[1]
188        builds['build'] = build
189        builds_ = self.config.get_item_names('builds')
190        builds['builds'] = {}
191        for build in builds_:
192            build_builds = self.config.comma_list('builds', build)
193            has_config = False
194            has_build = False
195            for b in build_builds:
196                if ':' in b:
197                    if has_build:
198                        raise error.general('config and build in build: %s' % (build))
199                    has_config = True
200                else:
201                    if has_config:
202                        raise error.general('config and build in build: %s' % (build))
203                    has_build = True
204            builds['builds'][build] = build_builds
205        self.builds_ = builds
206
207    def configs(self):
208        return sorted(list(self.builds_['config'].keys()))
209
210    def config_flags(self, config):
211        if config not in self.builds_['config']:
212            raise error.general('config entry not found: %s' % (config))
213        return self.builds_['config'][config]
214
215    def build(self):
216        return self.builds_['build']
217
218    def builds(self):
219        if self.builds_['build'] in self.builds_['builds']:
220            build = copy.copy(self.builds_['builds'][self.builds_['build']])
221            if ':' in build[0]:
222                return [self.builds_['build']]
223            return build
224        return None
225
226    def build_options(self, build):
227        return ' '.join(self._build_options(build))
228
229    def excludes(self, arch, bsp):
230        return list(set(self.arch_excludes(arch) + self.bsp_excludes(arch, bsp)))
231
232    def exclude_options(self, arch, bsp):
233        return ' '.join([self.config_flags('no-' + e) for e in self.excludes(arch, bsp)])
234
235    def archs(self):
236        return sorted(list(self.archs.keys()))
237
238    def arch_present(self, arch):
239        return arch in self.archs
240
241    def arch_excludes(self, arch):
242        excludes = list(self.archs[arch]['excludes'].keys())
243        for exclude in self.archs[arch]['excludes']:
244            if 'all' not in self.archs[arch]['excludes'][exclude]:
245                excludes.remove(exclude)
246        return sorted(excludes)
247
248    def arch_bsps(self, arch):
249        return sorted(self.archs[arch]['bsps'])
250
251    def bsp_present(self, arch, bsp):
252        return bsp in self.archs[arch]['bsps']
253
254    def bsp_excludes(self, arch, bsp):
255        excludes = list(self.archs[arch]['excludes'].keys())
256        for exclude in self.archs[arch]['excludes']:
257            if 'all' not in self.archs[arch]['excludes'][exclude] and \
258               bsp not in self.archs[arch]['excludes'][exclude]:
259                excludes.remove(exclude)
260        return sorted(excludes)
261
262    def bspopts(self, arch, bsp):
263        if arch not in self.archs:
264            raise error.general('invalid architecture: %s' % (arch))
265        if bsp not in self.archs[arch]:
266            raise error.general('invalid BSP: %s' % (bsp))
267        return self.archs[arch][bsp]['bspopts']
268
269    def profile_present(self, profile):
270        return profile in self.profiles
271
272    def profile_archs(self, profile):
273        if profile not in self.profiles:
274            raise error.general('invalid profile: %s' % (profile))
275        return self.profiles[profile]['archs']
276
277    def profile_arch_bsps(self, profile, arch):
278        if profile not in self.profiles:
279            raise error.general('invalid profile: %s' % (profile))
280        if 'bsps_%s' % (arch) not in self.profiles[profile]:
281            raise error.general('invalid profile arch: %s' % (arch))
282        return ['%s/%s' % (arch, bsp) for bsp in self.profiles[profile]['bsps_%s' % (arch)]]
283
284    def report(self, profiles = True, builds = True, architectures = True):
285        width = 70
286        cols_1 = [width]
287        cols_2 = [10, width - 10]
288        s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
289        s1 = ' File(s)'
290        for f in self.config.files():
291            colon = ':'
292            for l in textwrap.wrap(f, width = cols_2[1] - 3):
293                s += textbox.row(cols_2, [s1, ' ' + l], marker = colon, indent = 1)
294                colon = ' '
295                s1 = ' ' * len(s1)
296        s += textbox.line(cols_1, marker = '+', indent = 1)
297        s += os.linesep
298        if profiles:
299            s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
300            profiles = sorted(self.profiles['profiles'])
301            archs = []
302            bsps = []
303            for profile in profiles:
304                archs += self.profiles[profile]['archs']
305                for arch in sorted(self.profiles[profile]['archs']):
306                    bsps += self.profiles[profile]['bsps_%s' % (arch)]
307            archs = len(set(archs))
308            bsps = len(set(bsps))
309            s += textbox.row(cols_1,
310                             [' Profiles : %d (archs:%d, bsps:%d)' % \
311                              (len(profiles), archs, bsps)],
312                             indent = 1)
313            for profile in profiles:
314                textbox.row(cols_2,
315                            [profile, self.profiles[profile]['name']],
316                            indent = 1)
317            s += textbox.line(cols_1, marker = '+', indent = 1)
318            for profile in profiles:
319                s += textbox.row(cols_1, [' %s' % (profile)], indent = 1)
320                profile = self.profiles[profile]
321                archs = sorted(profile['archs'])
322                for arch in archs:
323                    arch_bsps = ', '.join(profile['bsps_%s' % (arch)])
324                    if len(arch_bsps) > 0:
325                        s += textbox.line(cols_2, marker = '+', indent = 1)
326                        s1 = ' ' + arch
327                        for l in textwrap.wrap(arch_bsps,
328                                               width = cols_2[1] - 3):
329                            s += textbox.row(cols_2, [s1, ' ' + l], indent = 1)
330                            s1 = ' ' * len(s1)
331                s += textbox.line(cols_2, marker = '+', indent = 1)
332            s += os.linesep
333        if builds:
334            s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
335            s += textbox.row(cols_1,
336                             [' Builds:  %s (default)' % (self.builds_['default'])],
337                             indent = 1)
338            builds = self.builds_['builds']
339            bsize = 0
340            for build in builds:
341                if len(build) > bsize:
342                    bsize = len(build)
343            cols_b = [bsize + 2, width - bsize - 2]
344            s += textbox.line(cols_b, marker = '+', indent = 1)
345            for build in builds:
346                s1 = ' ' + build
347                for l in textwrap.wrap(', '.join(builds[build]),
348                                       width = cols_b[1] - 3):
349                    s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
350                    s1 = ' ' * len(s1)
351                s += textbox.line(cols_b, marker = '+', indent = 1)
352            configs = self.builds_['config']
353            s += textbox.row(cols_1,
354                             [' Configure Options: %d' % (len(configs))],
355                             indent = 1)
356            csize = 0
357            for config in configs:
358                if len(config) > csize:
359                    csize = len(config)
360            cols_c = [csize + 3, width - csize - 3]
361            s += textbox.line(cols_c, marker = '+', indent = 1)
362            for config in configs:
363                s1 = ' ' + config
364                for l in textwrap.wrap(configs[config], width = cols_c[1] - 3):
365                    s += textbox.row(cols_c, [s1, ' ' + l], indent = 1)
366                    s1 = ' ' * len(s1)
367                s += textbox.line(cols_c, marker = '+', indent = 1)
368            s += os.linesep
369        if architectures:
370            s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
371            archs = sorted(list(self.archs.keys()))
372            bsps = 0
373            asize = 0
374            for arch in archs:
375                if len(arch) > asize:
376                    asize = len(arch)
377                bsps += len(self.archs[arch]['bsps'])
378            s += textbox.row(cols_1,
379                             [' Architectures : %d (bsps: %d)' % (len(archs), bsps)],
380                             indent = 1)
381            cols_a = [asize + 2, width - asize - 2]
382            s += textbox.line(cols_a, marker = '+', indent = 1)
383            for arch in archs:
384                s += textbox.row(cols_a,
385                                 [' ' + arch, ' %d' % (len(self.archs[arch]['bsps']))],
386                                 indent = 1)
387            s += textbox.line(cols_a, marker = '+', indent = 1)
388            for archn in archs:
389                arch = self.archs[archn]
390                if len(arch['bsps']) > 0:
391                    bsize = 0
392                    for bsp in arch['bsps']:
393                        if len(bsp) > bsize:
394                            bsize = len(bsp)
395                    cols_b = [bsize + 3, width - bsize - 3]
396                    s += textbox.row(cols_1, [' ' + archn + ':'], indent = 1)
397                    s += textbox.line(cols_b, marker = '+', indent = 1)
398                    for bsp in arch['bsps']:
399                        s1 = ' ' + bsp
400                        bspopts = ' '.join(arch[bsp]['bspopts'])
401                        if len(bspopts):
402                            for l in textwrap.wrap('bopt: ' + bspopts,
403                                                   width = cols_b[1] - 3):
404                                s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
405                                s1 = ' ' * len(s1)
406                        excludes = []
407                        for exclude in arch['excludes']:
408                            if 'all' in arch['excludes'][exclude] or \
409                               bsp in arch['excludes'][exclude]:
410                                excludes += [exclude]
411                        excludes = ', '.join(excludes)
412                        if len(excludes):
413                            for l in textwrap.wrap('ex: ' + excludes,
414                                                   width = cols_b[1] - 3):
415                                s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
416                                s1 = ' ' * len(s1)
417                        if len(bspopts) == 0 and len(excludes) == 0:
418                            s += textbox.row(cols_b, [s1, ' '], indent = 1)
419                    s += textbox.line(cols_b, marker = '+', indent = 1)
420            s += os.linesep
421        return s
Note: See TracBrowser for help on using the repository browser.