source: rtems/wscript @ 2802896

Last change on this file since 2802896 was cc0bd08, checked in by Sebastian Huber <sebastian.huber@…>, on 07/25/21 at 15:08:04

build: Bring Item.link() in line with waf link

The LDFLAGS are placed after the static libraries in the standard waf link
command, see "waflib/Tools/c.py" in the waf sources.

  • Property mode set to 100755
File size: 50.6 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        for default in self.data["default-by-family"]:
702            if OptionItem._is_variant(default["families"], family):
703                value = default["value"]
704                break
705        if value is None:
706            return value
707        if isinstance(value, list):
708            return " ".join(value)
709        if isinstance(value, bool):
710            return value
711        return self.data["format"].format(value)
712
713    def do_defaults(self, variant, family):
714        value = self.default_value(variant, family)
715        if value is None:
716            return
717        description = self.data["description"]
718        if description:
719            import textwrap
720
721            tw = textwrap.TextWrapper()
722            tw.drop_whitespace = True
723            tw.initial_indent = "# "
724            tw.subsequent_indent = "# "
725            for line in tw.wrap(description):
726                print(line)
727        print("{} = {}".format(self.data["name"], value))
728
729    def _do_append_test_cppflags(self, conf, name, state):
730        conf.env.append_value(
731            "TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state
732        )
733
734    def _append_test_cppflags(self, conf, cic, value, arg):
735        self._do_append_test_cppflags(conf, arg, value)
736        return value
737
738    def _assert_aligned(self, conf, cic, value, arg):
739        if value % arg != 0:
740            conf.fatal(
741                "Value '{}' for option '{}' is not aligned by '{}'".format(
742                    value, self.data["name"], arg
743                )
744            )
745        return value
746
747    def _assert_eq(self, conf, cic, value, arg):
748        if value != arg:
749            conf.fatal(
750                "Value '{}' for option '{}' is not equal to {}".format(
751                    value, self.data["name"], arg
752                )
753            )
754        return value
755
756    def _assert_ge(self, conf, cic, value, arg):
757        if value < arg:
758            conf.fatal(
759                "Value '{}' for option '{}' is not greater than or equal to {}".format(
760                    value, self.data["name"], arg
761                )
762            )
763        return value
764
765    def _assert_gt(self, conf, cic, value, arg):
766        if value <= arg:
767            conf.fatal(
768                "Value '{}' for option '{}' is not greater than {}".format(
769                    value, self.data["name"], arg
770                )
771            )
772        return value
773
774    def _assert_in_interval(self, conf, cic, value, arg):
775        if value < arg[0] or value > arg[1]:
776            conf.fatal(
777                "Value '{}' for option '{}' is not in closed interval [{}, {}]".format(
778                    value, self.data["name"], arg[0], arg[1]
779                )
780            )
781        return value
782
783    def _assert_int8(self, conf, cic, value, arg):
784        return self._assert_in_interval(conf, cic, value, [-128, 127])
785
786    def _assert_int16(self, conf, cic, value, arg):
787        return self._assert_in_interval(conf, cic, value, [-32768, 32767])
788
789    def _assert_int32(self, conf, cic, value, arg):
790        return self._assert_in_interval(
791            conf, cic, value, [-2147483648, 2147483647]
792        )
793
794    def _assert_int64(self, conf, cic, value, arg):
795        return self._assert_in_interval(
796            conf, cic, value, [-9223372036854775808, 9223372036854775807]
797        )
798
799    def _assert_le(self, conf, cic, value, arg):
800        if value > arg:
801            conf.fatal(
802                "Value '{}' for option '{}' is not less than or equal to {}".format(
803                    value, self.data["name"], arg
804                )
805            )
806        return value
807
808    def _assert_lt(self, conf, cic, value, arg):
809        if value >= arg:
810            conf.fatal(
811                "Value '{}' for option '{}' is not less than {}".format(
812                    value, self.data["name"], arg
813                )
814            )
815        return value
816
817    def _assert_ne(self, conf, cic, value, arg):
818        if value == arg:
819            conf.fatal(
820                "Value '{}' for option '{}' is not unequal to {}".format(
821                    value, self.data["name"], arg
822                )
823            )
824        return value
825
826    def _assert_power_of_two(self, conf, cic, value, arg):
827        if value <= 0 or (value & (value - 1)) != 0:
828            conf.fatal(
829                "Value '{}' for option '{}' is not a power of two".format(
830                    value, self.data["name"]
831                )
832            )
833        return value
834
835    def _assert_uint8(self, conf, cic, value, arg):
836        return self._assert_in_interval(conf, cic, value, [0, 255])
837
838    def _assert_uint16(self, conf, cic, value, arg):
839        return self._assert_in_interval(conf, cic, value, [0, 65535])
840
841    def _assert_uint32(self, conf, cic, value, arg):
842        return self._assert_in_interval(conf, cic, value, [0, 4294967295])
843
844    def _assert_uint64(self, conf, cic, value, arg):
845        return self._assert_in_interval(
846            conf, cic, value, [0, 18446744073709551615]
847        )
848
849    def _check_cc(self, conf, cic, value, arg):
850        result = conf.check_cc(
851            fragment=arg["fragment"],
852            cflags=arg["cflags"],
853            msg="Checking for " + arg["message"],
854            mandatory=False,
855        )
856        return value and result
857
858    def _check_cxx(self, conf, cic, value, arg):
859        result = conf.check_cxx(
860            fragment=arg["fragment"],
861            cxxflags=arg["cxxflags"],
862            msg="Checking for " + arg["message"],
863            mandatory=False,
864        )
865        return value and result
866
867    def _define_condition(self, conf, cic, value, arg):
868        name = self.data["name"] if arg is None else arg
869        conf.define_cond(name, value)
870        return value
871
872    def _define(self, conf, cic, value, arg):
873        name = self.data["name"] if arg is None else arg
874        if value is not None:
875            conf.define(name, value)
876        else:
877            conf.define_cond(name, False)
878        return value
879
880    def _define_unquoted(self, conf, cic, value, arg):
881        name = self.data["name"] if arg is None else arg
882        if value is not None:
883            conf.define(name, value, quote=False)
884        else:
885            conf.define_cond(name, False)
886        return value
887
888    def _env_append(self, conf, cic, value, arg):
889        name = self.data["name"] if arg is None else arg
890        conf.env.append_value(name, value)
891        return value
892
893    def _env_assign(self, conf, cic, value, arg):
894        name = self.data["name"] if arg is None else arg
895        conf.env[name] = value
896        return value
897
898    def _env_enable(self, conf, cic, value, arg):
899        if value:
900            name = self.data["name"] if arg is None else arg
901            conf.env.append_value("ENABLE", name)
902        return value
903
904    def _find_program(self, conf, cic, value, arg):
905        return conf.find_program(value, path_list=cic.path_list)
906
907    def _format_and_define(self, conf, cic, value, arg):
908        name = self.data["name"] if arg is None else arg
909        if value is not None:
910            conf.define(name, self.data["format"].format(value), quote=False)
911        else:
912            conf.define_cond(name, False)
913        return value
914
915    def _get_boolean(self, conf, cic, value, arg):
916        name = self.data["name"]
917        try:
918            value = cic.cp.getboolean(conf.variant, name)
919            cic.add_option(name)
920        except configparser.NoOptionError:
921            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
922        except ValueError as ve:
923            conf.fatal(
924                "Invalid value for configuration option {}: {}".format(name, ve)
925            )
926        return value
927
928    def _get_env(self, conf, cic, value, arg):
929        return conf.env[arg]
930
931    def _get_integer(self, conf, cic, value, arg):
932        name = self.data["name"]
933        try:
934            value = cic.cp.get(conf.variant, name)
935            cic.add_option(name)
936        except configparser.NoOptionError:
937            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
938            if value is None:
939                return value
940        try:
941            return eval(value)
942        except Exception as e:
943            conf.fatal(
944                "Value '{}' for option '{}' is an invalid integer expression: {}".format(
945                    value, name, e
946                )
947            )
948
949    def _get_string(self, conf, cic, value, arg):
950        name = self.data["name"]
951        try:
952            value = cic.cp.get(conf.variant, name)
953            cic.add_option(name)
954            value = no_unicode(value)
955        except configparser.NoOptionError:
956            value = self.default_value(conf.env.ARCH_BSP, conf.env.ARCH_FAMILY)
957        return value
958
959    def _script(self, conf, cic, value, arg):
960        exec(arg)
961        return value
962
963    def _test_state_benchmark(self, conf, name):
964        self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
965
966    def _test_state_exclude(self, conf, name):
967        conf.env.append_value(
968            "ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE"
969        )
970
971    def _test_state_expected_fail(self, conf, name):
972        self._do_append_test_cppflags(
973            conf, name, "-DTEST_STATE_EXPECTED_FAIL=1"
974        )
975
976    def _test_state_indeterminate(self, conf, name):
977        self._do_append_test_cppflags(
978            conf, name, "-DTEST_STATE_INDETERMINATE=1"
979        )
980
981    def _test_state_user_input(self, conf, name):
982        self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
983
984    def _set_test_state(self, conf, cic, value, arg):
985        actions = {
986            "benchmark": self._test_state_benchmark,
987            "exclude": self._test_state_exclude,
988            "expected-fail": self._test_state_expected_fail,
989            "indeterminate": self._test_state_indeterminate,
990            "user-input": self._test_state_user_input,
991        }
992        for k, v in arg.items():
993            actions[v](conf, k)
994        return value
995
996    def _set_value(self, conf, cic, value, arg):
997        return arg
998
999    def _split(self, conf, cic, value, arg):
1000        return value.split()
1001
1002    def _substitute(self, conf, cic, value, arg):
1003        if isinstance(value, list):
1004            return [self.substitute(conf, v) for v in value]
1005        else:
1006            return self.substitute(conf, value)
1007
1008    def do_configure(self, conf, cic):
1009        actions = {
1010            "append-test-cppflags": self._append_test_cppflags,
1011            "assert-aligned": self._assert_aligned,
1012            "assert-eq": self._assert_eq,
1013            "assert-ge": self._assert_ge,
1014            "assert-gt": self._assert_gt,
1015            "assert-int8": self._assert_int8,
1016            "assert-int16": self._assert_int16,
1017            "assert-int32": self._assert_int32,
1018            "assert-int64": self._assert_int64,
1019            "assert-le": self._assert_le,
1020            "assert-lt": self._assert_lt,
1021            "assert-ne": self._assert_ne,
1022            "assert-power-of-two": self._assert_power_of_two,
1023            "assert-uint8": self._assert_uint8,
1024            "assert-uint16": self._assert_uint16,
1025            "assert-uint32": self._assert_uint32,
1026            "assert-uint64": self._assert_uint64,
1027            "check-cc": self._check_cc,
1028            "check-cxx": self._check_cxx,
1029            "define-condition": self._define_condition,
1030            "define": self._define,
1031            "define-unquoted": self._define_unquoted,
1032            "env-append": self._env_append,
1033            "env-assign": self._env_assign,
1034            "env-enable": self._env_enable,
1035            "find-program": self._find_program,
1036            "format-and-define": self._format_and_define,
1037            "get-boolean": self._get_boolean,
1038            "get-env": self._get_env,
1039            "get-integer": self._get_integer,
1040            "get-string": self._get_string,
1041            "script": self._script,
1042            "set-test-state": self._set_test_state,
1043            "set-value": self._set_value,
1044            "split": self._split,
1045            "substitute": self._substitute,
1046        }
1047        value = None
1048        for action in self.data["actions"]:
1049            for action_arg in action.items():
1050                value = actions[action_arg[0]](conf, cic, value, action_arg[1])
1051
1052
1053class ScriptItem(Item):
1054    def __init__(self, uid, data):
1055        super(ScriptItem, self).__init__(uid, data)
1056
1057    def prepare_configure(self, conf, cic):
1058        script = self.data["prepare-configure"]
1059        if script:
1060            exec(script)
1061
1062    def do_configure(self, conf, cic):
1063        script = self.data["do-configure"]
1064        if script:
1065            exec(script)
1066
1067    def prepare_build(self, bld, bic):
1068        script = self.data["prepare-build"]
1069        if script:
1070            exec(script)
1071        return bic
1072
1073    def do_build(self, bld, bic):
1074        script = self.data["do-build"]
1075        if script:
1076            exec(script)
1077
1078
1079class ConfigItemContext(object):
1080    def __init__(self, cp, path_list):
1081        self.cp = cp
1082        self.options = set()
1083        self.path_list = path_list
1084
1085    def add_option(self, name):
1086        self.options.add(name.upper())
1087
1088
1089class BuildItemContext(object):
1090    def __init__(self, includes, use, ldflags, objects):
1091        self.includes = includes
1092        self.use = use
1093        self.ldflags = ldflags
1094        self.objects = objects
1095
1096
1097def is_one_item_newer(ctx, path, mtime):
1098    try:
1099        mtime2 = os.path.getmtime(path)
1100        if mtime <= mtime2:
1101            return True
1102        names = os.listdir(path)
1103    except Exception as e:
1104        ctx.fatal("Cannot access build specification directory: {}".format(e))
1105    for name in names:
1106        path2 = os.path.join(path, name)
1107        if name.endswith(".yml") and not name.startswith("."):
1108            mtime2 = os.path.getmtime(path2)
1109            if mtime <= mtime2:
1110                return True
1111        else:
1112            mode = os.lstat(path2).st_mode
1113            if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
1114                return True
1115    return False
1116
1117
1118def must_update_item_cache(ctx, path, cache_file):
1119    try:
1120        mtime = os.path.getmtime(cache_file)
1121    except:
1122        return True
1123    return is_one_item_newer(ctx, path, mtime)
1124
1125
1126def load_from_yaml(load, ctx, data_by_uid, base, path):
1127    try:
1128        names = os.listdir(path)
1129    except Exception as e:
1130        ctx.fatal("Cannot list build specification directory: {}".format(e))
1131    for name in names:
1132        path2 = os.path.join(path, name)
1133        if name.endswith(".yml") and not name.startswith("."):
1134            uid = "/" + os.path.relpath(path2, base).replace(".yml", "")
1135            with open(path2, "r") as f:
1136                data_by_uid[uid] = load(f.read())
1137        else:
1138            mode = os.lstat(path2).st_mode
1139            if stat.S_ISDIR(mode):
1140                load_from_yaml(load, ctx, data_by_uid, base, path2)
1141
1142
1143def load_items_in_directory(ctx, ctors, path):
1144    p = "c4che/" + re.sub(r"[^\w]", "_", path) + ".pickle"
1145    try:
1146        f = ctx.bldnode.make_node(p)
1147    except AttributeError:
1148        f = ctx.path.make_node("build/" + p)
1149    f.parent.mkdir()
1150    cache_file = f.abspath()
1151    data_by_uid = {}
1152    if must_update_item_cache(ctx, path, cache_file):
1153        from waflib import Logs
1154
1155        Logs.warn(
1156            "Regenerate build specification cache (needs a couple of seconds)..."
1157        )
1158
1159        #
1160        # Do not use a system provided yaml module and instead import it from
1161        # the project.  This reduces the host system requirements to a simple
1162        # Python 2.7 or 3 installation without extra modules.
1163        #
1164        if sys.version_info[0] == 2:
1165            yaml_path = "yaml/lib"
1166        else:
1167            yaml_path = "yaml/lib3"
1168        sys.path += [yaml_path]
1169        from yaml import safe_load
1170
1171        load_from_yaml(safe_load, ctx, data_by_uid, path, path)
1172        with open(cache_file, "wb") as f:
1173            pickle.dump(data_by_uid, f)
1174    else:
1175        with open(cache_file, "rb") as f:
1176            data_by_uid = pickle.load(f)
1177    for uid, data in data_by_uid.items():
1178        if data["type"] == "build":
1179            items[uid] = ctors[data["build-type"]](uid, data)
1180
1181
1182def load_items(ctx, specs):
1183    if items:
1184        return
1185
1186    ctors = {
1187        "ada-test-program": AdaTestProgramItem,
1188        "bsp": BSPItem,
1189        "config-file": ConfigFileItem,
1190        "config-header": ConfigHeaderItem,
1191        "test-program": TestProgramItem,
1192        "group": GroupItem,
1193        "library": LibraryItem,
1194        "objects": ObjectsItem,
1195        "option": OptionItem,
1196        "script": ScriptItem,
1197        "start-file": StartFileItem,
1198    }
1199
1200    for path in specs:
1201        load_items_in_directory(ctx, ctors, path)
1202
1203
1204def load_items_from_options(ctx):
1205    specs = ctx.options.rtems_specs
1206    if specs is not None:
1207        specs = specs.split(",")
1208    else:
1209        specs = ["spec/build"]
1210    load_items(ctx, specs)
1211    return specs
1212
1213
1214def options(ctx):
1215    prefix = ctx.parser.get_option("--prefix")
1216    prefix.default = default_prefix
1217    prefix.help = "installation prefix [default: '{}']".format(default_prefix)
1218    rg = ctx.add_option_group("RTEMS options")
1219    rg.add_option(
1220        "--rtems-bsps",
1221        metavar="REGEX,...",
1222        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",
1223    )
1224    rg.add_option(
1225        "--rtems-compiler",
1226        metavar="COMPILER",
1227        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(
1228            ", ".join(compilers)
1229        ),
1230    )
1231    rg.add_option(
1232        "--rtems-config",
1233        metavar="CONFIG.INI,...",
1234        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",
1235    )
1236    rg.add_option(
1237        "--rtems-specs",
1238        metavar="SPECDIRS,...",
1239        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",
1240    )
1241    rg.add_option(
1242        "--rtems-tools",
1243        metavar="PREFIX,...",
1244        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",
1245    )
1246    rg.add_option(
1247        "--rtems-top-group",
1248        metavar="UID",
1249        help="the UID of the top-level group [default: '/grp']; it may be used in the bsp_defaults and configure commands",
1250    )
1251
1252
1253def check_environment(conf):
1254    for ev in [
1255        "AR",
1256        "AS",
1257        "ASFLAGS",
1258        "CC",
1259        "CFLAGS",
1260        "CPPFLAGS",
1261        "CXX",
1262        "CXXFLAGS",
1263        "IFLAGS",
1264        "LD",
1265        "LIB",
1266        "LINK_CC",
1267        "LINK_CXX",
1268        "LINKFLAGS",
1269        "MFLAGS",
1270        "RFLAGS",
1271        "WFLAGS",
1272    ]:
1273        if ev in os.environ:
1274            conf.msg("Environment variable set", ev, color="RED")
1275
1276
1277def load_config_files(ctx):
1278    cp = configparser.ConfigParser()
1279    files = ctx.options.rtems_config
1280    if files is not None:
1281        files = files.split(",")
1282    else:
1283        files = ["config.ini"]
1284    actual_files = cp.read(files)
1285    for o in files:
1286        if not o in actual_files:
1287            ctx.fatal("Option file '{}' was not readable".format(o))
1288    return cp
1289
1290
1291def inherit(conf, cp, bsp_map, arch, bsp, path):
1292    variant = arch + "/" + bsp
1293    if variant in path:
1294        path = " -> ".join(path + [variant])
1295        conf.fatal("Recursion in BSP options inheritance: {}".format(path))
1296
1297    try:
1298        base = cp.get(variant, "INHERIT")
1299        cp.remove_option(variant, "INHERIT")
1300        base = no_unicode(base)
1301        base_variant = arch + "/" + base
1302        conf.msg(
1303            "Inherit options from '{}'".format(base_variant),
1304            variant,
1305            color="YELLOW",
1306        )
1307        if not cp.has_section(base_variant):
1308            if (not arch in bsps) or (not base in bsps[arch]):
1309                conf.fatal(
1310                    "BSP variant '{}' cannot inherit options from not existing variant '{}'".format(
1311                        variant, base_variant
1312                    )
1313                )
1314            bsp_map[bsp] = base
1315            return base
1316        top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
1317        for i in cp.items(base_variant):
1318            name = i[0]
1319            if not cp.has_option(variant, name):
1320                cp.set(variant, name, i[1])
1321        bsp_map[bsp] = top
1322        return top
1323    except configparser.NoOptionError:
1324        return bsp_map.get(bsp, bsp)
1325
1326
1327def resolve_option_inheritance(conf, cp):
1328    bsp_map = {}
1329    for variant in cp.sections():
1330        variant = no_unicode(variant)
1331        try:
1332            arch, bsp = variant.split("/")
1333        except:
1334            conf.fatal(
1335                "Section name '{}' is a malformed 'arch/bsp' tuple".format(
1336                    variant
1337                )
1338            )
1339        inherit(conf, cp, bsp_map, arch, bsp, [])
1340    return bsp_map
1341
1342
1343def check_compiler(ctx, compiler):
1344    if compiler not in compilers:
1345        ctx.fatal(
1346            "Specified compiler '{}' is not one of {}".format(
1347                compiler, compilers
1348            )
1349        )
1350
1351
1352def get_compiler(conf, cp, variant):
1353    try:
1354        value = cp.get(variant, "COMPILER")
1355        cp.remove_option(variant, "COMPILER")
1356        value = no_unicode(value)
1357        check_compiler(conf, value)
1358    except configparser.NoOptionError:
1359        value = "gcc"
1360    return value
1361
1362
1363def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
1364    conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
1365
1366    conf.setenv(variant)
1367    arch, bsp_name = variant.split("/")
1368    bsp_base = bsp_map.get(bsp_name, bsp_name)
1369
1370    try:
1371        bsp_item = bsps[arch][bsp_base]
1372    except KeyError:
1373        conf.fatal("No such base BSP: '{}'".format(variant))
1374
1375    family = bsp_item.data["family"]
1376
1377    arch_bsp = arch + "/" + bsp_base
1378    arch_family = arch + "/" + family
1379
1380    conf.env["ARCH"] = arch
1381    conf.env["ARCH_BSP"] = arch_bsp
1382    conf.env["ARCH_FAMILY"] = arch_family
1383    conf.env["BSP_BASE"] = bsp_base
1384    conf.env["BSP_NAME"] = bsp_name
1385    conf.env["BSP_FAMILY"] = family
1386    conf.env["DEST_OS"] = "rtems"
1387
1388    # For the enabled-by evaluation we have to use the base BSP defined by the
1389    # build specification and not the BSP name provided by the user.
1390    conf.env["ENABLE"] = [
1391        get_compiler(conf, cp, variant),
1392        arch,
1393        "bsps/" + arch_family,
1394        arch_bsp,
1395    ]
1396
1397    conf.env["TOP"] = conf.path.abspath()
1398    conf.env["TOPGROUP"] = top_group
1399    conf.env["VARIANT"] = variant
1400
1401    cic = ConfigItemContext(cp, path_list)
1402
1403    items[conf.env.TOPGROUP].configure(conf, cic)
1404
1405    bsp_item.configure(conf, cic)
1406
1407    options = set([o[0].upper() for o in cp.items(variant)])
1408    for o in options.difference(cic.options):
1409        conf.msg("Unknown configuration option", o.upper(), color="RED")
1410
1411
1412def check_forbidden_options(ctx, opts):
1413    for o in opts:
1414        if getattr(ctx.options, "rtems_" + o):
1415            ctx.fatal(
1416                "The --rtems-{} command line option is not allowed in the {} command".format(
1417                    o.replace("_", "-"), ctx.cmd
1418                )
1419            )
1420
1421
1422def get_path_list(conf):
1423    path_list = []
1424    tools = conf.options.rtems_tools
1425    if tools is not None:
1426        for t in tools.split(","):
1427            path_list.extend([t + "/bin", t])
1428    path_list.append(conf.env.PREFIX + "/bin")
1429    path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
1430    return path_list
1431
1432
1433def get_top_group(ctx):
1434    top_group = ctx.options.rtems_top_group
1435    if top_group is None:
1436        top_group = "/grp"
1437    if top_group not in items:
1438        ctx.fatal(
1439            "There is no top-level group with UID '{}' in the specification".format(
1440                top_group
1441            )
1442        )
1443    return top_group
1444
1445
1446def configure(conf):
1447    check_forbidden_options(conf, ["compiler"])
1448    check_environment(conf)
1449    conf.env["SPECS"] = load_items_from_options(conf)
1450    top_group = get_top_group(conf)
1451    cp = load_config_files(conf)
1452    bsp_map = resolve_option_inheritance(conf, cp)
1453    path_list = get_path_list(conf)
1454    variant_list = []
1455    for variant in cp.sections():
1456        variant = no_unicode(variant)
1457        variant_list.append(variant)
1458        configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
1459    conf.setenv("")
1460    conf.env["VARIANTS"] = variant_list
1461
1462
1463def append_variant_builds(bld):
1464    import waflib.Options
1465    from waflib.Build import (
1466        BuildContext,
1467        CleanContext,
1468        InstallContext,
1469        UninstallContext,
1470    )
1471
1472    for var in bld.env["VARIANTS"]:
1473        for c in (BuildContext, CleanContext, InstallContext, UninstallContext):
1474            name = c.__name__.replace("Context", "").lower()
1475
1476            class magic(c):
1477                cmd = name + "_" + var
1478                variant = var
1479
1480        waflib.Options.commands.append(bld.cmd + "_" + var)
1481
1482
1483def long_command_line_workaround(bld):
1484    if is_windows_host:
1485        bld.load("long_gcc")
1486
1487
1488def build(bld):
1489    if not bld.variant:
1490        check_forbidden_options(
1491            bld, ["compiler", "config", "specs", "tools", "top_group"]
1492        )
1493        load_items(bld, bld.env.SPECS)
1494        append_variant_builds(bld)
1495        return
1496    long_command_line_workaround(bld)
1497    bic = BuildItemContext(bld.env.ARCH_INCLUDES.split(), [], [], [])
1498    bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
1499    items[bld.env.TOPGROUP].build(bld, bic)
1500
1501
1502def add_log_filter(name):
1503    msg = "'" + name + "' finished successfully"
1504
1505    class Filter:
1506        def filter(self, rec):
1507            return not msg in rec.getMessage()
1508
1509    import logging
1510
1511    logging.getLogger("waflib").addFilter(Filter())
1512
1513
1514def get_white_list(ctx):
1515    white_list = ctx.options.rtems_bsps
1516    if white_list:
1517        white_list = white_list.split(",")
1518    return white_list
1519
1520
1521def is_in_white_list(variant, white_list):
1522    if not white_list:
1523        return True
1524    for pattern in white_list:
1525        if re.match(pattern + "$", variant):
1526            return True
1527    return False
1528
1529
1530def no_matches_error(ctx, white_list):
1531    if white_list:
1532        ctx.fatal(
1533            "No BSP matches with the specified patterns: '{}'".format(
1534                "', '".join(white_list)
1535            )
1536        )
1537    else:
1538        ctx.fatal("The build specification contains no BSPs")
1539
1540
1541def bsp_defaults(ctx):
1542    """get all options with default values for base BSP variants"""
1543    check_forbidden_options(ctx, ["config", "tools"])
1544    add_log_filter(ctx.cmd)
1545    load_items_from_options(ctx)
1546    top_group = get_top_group(ctx)
1547    white_list = get_white_list(ctx)
1548    compiler = ctx.options.rtems_compiler
1549    if compiler is not None:
1550        check_compiler(ctx, compiler)
1551    else:
1552        compiler = "gcc"
1553    first = True
1554    for arch in sorted(bsps):
1555        for bsp in sorted(bsps[arch]):
1556            variant = arch + "/" + bsp
1557            if is_in_white_list(variant, white_list):
1558                if not first:
1559                    print("")
1560                first = False
1561                print(
1562                    """[{}]
1563# Selects the compiler used to build the BSP (allowed values are "gcc" and
1564# "clang").  Please note that the values of some options depend on the compiler
1565# selection and changing the compiler may lead to unpredictable behaviour if
1566# these options are not adjusted as well.  Use the --rtems-compiler command line
1567# option to get the default values for a particular compiler via
1568# ./waf bsp_defaults.
1569COMPILER = {}""".format(
1570                        variant, compiler
1571                    )
1572                )
1573                enable = [compiler, arch, variant]
1574                bsp_item = bsps[arch][bsp]
1575                family = arch + "/" + bsp_item.data["family"]
1576                items[top_group].defaults(enable, variant, family)
1577                bsp_item.defaults(enable, variant, family)
1578    if first:
1579        no_matches_error(ctx, white_list)
1580
1581
1582def bsp_list(ctx):
1583    """lists base BSP variants"""
1584    check_forbidden_options(ctx, ["compiler", "config", "tools", "top_group"])
1585    add_log_filter(ctx.cmd)
1586    load_items_from_options(ctx)
1587    white_list = get_white_list(ctx)
1588    first = True
1589    for arch in sorted(bsps):
1590        for bsp in sorted(bsps[arch]):
1591            variant = arch + "/" + bsp
1592            if is_in_white_list(variant, white_list):
1593                first = False
1594                print(variant)
1595    if first:
1596        no_matches_error(ctx, white_list)
Note: See TracBrowser for help on using the repository browser.