source: rtems/wscript @ 2f6ee01

Last change on this file since 2f6ee01 was a13047c, checked in by Sebastian Huber <sebastian.huber@…>, on 06/27/22 at 09:28:34

build: Allow separate optimization flags

Allow separate optimization flags for the BSP, cpukit, and tests. For example,
the BSP and cpukit may be built without optimization if coverage
instrumentation is enabled, however, the tests may still use optimization.

Update #4670.

  • Property mode set to 100755
File size: 54.9 KB
Line 
1#!/usr/bin/env python
2
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (C) 2020 Hesham Almatary <Hesham.Almatary@cl.cam.ac.uk>
6# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
29from __future__ import print_function
30
31import pickle
32import os
33import re
34import stat
35import string
36import sys
37
38try:
39    import configparser
40except:
41    import ConfigParser as configparser
42
43from waflib.TaskGen import after, before_method, feature
44
45is_windows_host = os.name == "nt" or sys.platform in ["msys", "cygwin"]
46default_prefix = "/opt/rtems/6"
47compilers = ["gcc", "clang"]
48items = {}
49bsps = {}
50
51
52def no_unicode(value):
53    if sys.version_info[0] > 2:
54        return value
55    if isinstance(value, unicode):
56        return str(value)
57    return value
58
59
60class VersionControlKeyHeader:
61    _content = None
62
63    @staticmethod
64    def write(bld, filename):
65        if VersionControlKeyHeader._content is None:
66            from waflib.Build import Context
67            from waflib.Errors import WafError
68
69            content = """/*
70 * Automatically generated. Do not edit.
71 */
72#if !defined(_RTEMS_VERSION_VC_KEY_H_)
73#define _RTEMS_VERSION_VC_KEY_H_
74"""
75            try:
76                rev = bld.cmd_and_log(
77                    "git rev-parse HEAD", quiet=Context.STDOUT
78                ).strip()
79                content += """#define RTEMS_VERSION_VC_KEY "{}"
80""".format(
81                    rev
82                )
83            except WafError:
84                content += """/* No version control key found; release? */
85"""
86            content += """#endif
87"""
88            VersionControlKeyHeader._content = content
89        f = bld.bldnode.make_node(filename)
90        f.parent.mkdir()
91        try:
92            if content != f.read():
93                f.write(VersionControlKeyHeader._content)
94        except:
95            f.write(VersionControlKeyHeader._content)
96
97
98class EnvWrapper(object):
99    def __init__(self, env):
100        self._env = env
101
102    def __getitem__(self, name):
103        fields = name.split(":")
104        v = self._env[fields[0]]
105        try:
106            fmt = "{:" + fields[1] + "}"
107        except IndexError:
108            fmt = "{}"
109        if isinstance(v, list):
110            return " ".join([fmt.format(w) for w in v])
111        return fmt.format(v)
112
113
114class Template(string.Template):
115    idpattern = "[_A-Za-z][_A-Za-z0-9:#]*"
116
117
118_VAR_PATTERN = re.compile("\$\{?(" + Template.idpattern + ")\}?$")
119
120
121def _is_enabled_op_and(enabled, enabled_by):
122    for next_enabled_by in enabled_by:
123        if not _is_enabled(enabled, next_enabled_by):
124            return False
125    return True
126
127
128def _is_enabled_op_not(enabled, enabled_by):
129    return not _is_enabled(enabled, enabled_by)
130
131
132def _is_enabled_op_or(enabled, enabled_by):
133    for next_enabled_by in enabled_by:
134        if _is_enabled(enabled, next_enabled_by):
135            return True
136    return False
137
138
139_IS_ENABLED_OP = {
140    "and": _is_enabled_op_and,
141    "not": _is_enabled_op_not,
142    "or": _is_enabled_op_or,
143}
144
145
146def _is_enabled(enabled, enabled_by):
147    if isinstance(enabled_by, bool):
148        return enabled_by
149    if isinstance(enabled_by, list):
150        return _is_enabled_op_or(enabled, enabled_by)
151    if isinstance(enabled_by, dict):
152        key, value = next(iter(enabled_by.items()))
153        return _IS_ENABLED_OP[key](enabled, value)
154    return enabled_by in enabled
155
156
157def _asm_explicit_target(self, node):
158    task = self.create_task(
159        "asm", node, self.bld.bldnode.make_node(self.target)
160    )
161    try:
162        self.compiled_tasks.append(task)
163    except AttributeError:
164        self.compiled_tasks = [task]
165    return task
166
167
168@feature("asm_explicit_target")
169@before_method("process_source")
170def _enable_asm_explicit_target(self):
171    self.mappings = dict(self.mappings)  # Copy
172    self.mappings[".S"] = _asm_explicit_target
173
174
175@after("apply_link")
176@feature("cprogram", "cxxprogram")
177def process_start_files(self):
178    if getattr(self, "start_files", False):
179        self.link_task.dep_nodes.extend(self.bld.start_files)
180
181
182class Item(object):
183    def __init__(self, uid, data):
184        self.uid = uid
185        self.data = data
186        self.links = self._init_links
187
188    def _init_links(self):
189        self._links = []
190        for link in self.data["links"]:
191            if link["role"] == "build-dependency":
192                uid = link["uid"]
193                if not os.path.isabs(uid):
194                    uid = os.path.normpath(
195                        os.path.join(os.path.dirname(self.uid), uid)
196                    )
197                self._links.append(items[uid])
198        self.links = self._yield_links
199        for link in self._links:
200            yield link
201
202    def _yield_links(self):
203        for link in self._links:
204            yield link
205
206    def get_enabled_by(self):
207        return self.data["enabled-by"]
208
209    def defaults(self, enable, variant, family):
210        if _is_enabled(enable, self.get_enabled_by()):
211            for p in self.links():
212                p.defaults(enable, variant, family)
213            self.do_defaults(variant, family)
214
215    def configure(self, conf, cic):
216        if _is_enabled(conf.env.ENABLE, self.get_enabled_by()):
217            self.prepare_configure(conf, cic)
218            for p in self.links():
219                p.configure(conf, cic)
220            self.do_configure(conf, cic)
221
222    def build(self, bld, bic):
223        if _is_enabled(bld.env.ENABLE, self.get_enabled_by()):
224            bic = self.prepare_build(bld, bic)
225            for p in self.links():
226                p.build(bld, bic)
227            self.do_build(bld, bic)
228
229    def do_defaults(self, variant, family):
230        return
231
232    def prepare_configure(self, conf, cic):
233        return
234
235    def do_configure(self, conf, cic):
236        return
237
238    def prepare_build(self, bld, bic):
239        return bic
240
241    def do_build(self, bld, bic):
242        return
243
244    def substitute(self, ctx, value):
245        if isinstance(value, str):
246            try:
247                return Template(value).substitute(EnvWrapper(ctx.env))
248            except Exception as e:
249                ctx.fatal(
250                    "In item '{}' substitution in '{}' failed: {}".format(
251                        self.uid, value, e
252                    )
253                )
254        if isinstance(value, list):
255            more = []
256            for item in value:
257                if isinstance(item, str):
258                    m = _VAR_PATTERN.match(item)
259                else:
260                    m = None
261                if m:
262                    more.extend(ctx.env[m.group(1).strip("{}")])
263                else:
264                    more.append(self.substitute(ctx, item))
265            return more
266        return value
267
268    def get(self, ctx, name):
269        return self.substitute(ctx, self.data[name])
270
271    def install_target(self, bld):
272        install_path = self.data["install-path"]
273        if install_path:
274            bld.install_files(install_path, self.get(bld, "target"))
275
276    def install_files(self, bld):
277        for install in self.data["install"]:
278            bld.install_files(install["destination"], install["source"])
279
280    def asm(self, bld, bic, source, target=None):
281        if target is None:
282            target = os.path.splitext(source)[0] + ".o"
283        bld(
284            asflags=self.substitute(bld, self.data["asflags"]),
285            cppflags=self.substitute(bld, self.data["cppflags"]),
286            features="asm_explicit_target asm c",
287            includes=bic.includes + self.substitute(bld, self.data["includes"]),
288            source=[source],
289            target=target,
290        )
291        return target
292
293    def cc(self, bld, bic, source, target=None, deps=[], cppflags=[]):
294        if target is None:
295            target = os.path.splitext(source)[0] + ".o"
296        bld(
297            cflags=self.substitute(bld, self.data["cflags"]),
298            cppflags=cppflags + self.substitute(bld, self.data["cppflags"]),
299            features="c",
300            includes=bic.includes + self.substitute(bld, self.data["includes"]),
301            rule="${CC} ${CFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
302            source=[source] + deps,
303            target=target,
304        )
305        return target
306
307    def cxx(self, bld, bic, source, target=None, deps=[], cppflags=[]):
308        if target is None:
309            target = os.path.splitext(source)[0] + ".o"
310        bld(
311            cppflags=cppflags + self.substitute(bld, self.data["cppflags"]),
312            cxxflags=self.substitute(bld, self.data["cxxflags"]),
313            features="cxx",
314            includes=bic.includes + self.substitute(bld, self.data["includes"]),
315            rule="${CXX} ${CXXFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
316            source=[source] + deps,
317            target=target,
318        )
319        return target
320
321    def link(self, bld, bic, cmd, source, target):
322        from waflib.Task import Task
323
324        class link(Task):
325            def __init__(self, item, bic, cmd, env):
326                super(link, self).__init__(self, env=env)
327                self.cmd = cmd
328                self.ldflags = bic.ldflags + item.data["ldflags"]
329                self.stlib = item.data["stlib"]
330                self.use = (
331                    item.data["use-before"] + bic.use + item.data["use-after"]
332                )
333
334            def run(self):
335                cmd = [self.cmd]
336                cmd.extend(self.env.LINKFLAGS)
337                cmd.extend([i.abspath() for i in self.inputs])
338                cmd.append("-o" + self.outputs[0].abspath())
339                cmd.append("-L.")
340                cmd.extend(["-l" + l for l in self.stlib])
341                cmd.extend(["-l" + l for l in self.use])
342                cmd.extend(self.env.LDFLAGS)
343                cmd.extend(self.ldflags)
344                return self.exec_command(cmd)
345
346            def scan(self):
347                return (
348                    [
349                        self.generator.bld.bldnode.make_node("lib" + u + ".a")
350                        for u in self.use
351                    ],
352                    [],
353                )
354
355        tsk = link(self, bic, cmd, bld.env)
356        tsk.set_inputs([bld.bldnode.make_node(s) for s in source])
357        tsk.set_outputs(bld.bldnode.make_node(target))
358        bld.add_to_group(tsk)
359        return target
360
361    def link_cc(self, bld, bic, source, target):
362        return self.link(bld, bic, bld.env.LINK_CC[0], source, target)
363
364    def link_cxx(self, bld, bic, source, target):
365        return self.link(bld, bic, bld.env.LINK_CXX[0], source, target)
366
367    def gnatmake(self, bld, bic, objdir, objs, main, target):
368        from waflib.Task import Task
369
370        class gnatmake(Task):
371            def __init__(self, bld, bic, objdir, objs, main, target, item):
372                super(gnatmake, self).__init__(self, env=bld.env)
373                self.objdir = objdir
374                self.objs = [bld.bldnode.make_node(o) for o in objs]
375                self.main = bld.path.make_node(main)
376                self.set_inputs(self.objs + [self.main])
377                self.set_outputs(bld.bldnode.make_node(target))
378                self.adaflags = item.data["adaflags"]
379                self.adaincludes = []
380                for i in item.data["adaincludes"]:
381                    self.adaincludes.append(bld.bldnode.make_node(i))
382                    self.adaincludes.append(bld.path.make_node(i))
383                self.ldflags = bic.ldflags + item.data["ldflags"]
384                self.stlib = item.data["stlib"]
385                self.use = (
386                    item.data["use-before"] + bic.use + item.data["use-after"]
387                )
388
389            def run(self):
390                cwd = self.get_cwd()
391                cmd = [
392                    self.env.GNATMAKE[0],
393                    "-D",
394                    self.objdir,
395                    "-bargs",
396                    "-Mgnat_main",
397                    "-margs",
398                ]
399                cmd.extend(self.adaflags)
400                cmd.extend(["-I" + i.path_from(cwd) for i in self.adaincludes])
401                cmd.append("-cargs")
402                cmd.extend(self.env.ABI_FLAGS)
403                cmd.append("-largs")
404                cmd.extend([o.path_from(cwd) for o in self.objs])
405                cmd.extend(self.env.LINKFLAGS)
406                cmd.extend(self.ldflags)
407                cmd.append("-L.")
408                cmd.extend(["-l" + l for l in self.stlib])
409                cmd.extend(["-l" + l for l in self.use])
410                cmd.extend(self.env.LDFLAGS)
411                cmd.extend(["-margs", "-a"])
412                cmd.append(self.main.abspath())
413                cmd.append("-o")
414                cmd.append(self.outputs[0].abspath())
415                return self.exec_command(cmd)
416
417            def scan(self):
418                return (
419                    [
420                        self.generator.bld.bldnode.make_node("lib" + u + ".a")
421                        for u in self.use
422                    ],
423                    [],
424                )
425
426        tsk = gnatmake(bld, bic, objdir, objs, main, target, self)
427        bld.add_to_group(tsk)
428        return target
429
430    def ar(self, bld, source, target):
431        bld(rule="${AR} ${ARFLAGS} ${TGT} ${SRC}", source=source, target=target)
432        return target
433
434    def gzip(self, bld, source):
435        target = source + ".gz"
436        bld(rule="${GZIP} < ${SRC} > ${TGT}", source=source, target=target)
437        return target
438
439    def xz(self, bld, source):
440        target = source + ".xz"
441        bld(rule="${XZ} < ${SRC} > ${TGT}", source=source, target=target)
442        return target
443
444    def tar(self, bld, source, remove, target):
445        def run(task):
446            import tarfile
447
448            tar = tarfile.TarFile(
449                task.outputs[0].abspath(), "w", format=tarfile.USTAR_FORMAT
450            )
451            srcpath = bld.path.abspath() + "/"
452            bldpath = bld.bldnode.abspath() + "/"
453            for src in task.inputs:
454                src = src.abspath()
455                dst = src
456                for r in remove:
457                    dst = src.replace(srcpath + r, "").replace(bldpath + r, "")
458                tar.add(src, dst)
459            tar.close()
460            return 0
461
462        bld(rule=run, source=source, target=target)
463        return target
464
465    def bin2c(self, bld, source, name=None, target=None):
466        def run(task):
467            cmd = [bld.env.BIN2C[0]]
468            if name is not None:
469                cmd.extend(["-N", name])
470            cmd.append(task.inputs[0].abspath())
471            cmd.append(task.outputs[0].abspath())
472            return task.exec_command(cmd)
473
474        path, base = os.path.split(source)
475        if target is None:
476            target = path + "/" + base.replace(".", "-")
477        target_c = target + ".c"
478        target_h = target + ".h"
479        bld(rule=run, source=source, target=[target_c, target_h])
480        return target_c, target_h
481
482    def rtems_syms(self, bld, source, target):
483        bld(
484            rule='${RTEMS_SYMS} -e -C ${CC} -c "${CFLAGS}" -o ${TGT} ${SRC}',
485            source=source,
486            target=target,
487        )
488        return target
489
490    def rtems_rap(self, bld, base, objects, libs, target):
491        def run(task):
492            cmd = [
493                bld.env.RTEMS_LD[0],
494                "-C",
495                bld.env.CC[0],
496                "-c",
497                " ".join(bld.env.CFLAGS),
498                "-O",
499                "rap",
500                "-b",
501                task.inputs[0].abspath(),
502                "-e",
503                "rtems_main",
504                "-s",
505                "-o",
506            ]
507            cmd.append(task.outputs[0].abspath())
508            cmd.extend([i.abspath() for i in task.inputs[1:]])
509            cmd.extend(["-l" + l for l in libs])
510            return task.exec_command(cmd)
511
512        bld(rule=run, source=[base] + objects, target=target)
513        return target
514
515
516class GroupItem(Item):
517    def __init__(self, uid, data):
518        super(GroupItem, self).__init__(uid, data)
519
520    def prepare_build(self, bld, bic):
521        return BuildItemContext(
522            bic.includes + self.substitute(bld, self.data["includes"]),
523            bic.cppflags + self.substitute(bld, self.data["cppflags"]),
524            bic.cflags + self.substitute(bld, self.data["cflags"]),
525            bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
526            self.data["use-before"] + bic.use + self.data["use-after"],
527            bic.ldflags + self.substitute(bld, self.data["ldflags"]),
528            bic.objects,
529        )
530
531    def do_build(self, bld, bic):
532        self.install_files(bld)
533
534
535class ConfigFileItem(Item):
536    def __init__(self, uid, data):
537        super(ConfigFileItem, self).__init__(uid, data)
538
539    def do_configure(self, conf, cic):
540        content = self.substitute(conf, self.data["content"])
541        f = conf.bldnode.make_node(
542            conf.env.VARIANT + "/" + self.get(conf, "target")
543        )
544        f.parent.mkdir()
545        f.write(content)
546        conf.env.append_value("cfg_files", f.abspath())
547
548    def do_build(self, bld, bic):
549        self.install_target(bld)
550
551
552class ConfigHeaderItem(Item):
553    def __init__(self, uid, data):
554        super(ConfigHeaderItem, self).__init__(uid, data)
555
556    def do_configure(self, conf, cic):
557        conf.env.include_key = self.data["include-headers"]
558        conf.write_config_header(
559            conf.env.VARIANT + "/" + self.get(conf, "target"),
560            guard=self.data["guard"],
561            headers=True,
562        )
563        conf.env.include_key = None
564
565    def do_build(self, bld, bic):
566        self.install_target(bld)
567
568
569class StartFileItem(Item):
570    def __init__(self, uid, data):
571        super(StartFileItem, self).__init__(uid, data)
572
573    def do_build(self, bld, bic):
574        source = self.data["source"]
575        if os.path.splitext(source[0])[1] == ".S":
576            tgt = self.asm(bld, bic, source, self.get(bld, "target"))
577        else:
578            tgt = self.cc(bld, bic, source, self.get(bld, "target"))
579        node = bld.bldnode.make_node(tgt)
580        try:
581            bld.start_files.append(node)
582        except AttributeError:
583            bld.start_files = [node]
584        self.install_target(bld)
585
586
587class ObjectsItem(Item):
588    def __init__(self, uid, data):
589        super(ObjectsItem, self).__init__(uid, data)
590
591    def prepare_build(self, bld, bic):
592        return BuildItemContext(
593            bic.includes + self.substitute(bld, self.data["includes"]),
594            bic.cppflags + self.substitute(bld, self.data["cppflags"]),
595            bic.cflags + self.substitute(bld, self.data["cflags"]),
596            bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
597            bic.use,
598            bic.ldflags,
599            bic.objects,
600        )
601
602    def do_build(self, bld, bic):
603        bld.objects(
604            asflags=bic.cppflags,
605            cflags=bic.cflags,
606            cppflags=bic.cppflags,
607            cxxflags=bic.cxxflags,
608            includes=bic.includes,
609            source=self.data["source"],
610            target=self.uid,
611        )
612        bic.objects.append(self.uid)
613        self.install_files(bld)
614
615
616class BSPItem(Item):
617    def __init__(self, uid, data):
618        super(BSPItem, self).__init__(uid, data)
619        arch_bsps = bsps.setdefault(data["arch"].strip(), {})
620        arch_bsps[data["bsp"].strip()] = self
621
622    def prepare_build(self, bld, bic):
623        return BuildItemContext(
624            bic.includes
625            + bld.env.BSP_INCLUDES
626            + self.substitute(bld, self.data["includes"]),
627            self.substitute(bld, self.data["cppflags"]),
628            bld.env.BSP_CFLAGS + self.substitute(bld, self.data["cflags"]),
629            [],
630            [],
631            [],
632            [],
633        )
634
635    def do_build(self, bld, bic):
636        bld(
637            cflags=bic.cflags,
638            cppflags=bic.cppflags,
639            features="c cstlib",
640            includes=bic.includes,
641            install_path="${BSP_LIBDIR}",
642            source=self.data["source"],
643            target="rtemsbsp",
644            use=bic.objects,
645        )
646        self.install_files(bld)
647
648
649class LibraryItem(Item):
650    def __init__(self, uid, data):
651        super(LibraryItem, self).__init__(uid, data)
652
653    def prepare_build(self, bld, bic):
654        return BuildItemContext(
655            bic.includes + self.substitute(bld, self.data["includes"]),
656            bic.cppflags + self.substitute(bld, self.data["cppflags"]),
657            bic.cflags + self.substitute(bld, self.data["cflags"]),
658            bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
659            bic.use,
660            bic.ldflags,
661            [],
662        )
663
664    def do_build(self, bld, bic):
665        bld(
666            cflags=bic.cflags,
667            cppflags=bic.cppflags,
668            cxxflags=bic.cxxflags,
669            features="c cxx cstlib",
670            includes=bic.includes,
671            install_path=self.data["install-path"],
672            source=self.data["source"],
673            target=self.get(bld, "target"),
674            use=bic.objects,
675        )
676        self.install_files(bld)
677
678
679class TestProgramItem(Item):
680    def __init__(self, uid, data):
681        super(TestProgramItem, self).__init__(uid, data)
682        name = uid.split("/")[-1].upper().replace("-", "_")
683        self.exclude = "TEST_" + name + "_EXCLUDE"
684        self.cppflags = "TEST_" + name + "_CPPFLAGS"
685
686    def get_enabled_by(self):
687        return [{"and": [{"not": self.exclude}, self.data["enabled-by"]]}]
688
689    def prepare_build(self, bld, bic):
690        return BuildItemContext(
691            bic.includes + self.substitute(bld, self.data["includes"]),
692            bic.cppflags
693            + bld.env[self.cppflags]
694            + self.substitute(bld, self.data["cppflags"]),
695            bic.cflags + self.substitute(bld, self.data["cflags"]),
696            bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
697            self.data["use-before"] + bic.use + self.data["use-after"],
698            bic.ldflags + self.substitute(bld, self.data["ldflags"]),
699            [],
700        )
701
702    def do_build(self, bld, bic):
703        bld(
704            cflags=bic.cflags,
705            cppflags=bic.cppflags,
706            cxxflags=bic.cxxflags,
707            features=self.data["features"],
708            includes=bic.includes,
709            install_path=None,
710            ldflags=bic.ldflags,
711            source=self.data["source"],
712            start_files=True,
713            stlib=self.data["stlib"],
714            target=self.get(bld, "target"),
715            use=bic.objects + bic.use,
716        )
717
718
719class AdaTestProgramItem(TestProgramItem):
720    def __init__(self, uid, data):
721        super(AdaTestProgramItem, self).__init__(uid, data)
722
723    def do_build(self, bld, bic):
724        objs = []
725        for s in self.data["source"]:
726            objs.append(self.cc(bld, bic, s))
727        self.gnatmake(
728            bld,
729            bic,
730            self.data["ada-object-directory"],
731            objs,
732            self.data["ada-main"],
733            self.data["target"],
734        )
735
736
737class OptionItem(Item):
738    def __init__(self, uid, data):
739        super(OptionItem, self).__init__(uid, data)
740
741    @staticmethod
742    def _is_variant(variants, variant):
743        for pattern in variants:
744            if re.match(pattern + "$", variant):
745                return True
746        return False
747
748    def default_value(self, variant, family):
749        value = self.data["default"]
750        for default in self.data["default-by-variant"]:
751            if OptionItem._is_variant(default["variants"], variant):
752                value = default["value"]
753                break
754        else:
755            family = "bsps/" + family
756            for default in self.data["default-by-variant"]:
757                if OptionItem._is_variant(default["variants"], family):
758                    value = default["value"]
759                    break
760        if value is None:
761            return value
762        if isinstance(value, list):
763            return " ".join(value)
764        if isinstance(value, bool):
765            return value
766        return self.data["format"].format(value)
767
768    def do_defaults(self, variant, family):
769        value = self.default_value(variant, family)
770        if value is None:
771            return
772        description = self.data["description"]
773        if description:
774            import textwrap
775
776            tw = textwrap.TextWrapper()
777            tw.drop_whitespace = True
778            tw.initial_indent = "# "
779            tw.subsequent_indent = "# "
780            for line in tw.wrap(description):
781                print(line)
782        print("{} = {}".format(self.data["name"], value))
783
784    def _do_append_test_cppflags(self, conf, name, state):
785        conf.env.append_value(
786            "TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state
787        )
788
789    def _append_test_cppflags(self, conf, cic, value, arg):
790        self._do_append_test_cppflags(conf, arg, value)
791        return value
792
793    def _assert_aligned(self, conf, cic, value, arg):
794        if value is not None and value % arg != 0:
795            conf.fatal(
796                "Value '{}' for option '{}' is not aligned by '{}'".format(
797                    value, self.data["name"], arg
798                )
799            )
800        return value
801
802    def _assert_eq(self, conf, cic, value, arg):
803        if value is not None and value != arg:
804            conf.fatal(
805                "Value '{}' for option '{}' is not equal to {}".format(
806                    value, self.data["name"], arg
807                )
808            )
809        return value
810
811    def _assert_ge(self, conf, cic, value, arg):
812        if value is not None and value < arg:
813            conf.fatal(
814                "Value '{}' for option '{}' is not greater than or equal to {}".format(
815                    value, self.data["name"], arg
816                )
817            )
818        return value
819
820    def _assert_gt(self, conf, cic, value, arg):
821        if value is not None and value <= arg:
822            conf.fatal(
823                "Value '{}' for option '{}' is not greater than {}".format(
824                    value, self.data["name"], arg
825                )
826            )
827        return value
828
829    def _assert_in_interval(self, conf, cic, value, arg):
830        if value is not None and (value < arg[0] or value > arg[1]):
831            conf.fatal(
832                "Value '{}' for option '{}' is not in closed interval [{}, {}]".format(
833                    value, self.data["name"], arg[0], arg[1]
834                )
835            )
836        return value
837
838    def _assert_int8(self, conf, cic, value, arg):
839        return self._assert_in_interval(conf, cic, value, [-128, 127])
840
841    def _assert_int16(self, conf, cic, value, arg):
842        return self._assert_in_interval(conf, cic, value, [-32768, 32767])
843
844    def _assert_int32(self, conf, cic, value, arg):
845        return self._assert_in_interval(
846            conf, cic, value, [-2147483648, 2147483647]
847        )
848
849    def _assert_int64(self, conf, cic, value, arg):
850        return self._assert_in_interval(
851            conf, cic, value, [-9223372036854775808, 9223372036854775807]
852        )
853
854    def _assert_le(self, conf, cic, value, arg):
855        if value is not None and value > arg:
856            conf.fatal(
857                "Value '{}' for option '{}' is not less than or equal to {}".format(
858                    value, self.data["name"], arg
859                )
860            )
861        return value
862
863    def _assert_lt(self, conf, cic, value, arg):
864        if value is not None and value >= arg:
865            conf.fatal(
866                "Value '{}' for option '{}' is not less than {}".format(
867                    value, self.data["name"], arg
868                )
869            )
870        return value
871
872    def _assert_ne(self, conf, cic, value, arg):
873        if value is not None and value == arg:
874            conf.fatal(
875                "Value '{}' for option '{}' is not unequal to {}".format(
876                    value, self.data["name"], arg
877                )
878            )
879        return value
880
881    def _assert_power_of_two(self, conf, cic, value, arg):
882        if value is not None and (value <= 0 or (value & (value - 1)) != 0):
883            conf.fatal(
884                "Value '{}' for option '{}' is not a power of two".format(
885                    value, self.data["name"]
886                )
887            )
888        return value
889
890    def _assert_uint8(self, conf, cic, value, arg):
891        return self._assert_in_interval(conf, cic, value, [0, 255])
892
893    def _assert_uint16(self, conf, cic, value, arg):
894        return self._assert_in_interval(conf, cic, value, [0, 65535])
895
896    def _assert_uint32(self, conf, cic, value, arg):
897        return self._assert_in_interval(conf, cic, value, [0, 4294967295])
898
899    def _assert_uint64(self, conf, cic, value, arg):
900        return self._assert_in_interval(
901            conf, cic, value, [0, 18446744073709551615]
902        )
903
904    def _check_cc(self, conf, cic, value, arg):
905        result = conf.check_cc(
906            fragment=arg["fragment"],
907            cflags=arg["cflags"],
908            msg="Checking for " + arg["message"],
909            mandatory=False,
910        )
911        return value and result
912
913    def _check_cxx(self, conf, cic, value, arg):
914        result = conf.check_cxx(
915            fragment=arg["fragment"],
916            cxxflags=arg["cxxflags"],
917            msg="Checking for " + arg["message"],
918            mandatory=False,
919        )
920        return value and result
921
922    def _define_condition(self, conf, cic, value, arg):
923        name = self.data["name"] if arg is None else arg
924        conf.define_cond(name, value)
925        return value
926
927    def _define(self, conf, cic, value, arg):
928        name = self.data["name"] if arg is None else arg
929        if value is not None:
930            conf.define(name, value)
931        else:
932            conf.define_cond(name, False)
933        return value
934
935    def _define_unquoted(self, conf, cic, value, arg):
936        name = self.data["name"] if arg is None else arg
937        if value is not None:
938            conf.define(name, value, quote=False)
939        else:
940            conf.define_cond(name, False)
941        return value
942
943    def _env_append(self, conf, cic, value, arg):
944        name = self.data["name"] if arg is None else arg
945        conf.env.append_value(name, value)
946        return value
947
948    def _env_assign(self, conf, cic, value, arg):
949        name = self.data["name"] if arg is None else arg
950        conf.env[name] = value
951        return value
952
953    def _env_enable(self, conf, cic, value, arg):
954        if value:
955            name = self.data["name"] if arg is None else arg
956            conf.env.append_value("ENABLE", name)
957        return value
958
959    def _find_program(self, conf, cic, value, arg):
960        return conf.find_program(value, path_list=cic.path_list)
961
962    def _format_and_define(self, conf, cic, value, arg):
963        name = self.data["name"] if arg is None else arg
964        if value is not None:
965            conf.define(name, self.data["format"].format(value), quote=False)
966        else:
967            conf.define_cond(name, False)
968        return value
969
970    def _get_boolean(self, conf, cic, value, arg):
971        name = self.data["name"]
972        try:
973            value = cic.cp.getboolean(conf.variant, name)
974            cic.add_option(name)
975        except configparser.NoOptionError:
976            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
977        except ValueError as ve:
978            conf.fatal(
979                "Invalid value for configuration option {}: {}".format(name, ve)
980            )
981        return value
982
983    def _get_env(self, conf, cic, value, arg):
984        return conf.env[arg]
985
986    def _get_integer(self, conf, cic, value, arg):
987        name = self.data["name"]
988        try:
989            value = cic.cp.get(conf.variant, name)
990            cic.add_option(name)
991        except configparser.NoOptionError:
992            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
993            if value is None:
994                return value
995        try:
996            return eval(value)
997        except Exception as e:
998            conf.fatal(
999                "Value '{}' for option '{}' is an invalid integer expression: {}".format(
1000                    value, name, e
1001                )
1002            )
1003
1004    def _get_string(self, conf, cic, value, arg):
1005        name = self.data["name"]
1006        try:
1007            value = cic.cp.get(conf.variant, name)
1008            cic.add_option(name)
1009            value = no_unicode(value)
1010        except configparser.NoOptionError:
1011            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
1012        return value
1013
1014    def _get_string_command_line(self, conf, cic, value, arg):
1015        name = self.data["name"]
1016        try:
1017            value = conf.rtems_options[name]
1018            del conf.rtems_options[name]
1019        except KeyError:
1020            value = arg[0]
1021        return value
1022
1023    def _script(self, conf, cic, value, arg):
1024        exec(arg)
1025        return value
1026
1027    def _test_state_benchmark(self, conf, name):
1028        self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
1029
1030    def _test_state_exclude(self, conf, name):
1031        conf.env.append_value(
1032            "ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE"
1033        )
1034
1035    def _test_state_expected_fail(self, conf, name):
1036        self._do_append_test_cppflags(
1037            conf, name, "-DTEST_STATE_EXPECTED_FAIL=1"
1038        )
1039
1040    def _test_state_indeterminate(self, conf, name):
1041        self._do_append_test_cppflags(
1042            conf, name, "-DTEST_STATE_INDETERMINATE=1"
1043        )
1044
1045    def _test_state_user_input(self, conf, name):
1046        self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
1047
1048    def _set_test_state(self, conf, cic, value, arg):
1049        actions = {
1050            "benchmark": self._test_state_benchmark,
1051            "exclude": self._test_state_exclude,
1052            "expected-fail": self._test_state_expected_fail,
1053            "indeterminate": self._test_state_indeterminate,
1054            "user-input": self._test_state_user_input,
1055        }
1056        for k, v in arg.items():
1057            actions[v](conf, k)
1058        return value
1059
1060    def _set_value(self, conf, cic, value, arg):
1061        return arg
1062
1063    def _split(self, conf, cic, value, arg):
1064        return value.split()
1065
1066    def _substitute(self, conf, cic, value, arg):
1067        if isinstance(value, list):
1068            return [self.substitute(conf, v) for v in value]
1069        else:
1070            return self.substitute(conf, value)
1071
1072    def do_configure(self, conf, cic):
1073        actions = {
1074            "append-test-cppflags": self._append_test_cppflags,
1075            "assert-aligned": self._assert_aligned,
1076            "assert-eq": self._assert_eq,
1077            "assert-ge": self._assert_ge,
1078            "assert-gt": self._assert_gt,
1079            "assert-int8": self._assert_int8,
1080            "assert-int16": self._assert_int16,
1081            "assert-int32": self._assert_int32,
1082            "assert-int64": self._assert_int64,
1083            "assert-le": self._assert_le,
1084            "assert-lt": self._assert_lt,
1085            "assert-ne": self._assert_ne,
1086            "assert-power-of-two": self._assert_power_of_two,
1087            "assert-uint8": self._assert_uint8,
1088            "assert-uint16": self._assert_uint16,
1089            "assert-uint32": self._assert_uint32,
1090            "assert-uint64": self._assert_uint64,
1091            "check-cc": self._check_cc,
1092            "check-cxx": self._check_cxx,
1093            "define-condition": self._define_condition,
1094            "define": self._define,
1095            "define-unquoted": self._define_unquoted,
1096            "env-append": self._env_append,
1097            "env-assign": self._env_assign,
1098            "env-enable": self._env_enable,
1099            "find-program": self._find_program,
1100            "format-and-define": self._format_and_define,
1101            "get-boolean": self._get_boolean,
1102            "get-env": self._get_env,
1103            "get-integer": self._get_integer,
1104            "get-string": self._get_string,
1105            "get-string-command-line": self._get_string_command_line,
1106            "script": self._script,
1107            "set-test-state": self._set_test_state,
1108            "set-value": self._set_value,
1109            "split": self._split,
1110            "substitute": self._substitute,
1111        }
1112        value = None
1113        for action in self.data["actions"]:
1114            for action_arg in action.items():
1115                value = actions[action_arg[0]](conf, cic, value, action_arg[1])
1116
1117
1118class ScriptItem(Item):
1119    def __init__(self, uid, data):
1120        super(ScriptItem, self).__init__(uid, data)
1121
1122    def prepare_configure(self, conf, cic):
1123        script = self.data["prepare-configure"]
1124        if script:
1125            exec(script)
1126
1127    def do_configure(self, conf, cic):
1128        script = self.data["do-configure"]
1129        if script:
1130            exec(script)
1131
1132    def prepare_build(self, bld, bic):
1133        script = self.data["prepare-build"]
1134        if script:
1135            exec(script)
1136        return bic
1137
1138    def do_build(self, bld, bic):
1139        script = self.data["do-build"]
1140        if script:
1141            exec(script)
1142
1143
1144class ConfigItemContext(object):
1145    def __init__(self, cp, path_list):
1146        self.cp = cp
1147        self.options = set()
1148        self.path_list = path_list
1149
1150    def add_option(self, name):
1151        self.options.add(name.upper())
1152
1153
1154class BuildItemContext(object):
1155    def __init__(
1156        self, includes, cppflags, cflags, cxxflags, use, ldflags, objects
1157    ):
1158        self.includes = includes
1159        self.cppflags = cppflags
1160        self.cflags = cflags
1161        self.cxxflags = cxxflags
1162        self.use = use
1163        self.ldflags = ldflags
1164        self.objects = objects
1165
1166
1167def is_one_item_newer(ctx, path, mtime):
1168    try:
1169        mtime2 = os.path.getmtime(path)
1170        if mtime <= mtime2:
1171            return True
1172        names = os.listdir(path)
1173    except Exception as e:
1174        ctx.fatal("Cannot access build specification directory: {}".format(e))
1175    for name in names:
1176        path2 = os.path.join(path, name)
1177        if name.endswith(".yml") and not name.startswith("."):
1178            mtime2 = os.path.getmtime(path2)
1179            if mtime <= mtime2:
1180                return True
1181        else:
1182            mode = os.lstat(path2).st_mode
1183            if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
1184                return True
1185    return False
1186
1187
1188def must_update_item_cache(ctx, path, cache_file):
1189    try:
1190        mtime = os.path.getmtime(cache_file)
1191    except:
1192        return True
1193    return is_one_item_newer(ctx, path, mtime)
1194
1195
1196def load_from_yaml(load, ctx, data_by_uid, base, path):
1197    try:
1198        names = os.listdir(path)
1199    except Exception as e:
1200        ctx.fatal("Cannot list build specification directory: {}".format(e))
1201    for name in names:
1202        path2 = os.path.join(path, name)
1203        if name.endswith(".yml") and not name.startswith("."):
1204            uid = "/" + os.path.relpath(path2, base).replace(".yml", "")
1205            with open(path2, "r") as f:
1206                data_by_uid[uid] = load(f.read())
1207        else:
1208            mode = os.lstat(path2).st_mode
1209            if stat.S_ISDIR(mode):
1210                load_from_yaml(load, ctx, data_by_uid, base, path2)
1211
1212
1213def load_items_in_directory(ctx, ctors, path):
1214    p = "c4che/" + re.sub(r"[^\w]", "_", path) + ".pickle"
1215    try:
1216        f = ctx.bldnode.make_node(p)
1217    except AttributeError:
1218        f = ctx.path.make_node("build/" + p)
1219    f.parent.mkdir()
1220    cache_file = f.abspath()
1221    data_by_uid = {}
1222    if must_update_item_cache(ctx, path, cache_file):
1223        from waflib import Logs
1224
1225        Logs.warn(
1226            "Regenerate build specification cache (needs a couple of seconds)..."
1227        )
1228
1229        #
1230        # Do not use a system provided yaml module and instead import it from
1231        # the project.  This reduces the host system requirements to a simple
1232        # Python 2.7 or 3 installation without extra modules.
1233        #
1234        if sys.version_info[0] == 2:
1235            yaml_path = "yaml/lib"
1236        else:
1237            yaml_path = "yaml/lib3"
1238        sys.path += [yaml_path]
1239        from yaml import safe_load
1240
1241        load_from_yaml(safe_load, ctx, data_by_uid, path, path)
1242        with open(cache_file, "wb") as f:
1243            pickle.dump(data_by_uid, f)
1244    else:
1245        with open(cache_file, "rb") as f:
1246            data_by_uid = pickle.load(f)
1247    for uid, data in data_by_uid.items():
1248        if data["type"] == "build":
1249            items[uid] = ctors[data["build-type"]](uid, data)
1250
1251
1252def load_items(ctx, specs):
1253    if items:
1254        return
1255
1256    ctors = {
1257        "ada-test-program": AdaTestProgramItem,
1258        "bsp": BSPItem,
1259        "config-file": ConfigFileItem,
1260        "config-header": ConfigHeaderItem,
1261        "test-program": TestProgramItem,
1262        "group": GroupItem,
1263        "library": LibraryItem,
1264        "objects": ObjectsItem,
1265        "option": OptionItem,
1266        "script": ScriptItem,
1267        "start-file": StartFileItem,
1268    }
1269
1270    for path in specs:
1271        load_items_in_directory(ctx, ctors, path)
1272
1273
1274def load_items_from_options(ctx):
1275    specs = ctx.options.rtems_specs
1276    if specs is not None:
1277        specs = specs.split(",")
1278    else:
1279        specs = ["spec/build"]
1280    load_items(ctx, specs)
1281    return specs
1282
1283
1284def options(ctx):
1285    prefix = ctx.parser.get_option("--prefix")
1286    prefix.default = default_prefix
1287    prefix.help = "installation prefix [default: '{}']".format(default_prefix)
1288    rg = ctx.add_option_group("RTEMS options")
1289    rg.add_option(
1290        "--rtems-bsps",
1291        metavar="REGEX,...",
1292        help="a comma-separated list of Python regular expressions which select the desired BSP variants (e.g. 'sparc/erc32'); it may be used in the bsp_defaults and bsp_list commands",
1293    )
1294    rg.add_option(
1295        "--rtems-compiler",
1296        metavar="COMPILER",
1297        help="determines which compiler is used to list the BSP option defaults [default: 'gcc']; it may be used in the bsp_defaults command; valid compilers are: {}".format(
1298            ", ".join(compilers)
1299        ),
1300    )
1301    rg.add_option(
1302        "--rtems-config",
1303        metavar="CONFIG.INI,...",
1304        help="a comma-separated list of paths to the BSP configuration option files [default: 'config.ini']; default option values can be obtained via the bsp_defaults command; it may be used in the configure command",
1305    )
1306    rg.add_option(
1307        "--rtems-specs",
1308        metavar="SPECDIRS,...",
1309        help="a comma-separated list of directory paths to build specification items [default: 'spec/build']; it may be used in the bsp_defaults, bsp_list, and configure commands",
1310    )
1311    rg.add_option(
1312        "--rtems-tools",
1313        metavar="PREFIX,...",
1314        help="a comma-separated list of prefix paths to tools, e.g. compiler, linker, etc. [default: the installation prefix]; tools are searched in the prefix path and also in a 'bin' subdirectory of the prefix path; it may be used in the configure command",
1315    )
1316    rg.add_option(
1317        "--rtems-top-group",
1318        metavar="UID",
1319        help="the UID of the top-level group [default: '/grp']; it may be used in the bsp_defaults and configure commands",
1320    )
1321    rg.add_option(
1322        "--rtems-version",
1323        metavar="VALUE",
1324        help="sets the RTEMS major version number; it is intended for RTEMS maintainers and may be used in the bsp_defaults and configure commands",
1325    )
1326    rg.add_option(
1327        "--rtems-option",
1328        metavar="KEY=VALUE",
1329        action="append",
1330        dest="rtems_options",
1331        default=[],
1332        help="sets the option identified by KEY to the VALUE in the build specification; it is intended for RTEMS maintainers and may be used in the bsp_defaults and configure commands",
1333    )
1334
1335
1336def check_environment(conf):
1337    for ev in [
1338        "AR",
1339        "AS",
1340        "ASFLAGS",
1341        "CC",
1342        "CFLAGS",
1343        "CPPFLAGS",
1344        "CXX",
1345        "CXXFLAGS",
1346        "IFLAGS",
1347        "LD",
1348        "LIB",
1349        "LINK_CC",
1350        "LINK_CXX",
1351        "LINKFLAGS",
1352        "MFLAGS",
1353        "RFLAGS",
1354        "WFLAGS",
1355    ]:
1356        if ev in os.environ:
1357            conf.msg("Environment variable set", ev, color="RED")
1358
1359
1360def load_config_files(ctx):
1361    cp = configparser.ConfigParser()
1362    files = ctx.options.rtems_config
1363    if files is not None:
1364        files = files.split(",")
1365    else:
1366        files = ["config.ini"]
1367    actual_files = cp.read(files)
1368    for o in files:
1369        if not o in actual_files:
1370            ctx.fatal("Option file '{}' was not readable".format(o))
1371    return cp
1372
1373
1374def inherit(conf, cp, bsp_map, arch, bsp, path):
1375    variant = arch + "/" + bsp
1376    if variant in path:
1377        path = " -> ".join(path + [variant])
1378        conf.fatal("Recursion in BSP options inheritance: {}".format(path))
1379
1380    try:
1381        base = cp.get(variant, "INHERIT")
1382        cp.remove_option(variant, "INHERIT")
1383        base = no_unicode(base)
1384        base_variant = arch + "/" + base
1385        conf.msg(
1386            "Inherit options from '{}'".format(base_variant),
1387            variant,
1388            color="YELLOW",
1389        )
1390        if not cp.has_section(base_variant):
1391            if (not arch in bsps) or (not base in bsps[arch]):
1392                conf.fatal(
1393                    "BSP variant '{}' cannot inherit options from not existing variant '{}'".format(
1394                        variant, base_variant
1395                    )
1396                )
1397            bsp_map[bsp] = base
1398            return base
1399        top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
1400        for i in cp.items(base_variant):
1401            name = i[0]
1402            if not cp.has_option(variant, name):
1403                cp.set(variant, name, i[1])
1404        bsp_map[bsp] = top
1405        return top
1406    except configparser.NoOptionError:
1407        return bsp_map.get(bsp, bsp)
1408
1409
1410def resolve_option_inheritance(conf, cp):
1411    bsp_map = {}
1412    for variant in cp.sections():
1413        variant = no_unicode(variant)
1414        try:
1415            arch, bsp = variant.split("/")
1416        except:
1417            conf.fatal(
1418                "Section name '{}' is a malformed 'arch/bsp' tuple".format(
1419                    variant
1420                )
1421            )
1422        inherit(conf, cp, bsp_map, arch, bsp, [])
1423    return bsp_map
1424
1425
1426def check_compiler(ctx, compiler):
1427    if compiler not in compilers:
1428        ctx.fatal(
1429            "Specified compiler '{}' is not one of {}".format(
1430                compiler, compilers
1431            )
1432        )
1433
1434
1435def get_compiler(conf, cp, variant):
1436    try:
1437        value = cp.get(variant, "COMPILER")
1438        cp.remove_option(variant, "COMPILER")
1439        value = no_unicode(value)
1440        check_compiler(conf, value)
1441    except configparser.NoOptionError:
1442        value = "gcc"
1443    return value
1444
1445
1446def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
1447    conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
1448
1449    conf.setenv(variant)
1450    arch, bsp_name = variant.split("/")
1451    bsp_base = bsp_map.get(bsp_name, bsp_name)
1452
1453    try:
1454        bsp_item = bsps[arch][bsp_base]
1455    except KeyError:
1456        conf.fatal("No such base BSP: '{}'".format(variant))
1457
1458    family = bsp_item.data["family"]
1459
1460    arch_bsp = arch + "/" + bsp_base
1461    arch_family = arch + "/" + family
1462
1463    conf.env["ARCH"] = arch
1464    conf.env["ARCH_BSP"] = arch_bsp
1465    conf.env["ARCH_FAMILY"] = arch_family
1466    conf.env["BSP_BASE"] = bsp_base
1467    conf.env["BSP_NAME"] = bsp_name
1468    conf.env["BSP_FAMILY"] = family
1469    conf.env["DEST_OS"] = "rtems"
1470
1471    # For the enabled-by evaluation we have to use the base BSP defined by the
1472    # build specification and not the BSP name provided by the user.
1473    conf.env["ENABLE"] = [
1474        get_compiler(conf, cp, variant),
1475        arch,
1476        "bsps/" + arch_family,
1477        arch_bsp,
1478    ]
1479
1480    conf.env["TOP"] = conf.path.abspath()
1481    conf.env["TOPGROUP"] = top_group
1482    conf.env["VARIANT"] = variant
1483
1484    prepare_rtems_options(conf)
1485    cic = ConfigItemContext(cp, path_list)
1486    items[conf.env.TOPGROUP].configure(conf, cic)
1487    bsp_item.configure(conf, cic)
1488
1489    options = set([o[0].upper() for o in cp.items(variant)])
1490    for o in options.difference(cic.options):
1491        conf.msg("Unknown configuration option", o.upper(), color="RED")
1492    for key in conf.rtems_options:
1493        conf.msg("Unknown command line RTEMS option", key, color="RED")
1494
1495
1496def check_forbidden_options(ctx, opts):
1497    for o in opts:
1498        if getattr(ctx.options, "rtems_" + o):
1499            ctx.fatal(
1500                "The --rtems-{} command line option is not allowed in the {} command".format(
1501                    o.replace("_", "-"), ctx.cmd
1502                )
1503            )
1504
1505
1506def get_path_list(conf):
1507    path_list = []
1508    tools = conf.options.rtems_tools
1509    if tools is not None:
1510        for t in tools.split(","):
1511            path_list.extend([t + "/bin", t])
1512    path_list.append(conf.env.PREFIX + "/bin")
1513    path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
1514    return path_list
1515
1516
1517def get_top_group(ctx):
1518    top_group = ctx.options.rtems_top_group
1519    if top_group is None:
1520        top_group = "/grp"
1521    if top_group not in items:
1522        ctx.fatal(
1523            "There is no top-level group with UID '{}' in the specification".format(
1524                top_group
1525            )
1526        )
1527    return top_group
1528
1529
1530def prepare_rtems_options(conf):
1531    conf.rtems_options = {}
1532    for x in conf.options.rtems_options:
1533        try:
1534            k, v = x.split("=", 1)
1535            conf.rtems_options[k] = v
1536        except:
1537            conf.fatal(
1538                "The RTEMS option '{}' is not in KEY=VALUE format".format(x)
1539            )
1540    version = conf.options.rtems_version
1541    if version is not None:
1542        key = "__RTEMS_MAJOR__"
1543        if conf.rtems_options.get(key, version) != version:
1544            conf.fatal(
1545                "Conflicting RTEMS major versions specified at the command line"
1546            )
1547        conf.rtems_options[key] = version
1548
1549
1550def configure(conf):
1551    check_forbidden_options(conf, ["compiler"])
1552    check_environment(conf)
1553    conf.env["SPECS"] = load_items_from_options(conf)
1554    top_group = get_top_group(conf)
1555    cp = load_config_files(conf)
1556    bsp_map = resolve_option_inheritance(conf, cp)
1557    path_list = get_path_list(conf)
1558    variant_list = []
1559    for variant in cp.sections():
1560        variant = no_unicode(variant)
1561        variant_list.append(variant)
1562        configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
1563    conf.setenv("")
1564    conf.env["VARIANTS"] = variant_list
1565
1566
1567def append_variant_builds(bld):
1568    import waflib.Options
1569    from waflib.Build import (
1570        BuildContext,
1571        CleanContext,
1572        InstallContext,
1573        UninstallContext,
1574    )
1575
1576    for var in bld.env["VARIANTS"]:
1577        for c in (BuildContext, CleanContext, InstallContext, UninstallContext):
1578            name = c.__name__.replace("Context", "").lower()
1579
1580            class magic(c):
1581                cmd = name + "_" + var
1582                variant = var
1583
1584        waflib.Options.commands.append(bld.cmd + "_" + var)
1585
1586
1587def long_command_line_workaround(bld):
1588    if is_windows_host:
1589        bld.load("long_gcc")
1590
1591
1592def build(bld):
1593    if not bld.variant:
1594        check_forbidden_options(
1595            bld,
1596            [
1597                "compiler",
1598                "config",
1599                "options",
1600                "specs",
1601                "tools",
1602                "top_group",
1603                "version",
1604            ],
1605        )
1606        load_items(bld, bld.env.SPECS)
1607        append_variant_builds(bld)
1608        return
1609    long_command_line_workaround(bld)
1610    bic = BuildItemContext(
1611        bld.env.ARCH_INCLUDES.split(), [], [], [], [], [], []
1612    )
1613    bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
1614    items[bld.env.TOPGROUP].build(bld, bic)
1615
1616
1617def add_log_filter(name):
1618    msg = "'" + name + "' finished successfully"
1619
1620    class Filter:
1621        def filter(self, rec):
1622            return not msg in rec.getMessage()
1623
1624    import logging
1625
1626    logging.getLogger("waflib").addFilter(Filter())
1627
1628
1629def get_white_list(ctx):
1630    white_list = ctx.options.rtems_bsps
1631    if white_list:
1632        white_list = white_list.split(",")
1633    return white_list
1634
1635
1636def is_in_white_list(variant, white_list):
1637    if not white_list:
1638        return True
1639    for pattern in white_list:
1640        if re.match(pattern + "$", variant):
1641            return True
1642    return False
1643
1644
1645def no_matches_error(ctx, white_list):
1646    if white_list:
1647        ctx.fatal(
1648            "No BSP matches with the specified patterns: '{}'".format(
1649                "', '".join(white_list)
1650            )
1651        )
1652    else:
1653        ctx.fatal("The build specification contains no BSPs")
1654
1655
1656def bsp_defaults(ctx):
1657    """get all options with default values for base BSP variants"""
1658    check_forbidden_options(ctx, ["config", "tools"])
1659    add_log_filter(ctx.cmd)
1660    load_items_from_options(ctx)
1661    top_group = get_top_group(ctx)
1662    white_list = get_white_list(ctx)
1663    compiler = ctx.options.rtems_compiler
1664    if compiler is not None:
1665        check_compiler(ctx, compiler)
1666    else:
1667        compiler = "gcc"
1668    first = True
1669    for arch in sorted(bsps):
1670        for bsp in sorted(bsps[arch]):
1671            variant = arch + "/" + bsp
1672            if is_in_white_list(variant, white_list):
1673                if not first:
1674                    print("")
1675                first = False
1676                print(
1677                    """[{}]
1678# Selects the compiler used to build the BSP (allowed values are "gcc" and
1679# "clang").  Please note that the values of some options depend on the compiler
1680# selection and changing the compiler may lead to unpredictable behaviour if
1681# these options are not adjusted as well.  Use the --rtems-compiler command line
1682# option to get the default values for a particular compiler via
1683# ./waf bsp_defaults.
1684COMPILER = {}""".format(
1685                        variant, compiler
1686                    )
1687                )
1688                enable = [compiler, arch, variant]
1689                bsp_item = bsps[arch][bsp]
1690                family = arch + "/" + bsp_item.data["family"]
1691                items[top_group].defaults(enable, variant, family)
1692                bsp_item.defaults(enable, variant, family)
1693    if first:
1694        no_matches_error(ctx, white_list)
1695
1696
1697def bsp_list(ctx):
1698    """lists base BSP variants"""
1699    check_forbidden_options(
1700        ctx, ["compiler", "config", "options", "tools", "top_group", "version"]
1701    )
1702    add_log_filter(ctx.cmd)
1703    load_items_from_options(ctx)
1704    white_list = get_white_list(ctx)
1705    first = True
1706    for arch in sorted(bsps):
1707        for bsp in sorted(bsps[arch]):
1708            variant = arch + "/" + bsp
1709            if is_in_white_list(variant, white_list):
1710                first = False
1711                print(variant)
1712    if first:
1713        no_matches_error(ctx, white_list)
Note: See TracBrowser for help on using the repository browser.