source: examples-v2/rtems_waf/rtems.py @ 0d56108

4.11
Last change on this file since 0d56108 was 0d56108, checked in by Gedare Bloom <gedare@…>, on Nov 22, 2013 at 11:14:18 AM

Add LICENSE.2 with 2-clause BSD. Point to it from 2-clause BSD licensed files.

  • Property mode set to 100644
File size: 20.0 KB
Line 
1# Copyright 2012 Chris Johns (chrisj@rtems.org)
2#
3# This file's license is 2-clause BSD as in this distribution's LICENSE.2 file.
4#
5
6#
7# RTEMS support for applications.
8#
9import os
10import os.path
11import pkgconfig
12import re
13import subprocess
14
15default_version = '4.11'
16default_label = 'rtems-' + default_version
17default_path = '/opt/' + default_label
18default_postfix = 'rtems' + default_version
19
20rtems_filters = None
21
22def options(opt):
23    opt.add_option('--rtems',
24                   default = default_path,
25                   dest = 'rtems_path',
26                   help = 'Path to an installed RTEMS.')
27    opt.add_option('--rtems-tools',
28                   default = None,
29                   dest = 'rtems_tools',
30                   help = 'Path to RTEMS tools.')
31    opt.add_option('--rtems-version',
32                   default = default_version,
33                   dest = 'rtems_version',
34                   help = 'RTEMS version (default ' + default_version + ').')
35    opt.add_option('--rtems-archs',
36                   default = 'all',
37                   dest = 'rtems_archs',
38                   help = 'List of RTEMS architectures to build.')
39    opt.add_option('--rtems-bsps',
40                   default = 'all',
41                   dest = 'rtems_bsps',
42                   help = 'List of BSPs to build.')
43    opt.add_option('--show-commands',
44                   action = 'store_true',
45                   default = False,
46                   dest = 'show_commands',
47                   help = 'Print the commands as strings.')
48
49def init(ctx, filters = None):
50    global rtems_filters
51
52    try:
53        import waflib.Options
54        import waflib.ConfigSet
55
56        #
57        # Load the configuation set from the lock file.
58        #
59        env = waflib.ConfigSet.ConfigSet()
60        env.load(waflib.Options.lockfile)
61
62        #
63        # Set the RTEMS filter to the context.
64        #
65        rtems_filters = filters
66
67        #
68        # Check the tools, architectures and bsps.
69        #
70        rtems_tools, archs, arch_bsps = check_options(ctx,
71                                                      env.options['rtems_tools'],
72                                                      env.options['rtems_path'],
73                                                      env.options['rtems_version'],
74                                                      env.options['rtems_archs'],
75                                                      env.options['rtems_bsps'])
76
77        #
78        # Update the contextes for all the bsps.
79        #
80        from waflib.Build import BuildContext, CleanContext, \
81            InstallContext, UninstallContext
82        for x in arch_bsps:
83            for y in (BuildContext, CleanContext, InstallContext, UninstallContext):
84                name = y.__name__.replace('Context','').lower()
85                class context(y):
86                    cmd = name + '-' + x
87                    variant = x
88
89        #
90        # Add the various commands.
91        #
92        for cmd in ['build', 'clean', 'install']:
93            if cmd in waflib.Options.commands:
94                waflib.Options.commands.remove(cmd)
95                for x in arch_bsps:
96                    waflib.Options.commands.insert(0, cmd + '-' + x)
97    except:
98        pass
99
100def configure(conf):
101    #
102    # Handle the show commands option.
103    #
104    if conf.options.show_commands:
105        show_commands = 'yes'
106    else:
107        show_commands = 'no'
108
109    rtems_tools, archs, arch_bsps = check_options(conf,
110                                                  conf.options.rtems_tools,
111                                                  conf.options.rtems_path,
112                                                  conf.options.rtems_version,
113                                                  conf.options.rtems_archs,
114                                                  conf.options.rtems_bsps)
115
116    _log_header(conf)
117
118    conf.msg('Architectures', ', '.join(archs), 'YELLOW')
119
120    tools = {}
121    env = conf.env.derive()
122
123    for ab in arch_bsps:
124        conf.setenv(ab, env)
125
126        conf.msg('Board Support Package', ab, 'YELLOW')
127
128        arch = _arch_from_arch_bsp(ab)
129
130        conf.env.RTEMS_PATH = conf.options.rtems_path
131        conf.env.RTEMS_VERSION = conf.options.rtems_version
132        conf.env.RTEMS_ARCH_BSP = ab
133        conf.env.RTEMS_ARCH = arch.split('-')[0]
134        conf.env.RTEMS_ARCH_RTEMS = arch
135        conf.env.RTEMS_BSP = _bsp_from_arch_bsp(ab)
136
137        tools = _find_tools(conf, arch, rtems_tools, tools)
138        for t in tools[arch]:
139            conf.env[t] = tools[arch][t]
140
141        conf.load('gcc')
142        conf.load('g++')
143        conf.load('gas')
144
145        flags = _load_flags(conf, ab, conf.options.rtems_path)
146
147        cflags = _filter_flags('cflags', flags['CFLAGS'],
148                               arch, conf.options.rtems_path)
149        ldflags = _filter_flags('ldflags', flags['LDFLAGS'],
150                               arch, conf.options.rtems_path)
151
152        conf.env.CFLAGS    = cflags['cflags']
153        conf.env.CXXFLAGS  = conf.env.CFLAGS
154        conf.env.ASFLAGS   = cflags['cflags']
155        conf.env.WFLAGS    = cflags['warnings']
156        conf.env.RFLAGS    = cflags['specs']
157        conf.env.MFLAGS    = cflags['machines']
158        conf.env.IFLAGS    = cflags['includes']
159        conf.env.LINKFLAGS = cflags['cflags'] + ldflags['ldflags']
160        conf.env.LIB       = flags['LIB']
161
162        #
163        # Add tweaks.
164        #
165        tweaks(conf, ab)
166
167        conf.env.SHOW_COMMANDS = show_commands
168
169        conf.setenv('', env)
170
171    conf.env.RTEMS_TOOLS = rtems_tools
172    conf.env.ARCHS = archs
173    conf.env.ARCH_BSPS = arch_bsps
174
175    conf.env.SHOW_COMMANDS = show_commands
176
177def build(bld):
178    if bld.env.SHOW_COMMANDS == 'yes':
179        output_command_line()
180
181def tweaks(conf, arch_bsp):
182    #
183    # Hack to work around NIOS2 naming.
184    #
185    if conf.env.RTEMS_ARCH in ['nios2']:
186        conf.env.OBJCOPY_FLAGS = ['-O', 'elf32-littlenios2']
187    elif conf.env.RTEMS_ARCH in ['arm']:
188        conf.env.OBJCOPY_FLAGS = ['-I', 'binary', '-O', 'elf32-littlearm']
189    else:
190        conf.env.OBJCOPY_FLAGS = ['-O', 'elf32-' + conf.env.RTEMS_ARCH]
191
192    #
193    # Check for a i386 PC bsp.
194    #
195    if re.match('i386-.*-pc[3456]86', arch_bsp) is not None:
196        conf.env.LINKFLAGS += ['-Wl,-Ttext,0x00100000']
197
198def check_options(ctx, rtems_tools, rtems_path, rtems_version, rtems_archs, rtems_bsps):
199    #
200    # Check the paths are valid.
201    #
202    if not os.path.exists(rtems_path):
203        ctx.fatal('RTEMS path not found.')
204    if os.path.exists(os.path.join(rtems_path, 'lib', 'pkgconfig')):
205        rtems_config = None
206    elif os.path.exists(os.path.join(rtems_path, 'rtems-config')):
207        rtems_config = os.path.join(rtems_path, 'rtems-config')
208    else:
209        ctx.fatal('RTEMS path is not valid. No lib/pkgconfig or rtems-config found.')
210
211    #
212    # We can more than one path to tools. This happens when testing different
213    # versions.
214    #
215    if rtems_tools is not None:
216        rt = rtems_tools.split(',')
217        tools = []
218        for path in rt:
219            if not os.path.exists(path):
220                ctx.fatal('RTEMS tools path not found: ' + path)
221            if not os.path.exists(os.path.join(path, 'bin')):
222                ctx.fatal('RTEMS tools path does not contain a \'bin\' directory: ' + path)
223            tools += [os.path.join(path, 'bin')]
224    else:
225        tools = None
226
227    #
228    # Filter the tools.
229    #
230    tools = filter(ctx, 'tools', tools)
231
232    #
233    # Match the archs requested against the ones found. If the user
234    # wants all (default) set all used.
235    #
236    if rtems_archs == 'all':
237        archs = _find_installed_archs(rtems_config, rtems_path, rtems_version)
238    else:
239        archs = _check_archs(rtems_config, rtems_archs, rtems_path, rtems_version)
240
241    #
242    # Filter the architectures.
243    #
244    archs = filter(ctx, 'archs', archs)
245
246    #
247    # We some.
248    #
249    if len(archs) == 0:
250        ctx.fatal('Could not find any architectures')
251
252    #
253    # Get the list of valid BSPs. This process filters the architectures
254    # to those referenced by the BSPs.
255    #
256    if rtems_bsps == 'all':
257        arch_bsps = _find_installed_arch_bsps(rtems_config, rtems_path, archs, rtems_version)
258    else:
259        arch_bsps = _check_arch_bsps(rtems_bsps, rtems_config, rtems_path, archs, rtems_version)
260
261    if len(arch_bsps) == 0:
262        ctx.fatal('No valid arch/bsps found')
263
264    #
265    # Filter the bsps.
266    #
267    arch_bsps = filter(ctx, 'bsps', arch_bsps)
268
269    return tools, archs, arch_bsps
270
271def arch(arch_bsp):
272    """ Given an arch/bsp return the architecture."""
273    return _arch_from_arch_bsp(arch_bsp).split('-')[0]
274
275def bsp(arch_bsp):
276    """ Given an arch/bsp return the BSP."""
277    return _bsp_from_arch_bsp(arch_bsp)
278
279def arch_bsps(ctx):
280    """ Return the list of arch/bsps we are building."""
281    return ctx.env.ARCH_BSPS
282
283def arch_bsp_env(ctx, arch_bsp):
284    return ctx.env_of_name(arch_bsp).derive()
285
286def filter(ctx, filter, items):
287    if rtems_filters is None:
288        return items
289    if type(rtems_filters) is not dict:
290        ctx.fatal("Invalid RTEMS filter type, ie { 'tools': { 'in': [], 'out': [] }, 'arch': {}, 'bsps': {} }")
291    if filter not in rtems_filters:
292        return items
293    items_in = []
294    items_out = []
295    filtered_items = []
296    if 'in' in rtems_filters[filter]:
297        items_in = rtems_filters[filter]['in']
298    if 'out' in rtems_filters[filter]:
299        items_out = rtems_filters[filter]['out']
300    for i in items:
301        ab = '%s/%s' % (arch(i), bsp(i))
302        if ab in items_out:
303            i = None
304        elif ab in items_in:
305            items_in.remove(ab)
306        if i is not None:
307            filtered_items += [i]
308        if len(items_in) != 0:
309            ctx.fatal('Following %s not found: %s' % (filter, ', '.join(items_in)))
310    filtered_items.sort()
311    return filtered_items
312
313def arch_rtems_version(arch):
314    """ Return the RTEMS architecture path, ie sparc-rtems4.11."""
315    return '%s-%s' % (arch, default_postfix)
316
317def arch_bsp_path(arch_bsp):
318    """ Return the BSP path."""
319    return '%s/%s' % (arch_rtems_version(arch(arch_bsp)), bsp(arch_bsp))
320
321def arch_bsp_include_path(arch_bsp):
322    """ Return the BSP include path."""
323    return '%s/lib/include' % (arch_bsp_path(arch_bsp))
324
325def arch_bsp_lib_path(arch_bsp):
326    """ Return the BSP library path. """
327    return '%s/lib' % (arch_bsp_path(arch_bsp))
328
329def library_path(library, cc, cflags):
330    cmd = cc + cflags + ['-print-file-name=%s' % library]
331    a = subprocess.check_output(cmd)
332    lib = os.path.abspath(a.strip())
333    if os.path.exists(lib):
334        return os.path.dirname(lib)
335    return None
336
337def clone_tasks(bld):
338    if bld.cmd == 'build':
339        for obj in bld.all_task_gen[:]:
340            for x in arch_bsp:
341                cloned_obj = obj.clone(x)
342                kind = Options.options.build_kind
343                if kind.find(x) < 0:
344                    cloned_obj.posted = True
345            obj.posted = True
346
347#
348# From the demos. Use this to get the command to cut+paste to play.
349#
350def output_command_line():
351    # first, display strings, people like them
352    from waflib import Utils, Logs
353    from waflib.Context import Context
354    def exec_command(self, cmd, **kw):
355        subprocess = Utils.subprocess
356        kw['shell'] = isinstance(cmd, str)
357        if isinstance(cmd, str):
358            Logs.info('%s' % cmd)
359        else:
360            Logs.info('%s' % ' '.join(cmd)) # here is the change
361        Logs.debug('runner_env: kw=%s' % kw)
362        try:
363            if self.logger:
364                self.logger.info(cmd)
365                kw['stdout'] = kw['stderr'] = subprocess.PIPE
366                p = subprocess.Popen(cmd, **kw)
367                (out, err) = p.communicate()
368                if out:
369                    self.logger.debug('out: %s' % out.decode(sys.stdout.encoding or 'iso8859-1'))
370                if err:
371                    self.logger.error('err: %s' % err.decode(sys.stdout.encoding or 'iso8859-1'))
372                return p.returncode
373            else:
374                p = subprocess.Popen(cmd, **kw)
375                return p.wait()
376        except OSError:
377            return -1
378    Context.exec_command = exec_command
379
380    # Change the outputs for tasks too
381    from waflib.Task import Task
382    def display(self):
383        return '' # no output on empty strings
384
385    Task.__str__ = display
386
387def _find_tools(conf, arch, paths, tools):
388    if arch not in tools:
389        arch_tools = {}
390        arch_tools['CC']       = conf.find_program([arch + '-gcc'], path_list = paths)
391        arch_tools['CXX']      = conf.find_program([arch + '-g++'], path_list = paths)
392        arch_tools['AS']       = conf.find_program([arch + '-gcc'], path_list = paths)
393        arch_tools['LD']       = conf.find_program([arch + '-ld'],  path_list = paths)
394        arch_tools['AR']       = conf.find_program([arch + '-ar'],  path_list = paths)
395        arch_tools['LINK_CC']  = arch_tools['CC']
396        arch_tools['LINK_CXX'] = arch_tools['CXX']
397        arch_tools['AR']       = conf.find_program([arch + '-ar'], path_list = paths)
398        arch_tools['LD']       = conf.find_program([arch + '-ld'], path_list = paths)
399        arch_tools['NM']       = conf.find_program([arch + '-nm'], path_list = paths)
400        arch_tools['OBJDUMP']  = conf.find_program([arch + '-objdump'], path_list = paths)
401        arch_tools['OBJCOPY']  = conf.find_program([arch + '-objcopy'], path_list = paths)
402        arch_tools['READELF']  = conf.find_program([arch + '-readelf'], path_list = paths)
403        arch_tools['STRIP']    = conf.find_program([arch + '-strip'], path_list = paths)
404        arch_tools['RTEMS_LD'] = conf.find_program(['rtems-ld'], path_list = paths, mandatory = False)
405        tools[arch] = arch_tools
406    return tools
407
408def _find_installed_archs(config, path, version):
409    archs = []
410    if config is None:
411        for d in os.listdir(path):
412            if d.endswith('-rtems' + version):
413                archs += [d]
414    else:
415        a = subprocess.check_output([config, '--list-format', '"%(arch)s"'])
416        a = a[:-1].replace('"', '')
417        archs = set(a.split())
418        archs = ['%s-rtems4.11' % (x) for x in archs]
419    archs.sort()
420    return archs
421
422def _check_archs(config, req, path, version):
423    installed = _find_installed_archs(config, path, version)
424    archs = []
425    for a in req.split(','):
426        arch = a + '-rtems' + version
427        if arch in installed:
428            archs += [arch]
429    archs.sort()
430    return archs
431
432def _find_installed_arch_bsps(config, path, archs, version):
433    arch_bsps = []
434    if config is None:
435        for f in os.listdir(_pkgconfig_path(path)):
436            if f.endswith('.pc'):
437                if _arch_from_arch_bsp(f[:-3]) in archs:
438                    arch_bsps += [f[:-3]]
439    else:
440        ab = subprocess.check_output([config, '--list-format'])
441        ab = ab[:-1].replace('"', '')
442        ab = ab.replace('/', '-rtems%s-' % (version))
443        arch_bsps = [x for x in set(ab.split())]
444    arch_bsps.sort()
445    return arch_bsps
446
447def _check_arch_bsps(req, config, path, archs, version):
448    archs_bsps = []
449    for ab in req.split(','):
450        abl = ab.split('/')
451        if len(abl) != 2:
452            return []
453        found = False
454        for arch in archs:
455            a = '%s-rtems%s' % (abl[0], version)
456            if a == arch:
457                found = True
458                break
459        if not found:
460            return []
461        archs_bsps += ['%s-%s' % (a, abl[1])]
462    if len(archs_bsps) == 0:
463        return []
464    installed = _find_installed_arch_bsps(config, path, archs, version)
465    bsps = []
466    for b in archs_bsps:
467        if b in installed:
468            bsps += [b]
469    bsps.sort()
470    return bsps
471
472def _arch_from_arch_bsp(arch_bsp):
473    return '-'.join(arch_bsp.split('-')[:2])
474
475def _bsp_from_arch_bsp(arch_bsp):
476    return '-'.join(arch_bsp.split('-')[2:])
477
478def _pkgconfig_path(path):
479    return os.path.join(path, 'lib', 'pkgconfig')
480
481def _load_flags(conf, arch_bsp, path):
482    if not os.path.exists(path):
483        ctx.fatal('RTEMS path not found.')
484    if os.path.exists(_pkgconfig_path(path)):
485        pc = os.path.join(_pkgconfig_path(path), arch_bsp + '.pc')
486        conf.to_log('Opening and load pkgconfig: ' + pc)
487        pkg = pkgconfig.package(pc)
488        config = None
489    elif os.path.exists(os.path.join(path, 'rtems-config')):
490        config = os.path.join(path, 'rtems-config')
491        pkg = None
492    flags = {}
493    _log_header(conf)
494    flags['CFLAGS'] = _load_flags_set('CFLAGS', arch_bsp, conf, config, pkg)
495    flags['LDFLAGS'] = _load_flags_set('LDFLAGS', arch_bsp, conf, config, pkg)
496    flags['LIB'] = _load_flags_set('LIB', arch_bsp, conf, config, pkg)
497    return flags
498
499def _load_flags_set(flags, arch_bsp, conf, config, pkg):
500    conf.to_log('%s ->' % flags)
501    if pkg is not None:
502        flagstr = ''
503        try:
504            flagstr = pkg.get(flags)
505        except pkgconfig.error as e:
506            conf.to_log('pkconfig warning: ' + e.msg)
507        conf.to_log('  ' + flagstr)
508    else:
509        flags_map = { 'CFLAGS': '--cflags',
510                      'LDFLAGS': '--ldflags',
511                      'LIB': '--libs' }
512        ab = arch_bsp.split('-')
513        #conf.check_cfg(path = config,
514        #               package = '',
515        #               uselib_store = 'rtems',
516        #               args = '--bsp %s/%s %s' % (ab[0], ab[2], flags_map[flags]))
517        #print conf.env
518        #print '%r' % conf
519        #flagstr = '-l -c'
520        flagstr = subprocess.check_output([config, '--bsp', '%s/%s' % (ab[0], ab[2]), flags_map[flags]])
521        #print flags, ">>>>", flagstr
522        if flags == 'CFLAGS':
523            flagstr += ' -DWAF_BUILD=1'
524        if flags == 'LIB':
525            flagstr = 'rtemscpu rtemsbsp c rtemscpu rtemsbsp'
526    return flagstr.split()
527
528def _filter_flags(label, flags, arch, rtems_path):
529
530    flag_groups = \
531        [ { 'key': 'warnings', 'path': False, 'flags': { '-W': 1 }, 'cflags': False, 'lflags': False },
532          { 'key': 'includes', 'path': True,  'flags': { '-I': 1, '-isystem': 2, '-sysroot': 2 } },
533          { 'key': 'machines', 'path': True,  'flags': { '-O': 1, '-m': 1, '-f': 1 } },
534          { 'key': 'specs',    'path': True,  'flags': { '-q': 1, '-B': 2, '--specs': 2 } } ]
535
536    flags = _strip_cflags(flags)
537
538    _flags = { label: [] }
539    for fg in flag_groups:
540        _flags[fg['key']] = []
541
542    iflags = iter(flags)
543    for opt in iflags:
544        in_label = True
545        opts = []
546        for fg in flag_groups:
547            key = fg['key']
548            for flag in fg['flags']:
549                if opt.startswith(flag):
550                    opt_count = fg['flags'][flag]
551                    if opt_count > 1:
552                        if opt != flag:
553                            opt_count -= 1
554                            if fg['path'] and arch in opt:
555                                opt = '%s%s/%s' % (flag, rtems_path,
556                                                   opt[opt.find(arch):])
557                    opts += [opt]
558                    for c in range(1, opt_count):
559                        opt = next(iflags)
560                        if fg['path'] and arch in opt:
561                            opt = '%s%s/%s' % (f, rtems_path,
562                                               opt[opt.find(arch):])
563                        opts += [opt]
564                    _flags[key] += opts
565                    if label in fg and not fg[label]:
566                        in_label = False
567                    break
568            if in_label:
569                _flags[label] += opts
570    return _flags
571
572def _strip_cflags(cflags):
573    _cflags = []
574    for o in cflags:
575        if o.startswith('-O'):
576            pass
577        elif o.startswith('-g'):
578            pass
579        else:
580            _cflags += [o]
581    return _cflags
582
583def _log_header(conf):
584    conf.to_log('-----------------------------------------')
585
586from waflib import TaskGen
587from waflib.Tools.ccroot import link_task, USELIB_VARS
588USELIB_VARS['rap'] = set(['RTEMS_LINKFLAGS'])
589@TaskGen.extension('.c')
590class rap(link_task):
591        "Link object files into a RTEMS applicatoin"
592        run_str = '${RTEMS_LD} ${RTEMS_LINKFLAGS} --cc ${CC} ${SRC} -o ${TGT[0].abspath()} ${STLIB_MARKER} ${STLIBPATH_ST:STLIBPATH} ${STLIB_ST:STLIB} ${LIBPATH_ST:LIBPATH} ${LIB_ST:LIB}'
593        ext_out = ['.rap']
594        vars    = ['RTEMS_LINKFLAGS', 'LINKDEPS']
595        inst_to = '${BINDIR}'
Note: See TracBrowser for help on using the repository browser.