source: rtems-libbsd/waf_libbsd.py @ f7a09b5

55-freebsd-126-freebsd-12
Last change on this file since f7a09b5 was f7a09b5, checked in by Chris Johns <chrisj@…>, on Mar 26, 2018 at 4:14:52 AM

waf: Support building from libbsd.py directly from waf.

Remove the need to generate a waf script.

Move various pieces of data from the builder code to libbsd.py and make
it configuration data.

Update #3351

  • Property mode set to 100644
File size: 24.0 KB
Line 
1#
2#  Copyright (c) 2015-2018 Chris Johns <chrisj@rtems.org>. All rights reserved.
3#
4#  Copyright (c) 2009-2015 embedded brains GmbH.  All rights reserved.
5#
6#   embedded brains GmbH
7#   Dornierstr. 4
8#   82178 Puchheim
9#   Germany
10#   <info@embedded-brains.de>
11#
12#  Copyright (c) 2012 OAR Corporation. All rights reserved.
13#
14#  Redistribution and use in source and binary forms, with or without
15#  modification, are permitted provided that the following conditions
16#  are met:
17#  1. Redistributions of source code must retain the above copyright
18#     notice, this list of conditions and the following disclaimer.
19#  2. Redistributions in binary form must reproduce the above copyright
20#     notice, this list of conditions and the following disclaimer in the
21#     documentation and/or other materials provided with the distribution.
22#
23#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27#  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35from __future__ import print_function
36
37import os
38import sys
39import tempfile
40
41import builder
42
43import rtems_waf.rtems as rtems
44
45windows = os.name == 'nt'
46
47if windows:
48    host_shell = 'sh -c '
49else:
50    host_shell = ''
51
52def _cflagsIncludes(cflags, includes):
53    if type(cflags) is not list:
54        if cflags is not None:
55            _cflags = cflags.split(' ')
56        else:
57            _cflags = [None]
58    else:
59        _cflags = cflags
60    if type(includes) is not list:
61        _includes = [includes]
62    else:
63        _includes = includes
64    return _cflags, _includes
65
66class SourceFileFragmentComposer(builder.BuildSystemFragmentComposer):
67
68    def __init__(self, cflags = "default", includes = None):
69        self.cflags, self.includes = _cflagsIncludes(cflags, includes)
70
71    def compose(self, path):
72        if None in self.includes:
73            flags = self.cflags
74        else:
75            flags = self.cflags + self.includes
76        return ['sources', flags, ('default', None)], [path], self.cflags, self.includes
77
78class SourceFileIfHeaderComposer(SourceFileFragmentComposer):
79
80    def __init__(self, headers, cflags = "default", includes = None):
81        if headers is not list:
82            headers = [headers]
83        self.headers = headers
84        super(SourceFileIfHeaderComposer, self).__init__(cflags = cflags, includes = includes)
85
86    def compose(self, path):
87        r = SourceFileFragmentComposer.compose(self, path)
88        define_keys = ''
89        for h in self.headers:
90            h = h.upper()
91            for c in '\/-.':
92                h = h.replace(c, '_')
93            define_keys += ' ' + h
94        r[0][2] = (define_keys.strip(), self.headers)
95        return r
96
97class TestFragementComposer(builder.BuildSystemFragmentComposer):
98
99    def __init__(self, testName, fileFragments, runTest = True, netTest = False):
100        self.testName = testName
101        self.fileFragments = fileFragments
102        self.runTest = runTest
103        self.netTest = netTest
104
105    def compose(self, path):
106        return ['tests', self.testName, ('default', None)], { 'files': self.fileFragments,
107                                                              'run': self.runTest,
108                                                              'net': self.netTest }
109
110class TestIfHeaderComposer(TestFragementComposer):
111
112    def __init__(self, testName, headers, fileFragments, runTest = True, netTest = False):
113        if headers is not list:
114            headers = [headers]
115        self.headers = headers
116        super(TestIfHeaderComposer, self).__init__(testName, fileFragments,
117                                                   runTest = runTest, netTest = netTest)
118
119    def compose(self, path):
120        r = TestFragementComposer.compose(self, path)
121        define_keys = ''
122        for h in self.headers:
123            h = h.upper()
124            for c in '\/-.':
125                h = h.replace(c, '_')
126            define_keys += ' ' + h
127        r[0][2] = (define_keys.strip(), self.headers)
128        return r
129
130class KVMSymbolsFragmentComposer(builder.BuildSystemFragmentComposer):
131
132    def compose(self, path):
133        return ['KVMSymbols', 'files', ('default', None)], [path], self.includes
134
135class RPCGENFragmentComposer(builder.BuildSystemFragmentComposer):
136
137    def compose(self, path):
138        return ['RPCGen', 'files', ('default', None)], [path]
139
140class RouteKeywordsFragmentComposer(builder.BuildSystemFragmentComposer):
141
142    def compose(self, path):
143        return ['RouteKeywords', 'files', ('default', None)], [path]
144
145class LexFragmentComposer(builder.BuildSystemFragmentComposer):
146
147    def __init__(self, sym, dep, cflags = None, includes = None):
148        self.sym = sym
149        self.dep = dep
150        self.cflags, self.includes = _cflagsIncludes(cflags, includes)
151
152    def compose(self, path):
153        d = { 'file': path,
154              'sym': self.sym,
155              'dep': self.dep }
156        if None not in self.cflags:
157            d['cflags'] = self.cflags
158        if None not in self.includes:
159            d['includes'] = self.includes
160        return ['lex', path, ('default', None)], d
161
162class YaccFragmentComposer(builder.BuildSystemFragmentComposer):
163
164    def __init__(self, sym, header, cflags = None, includes = None):
165        self.sym = sym
166        self.header = header
167        self.cflags, self.includes = _cflagsIncludes(cflags, includes)
168
169    def compose(self, path):
170        d = { 'file': path,
171              'sym': self.sym,
172              'header': self.header }
173        if None not in self.cflags:
174            d['cflags'] = self.cflags
175        if None not in self.includes:
176            d['includes'] = self.includes
177        return ['yacc', path, ('default', None)], d
178
179#
180# The waf builder for libbsd.
181#
182class Builder(builder.ModuleManager):
183
184    def __init__(self, trace = False):
185        super(Builder, self).__init__()
186        self.trace = trace
187        self.data = {}
188
189    @staticmethod
190    def _sourceList(bld, files):
191        sources = []
192        if type(files) is dict:
193            for cfg in files:
194                if cfg in ['cflags', 'includes']:
195                    continue
196                if cfg != 'default':
197                    for c in cfg.split(' '):
198                        if not bld.env['HAVE_%s' % (c)]:
199                            continue
200                sources += sorted(files[cfg])
201        else:
202            sources = sorted(files)
203        return sources
204
205    def setGenerators(self):
206        #
207        # Called when the builder.ModuleManager.__init__ is run
208        #
209        self.generator['convert'] = builder.Converter
210        self.generator['no-convert'] = builder.NoConverter
211        self.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'] = builder.FromFreeBSDToRTEMSUserSpaceSourceConverter
212        self.generator['from-RTEMS-To-FreeBSD-SourceConverter'] = builder.FromRTEMSToFreeBSDSourceConverter
213        self.generator['buildSystemFragmentComposer'] = builder.BuildSystemFragmentComposer
214
215        self.generator['file'] = builder.File
216
217        self.generator['path'] = builder.PathComposer
218        self.generator['freebsd-path'] = builder.FreeBSDPathComposer
219        self.generator['rtems-path'] = builder.RTEMSPathComposer
220        self.generator['cpu-path'] = builder.CPUDependentFreeBSDPathComposer
221        self.generator['target-src-cpu--path'] = builder.TargetSourceCPUDependentPathComposer
222
223        self.generator['source'] = SourceFileFragmentComposer
224        self.generator['test'] = TestFragementComposer
225        self.generator['kvm-symbols'] = KVMSymbolsFragmentComposer
226        self.generator['rpc-gen'] = RPCGENFragmentComposer
227        self.generator['route-keywords'] = RouteKeywordsFragmentComposer
228        self.generator['lex'] = LexFragmentComposer
229        self.generator['yacc'] = YaccFragmentComposer
230
231        self.generator['source-if-header'] = SourceFileIfHeaderComposer
232        self.generator['test-if-header'] = TestIfHeaderComposer
233
234    def generate(self, rtems_version):
235
236        def _dataInsert(data, cpu, frag):
237            #
238            # The default handler returns an empty string. Skip it.
239            #
240            if type(frag) is not str:
241                # Start at the top of the tree
242                d = data
243                path = frag[0]
244                if path[0] not in d:
245                    d[path[0]] = {}
246                # Select the sub-part of the tree as the compile options
247                # specialise how files are built.
248                d = d[path[0]]
249                if type(path[1]) is list:
250                    p = ' '.join(path[1])
251                else:
252                    p = path[1]
253                if p not in d:
254                    d[p] = {}
255                d = d[p]
256                if cpu not in d:
257                    d[cpu] = { }
258                config = frag[0][2][0]
259                if config != 'default':
260                    if 'configure' not in data:
261                        data['configure'] = { }
262                    data['configure'][config] = frag[0][2][1]
263                if type(frag[1]) is list:
264                    if config not in d[cpu]:
265                        d[cpu][config] = []
266                    d[cpu][config] += frag[1]
267                else:
268                    d[cpu][config] = frag[1]
269                #
270                # The CPU is for files and the flags and includes are common.
271                #
272                if len(frag) > 3:
273                    if 'cflags' not in d:
274                        d['cflags'] = []
275                    d['cflags'] += frag[2]
276                    d['cflags'] = list(set(d['cflags']))
277                if len(frag) >= 3 and None not in frag[-1]:
278                    if 'includes' not in d:
279                        d['includes'] = []
280                    d['includes'] += frag[-1]
281                    d['includes'] = list(set(d['includes']))
282
283        self.data = {}
284
285        for mn in self.getModules():
286            m = self[mn]
287            if m.conditionalOn == "none":
288                for f in m.files:
289                    _dataInsert(self.data, 'all', f.getFragment())
290            for cpu, files in sorted(m.cpuDependentSourceFiles.items()):
291                for f in files:
292                    _dataInsert(self.data, cpu, f.getFragment())
293
294        if self.trace:
295            import pprint
296            pprint.pprint(self.data)
297
298    def init(self, ctx):
299        pass
300
301    def options(self, opt):
302        pass
303
304    def bsp_configure(self, conf, arch_bsp):
305        if 'configure' in self.data:
306            for cfg in self.data['configure']:
307                for h in self.data['configure'][cfg]:
308                    conf.check(header_name = h,
309                               features = "c",
310                               includes = conf.env.IFLAGS,
311                               mandatory = False)
312
313    def configure(self, conf):
314        pass
315
316    def build(self, bld):
317        #
318        # Localize the config.
319        #
320        config = self.getConfiguration()
321
322        #
323        #
324        # C/C++ flags
325        #
326        common_flags = []
327        common_flags += ['-O%s' % (bld.env.OPTIMIZATION)]
328        if 'common-flags' in config:
329            common_flags += [f for f in config['common-flags']]
330        if bld.env.WARNINGS and 'common-warnings' in config:
331            common_flags += [f for f in config['common-warnings']]
332        elif 'common-no-warnings' in config:
333            common_flags += [f for f in config['common-no-warnings']]
334        if 'cflags' in config:
335            cflags = config['cflags'] + common_flags
336        if 'cxxflags' in config:
337            cxxflags = config['cxxflags'] + common_flags
338
339        #
340        # Defines
341        #
342        defines = []
343        if len(bld.env.FREEBSD_OPTIONS) > 0:
344            for o in bld.env.FREEBSD_OPTIONS.split(','):
345                defines += ['%s=1' % (o.strip().upper())]
346
347        #
348        # Include paths
349        #
350        includes = []
351        if 'cpu-include-paths' in config:
352            cpu = bld.get_env()['RTEMS_ARCH']
353            if cpu == "i386":
354                cpu = 'x86'
355            for i in config['cpu-include-paths']:
356                includes += [i.replace('@CPU@', cpu)]
357        if 'include-paths' in config:
358            includes += config['include-paths']
359        if 'build-include-path' in config:
360            includes += config['build-include-path']
361
362        #
363        # Collect the libbsd uses
364        #
365        libbsd_use = []
366
367        #
368        # Network test configuration
369        #
370        if not os.path.exists(bld.env.NET_CONFIG):
371            bld.fatal('network configuraiton \'%s\' not found' % (bld.env.NET_CONFIG))
372        tags = [ 'NET_CFG_SELF_IP',
373                 'NET_CFG_NETMASK',
374                 'NET_CFG_PEER_IP',
375                 'NET_CFG_GATEWAY_IP' ]
376        try:
377            net_cfg_lines = open(bld.env.NET_CONFIG).readlines()
378        except:
379            bld.fatal('network configuraiton \'%s\' read failed' % (bld.env.NET_CONFIG))
380        lc = 0
381        for l in net_cfg_lines:
382            lc += 1
383            if l.strip().startswith('NET_CFG_'):
384                ls = l.split('=')
385                if len(ls) != 2:
386                    bld.fatal('network configuraiton \'%s\' ' + \
387                              'parse error: %d: %s' % (bld.env.NET_CONFIG, lc, l))
388                lhs = ls[0].strip()
389                rhs = ls[1].strip()
390                sed = 'sed '
391                for t in tags:
392                    if lhs == t:
393                        sed += "-e 's/@%s@/%s/'" % (t, rhs)
394        bld(target = "testsuite/include/rtems/bsd/test/network-config.h",
395            source = "testsuite/include/rtems/bsd/test/network-config.h.in",
396            rule = sed + " < ${SRC} > ${TGT}",
397            update_outputs = True)
398
399        #
400        # Add a copy rule for all headers where the install path and the source
401        # path are not the same.
402        #
403        if 'header-paths' in config:
404            header_build_copy_paths = [
405                hp for hp in config['header-paths'] if hp[2] != '' and not hp[0].endswith(hp[2])
406            ]
407            for headers in header_build_copy_paths:
408                target = os.path.join("build-include", headers[2])
409                start_dir = bld.path.find_dir(headers[0])
410                for header in start_dir.ant_glob(headers[1]):
411                    relsourcepath = header.path_from(start_dir)
412                    targetheader = os.path.join(target, relsourcepath)
413                    bld(features = 'subst',
414                        target = targetheader,
415                        source = header,
416                        is_copy = True)
417
418        #
419        # Add the specific rule based builders
420        #
421
422        #
423        # KVM Symbols
424        #
425        if 'KVMSymbols' in self.data:
426            kvmsymbols = self.data['KVMSymbols']
427            if 'includes' in kvmsymbols['files']:
428                kvmsymbols_includes = kvmsymbols['files']['includes']
429            else:
430                kvmsymbols_includes = []
431            bld(target = kvmsymbols['files']['all']['default'][0],
432                source = 'rtemsbsd/rtems/generate_kvm_symbols',
433                rule = host_shell + './${SRC} > ${TGT}',
434                update_outputs = True)
435            bld.objects(target = 'kvmsymbols',
436                        features = 'c',
437                        cflags = cflags,
438                        includes = kvmsymbols_includes + includes,
439                        source = kvmsymbols['files']['all']['default'][0])
440            libbsd_use += ["kvmsymbols"]
441
442        bld.add_group()
443
444        #
445        # RPC Generation
446        #
447        if 'RPCGen' in self.data:
448            if bld.env.AUTO_REGEN:
449                rpcgen = self.data['RPCGen']
450                rpcname = rpcgen['files']['all']['default'][0][:-2]
451                bld(target = rpcname + '.h',
452                    source = y + '.x',
453                    rule = host_shell + '${RPCGEN} -h -o ${TGT} ${SRC}')
454
455        #
456        # Route keywords
457        #
458        if 'RouteKeywords' in self.data:
459            if bld.env.AUTO_REGEN:
460                routekw = self.data['RouteKeywords']
461                rkwname = routekw['files']['all']['default'][0]
462                rkw_rule = host_shell + 'cat ${SRC} | ' + \
463                           'awk \'BEGIN { r = 0 } { if (NF == 1) ' + \
464                           'printf \\"#define\\\\tK_%%s\\\\t%%d\\\\n\\\\t{\\\\\\"%%s\\\\\\", K_%%s},\\\\n\\", ' + \
465                           'toupper($1), ++r, $1, toupper($1)}\' > ${TGT}'
466                bld(target = rkwname + '.h',
467                    source = rkwname,
468                    rule = rkw_rule)
469
470        #
471        # Lex
472        #
473        if 'lex' in self.data:
474            lexes = self.data['lex']
475            for l in sorted(lexes.keys()):
476                lex = lexes[l]['all']['default']
477                if 'cflags' in lex:
478                    lexDefines = [d[2:] for d in lex['cflags']]
479                else:
480                    lexDefines = []
481                if 'includes' in lex:
482                    lexIncludes = lex['includes']
483                else:
484                    lexIncludes = []
485                lex_rule = host_shell + '${LEX} -P ' + lex['sym'] + ' -t ${SRC} | ' + \
486                           'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' > ${TGT}")'
487                if bld.env.AUTO_REGEN:
488                    bld(target = lex['file'][:-2]+ '.c',
489                        source = lex['file'],
490                        rule = lex_rule)
491                bld.objects(target = 'lex_%s' % (lex['sym']),
492                            features = 'c',
493                            cflags = cflags,
494                            includes = lexIncludes + includes,
495                            defines = defines + lexDefines,
496                            source = lex['file'][:-2] + '.c')
497                libbsd_use += ['lex_%s' % (lex['sym'])]
498
499        #
500        # Yacc
501        #
502        if 'yacc' in self.data:
503            yaccs = self.data['yacc']
504            for y in sorted(yaccs.keys()):
505                yacc = yaccs[y]['all']['default']
506                yaccFile = yacc['file']
507                if yacc['sym'] is not None:
508                    yaccSym = yacc['sym']
509                else:
510                    yaccSym = os.path.basename(yaccFile)[:-2]
511                yaccHeader = '%s/%s' % (os.path.dirname(yaccFile), yacc['header'])
512                if 'cflags' in yacc:
513                    yaccDefines = [d[2:] for d in yacc['cflags']]
514                else:
515                    yaccDefines = []
516                if 'includes' in yacc:
517                    yaccIncludes = yacc['includes']
518                else:
519                    yaccIncludes = []
520                yacc_rule = host_shell + '${YACC} -b ' + yaccSym + \
521                            ' -d -p ' + yaccSym + ' ${SRC} && ' + \
522                            'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < ' + yaccSym + '.tab.c > ${TGT} && ' + \
523                            'rm -f ' + yaccSym + '.tab.c && mv ' + yaccSym + '.tab.h ' + yaccHeader
524                if bld.env.AUTO_REGEN:
525                    bld(target = yaccFile[:-2] + '.c',
526                        source = yaccFile,
527                        rule = yacc_rule)
528                bld.objects(target = 'yacc_%s' % (yaccSym),
529                            features = 'c',
530                            cflags = cflags,
531                            includes = yaccIncludes + includes,
532                            defines = defines + yaccDefines,
533                            source = yaccFile[:-2] + '.c')
534                libbsd_use += ['yacc_%s' % (yaccSym)]
535
536        #
537        # We have 'm' different sets of flags and there can be 'n' cpus
538        # specific files for those flags.
539        #
540        objs = 0
541        sources = sorted(self.data['sources'])
542        if 'default' in sources:
543            sources.remove('default')
544        for flags in sources:
545            objs += 1
546            build = self.data['sources'][flags]
547            target = 'objs%02d' % (objs)
548            bld_sources = Builder._sourceList(bld, build['all'])
549            archs = sorted(build)
550            for i in ['all', 'cflags', 'includes']:
551                if i in archs:
552                    archs.remove(i)
553            for arch in archs:
554                if bld.get_env()['RTEMS_ARCH'] == arch:
555                    bld_sources += Builder._sourceList(bld, build[arch])
556            if 'cflags' in build:
557                bld_defines = [d[2:] for d in build['cflags']]
558            else:
559                bld_defines = []
560            if 'includes' in build:
561                bld_includes = build['includes']
562            else:
563                bld_includes = []
564            bld.objects(target = target,
565                        features = 'c',
566                        cflags = cflags,
567                        includes = sorted(bld_includes) + includes,
568                        defines = defines + sorted(bld_defines),
569                        source = bld_sources)
570            libbsd_use += [target]
571
572        #
573        # We hold the 'default' cflags set of files to the end to create the
574        # static library with.
575        #
576        build = self.data['sources']['default']
577        bld_sources = Builder._sourceList(bld, build['all'])
578        archs = sorted(build)
579        archs.remove('all')
580        for arch in archs:
581            if bld.get_env()['RTEMS_ARCH'] == arch:
582                bld_sources += Builder._sourceList(bld, build[arch])
583        bld.stlib(target = 'bsd',
584                  features = 'c cxx',
585                  cflags = cflags,
586                  cxxflags = cxxflags,
587                  includes = includes,
588                  defines = defines,
589                  source = bld_sources,
590                  use = libbsd_use)
591
592        #
593        # Installs.
594        #
595        # Header file collector.
596        #
597        arch_lib_path = rtems.arch_bsp_lib_path(bld.env.RTEMS_VERSION,
598                                                bld.env.RTEMS_ARCH_BSP)
599        arch_inc_path = rtems.arch_bsp_include_path(bld.env.RTEMS_VERSION,
600                                                    bld.env.RTEMS_ARCH_BSP)
601
602        bld.install_files("${PREFIX}/" + arch_lib_path, ["libbsd.a"])
603
604        if 'header-paths' in config:
605            headerPaths = config['header-paths']
606            cpu = bld.get_env()['RTEMS_ARCH']
607            if cpu == "i386":
608                cpu = 'x86'
609            for headers in headerPaths:
610                # Get the dest path
611                ipath = os.path.join(arch_inc_path, headers[2])
612                start_dir = bld.path.find_dir(headers[0].replace('@CPU@', cpu))
613                if start_dir != None:
614                    bld.install_files("${PREFIX}/" + ipath,
615                                      start_dir.ant_glob(headers[1]),
616                                      cwd = start_dir,
617                                      relative_trick = True)
618
619        #
620        # Tests
621        #
622        tests = self.data['tests']
623        for testName in sorted(tests):
624            test = self.data['tests'][testName]['all']
625            test_source = []
626            for cfg in test:
627                build_test = True
628                if cfg != 'default':
629                    for c in cfg.split(' '):
630                        if not bld.env['HAVE_%s' % (c)]:
631                            build_test = False
632                            break
633                if build_test:
634                    test_sources = ['testsuite/%s/%s.c' % (testName, f) \
635                                    for f in test[cfg]['files']]
636            if build_test:
637                bld.program(target = '%s.exe' % (testName),
638                            features = 'cprogram',
639                            cflags = cflags,
640                            includes = includes,
641                            source = test_sources,
642                            use = ['bsd'],
643                            lib = ['m', 'z'],
644                            install_path = None)
Note: See TracBrowser for help on using the repository browser.