source: rtems/wscript @ e3f70b3

Last change on this file since e3f70b3 was c1395ca0, checked in by Sebastian Huber <sebastian.huber@…>, on 11/16/21 at 06:44:04

build: Add --rtems-version command line option

Update #4548.

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