source: rtems-tools/rtemstoolkit/rtems.py @ 3bd8def

5
Last change on this file since 3bd8def was 3bd8def, checked in by Chris Johns <chrisj@…>, on 10/03/18 at 01:38:09

config: Consolidate the version information into a single configuration file

  • Property mode set to 100755
File size: 17.3 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():
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
83def 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
91def 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
99class 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
Note: See TracBrowser for help on using the repository browser.