chief141

Untitled

Sep 28th, 2025
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 118.14 KB | Source Code | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # SPDX-License-Identifier: BSD-2-Clause
  4. #
  5. # Copyright (C) 2020 Hesham Almatary <[email protected]>
  6. # Copyright (C) 2019, 2020 embedded brains GmbH & Co. KG
  7. # Copyright (C) 2024 Contemporary Software <[email protected]>
  8. #
  9. # Redistribution and use in source and binary forms, with or without
  10. # modification, are permitted provided that the following conditions
  11. # are met:
  12. # 1. Redistributions of source code must retain the above copyright
  13. #    notice, this list of conditions and the following disclaimer.
  14. # 2. Redistributions in binary form must reproduce the above copyright
  15. #    notice, this list of conditions and the following disclaimer in the
  16. #    documentation and/or other materials provided with the distribution.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29.  
  30. import pickle
  31. import os
  32. import re
  33. import stat
  34. import string
  35. import sys
  36.  
  37. try:
  38.     import configparser
  39. except:
  40.     import ConfigParser as configparser
  41.  
  42. from waflib.TaskGen import after, before_method, feature
  43.  
  44. version = {
  45.     "__RTEMS_MAJOR__": "7",  # version
  46.     "__RTEMS_MINOR__": "0",  # revision
  47.     "__RTEMS_REVISION__": "0",  # not used
  48.     "RTEMS_RELEASE_VERSION_LABEL": "not-released"
  49. }
  50. default_prefix = "/opt/rtems/" + version["__RTEMS_MAJOR__"]
  51. compilers = ["gcc", "clang"]
  52. items = {}
  53. bsps = {}
  54. variant_errors = []
  55.  
  56.  
  57. def get_repo_release_label(ctx):
  58.     from waflib.Build import Context
  59.     from waflib.Errors import WafError
  60.     release_label = "not-released"
  61.     try:
  62.         modified = ctx.cmd_and_log("git ls-files --modified",
  63.                                    quiet=Context.BOTH).strip()
  64.         release_label = ctx.cmd_and_log("git rev-parse HEAD",
  65.                                         quiet=Context.BOTH).strip()
  66.         if modified:
  67.             release_label += "-modified"
  68.     except WafError:
  69.         pass
  70.     return release_label
  71.  
  72.  
  73. #
  74. # This class is not aligned the project's release labelling. It will
  75. # not be changed for RTEMS 6. A VC key is a type of release label and
  76. # there are more types.
  77. #
  78. # The term `key` and a repo normally relates to the signing key of
  79. # repository which is security related and it is important we do not
  80. # imply this here. They are unrelated.
  81. #
  82. class VersionControlKeyHeader:
  83.     _content = None
  84.  
  85.     @staticmethod
  86.     def write(bld, filename):
  87.         content = VersionControlKeyHeader._content
  88.         if content is None:
  89.             content = """/*
  90. * Automatically generated. Do not edit.
  91. *
  92. * Warning: This interface will be replaced with release labels
  93. *          after RTEMS 6.
  94. */
  95. #if !defined(_RTEMS_VERSION_VC_KEY_H_)
  96. #define _RTEMS_VERSION_VC_KEY_H_
  97. """
  98.             release_label = bld.env.RTEMS_RELEASE_VERSION_LABEL
  99.             if release_label == "not-released":
  100.                 release_label = get_repo_release_label(bld)
  101.             if release_label:
  102.                 content += """#define RTEMS_VERSION_CONTROL_KEY "{}"
  103. """.format(release_label)
  104.             content += """#endif
  105. """
  106.             VersionControlKeyHeader._content = content
  107.         f = bld.bldnode.make_node(filename)
  108.         f.parent.mkdir()
  109.         try:
  110.             if content != f.read():
  111.                 f.write(content)
  112.         except:
  113.             f.write(content)
  114.  
  115.  
  116. class EnvWrapper(object):
  117.  
  118.     def __init__(self, env):
  119.         self._env = env
  120.  
  121.     def __getitem__(self, name):
  122.         k, c, f = name.partition(":")
  123.         v = self._env[k]
  124.         fmt = "{" + c + f + "}"
  125.         if isinstance(v, list):
  126.             return " ".join([fmt.format(w) for w in v])
  127.         return fmt.format(v)
  128.  
  129.  
  130. class Template(string.Template):
  131.     idpattern = "[_A-Za-z][_A-Za-z0-9:#]*"
  132.  
  133.  
  134. _VAR_PATTERN = re.compile("\\$\\{?(" + Template.idpattern + ")\\}?$")
  135.  
  136.  
  137. def _is_enabled_op_and(enabled, enabled_by):
  138.     for next_enabled_by in enabled_by:
  139.         if not _is_enabled(enabled, next_enabled_by):
  140.             return False
  141.     return True
  142.  
  143.  
  144. def _is_enabled_op_not(enabled, enabled_by):
  145.     return not _is_enabled(enabled, enabled_by)
  146.  
  147.  
  148. def _is_enabled_op_or(enabled, enabled_by):
  149.     for next_enabled_by in enabled_by:
  150.         if _is_enabled(enabled, next_enabled_by):
  151.             return True
  152.     return False
  153.  
  154.  
  155. _IS_ENABLED_OP = {
  156.     "and": _is_enabled_op_and,
  157.     "not": _is_enabled_op_not,
  158.     "or": _is_enabled_op_or,
  159. }
  160.  
  161.  
  162. def _is_enabled(enabled, enabled_by):
  163.     if isinstance(enabled_by, bool):
  164.         return enabled_by
  165.     if isinstance(enabled_by, list):
  166.         return _is_enabled_op_or(enabled, enabled_by)
  167.     if isinstance(enabled_by, dict):
  168.         key, value = next(iter(enabled_by.items()))
  169.         return _IS_ENABLED_OP[key](enabled, value)
  170.     return enabled_by in enabled
  171.  
  172.  
  173. def _asm_explicit_target(self, node):
  174.     task = self.create_task("asm", node,
  175.                             self.bld.bldnode.make_node(self.target))
  176.     try:
  177.         self.compiled_tasks.append(task)
  178.     except AttributeError:
  179.         self.compiled_tasks = [task]
  180.     return task
  181.  
  182.  
  183. @feature("asm_explicit_target")
  184. @before_method("process_source")
  185. def _enable_asm_explicit_target(self):
  186.     self.mappings = dict(self.mappings)  # Copy
  187.     self.mappings[".S"] = _asm_explicit_target
  188.  
  189.  
  190. @after("apply_link")
  191. @feature("cprogram", "cxxprogram")
  192. def process_start_files(self):
  193.     if getattr(self, "start_files", False):
  194.         self.link_task.dep_nodes.extend(self.bld.start_files)
  195.  
  196.  
  197. def make_tar_info_reproducible(info):
  198.     # Reduce dependency on umask settings
  199.     info.mode &= ~(stat.S_IRWXG | stat.S_IRWXO)
  200.  
  201.     info.uid = 0
  202.     info.gid = 0
  203.     info.mtime = 0
  204.     info.uname = "root"
  205.     info.gname = "root"
  206.     return info
  207.  
  208.  
  209. class Item(object):
  210.  
  211.     def __init__(self, uid, data):
  212.         self.uid = uid
  213.         self.data = data
  214.         self.links = self._init_links
  215.  
  216.     def _init_links(self):
  217.         self._links = []
  218.         for link in self.data["links"]:
  219.             if link["role"] == "build-dependency":
  220.                 uid = link["uid"]
  221.                 if not os.path.isabs(uid):
  222.                     uid = os.path.normpath(
  223.                         os.path.join(os.path.dirname(self.uid),
  224.                                      uid)).replace("\\", "/")
  225.                 self._links.append(items[uid])
  226.         self.links = self._yield_links
  227.         for link in self._links:
  228.             yield link
  229.  
  230.     def _yield_links(self):
  231.         for link in self._links:
  232.             yield link
  233.  
  234.     def get_enabled_by(self):
  235.         return self.data["enabled-by"]
  236.  
  237.     def defaults(self, enabled):
  238.         if _is_enabled(enabled, self.get_enabled_by()):
  239.             for p in self.links():
  240.                 p.defaults(enabled)
  241.             self.do_defaults(enabled)
  242.  
  243.     def configure(self, conf, cic):
  244.         if _is_enabled(conf.env.ENABLE, self.get_enabled_by()):
  245.             self.prepare_configure(conf, cic)
  246.             for p in self.links():
  247.                 p.configure(conf, cic)
  248.             try:
  249.                 self.do_configure(conf, cic)
  250.             except Exception as e:
  251.                 raise type(e)(
  252.                     "Configuration error related to item spec:{}: {}".format(
  253.                         self.uid, str(e)))
  254.  
  255.     def build(self, bld, bic):
  256.         if _is_enabled(bld.env.ENABLE, self.get_enabled_by()):
  257.             bic = self.prepare_build(bld, bic)
  258.             for p in self.links():
  259.                 p.build(bld, bic)
  260.             try:
  261.                 self.do_build(bld, bic)
  262.             except Exception as e:
  263.                 raise type(e)("Build error related to item spec:{}: {}".format(
  264.                     self.uid, str(e)))
  265.  
  266.     def do_defaults(self, enabled):
  267.         return
  268.  
  269.     def prepare_configure(self, conf, cic):
  270.         return
  271.  
  272.     def do_configure(self, conf, cic):
  273.         return
  274.  
  275.     def prepare_build(self, bld, bic):
  276.         return bic
  277.  
  278.     def do_build(self, bld, bic):
  279.         return
  280.  
  281.     def substitute(self, ctx, value):
  282.         if isinstance(value, str):
  283.             try:
  284.                 return Template(value).substitute(EnvWrapper(ctx.env))
  285.             except Exception as e:
  286.                 ctx.fatal(
  287.                     "In item '{}' substitution in '{}' failed: {}".format(
  288.                         self.uid, value, e))
  289.         if isinstance(value, list):
  290.             more = []
  291.             for item in value:
  292.                 if isinstance(item, str):
  293.                     m = _VAR_PATTERN.match(item)
  294.                 else:
  295.                     m = None
  296.                 if m:
  297.                     more.extend(ctx.env[m.group(1).strip("{}")])
  298.                 else:
  299.                     more.append(self.substitute(ctx, item))
  300.             return more
  301.         return value
  302.  
  303.     def get(self, ctx, name):
  304.         return self.substitute(ctx, self.data[name])
  305.  
  306.     def install_target(self, bld):
  307.         install_path = self.data["install-path"]
  308.         if install_path:
  309.             bld.install_files(install_path, self.get(bld, "target"))
  310.  
  311.     def install_files(self, bld):
  312.         for install in self.data["install"]:
  313.             bld.install_files(install["destination"], install["source"])
  314.  
  315.     def asm(self, bld, bic, source, target=None):
  316.         if target is None:
  317.             target = os.path.splitext(source)[0] + ".o"
  318.         bld(
  319.             asflags=self.substitute(bld, self.data["asflags"]),
  320.             cppflags=bic.cppflags +
  321.             self.substitute(bld, self.data["cppflags"]),
  322.             features="asm_explicit_target asm c",
  323.             includes=bic.includes +
  324.             self.substitute(bld, self.data["includes"]),
  325.             source=[source],
  326.             target=target,
  327.         )
  328.         return target
  329.  
  330.     def cc(self, bld, bic, source, target=None, deps=[], cppflags=[]):
  331.         if target is None:
  332.             target = os.path.splitext(source)[0] + ".o"
  333.         bld(
  334.             cflags=bic.cflags + self.substitute(bld, self.data["cflags"]),
  335.             cppflags=bic.cppflags + cppflags +
  336.             self.substitute(bld, self.data["cppflags"]),
  337.             features="c",
  338.             includes=bic.includes +
  339.             self.substitute(bld, self.data["includes"]),
  340.             rule=
  341.             "${CC} ${CFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
  342.             source=[source] + deps,
  343.             target=target,
  344.         )
  345.         return target
  346.  
  347.     def cxx(self, bld, bic, source, target=None, deps=[], cppflags=[]):
  348.         if target is None:
  349.             target = os.path.splitext(source)[0] + ".o"
  350.         bld(
  351.             cppflags=bic.cppflags + cppflags +
  352.             self.substitute(bld, self.data["cppflags"]),
  353.             cxxflags=bic.cxxflags +
  354.             self.substitute(bld, self.data["cxxflags"]),
  355.             features="cxx",
  356.             includes=bic.includes +
  357.             self.substitute(bld, self.data["includes"]),
  358.             rule=
  359.             "${CXX} ${CXXFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
  360.             source=[source] + deps,
  361.             target=target,
  362.         )
  363.         return target
  364.  
  365.     def link(self, bld, bic, cmd, source, target):
  366.         from waflib.Task import Task
  367.  
  368.         class link(Task):
  369.  
  370.             def __init__(self, item, bic, cmd, env, ldflags):
  371.                 super(link, self).__init__(self, env=env)
  372.                 self.cmd = cmd
  373.                 self.ldflags = ldflags
  374.                 self.stlib = item.data["stlib"]
  375.                 self.use = (item.data["use-before"] + bic.use +
  376.                             item.data["use-after"])
  377.  
  378.             def run(self):
  379.                 cmd = [self.cmd]
  380.                 cmd.extend(self.env.LINKFLAGS)
  381.                 cmd.extend([i.abspath() for i in self.inputs])
  382.                 cmd.append("-o" + self.outputs[0].abspath())
  383.                 cmd.append("-L.")
  384.                 cmd.extend(["-l" + l for l in self.stlib])
  385.                 cmd.extend(["-l" + l for l in self.use])
  386.                 cmd.extend(self.env.LDFLAGS)
  387.                 cmd.extend(self.ldflags)
  388.                 return self.exec_command(cmd)
  389.  
  390.             def scan(self):
  391.                 return (
  392.                     [
  393.                         self.generator.bld.bldnode.make_node("lib" + u + ".a")
  394.                         for u in self.use
  395.                     ],
  396.                     [],
  397.                 )
  398.  
  399.         tsk = link(self, bic, cmd, bld.env,
  400.                    bic.ldflags + self.substitute(bld, self.data["ldflags"]))
  401.         tsk.set_inputs([bld.bldnode.make_node(s) for s in source])
  402.         tsk.set_outputs(bld.bldnode.make_node(target))
  403.         bld.add_to_group(tsk)
  404.         return target
  405.  
  406.     def link_cc(self, bld, bic, source, target):
  407.         return self.link(bld, bic, bld.env.LINK_CC[0], source, target)
  408.  
  409.     def link_cxx(self, bld, bic, source, target):
  410.         return self.link(bld, bic, bld.env.LINK_CXX[0], source, target)
  411.  
  412.     def gnatmake(self, bld, bic, objdir, objs, main, target):
  413.         from waflib.Task import Task
  414.  
  415.         class gnatmake(Task):
  416.  
  417.             def __init__(self, bld, bic, objdir, objs, main, target, item):
  418.                 super(gnatmake, self).__init__(self, env=bld.env)
  419.                 self.objdir = objdir
  420.                 self.objs = [bld.bldnode.make_node(o) for o in objs]
  421.                 self.main = bld.path.make_node(main)
  422.                 self.set_inputs(self.objs + [self.main])
  423.                 self.set_outputs(bld.bldnode.make_node(target))
  424.                 self.adaflags = item.data["adaflags"]
  425.                 self.adaincludes = []
  426.                 for i in item.data["adaincludes"]:
  427.                     self.adaincludes.append(bld.bldnode.make_node(i))
  428.                     self.adaincludes.append(bld.path.make_node(i))
  429.                 self.ldflags = bic.ldflags + item.data["ldflags"]
  430.                 self.stlib = item.data["stlib"]
  431.                 self.use = (item.data["use-before"] + bic.use +
  432.                             item.data["use-after"])
  433.  
  434.             def run(self):
  435.                 cwd = self.get_cwd()
  436.                 cmd = [
  437.                     self.env.GNATMAKE[0],
  438.                     "-D",
  439.                     self.objdir,
  440.                     "-bargs",
  441.                     "-Mgnat_main",
  442.                     "-margs",
  443.                 ]
  444.                 cmd.extend(self.adaflags)
  445.                 cmd.extend(["-I" + i.path_from(cwd) for i in self.adaincludes])
  446.                 cmd.append("-cargs")
  447.                 cmd.extend(self.env.ABI_FLAGS)
  448.                 cmd.append("-largs")
  449.                 cmd.extend([o.path_from(cwd) for o in self.objs])
  450.                 cmd.extend(self.env.LINKFLAGS)
  451.                 cmd.extend(self.ldflags)
  452.                 cmd.append("-L.")
  453.                 cmd.extend(["-l" + l for l in self.stlib])
  454.                 cmd.extend(["-l" + l for l in self.use])
  455.                 cmd.extend(self.env.LDFLAGS)
  456.                 cmd.extend(["-margs", "-a"])
  457.                 cmd.append(self.main.abspath())
  458.                 cmd.append("-o")
  459.                 cmd.append(self.outputs[0].abspath())
  460.                 return self.exec_command(cmd)
  461.  
  462.             def scan(self):
  463.                 return (
  464.                     [
  465.                         self.generator.bld.bldnode.make_node("lib" + u + ".a")
  466.                         for u in self.use
  467.                     ],
  468.                     [],
  469.                 )
  470.  
  471.         tsk = gnatmake(bld, bic, objdir, objs, main, target, self)
  472.         bld.add_to_group(tsk)
  473.         return target
  474.  
  475.     def ar(self, bld, source, target):
  476.         bld(rule="${AR} ${ARFLAGS} ${TGT} ${SRC}",
  477.             source=source,
  478.             target=target)
  479.         return target
  480.  
  481.     def gzip(self, bld, source):
  482.         target = source + ".gz"
  483.         bld(rule="${GZIP} -n < ${SRC} > ${TGT}", source=source, target=target)
  484.         return target
  485.  
  486.     def xz(self, bld, source):
  487.         target = source + ".xz"
  488.         bld(rule="${XZ} < ${SRC} > ${TGT}", source=source, target=target)
  489.         return target
  490.  
  491.     def tar(self, bld, source, remove, target):
  492.  
  493.         def run(task):
  494.             import tarfile
  495.  
  496.             tar = tarfile.TarFile(task.outputs[0].abspath(),
  497.                                   "w",
  498.                                   format=tarfile.USTAR_FORMAT)
  499.             srcpath = bld.path.abspath() + "/"
  500.             bldpath = bld.bldnode.abspath() + "/"
  501.             for src in task.inputs:
  502.                 src = src.abspath()
  503.                 dst = src
  504.                 for r in remove:
  505.                     dst = src.replace(srcpath + r, "").replace(bldpath + r, "")
  506.                 tar.add(src, dst, filter=make_tar_info_reproducible)
  507.             tar.close()
  508.             return 0
  509.  
  510.         bld(rule=run, source=source, target=target)
  511.         return target
  512.  
  513.     def bin2c(self, bld, source, name=None, target=None):
  514.  
  515.         def run(task):
  516.             cmd = [bld.env.BIN2C[0]]
  517.             if name is not None:
  518.                 cmd.extend(["-N", name])
  519.             cmd.append(task.inputs[0].abspath())
  520.             cmd.append(task.outputs[0].abspath())
  521.             return task.exec_command(cmd)
  522.  
  523.         path, base = os.path.split(source)
  524.         if target is None:
  525.             target = path + "/" + base.replace(".", "-")
  526.         target_c = target + ".c"
  527.         target_h = target + ".h"
  528.         bld(rule=run, source=source, target=[target_c, target_h])
  529.         return target_c, target_h
  530.  
  531.     def rtems_syms(self, bld, bic, source, target):
  532.         syms_source = os.path.splitext(target)[0] + ".c"
  533.         bld(
  534.             rule='${RTEMS_SYMS} -e -S ${TGT} ${SRC}',
  535.             source=source,
  536.             target=syms_source,
  537.         )
  538.         return self.cc(bld, bic, syms_source, target)
  539.  
  540.     def rtems_rap(self, bld, base, objects, libs, target):
  541.  
  542.         def run(task):
  543.             cmd = [
  544.                 bld.env.RTEMS_LD[0],
  545.                 "-C",
  546.                 bld.env.CC[0],
  547.                 "-c",
  548.                 " ".join(bld.env.CFLAGS),
  549.                 "-O",
  550.                 "rap",
  551.                 "-b",
  552.                 task.inputs[0].abspath(),
  553.                 "-e",
  554.                 "rtems_main",
  555.                 "-s",
  556.                 "-o",
  557.             ]
  558.             cmd.append(task.outputs[0].abspath())
  559.             cmd.extend([i.abspath() for i in task.inputs[1:]])
  560.             cmd.extend(["-l" + l for l in libs])
  561.             return task.exec_command(cmd)
  562.  
  563.         bld(rule=run, source=[base] + objects, target=target)
  564.         return target
  565.  
  566.  
  567. class GroupItem(Item):
  568.  
  569.     def __init__(self, uid, data):
  570.         super(GroupItem, self).__init__(uid, data)
  571.  
  572.     def prepare_build(self, bld, bic):
  573.         return BuildItemContext(
  574.             bic.includes + self.substitute(bld, self.data["includes"]),
  575.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  576.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  577.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  578.             self.data["use-before"] + bic.use + self.data["use-after"],
  579.             bic.ldflags + self.substitute(bld, self.data["ldflags"]),
  580.             bic.objects,
  581.         )
  582.  
  583.     def do_build(self, bld, bic):
  584.         self.install_files(bld)
  585.  
  586.  
  587. class ConfigFileItem(Item):
  588.  
  589.     def __init__(self, uid, data):
  590.         super(ConfigFileItem, self).__init__(uid, data)
  591.  
  592.     def do_configure(self, conf, cic):
  593.         content = self.substitute(conf, self.data["content"])
  594.         f = conf.bldnode.make_node(conf.env.VARIANT + "/" +
  595.                                    self.get(conf, "target"))
  596.         f.parent.mkdir()
  597.         f.write(content)
  598.         conf.env.append_value("cfg_files", f.abspath())
  599.  
  600.     def do_build(self, bld, bic):
  601.         self.install_target(bld)
  602.  
  603.  
  604. class ConfigHeaderItem(Item):
  605.  
  606.     def __init__(self, uid, data):
  607.         super(ConfigHeaderItem, self).__init__(uid, data)
  608.  
  609.     def do_configure(self, conf, cic):
  610.         conf.env.include_key = self.data["include-headers"]
  611.         conf.write_config_header(
  612.             conf.env.VARIANT + "/" + self.get(conf, "target"),
  613.             guard=self.data["guard"],
  614.             headers=True,
  615.         )
  616.         conf.env.include_key = None
  617.  
  618.     def do_build(self, bld, bic):
  619.         self.install_target(bld)
  620.  
  621.  
  622. class StartFileItem(Item):
  623.  
  624.     def __init__(self, uid, data):
  625.         super(StartFileItem, self).__init__(uid, data)
  626.  
  627.     def do_build(self, bld, bic):
  628.         source = self.data["source"]
  629.         if os.path.splitext(source[0])[1] == ".S":
  630.             tgt = self.asm(bld, bic, source, self.get(bld, "target"))
  631.         else:
  632.             tgt = self.cc(bld, bic, source, self.get(bld, "target"))
  633.         node = bld.bldnode.make_node(tgt)
  634.         try:
  635.             bld.start_files.append(node)
  636.         except AttributeError:
  637.             bld.start_files = [node]
  638.         self.install_target(bld)
  639.  
  640.  
  641. class ObjectsItem(Item):
  642.  
  643.     def __init__(self, uid, data):
  644.         super(ObjectsItem, self).__init__(uid, data)
  645.  
  646.     def prepare_build(self, bld, bic):
  647.         return BuildItemContext(
  648.             bic.includes + self.substitute(bld, self.data["includes"]),
  649.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  650.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  651.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  652.             bic.use,
  653.             bic.ldflags,
  654.             bic.objects,
  655.         )
  656.  
  657.     def do_build(self, bld, bic):
  658.         bld.objects(
  659.             asflags=bic.cppflags,
  660.             cflags=bic.cflags,
  661.             cppflags=bic.cppflags,
  662.             cxxflags=bic.cxxflags,
  663.             includes=bic.includes,
  664.             source=self.data["source"],
  665.             target=self.uid,
  666.         )
  667.         bic.objects.append(self.uid)
  668.         self.install_files(bld)
  669.  
  670.  
  671. class BSPItem(Item):
  672.  
  673.     def __init__(self, uid, data):
  674.         super(BSPItem, self).__init__(uid, data)
  675.         arch_bsps = bsps.setdefault(data["arch"].strip(), {})
  676.         arch_bsps[data["bsp"].strip()] = self
  677.  
  678.     def prepare_build(self, bld, bic):
  679.         return BuildItemContext(
  680.             bic.includes + bld.env.BSP_INCLUDES +
  681.             self.substitute(bld, self.data["includes"]),
  682.             self.substitute(bld, self.data["cppflags"]),
  683.             bld.env.BSP_CFLAGS + self.substitute(bld, self.data["cflags"]),
  684.             [],
  685.             [],
  686.             [],
  687.             [],
  688.         )
  689.  
  690.     def do_build(self, bld, bic):
  691.         bld(
  692.             cflags=bic.cflags,
  693.             cppflags=bic.cppflags,
  694.             features="c cstlib",
  695.             includes=bic.includes,
  696.             install_path="${BSP_LIBDIR}",
  697.             source=self.data["source"],
  698.             target="rtemsbsp",
  699.             use=bic.objects,
  700.         )
  701.         self.install_files(bld)
  702.  
  703.  
  704. class LibraryItem(Item):
  705.  
  706.     def __init__(self, uid, data):
  707.         super(LibraryItem, self).__init__(uid, data)
  708.  
  709.     def prepare_build(self, bld, bic):
  710.         return BuildItemContext(
  711.             bic.includes + self.substitute(bld, self.data["includes"]),
  712.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  713.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  714.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  715.             bic.use,
  716.             bic.ldflags,
  717.             [],
  718.         )
  719.  
  720.     def do_build(self, bld, bic):
  721.         bld(
  722.             cflags=bic.cflags,
  723.             cppflags=bic.cppflags,
  724.             cxxflags=bic.cxxflags,
  725.             features="c cxx cstlib",
  726.             includes=bic.includes,
  727.             install_path=self.data["install-path"],
  728.             source=self.data["source"],
  729.             target=self.get(bld, "target"),
  730.             use=bic.objects,
  731.         )
  732.         self.install_files(bld)
  733.  
  734.  
  735. class TestProgramItem(Item):
  736.  
  737.     def __init__(self, uid, data):
  738.         super(TestProgramItem, self).__init__(uid, data)
  739.         name = uid.split("/")[-1].upper().replace("-", "_")
  740.         self.exclude = "TEST_" + name + "_EXCLUDE"
  741.         self.cppflags = "TEST_" + name + "_CPPFLAGS"
  742.  
  743.     def get_enabled_by(self):
  744.         return [{"and": [{"not": self.exclude}, self.data["enabled-by"]]}]
  745.  
  746.     def prepare_build(self, bld, bic):
  747.         return BuildItemContext(
  748.             bic.includes + self.substitute(bld, self.data["includes"]),
  749.             bic.cppflags + bld.env[self.cppflags] +
  750.             self.substitute(bld, self.data["cppflags"]),
  751.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  752.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  753.             self.data["use-before"] + bic.use + self.data["use-after"],
  754.             bic.ldflags + self.substitute(bld, self.data["ldflags"]),
  755.             [],
  756.         )
  757.  
  758.     def do_build(self, bld, bic):
  759.         bld(
  760.             cflags=bic.cflags,
  761.             cppflags=bic.cppflags,
  762.             cxxflags=bic.cxxflags,
  763.             features=self.data["features"],
  764.             includes=bic.includes,
  765.             install_path=None,
  766.             ldflags=bic.ldflags,
  767.             source=self.data["source"],
  768.             start_files=True,
  769.             stlib=self.data["stlib"],
  770.             target=self.get(bld, "target"),
  771.             use=bic.objects + bic.use,
  772.         )
  773.  
  774.  
  775. class AdaTestProgramItem(TestProgramItem):
  776.  
  777.     def __init__(self, uid, data):
  778.         super(AdaTestProgramItem, self).__init__(uid, data)
  779.  
  780.     def do_build(self, bld, bic):
  781.         objs = []
  782.         for s in self.data["source"]:
  783.             objs.append(self.cc(bld, bic, s))
  784.         self.gnatmake(
  785.             bld,
  786.             bic,
  787.             self.data["ada-object-directory"],
  788.             objs,
  789.             self.data["ada-main"],
  790.             self.data["target"],
  791.         )
  792.  
  793.  
  794. class OptionItem(Item):
  795.  
  796.     def __init__(self, uid, data):
  797.         super(OptionItem, self).__init__(uid, data)
  798.  
  799.     def default_value(self, enabled):
  800.         value = None
  801.         for default in self.data["default"]:
  802.             if _is_enabled(enabled, default["enabled-by"]):
  803.                 value = default["value"]
  804.                 break
  805.         if value is None:
  806.             return value
  807.         if isinstance(value, list):
  808.             return " ".join(value)
  809.         if isinstance(value, bool):
  810.             return value
  811.         return self.data["format"].format(value)
  812.  
  813.     def do_defaults(self, enabled):
  814.         value = self.default_value(enabled)
  815.         if value is None:
  816.             return
  817.         description = self.data["description"]
  818.         if description:
  819.             import textwrap
  820.  
  821.             tw = textwrap.TextWrapper()
  822.             tw.drop_whitespace = True
  823.             tw.initial_indent = "# "
  824.             tw.subsequent_indent = "# "
  825.             for line in tw.wrap(description):
  826.                 print(line)
  827.         print("{} = {}".format(self.data["name"], value))
  828.  
  829.     def _do_append_test_cppflags(self, conf, name, state):
  830.         conf.env.append_value(
  831.             "TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state)
  832.  
  833.     def _append_test_cppflags(self, conf, cic, value, arg):
  834.         self._do_append_test_cppflags(conf, arg, value)
  835.         return value
  836.  
  837.     def _assert_aligned(self, conf, cic, value, arg):
  838.         if value is not None and value % arg != 0:
  839.             conf.fatal(
  840.                 "Value '{}' for option '{}' is not aligned by '{}'".format(
  841.                     value, self.data["name"], arg))
  842.         return value
  843.  
  844.     def _assert_eq(self, conf, cic, value, arg):
  845.         if value is not None and value != arg:
  846.             conf.fatal("Value '{}' for option '{}' is not equal to {}".format(
  847.                 value, self.data["name"], arg))
  848.         return value
  849.  
  850.     def _assert_ge(self, conf, cic, value, arg):
  851.         if value is not None and value < arg:
  852.             conf.fatal(
  853.                 "Value '{}' for option '{}' is not greater than or equal to {}"
  854.                 .format(value, self.data["name"], arg))
  855.         return value
  856.  
  857.     def _assert_gt(self, conf, cic, value, arg):
  858.         if value is not None and value <= arg:
  859.             conf.fatal(
  860.                 "Value '{}' for option '{}' is not greater than {}".format(
  861.                     value, self.data["name"], arg))
  862.         return value
  863.  
  864.     def _assert_in_set(self, conf, cic, value, arg):
  865.         if value is not None and value not in arg:
  866.             conf.fatal(
  867.                 "Value '{}' for option '{}' is not an element of {}".format(
  868.                     value, self.data["name"], arg))
  869.         return value
  870.  
  871.     def _assert_in_interval(self, conf, cic, value, arg):
  872.         if value is not None and (value < arg[0] or value > arg[1]):
  873.             conf.fatal(
  874.                 "Value '{}' for option '{}' is not in closed interval [{}, {}]"
  875.                 .format(value, self.data["name"], arg[0], arg[1]))
  876.         return value
  877.  
  878.     def _assert_int8(self, conf, cic, value, arg):
  879.         return self._assert_in_interval(conf, cic, value, [-128, 127])
  880.  
  881.     def _assert_int16(self, conf, cic, value, arg):
  882.         return self._assert_in_interval(conf, cic, value, [-32768, 32767])
  883.  
  884.     def _assert_int32(self, conf, cic, value, arg):
  885.         return self._assert_in_interval(conf, cic, value,
  886.                                         [-2147483648, 2147483647])
  887.  
  888.     def _assert_int64(self, conf, cic, value, arg):
  889.         return self._assert_in_interval(
  890.             conf, cic, value, [-9223372036854775808, 9223372036854775807])
  891.  
  892.     def _assert_le(self, conf, cic, value, arg):
  893.         if value is not None and value > arg:
  894.             conf.fatal(
  895.                 "Value '{}' for option '{}' is not less than or equal to {}".
  896.                 format(value, self.data["name"], arg))
  897.         return value
  898.  
  899.     def _assert_lt(self, conf, cic, value, arg):
  900.         if value is not None and value >= arg:
  901.             conf.fatal("Value '{}' for option '{}' is not less than {}".format(
  902.                 value, self.data["name"], arg))
  903.         return value
  904.  
  905.     def _assert_ne(self, conf, cic, value, arg):
  906.         if value is not None and value == arg:
  907.             conf.fatal(
  908.                 "Value '{}' for option '{}' is not unequal to {}".format(
  909.                     value, self.data["name"], arg))
  910.         return value
  911.  
  912.     def _assert_power_of_two(self, conf, cic, value, arg):
  913.         if value is not None and (value <= 0 or (value & (value - 1)) != 0):
  914.             conf.fatal(
  915.                 "Value '{}' for option '{}' is not a power of two".format(
  916.                     value, self.data["name"]))
  917.         return value
  918.  
  919.     def _assert_uint8(self, conf, cic, value, arg):
  920.         return self._assert_in_interval(conf, cic, value, [0, 255])
  921.  
  922.     def _assert_uint16(self, conf, cic, value, arg):
  923.         return self._assert_in_interval(conf, cic, value, [0, 65535])
  924.  
  925.     def _assert_uint32(self, conf, cic, value, arg):
  926.         return self._assert_in_interval(conf, cic, value, [0, 4294967295])
  927.  
  928.     def _assert_uint64(self, conf, cic, value, arg):
  929.         return self._assert_in_interval(conf, cic, value,
  930.                                         [0, 18446744073709551615])
  931.  
  932.     def _check_cc(self, conf, cic, value, arg):
  933.         result = conf.check_cc(
  934.             fragment=arg["fragment"],
  935.             cflags=arg["cflags"],
  936.             msg="Checking for " + arg["message"],
  937.             mandatory=False,
  938.         )
  939.         return value and result
  940.  
  941.     def _check_cxx(self, conf, cic, value, arg):
  942.         result = conf.check_cxx(
  943.             fragment=arg["fragment"],
  944.             cxxflags=arg["cxxflags"],
  945.             msg="Checking for " + arg["message"],
  946.             mandatory=False,
  947.         )
  948.         return value and result
  949.  
  950.     def _comment(self, conf, cic, value, arg):
  951.         return value
  952.  
  953.     def _define_condition(self, conf, cic, value, arg):
  954.         name = self.data["name"] if arg is None else arg
  955.         conf.define_cond(name, value)
  956.         return value
  957.  
  958.     def _define(self, conf, cic, value, arg):
  959.         name = self.data["name"] if arg is None else arg
  960.         if value is not None:
  961.             conf.define(name, value)
  962.         else:
  963.             conf.define_cond(name, False)
  964.         return value
  965.  
  966.     def _define_unquoted(self, conf, cic, value, arg):
  967.         name = self.data["name"] if arg is None else arg
  968.         if value is not None:
  969.             conf.define(name, value, quote=False)
  970.         else:
  971.             conf.define_cond(name, False)
  972.         return value
  973.  
  974.     def _env_append(self, conf, cic, value, arg):
  975.         name = self.data["name"] if arg is None else arg
  976.         conf.env.append_value(name, value)
  977.         return value
  978.  
  979.     def _env_assign(self, conf, cic, value, arg):
  980.         name = self.data["name"] if arg is None else arg
  981.         conf.env[name] = value
  982.         return value
  983.  
  984.     def _env_enable(self, conf, cic, value, arg):
  985.         if value:
  986.             name = self.data["name"] if arg is None else arg
  987.             conf.env.append_value("ENABLE", name)
  988.         return value
  989.  
  990.     def _find_program(self, conf, cic, value, arg):
  991.         return conf.find_program(value, path_list=cic.path_list)
  992.  
  993.     def _format_and_define(self, conf, cic, value, arg):
  994.         name = self.data["name"] if arg is None else arg
  995.         if value is not None:
  996.             conf.define(name, self.data["format"].format(value), quote=False)
  997.         else:
  998.             conf.define_cond(name, False)
  999.         return value
  1000.  
  1001.     def _get_boolean(self, conf, cic, value, arg):
  1002.         name = self.data["name"]
  1003.         try:
  1004.             value = cic.cp.getboolean(conf.variant, name)
  1005.             cic.add_option(name)
  1006.         except configparser.NoOptionError:
  1007.             value = self.default_value(conf.env.ENABLE)
  1008.         except ValueError as ve:
  1009.             conf.fatal("Invalid value for configuration option {}: {}".format(
  1010.                 name, ve))
  1011.         return value
  1012.  
  1013.     def _get_env(self, conf, cic, value, arg):
  1014.         return conf.env[arg]
  1015.  
  1016.     def _get_integer(self, conf, cic, value, arg):
  1017.         name = self.data["name"]
  1018.         try:
  1019.             value = cic.cp.get(conf.variant, name)
  1020.             cic.add_option(name)
  1021.         except configparser.NoOptionError:
  1022.             value = self.default_value(conf.env.ENABLE)
  1023.         if not value:
  1024.             return None
  1025.         try:
  1026.             return eval(value)
  1027.         except Exception as e:
  1028.             conf.fatal(
  1029.                 "Value '{}' for option '{}' is an invalid integer expression: {}"
  1030.                 .format(value, name, e))
  1031.  
  1032.     def _get_string(self, conf, cic, value, arg):
  1033.         name = self.data["name"]
  1034.         try:
  1035.             value = cic.cp.get(conf.variant, name)
  1036.             cic.add_option(name)
  1037.         except configparser.NoOptionError:
  1038.             value = self.default_value(conf.env.ENABLE)
  1039.         return value
  1040.  
  1041.     def _script(self, conf, cic, value, arg):
  1042.         local_variables = {
  1043.             "self": self,
  1044.             "conf": conf,
  1045.             "cic": cic,
  1046.             "value": value
  1047.         }
  1048.         exec(arg, None, local_variables)
  1049.         return local_variables["value"]
  1050.  
  1051.     def _test_state_benchmark(self, conf, name):
  1052.         self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
  1053.  
  1054.     def _test_state_exclude(self, conf, name):
  1055.         conf.env.append_value(
  1056.             "ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE")
  1057.  
  1058.     def _test_state_expected_fail(self, conf, name):
  1059.         self._do_append_test_cppflags(conf, name,
  1060.                                       "-DTEST_STATE_EXPECTED_FAIL=1")
  1061.  
  1062.     def _test_state_indeterminate(self, conf, name):
  1063.         self._do_append_test_cppflags(conf, name,
  1064.                                       "-DTEST_STATE_INDETERMINATE=1")
  1065.  
  1066.     def _test_state_user_input(self, conf, name):
  1067.         self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
  1068.  
  1069.     def _set_test_state(self, conf, cic, value, arg):
  1070.         actions = {
  1071.             "benchmark": self._test_state_benchmark,
  1072.             "exclude": self._test_state_exclude,
  1073.             "expected-fail": self._test_state_expected_fail,
  1074.             "indeterminate": self._test_state_indeterminate,
  1075.             "user-input": self._test_state_user_input,
  1076.         }
  1077.         action = actions[arg["state"]]
  1078.         for test in arg["tests"]:
  1079.             action(conf, test)
  1080.         return value
  1081.  
  1082.     def _set_value(self, conf, cic, value, arg):
  1083.         return arg
  1084.  
  1085.     def _set_value_enabled_by(self, conf, cic, value, arg):
  1086.         for value_enabled_by in arg:
  1087.             if _is_enabled(conf.env.ENABLE, value_enabled_by["enabled-by"]):
  1088.                 return value_enabled_by["value"]
  1089.         return None
  1090.  
  1091.     def _split(self, conf, cic, value, arg):
  1092.         return value.split()
  1093.  
  1094.     def _substitute(self, conf, cic, value, arg):
  1095.         if isinstance(value, list):
  1096.             return [self.substitute(conf, v) for v in value]
  1097.         else:
  1098.             return self.substitute(conf, value)
  1099.  
  1100.     def do_configure(self, conf, cic):
  1101.         actions = {
  1102.             "append-test-cppflags": self._append_test_cppflags,
  1103.             "assert-aligned": self._assert_aligned,
  1104.             "assert-eq": self._assert_eq,
  1105.             "assert-ge": self._assert_ge,
  1106.             "assert-gt": self._assert_gt,
  1107.             "assert-in-set": self._assert_in_set,
  1108.             "assert-int8": self._assert_int8,
  1109.             "assert-int16": self._assert_int16,
  1110.             "assert-int32": self._assert_int32,
  1111.             "assert-int64": self._assert_int64,
  1112.             "assert-le": self._assert_le,
  1113.             "assert-lt": self._assert_lt,
  1114.             "assert-ne": self._assert_ne,
  1115.             "assert-power-of-two": self._assert_power_of_two,
  1116.             "assert-uint8": self._assert_uint8,
  1117.             "assert-uint16": self._assert_uint16,
  1118.             "assert-uint32": self._assert_uint32,
  1119.             "assert-uint64": self._assert_uint64,
  1120.             "check-cc": self._check_cc,
  1121.             "check-cxx": self._check_cxx,
  1122.             "comment": self._comment,
  1123.             "define-condition": self._define_condition,
  1124.             "define": self._define,
  1125.             "define-unquoted": self._define_unquoted,
  1126.             "env-append": self._env_append,
  1127.             "env-assign": self._env_assign,
  1128.             "env-enable": self._env_enable,
  1129.             "find-program": self._find_program,
  1130.             "format-and-define": self._format_and_define,
  1131.             "get-boolean": self._get_boolean,
  1132.             "get-env": self._get_env,
  1133.             "get-integer": self._get_integer,
  1134.             "get-string": self._get_string,
  1135.             "script": self._script,
  1136.             "set-test-state": self._set_test_state,
  1137.             "set-value": self._set_value,
  1138.             "set-value-enabled-by": self._set_value_enabled_by,
  1139.             "split": self._split,
  1140.             "substitute": self._substitute,
  1141.         }
  1142.         value = None
  1143.         for action in self.data["actions"]:
  1144.             for action_arg in action.items():
  1145.                 value = actions[action_arg[0]](conf, cic, value, action_arg[1])
  1146.  
  1147.  
  1148. class ScriptItem(Item):
  1149.  
  1150.     def __init__(self, uid, data):
  1151.         super(ScriptItem, self).__init__(uid, data)
  1152.  
  1153.     def prepare_configure(self, conf, cic):
  1154.         script = self.data["prepare-configure"]
  1155.         if script:
  1156.             exec(script)
  1157.  
  1158.     def do_configure(self, conf, cic):
  1159.         script = self.data["do-configure"]
  1160.         if script:
  1161.             exec(script)
  1162.  
  1163.     def prepare_build(self, bld, bic):
  1164.         script = self.data["prepare-build"]
  1165.         if script:
  1166.             exec(script)
  1167.         return bic
  1168.  
  1169.     def do_build(self, bld, bic):
  1170.         script = self.data["do-build"]
  1171.         if script:
  1172.             exec(script)
  1173.  
  1174.  
  1175. class ConfigItemContext(object):
  1176.  
  1177.     def __init__(self, cp, path_list):
  1178.         self.cp = cp
  1179.         self.options = set()
  1180.         self.path_list = path_list
  1181.  
  1182.     def add_option(self, name):
  1183.         self.options.add(name.upper())
  1184.  
  1185.  
  1186. class BuildItemContext(object):
  1187.  
  1188.     def __init__(self, includes, cppflags, cflags, cxxflags, use, ldflags,
  1189.                  objects):
  1190.         self.includes = includes
  1191.         self.cppflags = cppflags
  1192.         self.cflags = cflags
  1193.         self.cxxflags = cxxflags
  1194.         self.use = use
  1195.         self.ldflags = ldflags
  1196.         self.objects = objects
  1197.  
  1198.  
  1199. def is_one_item_newer(ctx, path, mtime):
  1200.     try:
  1201.         mtime2 = os.path.getmtime(path)
  1202.         if mtime <= mtime2:
  1203.             return True
  1204.         names = os.listdir(path)
  1205.     except Exception as e:
  1206.         ctx.fatal("Cannot access build specification directory: {}".format(e))
  1207.     for name in names:
  1208.         path2 = os.path.join(path, name)
  1209.         if name.endswith(".yml") and not name.startswith("."):
  1210.             mtime2 = os.path.getmtime(path2)
  1211.             if mtime <= mtime2:
  1212.                 return True
  1213.         else:
  1214.             mode = os.lstat(path2).st_mode
  1215.             if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
  1216.                 return True
  1217.     return False
  1218.  
  1219.  
  1220. def must_update_item_cache(ctx, path, cache_file):
  1221.     try:
  1222.         mtime = os.path.getmtime(cache_file)
  1223.     except:
  1224.         return True
  1225.     return is_one_item_newer(ctx, path, mtime)
  1226.  
  1227.  
  1228. def load_from_yaml(load, ctx, data_by_uid, base, path):
  1229.     try:
  1230.         names = os.listdir(path)
  1231.     except Exception as e:
  1232.         ctx.fatal("Cannot list build specification directory: {}".format(e))
  1233.     for name in names:
  1234.         path2 = os.path.join(path, name)
  1235.         if name.endswith(".yml") and not name.startswith("."):
  1236.             uid = "/" + os.path.relpath(path2, base).replace(
  1237.                 ".yml", "").replace("\\", "/")
  1238.             with open(path2, "r") as f:
  1239.                 data_by_uid[uid] = load(f.read())
  1240.         else:
  1241.             mode = os.lstat(path2).st_mode
  1242.             if stat.S_ISDIR(mode):
  1243.                 load_from_yaml(load, ctx, data_by_uid, base, path2)
  1244.  
  1245.  
  1246. def load_items_in_directory(ctx, ctors, path):
  1247.     p = re.sub(r"[^\w]", "_", path) + ".pickle"
  1248.     try:
  1249.         f = ctx.bldnode.make_node(p)
  1250.     except AttributeError:
  1251.         f = ctx.path.make_node("build/" + p)
  1252.     f.parent.mkdir()
  1253.     cache_file = f.abspath()
  1254.     data_by_uid = {}
  1255.     if must_update_item_cache(ctx, path, cache_file):
  1256.         from waflib import Logs
  1257.  
  1258.         Logs.warn(
  1259.             "Regenerate build specification cache (needs a couple of seconds)..."
  1260.         )
  1261.  
  1262.         #
  1263.         # Do not use a system provided yaml module and instead import it from
  1264.         # the project.  This reduces the host system requirements to a simple
  1265.         # Python 3 installation without extra modules.
  1266.         #
  1267.         sys.path.append("yaml/lib3")
  1268.         from yaml import safe_load
  1269.  
  1270.         load_from_yaml(safe_load, ctx, data_by_uid, path, path)
  1271.         with open(cache_file, "wb") as f:
  1272.             pickle.dump(data_by_uid, f)
  1273.     else:
  1274.         with open(cache_file, "rb") as f:
  1275.             data_by_uid = pickle.load(f)
  1276.     for uid, data in data_by_uid.items():
  1277.         if data["type"] == "build":
  1278.             items[uid] = ctors[data["build-type"]](uid, data)
  1279.  
  1280.  
  1281. def load_items(ctx, specs):
  1282.     if items:
  1283.         return
  1284.  
  1285.     ctors = {
  1286.         "ada-test-program": AdaTestProgramItem,
  1287.         "bsp": BSPItem,
  1288.         "config-file": ConfigFileItem,
  1289.         "config-header": ConfigHeaderItem,
  1290.         "test-program": TestProgramItem,
  1291.         "group": GroupItem,
  1292.         "library": LibraryItem,
  1293.         "objects": ObjectsItem,
  1294.         "option": OptionItem,
  1295.         "script": ScriptItem,
  1296.         "start-file": StartFileItem,
  1297.     }
  1298.  
  1299.     for path in specs:
  1300.         load_items_in_directory(ctx, ctors, path)
  1301.  
  1302.  
  1303. def load_items_from_options(ctx):
  1304.     specs = ctx.options.rtems_specs
  1305.     if specs is not None:
  1306.         specs = specs.split(",")
  1307.     else:
  1308.         specs = ["spec/build"]
  1309.     load_items(ctx, specs)
  1310.     return specs
  1311.  
  1312.  
  1313. def options(ctx):
  1314.     load_version(ctx)
  1315.     prefix = ctx.parser.get_option("--prefix")
  1316.     prefix.default = default_prefix
  1317.     prefix.help = "installation prefix [default: '{}']".format(default_prefix)
  1318.     rg = ctx.add_option_group("RTEMS options")
  1319.     rg.add_option(
  1320.         "--rtems-bsps",
  1321.         metavar="REGEX,...",
  1322.         help=
  1323.         "a comma-separated list of Python regular expressions which select the desired BSP variants (e.g. 'sparc/erc32'); it may be used in the bspdefaults and bsps commands",
  1324.     )
  1325.     rg.add_option(
  1326.         "--rtems-compiler",
  1327.         metavar="COMPILER",
  1328.         help=
  1329.         "determines which compiler is used to list the BSP option defaults [default: 'gcc']; it may be used in the bspdefaults command; valid compilers are: {}"
  1330.         .format(", ".join(compilers)),
  1331.     )
  1332.     rg.add_option(
  1333.         "--rtems-config",
  1334.         metavar="CONFIG.INI,...",
  1335.         help=
  1336.         "a comma-separated list of paths to the BSP configuration option files [default: 'config.ini']; default option values can be obtained via the bspdefaults command; it may be used in the configure command",
  1337.     )
  1338.     rg.add_option(
  1339.         "--rtems-specs",
  1340.         metavar="SPECDIRS,...",
  1341.         help=
  1342.         "a comma-separated list of directory paths to build specification items [default: 'spec/build']; it may be used in the bspdefaults, bsps, and configure commands",
  1343.     )
  1344.     rg.add_option(
  1345.         "--rtems-tools",
  1346.         metavar="PREFIX,...",
  1347.         help=
  1348.         "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",
  1349.     )
  1350.     rg.add_option(
  1351.         "--rtems-top-group",
  1352.         metavar="UID",
  1353.         help=
  1354.         "the UID of the top-level group [default: '/grp']; it may be used in the bspdefaults and configure commands",
  1355.     )
  1356.  
  1357.  
  1358. def check_environment(conf):
  1359.     for ev in [
  1360.             "AR",
  1361.             "AS",
  1362.             "ASFLAGS",
  1363.             "CC",
  1364.             "CFLAGS",
  1365.             "CPPFLAGS",
  1366.             "CXX",
  1367.             "CXXFLAGS",
  1368.             "IFLAGS",
  1369.             "LD",
  1370.             "LIB",
  1371.             "LINK_CC",
  1372.             "LINK_CXX",
  1373.             "LINKFLAGS",
  1374.             "MFLAGS",
  1375.             "RFLAGS",
  1376.             "WFLAGS",
  1377.     ]:
  1378.         if ev in os.environ:
  1379.             conf.msg("Environment variable set", ev, color="RED")
  1380.  
  1381.  
  1382. def load_version(ctx):
  1383.     global default_prefix
  1384.  
  1385.     def _check_num(s):
  1386.         try:
  1387.             i = int(s)
  1388.         except:
  1389.             ctx.fatal(
  1390.                 "Invalid VERSION number: version number is not a number: " + s)
  1391.  
  1392.     cp = configparser.ConfigParser()
  1393.     version_file = "VERSION"
  1394.     version_major = None
  1395.     version_minor = None
  1396.     version_revision = None
  1397.     version_label = None
  1398.     prefix = None
  1399.     if cp.read([version_file]):
  1400.         try:
  1401.             value = cp.get("version", "revision")
  1402.             # The revision is <major>.<minor>[-label]
  1403.             # break is up and update the version
  1404.             if "." not in value:
  1405.                 ctx.fatal(
  1406.                     "Invalid VERSION revision: no number (dot) separator")
  1407.             # version-string => major.minor[.revsion][-label]
  1408.             vs = value.split(".", 2)
  1409.             _check_num(vs[0])
  1410.             version_major = vs[0]
  1411.             if "-" in vs[-1]:
  1412.                 ls = vs[-1].split("-", 1)
  1413.                 vs[-1] = ls[0]
  1414.                 version_label = ls[1]
  1415.             _check_num(vs[1])
  1416.             version_minor = vs[1]
  1417.             if len(vs) == 3:
  1418.                 _check_num(vs[2])
  1419.                 version_revision = vs[2]
  1420.             prefix = "/opt/rtems/" + version_major
  1421.         except configparser.NoOptionError:
  1422.             pass
  1423.     if version_label is None and version_revision is not None:
  1424.         ctx.fatal(
  1425.             "Invalid VERSION revision: a revision number requires a label")
  1426.     if version_major is not None:
  1427.         version["__RTEMS_MAJOR__"] = version_major
  1428.     if version_minor is not None:
  1429.         version["__RTEMS_MINOR__"] = version_minor
  1430.     if version_revision is not None:
  1431.         version["__RTEMS_REVISION__"] = version_revision
  1432.     if version_label is not None:
  1433.         version["RTEMS_RELEASE_VERSION_LABEL"] = version_label
  1434.     # Checking minor insures major and minor are valid
  1435.     if version_minor is None:
  1436.         version_label = get_repo_release_label(ctx)
  1437.     return version_label
  1438.  
  1439.  
  1440. def configure_version(conf):
  1441.     version_label = load_version(conf)
  1442.     v_str = version["__RTEMS_MAJOR__"] + "." + version["__RTEMS_MINOR__"]
  1443.     if int(version["__RTEMS_REVISION__"]) != 0:
  1444.         v_str += "." + version["__RTEMS_REVISION__"]
  1445.     if version_label is not None and version_label != "":
  1446.         v_str += "." + version_label
  1447.     conf.msg("Configure RTEMS version", v_str, color="YELLOW")
  1448.  
  1449.  
  1450. def load_config_files(ctx):
  1451.     cp = configparser.ConfigParser()
  1452.     files = ctx.options.rtems_config
  1453.     if files is not None:
  1454.         files = files.split(",")
  1455.     else:
  1456.         files = ["config.ini"]
  1457.     actual_files = cp.read(files)
  1458.     for o in files:
  1459.         if not o in actual_files:
  1460.             ctx.fatal("Option file '{}' was not readable".format(o))
  1461.     return cp
  1462.  
  1463.  
  1464. def inherit(conf, cp, bsp_map, arch, bsp, path):
  1465.     variant = arch + "/" + bsp
  1466.     if variant in path:
  1467.         path = " -> ".join(path + [variant])
  1468.         conf.fatal("Recursion in BSP options inheritance: {}".format(path))
  1469.  
  1470.     try:
  1471.         base = cp.get(variant, "INHERIT")
  1472.         cp.remove_option(variant, "INHERIT")
  1473.         base_variant = arch + "/" + base
  1474.         conf.msg(
  1475.             "Inherit options from '{}'".format(base_variant),
  1476.             variant,
  1477.             color="YELLOW",
  1478.         )
  1479.         if not cp.has_section(base_variant):
  1480.             if (not arch in bsps) or (not base in bsps[arch]):
  1481.                 conf.fatal(
  1482.                     "BSP variant '{}' cannot inherit options from not existing variant '{}'"
  1483.                     .format(variant, base_variant))
  1484.             bsp_map[bsp] = base
  1485.             return base
  1486.         top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
  1487.         for i in cp.items(base_variant):
  1488.             name = i[0]
  1489.             if not cp.has_option(variant, name):
  1490.                 cp.set(variant, name, i[1])
  1491.         bsp_map[bsp] = top
  1492.         return top
  1493.     except configparser.NoOptionError:
  1494.         return bsp_map.get(bsp, bsp)
  1495.  
  1496.  
  1497. def resolve_option_inheritance(conf, cp):
  1498.     bsp_map = {}
  1499.     for variant in cp.sections():
  1500.         try:
  1501.             arch, bsp = variant.split("/")
  1502.         except:
  1503.             conf.fatal(
  1504.                 "Section name '{}' is a malformed 'arch/bsp' tuple".format(
  1505.                     variant))
  1506.         inherit(conf, cp, bsp_map, arch, bsp, [])
  1507.     return bsp_map
  1508.  
  1509.  
  1510. def check_compiler(ctx, compiler):
  1511.     if compiler not in compilers:
  1512.         ctx.fatal("Specified compiler '{}' is not one of {}".format(
  1513.             compiler, compilers))
  1514.  
  1515.  
  1516. def get_compiler(conf, cp, variant):
  1517.     try:
  1518.         value = cp.get(variant, "COMPILER")
  1519.         cp.remove_option(variant, "COMPILER")
  1520.         check_compiler(conf, value)
  1521.     except configparser.NoOptionError:
  1522.         value = "gcc"
  1523.     return value
  1524.  
  1525.  
  1526. def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
  1527.     conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
  1528.  
  1529.     conf.setenv(variant)
  1530.     arch, bsp_name = variant.split("/")
  1531.     bsp_base = bsp_map.get(bsp_name, bsp_name)
  1532.  
  1533.     try:
  1534.         bsp_item = bsps[arch][bsp_base]
  1535.     except KeyError:
  1536.         conf.fatal("No such base BSP: '{}'".format(variant))
  1537.  
  1538.     family = bsp_item.data["family"]
  1539.  
  1540.     arch_bsp = arch + "/" + bsp_base
  1541.     arch_family = arch + "/" + family
  1542.  
  1543.     for key, value in version.items():
  1544.         conf.env[key] = value
  1545.  
  1546.     conf.env["ARCH"] = arch
  1547.     conf.env["ARCH_BSP"] = arch_bsp
  1548.     conf.env["ARCH_FAMILY"] = arch_family
  1549.     conf.env["BSP_BASE"] = bsp_base
  1550.     conf.env["BSP_NAME"] = bsp_name
  1551.     conf.env["BSP_FAMILY"] = family
  1552.     conf.env["DEST_OS"] = "rtems"
  1553.  
  1554.     # For the enabled-by evaluation we have to use the base BSP defined by the
  1555.     # build specification and not the BSP name provided by the user.
  1556.     conf.env["ENABLE"] = [
  1557.         get_compiler(conf, cp, variant),
  1558.         arch,
  1559.         "bsps/" + arch_family,
  1560.         arch_bsp,
  1561.     ]
  1562.  
  1563.     conf.env["TOP"] = conf.path.abspath()
  1564.     conf.env["TOPGROUP"] = top_group
  1565.     conf.env["VARIANT"] = variant
  1566.  
  1567.     cic = ConfigItemContext(cp, path_list)
  1568.     items[conf.env.TOPGROUP].configure(conf, cic)
  1569.     bsp_item.configure(conf, cic)
  1570.  
  1571.     options = set([o[0].upper() for o in cp.items(variant)])
  1572.     for o in options.difference(cic.options):
  1573.         conf.msg("Unknown configuration option", o.upper(), color="RED")
  1574.  
  1575.  
  1576. def check_forbidden_options(ctx, opts):
  1577.     for o in opts:
  1578.         if getattr(ctx.options, "rtems_" + o):
  1579.             ctx.fatal(
  1580.                 "The --rtems-{} command line option is not allowed in the {} command"
  1581.                 .format(o.replace("_", "-"), ctx.cmd))
  1582.  
  1583.  
  1584. def get_path_list(conf):
  1585.     path_list = []
  1586.     tools = conf.options.rtems_tools
  1587.     if tools is not None:
  1588.         for t in tools.split(","):
  1589.             path_list.extend([t + "/bin", t])
  1590.     path_list.append(conf.env.PREFIX + "/bin")
  1591.     path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
  1592.     return path_list
  1593.  
  1594.  
  1595. def get_top_group(ctx):
  1596.     top_group = ctx.options.rtems_top_group
  1597.     if top_group is None:
  1598.         top_group = "/grp"
  1599.     if top_group not in items:
  1600.         ctx.fatal(
  1601.             "There is no top-level group with UID '{}' in the specification".
  1602.             format(top_group))
  1603.     return top_group
  1604.  
  1605.  
  1606. def configure(conf):
  1607.     check_forbidden_options(conf, ["compiler"])
  1608.     configure_version(conf)
  1609.     check_environment(conf)
  1610.     conf.env["SPECS"] = load_items_from_options(conf)
  1611.     top_group = get_top_group(conf)
  1612.     cp = load_config_files(conf)
  1613.     bsp_map = resolve_option_inheritance(conf, cp)
  1614.     path_list = get_path_list(conf)
  1615.     variant_list = []
  1616.     for variant in cp.sections():
  1617.         variant_list.append(variant)
  1618.         configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
  1619.     conf.setenv("")
  1620.     conf.env["VARIANTS"] = variant_list
  1621.  
  1622.  
  1623. def append_variant_builds(bld):
  1624.     import waflib.Options
  1625.     from waflib.Build import (
  1626.         BuildContext,
  1627.         CleanContext,
  1628.         InstallContext,
  1629.         UninstallContext,
  1630.     )
  1631.  
  1632.     for var in bld.env["VARIANTS"]:
  1633.         for c in (BuildContext, CleanContext, InstallContext,
  1634.                   UninstallContext):
  1635.             name = c.__name__.replace("Context", "").lower()
  1636.  
  1637.             class rtems_context(c):
  1638.                 cmd = name + "_" + var
  1639.                 variant = var
  1640.                 last_variant = var == bld.env["VARIANTS"][-1]
  1641.  
  1642.                 def compile(self):
  1643.                     global variant_errors
  1644.                     try:
  1645.                         super().compile()
  1646.                     except:
  1647.                         variant_errors += [self.variant]
  1648.                         if not self.keep:
  1649.                             raise
  1650.                     if self.keep and self.last_variant and len(
  1651.                             variant_errors) > 0:
  1652.                         raise self.fatal('error: BSP(s) in errors: ' +
  1653.                                          ' '.join(variant_errors))
  1654.  
  1655.         waflib.Options.commands.append(bld.cmd + "_" + var)
  1656.  
  1657.  
  1658. def build(bld):
  1659.     if not bld.variant:
  1660.         check_forbidden_options(
  1661.             bld,
  1662.             ["compiler", "config", "specs", "tools", "top_group"],
  1663.         )
  1664.         load_items(bld, bld.env.SPECS)
  1665.         append_variant_builds(bld)
  1666.         return
  1667.     bic = BuildItemContext(bld.env.ARCH_INCLUDES.split(), [], [], [], [], [],
  1668.                            [])
  1669.     bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
  1670.     items[bld.env.TOPGROUP].build(bld, bic)
  1671.  
  1672.  
  1673. def add_log_filter(name):
  1674.     msg = "'" + name + "' finished successfully"
  1675.  
  1676.     class Filter:
  1677.  
  1678.         def filter(self, rec):
  1679.             return not msg in rec.getMessage()
  1680.  
  1681.     import logging
  1682.  
  1683.     logging.getLogger("waflib").addFilter(Filter())
  1684.  
  1685.  
  1686. def get_white_list(ctx):
  1687.     white_list = ctx.options.rtems_bsps
  1688.     if white_list:
  1689.         white_list = white_list.split(",")
  1690.     return white_list
  1691.  
  1692.  
  1693. def is_in_white_list(variant, white_list):
  1694.     if not white_list:
  1695.         return True
  1696.     for pattern in white_list:
  1697.         if re.match(pattern + "$", variant):
  1698.             return True
  1699.     return False
  1700.  
  1701.  
  1702. def no_matches_error(ctx, white_list):
  1703.     if white_list:
  1704.         ctx.fatal("No BSP matches with the specified patterns: '{}'".format(
  1705.             "', '".join(white_list)))
  1706.     else:
  1707.         ctx.fatal("The build specification contains no BSPs")
  1708.  
  1709.  
  1710. def bspdefaults(ctx):
  1711.     """get all options with default values for base BSP variants"""
  1712.     check_forbidden_options(ctx, ["config", "tools"])
  1713.     add_log_filter(ctx.cmd)
  1714.     load_items_from_options(ctx)
  1715.     top_group = get_top_group(ctx)
  1716.     white_list = get_white_list(ctx)
  1717.     compiler = ctx.options.rtems_compiler
  1718.     if compiler is not None:
  1719.         check_compiler(ctx, compiler)
  1720.     else:
  1721.         compiler = "gcc"
  1722.     first = True
  1723.     for arch in sorted(bsps):
  1724.         for bsp in sorted(bsps[arch]):
  1725.             variant = arch + "/" + bsp
  1726.             if is_in_white_list(variant, white_list):
  1727.                 if not first:
  1728.                     print("")
  1729.                 first = False
  1730.                 print("""[{}]
  1731. # Selects the compiler used to build the BSP (allowed values are "gcc" and
  1732. # "clang").  Please note that the values of some options depend on the compiler
  1733. # selection and changing the compiler may lead to unpredictable behaviour if
  1734. # these options are not adjusted as well.  Use the --rtems-compiler command line
  1735. # option to get the default values for a particular compiler via
  1736. # ./waf bspdefaults.
  1737. COMPILER = {}""".format(variant, compiler))
  1738.                 enable = [compiler, arch, variant]
  1739.                 bsp_item = bsps[arch][bsp]
  1740.                 family = "bsps/" + arch + "/" + bsp_item.data["family"]
  1741.                 enabled = [compiler, arch, family, variant]
  1742.                 items[top_group].defaults(enabled)
  1743.                 bsp_item.defaults(enabled)
  1744.     if first:
  1745.         no_matches_error(ctx, white_list)
  1746.  
  1747.  
  1748. def bsplist(ctx):
  1749.     """lists base BSP variants"""
  1750.     check_forbidden_options(ctx, ["compiler", "config", "tools", "top_group"])
  1751.     add_log_filter(ctx.cmd)
  1752.     load_items_from_options(ctx)
  1753.     white_list = get_white_list(ctx)
  1754.     first = True
  1755.     for arch in sorted(bsps):
  1756.         for bsp in sorted(bsps[arch]):
  1757.             variant = arch + "/" + bsp
  1758.             if is_in_white_list(variant, white_list):
  1759.                 first = False
  1760.                 print(variant)
  1761.     if first:
  1762.         no_matches_error(ctx, white_list)
  1763. #!/usr/bin/env python
  1764.  
  1765. # SPDX-License-Identifier: BSD-2-Clause
  1766. #
  1767. # Copyright (C) 2020 Hesham Almatary <[email protected]>
  1768. # Copyright (C) 2019, 2020 embedded brains GmbH & Co. KG
  1769. # Copyright (C) 2024 Contemporary Software <[email protected]>
  1770. #
  1771. # Redistribution and use in source and binary forms, with or without
  1772. # modification, are permitted provided that the following conditions
  1773. # are met:
  1774. # 1. Redistributions of source code must retain the above copyright
  1775. #    notice, this list of conditions and the following disclaimer.
  1776. # 2. Redistributions in binary form must reproduce the above copyright
  1777. #    notice, this list of conditions and the following disclaimer in the
  1778. #    documentation and/or other materials provided with the distribution.
  1779. #
  1780. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  1781. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  1782. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  1783. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  1784. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  1785. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  1786. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  1787. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  1788. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  1789. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  1790. # POSSIBILITY OF SUCH DAMAGE.
  1791.  
  1792. import pickle
  1793. import os
  1794. import re
  1795. import stat
  1796. import string
  1797. import sys
  1798.  
  1799. try:
  1800.     import configparser
  1801. except:
  1802.     import ConfigParser as configparser
  1803.  
  1804. from waflib.TaskGen import after, before_method, feature
  1805.  
  1806. version = {
  1807.     "__RTEMS_MAJOR__": "7",  # version
  1808.     "__RTEMS_MINOR__": "0",  # revision
  1809.     "__RTEMS_REVISION__": "0",  # not used
  1810.     "RTEMS_RELEASE_VERSION_LABEL": "not-released"
  1811. }
  1812. default_prefix = "/opt/rtems/" + version["__RTEMS_MAJOR__"]
  1813. compilers = ["gcc", "clang"]
  1814. items = {}
  1815. bsps = {}
  1816. variant_errors = []
  1817.  
  1818.  
  1819. def get_repo_release_label(ctx):
  1820.     from waflib.Build import Context
  1821.     from waflib.Errors import WafError
  1822.     release_label = "not-released"
  1823.     try:
  1824.         modified = ctx.cmd_and_log("git ls-files --modified",
  1825.                                    quiet=Context.BOTH).strip()
  1826.         release_label = ctx.cmd_and_log("git rev-parse HEAD",
  1827.                                         quiet=Context.BOTH).strip()
  1828.         if modified:
  1829.             release_label += "-modified"
  1830.     except WafError:
  1831.         pass
  1832.     return release_label
  1833.  
  1834.  
  1835. #
  1836. # This class is not aligned the project's release labelling. It will
  1837. # not be changed for RTEMS 6. A VC key is a type of release label and
  1838. # there are more types.
  1839. #
  1840. # The term `key` and a repo normally relates to the signing key of
  1841. # repository which is security related and it is important we do not
  1842. # imply this here. They are unrelated.
  1843. #
  1844. class VersionControlKeyHeader:
  1845.     _content = None
  1846.  
  1847.     @staticmethod
  1848.     def write(bld, filename):
  1849.         content = VersionControlKeyHeader._content
  1850.         if content is None:
  1851.             content = """/*
  1852. * Automatically generated. Do not edit.
  1853. *
  1854. * Warning: This interface will be replaced with release labels
  1855. *          after RTEMS 6.
  1856. */
  1857. #if !defined(_RTEMS_VERSION_VC_KEY_H_)
  1858. #define _RTEMS_VERSION_VC_KEY_H_
  1859. """
  1860.             release_label = bld.env.RTEMS_RELEASE_VERSION_LABEL
  1861.             if release_label == "not-released":
  1862.                 release_label = get_repo_release_label(bld)
  1863.             if release_label:
  1864.                 content += """#define RTEMS_VERSION_CONTROL_KEY "{}"
  1865. """.format(release_label)
  1866.             content += """#endif
  1867. """
  1868.             VersionControlKeyHeader._content = content
  1869.         f = bld.bldnode.make_node(filename)
  1870.         f.parent.mkdir()
  1871.         try:
  1872.             if content != f.read():
  1873.                 f.write(content)
  1874.         except:
  1875.             f.write(content)
  1876.  
  1877.  
  1878. class EnvWrapper(object):
  1879.  
  1880.     def __init__(self, env):
  1881.         self._env = env
  1882.  
  1883.     def __getitem__(self, name):
  1884.         k, c, f = name.partition(":")
  1885.         v = self._env[k]
  1886.         fmt = "{" + c + f + "}"
  1887.         if isinstance(v, list):
  1888.             return " ".join([fmt.format(w) for w in v])
  1889.         return fmt.format(v)
  1890.  
  1891.  
  1892. class Template(string.Template):
  1893.     idpattern = "[_A-Za-z][_A-Za-z0-9:#]*"
  1894.  
  1895.  
  1896. _VAR_PATTERN = re.compile("\\$\\{?(" + Template.idpattern + ")\\}?$")
  1897.  
  1898.  
  1899. def _is_enabled_op_and(enabled, enabled_by):
  1900.     for next_enabled_by in enabled_by:
  1901.         if not _is_enabled(enabled, next_enabled_by):
  1902.             return False
  1903.     return True
  1904.  
  1905.  
  1906. def _is_enabled_op_not(enabled, enabled_by):
  1907.     return not _is_enabled(enabled, enabled_by)
  1908.  
  1909.  
  1910. def _is_enabled_op_or(enabled, enabled_by):
  1911.     for next_enabled_by in enabled_by:
  1912.         if _is_enabled(enabled, next_enabled_by):
  1913.             return True
  1914.     return False
  1915.  
  1916.  
  1917. _IS_ENABLED_OP = {
  1918.     "and": _is_enabled_op_and,
  1919.     "not": _is_enabled_op_not,
  1920.     "or": _is_enabled_op_or,
  1921. }
  1922.  
  1923.  
  1924. def _is_enabled(enabled, enabled_by):
  1925.     if isinstance(enabled_by, bool):
  1926.         return enabled_by
  1927.     if isinstance(enabled_by, list):
  1928.         return _is_enabled_op_or(enabled, enabled_by)
  1929.     if isinstance(enabled_by, dict):
  1930.         key, value = next(iter(enabled_by.items()))
  1931.         return _IS_ENABLED_OP[key](enabled, value)
  1932.     return enabled_by in enabled
  1933.  
  1934.  
  1935. def _asm_explicit_target(self, node):
  1936.     task = self.create_task("asm", node,
  1937.                             self.bld.bldnode.make_node(self.target))
  1938.     try:
  1939.         self.compiled_tasks.append(task)
  1940.     except AttributeError:
  1941.         self.compiled_tasks = [task]
  1942.     return task
  1943.  
  1944.  
  1945. @feature("asm_explicit_target")
  1946. @before_method("process_source")
  1947. def _enable_asm_explicit_target(self):
  1948.     self.mappings = dict(self.mappings)  # Copy
  1949.     self.mappings[".S"] = _asm_explicit_target
  1950.  
  1951.  
  1952. @after("apply_link")
  1953. @feature("cprogram", "cxxprogram")
  1954. def process_start_files(self):
  1955.     if getattr(self, "start_files", False):
  1956.         self.link_task.dep_nodes.extend(self.bld.start_files)
  1957.  
  1958.  
  1959. def make_tar_info_reproducible(info):
  1960.     # Reduce dependency on umask settings
  1961.     info.mode &= ~(stat.S_IRWXG | stat.S_IRWXO)
  1962.  
  1963.     info.uid = 0
  1964.     info.gid = 0
  1965.     info.mtime = 0
  1966.     info.uname = "root"
  1967.     info.gname = "root"
  1968.     return info
  1969.  
  1970.  
  1971. class Item(object):
  1972.  
  1973.     def __init__(self, uid, data):
  1974.         self.uid = uid
  1975.         self.data = data
  1976.         self.links = self._init_links
  1977.  
  1978.     def _init_links(self):
  1979.         self._links = []
  1980.         for link in self.data["links"]:
  1981.             if link["role"] == "build-dependency":
  1982.                 uid = link["uid"]
  1983.                 if not os.path.isabs(uid):
  1984.                     uid = os.path.normpath(
  1985.                         os.path.join(os.path.dirname(self.uid),
  1986.                                      uid)).replace("\\", "/")
  1987.                 self._links.append(items[uid])
  1988.         self.links = self._yield_links
  1989.         for link in self._links:
  1990.             yield link
  1991.  
  1992.     def _yield_links(self):
  1993.         for link in self._links:
  1994.             yield link
  1995.  
  1996.     def get_enabled_by(self):
  1997.         return self.data["enabled-by"]
  1998.  
  1999.     def defaults(self, enabled):
  2000.         if _is_enabled(enabled, self.get_enabled_by()):
  2001.             for p in self.links():
  2002.                 p.defaults(enabled)
  2003.             self.do_defaults(enabled)
  2004.  
  2005.     def configure(self, conf, cic):
  2006.         if _is_enabled(conf.env.ENABLE, self.get_enabled_by()):
  2007.             self.prepare_configure(conf, cic)
  2008.             for p in self.links():
  2009.                 p.configure(conf, cic)
  2010.             try:
  2011.                 self.do_configure(conf, cic)
  2012.             except Exception as e:
  2013.                 raise type(e)(
  2014.                     "Configuration error related to item spec:{}: {}".format(
  2015.                         self.uid, str(e)))
  2016.  
  2017.     def build(self, bld, bic):
  2018.         if _is_enabled(bld.env.ENABLE, self.get_enabled_by()):
  2019.             bic = self.prepare_build(bld, bic)
  2020.             for p in self.links():
  2021.                 p.build(bld, bic)
  2022.             try:
  2023.                 self.do_build(bld, bic)
  2024.             except Exception as e:
  2025.                 raise type(e)("Build error related to item spec:{}: {}".format(
  2026.                     self.uid, str(e)))
  2027.  
  2028.     def do_defaults(self, enabled):
  2029.         return
  2030.  
  2031.     def prepare_configure(self, conf, cic):
  2032.         return
  2033.  
  2034.     def do_configure(self, conf, cic):
  2035.         return
  2036.  
  2037.     def prepare_build(self, bld, bic):
  2038.         return bic
  2039.  
  2040.     def do_build(self, bld, bic):
  2041.         return
  2042.  
  2043.     def substitute(self, ctx, value):
  2044.         if isinstance(value, str):
  2045.             try:
  2046.                 return Template(value).substitute(EnvWrapper(ctx.env))
  2047.             except Exception as e:
  2048.                 ctx.fatal(
  2049.                     "In item '{}' substitution in '{}' failed: {}".format(
  2050.                         self.uid, value, e))
  2051.         if isinstance(value, list):
  2052.             more = []
  2053.             for item in value:
  2054.                 if isinstance(item, str):
  2055.                     m = _VAR_PATTERN.match(item)
  2056.                 else:
  2057.                     m = None
  2058.                 if m:
  2059.                     more.extend(ctx.env[m.group(1).strip("{}")])
  2060.                 else:
  2061.                     more.append(self.substitute(ctx, item))
  2062.             return more
  2063.         return value
  2064.  
  2065.     def get(self, ctx, name):
  2066.         return self.substitute(ctx, self.data[name])
  2067.  
  2068.     def install_target(self, bld):
  2069.         install_path = self.data["install-path"]
  2070.         if install_path:
  2071.             bld.install_files(install_path, self.get(bld, "target"))
  2072.  
  2073.     def install_files(self, bld):
  2074.         for install in self.data["install"]:
  2075.             bld.install_files(install["destination"], install["source"])
  2076.  
  2077.     def asm(self, bld, bic, source, target=None):
  2078.         if target is None:
  2079.             target = os.path.splitext(source)[0] + ".o"
  2080.         bld(
  2081.             asflags=self.substitute(bld, self.data["asflags"]),
  2082.             cppflags=bic.cppflags +
  2083.             self.substitute(bld, self.data["cppflags"]),
  2084.             features="asm_explicit_target asm c",
  2085.             includes=bic.includes +
  2086.             self.substitute(bld, self.data["includes"]),
  2087.             source=[source],
  2088.             target=target,
  2089.         )
  2090.         return target
  2091.  
  2092.     def cc(self, bld, bic, source, target=None, deps=[], cppflags=[]):
  2093.         if target is None:
  2094.             target = os.path.splitext(source)[0] + ".o"
  2095.         bld(
  2096.             cflags=bic.cflags + self.substitute(bld, self.data["cflags"]),
  2097.             cppflags=bic.cppflags + cppflags +
  2098.             self.substitute(bld, self.data["cppflags"]),
  2099.             features="c",
  2100.             includes=bic.includes +
  2101.             self.substitute(bld, self.data["includes"]),
  2102.             rule=
  2103.             "${CC} ${CFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
  2104.             source=[source] + deps,
  2105.             target=target,
  2106.         )
  2107.         return target
  2108.  
  2109.     def cxx(self, bld, bic, source, target=None, deps=[], cppflags=[]):
  2110.         if target is None:
  2111.             target = os.path.splitext(source)[0] + ".o"
  2112.         bld(
  2113.             cppflags=bic.cppflags + cppflags +
  2114.             self.substitute(bld, self.data["cppflags"]),
  2115.             cxxflags=bic.cxxflags +
  2116.             self.substitute(bld, self.data["cxxflags"]),
  2117.             features="cxx",
  2118.             includes=bic.includes +
  2119.             self.substitute(bld, self.data["includes"]),
  2120.             rule=
  2121.             "${CXX} ${CXXFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
  2122.             source=[source] + deps,
  2123.             target=target,
  2124.         )
  2125.         return target
  2126.  
  2127.     def link(self, bld, bic, cmd, source, target):
  2128.         from waflib.Task import Task
  2129.  
  2130.         class link(Task):
  2131.  
  2132.             def __init__(self, item, bic, cmd, env, ldflags):
  2133.                 super(link, self).__init__(self, env=env)
  2134.                 self.cmd = cmd
  2135.                 self.ldflags = ldflags
  2136.                 self.stlib = item.data["stlib"]
  2137.                 self.use = (item.data["use-before"] + bic.use +
  2138.                             item.data["use-after"])
  2139.  
  2140.             def run(self):
  2141.                 cmd = [self.cmd]
  2142.                 cmd.extend(self.env.LINKFLAGS)
  2143.                 cmd.extend([i.abspath() for i in self.inputs])
  2144.                 cmd.append("-o" + self.outputs[0].abspath())
  2145.                 cmd.append("-L.")
  2146.                 cmd.extend(["-l" + l for l in self.stlib])
  2147.                 cmd.extend(["-l" + l for l in self.use])
  2148.                 cmd.extend(self.env.LDFLAGS)
  2149.                 cmd.extend(self.ldflags)
  2150.                 return self.exec_command(cmd)
  2151.  
  2152.             def scan(self):
  2153.                 return (
  2154.                     [
  2155.                         self.generator.bld.bldnode.make_node("lib" + u + ".a")
  2156.                         for u in self.use
  2157.                     ],
  2158.                     [],
  2159.                 )
  2160.  
  2161.         tsk = link(self, bic, cmd, bld.env,
  2162.                    bic.ldflags + self.substitute(bld, self.data["ldflags"]))
  2163.         tsk.set_inputs([bld.bldnode.make_node(s) for s in source])
  2164.         tsk.set_outputs(bld.bldnode.make_node(target))
  2165.         bld.add_to_group(tsk)
  2166.         return target
  2167.  
  2168.     def link_cc(self, bld, bic, source, target):
  2169.         return self.link(bld, bic, bld.env.LINK_CC[0], source, target)
  2170.  
  2171.     def link_cxx(self, bld, bic, source, target):
  2172.         return self.link(bld, bic, bld.env.LINK_CXX[0], source, target)
  2173.  
  2174.     def gnatmake(self, bld, bic, objdir, objs, main, target):
  2175.         from waflib.Task import Task
  2176.  
  2177.         class gnatmake(Task):
  2178.  
  2179.             def __init__(self, bld, bic, objdir, objs, main, target, item):
  2180.                 super(gnatmake, self).__init__(self, env=bld.env)
  2181.                 self.objdir = objdir
  2182.                 self.objs = [bld.bldnode.make_node(o) for o in objs]
  2183.                 self.main = bld.path.make_node(main)
  2184.                 self.set_inputs(self.objs + [self.main])
  2185.                 self.set_outputs(bld.bldnode.make_node(target))
  2186.                 self.adaflags = item.data["adaflags"]
  2187.                 self.adaincludes = []
  2188.                 for i in item.data["adaincludes"]:
  2189.                     self.adaincludes.append(bld.bldnode.make_node(i))
  2190.                     self.adaincludes.append(bld.path.make_node(i))
  2191.                 self.ldflags = bic.ldflags + item.data["ldflags"]
  2192.                 self.stlib = item.data["stlib"]
  2193.                 self.use = (item.data["use-before"] + bic.use +
  2194.                             item.data["use-after"])
  2195.  
  2196.             def run(self):
  2197.                 cwd = self.get_cwd()
  2198.                 cmd = [
  2199.                     self.env.GNATMAKE[0],
  2200.                     "-D",
  2201.                     self.objdir,
  2202.                     "-bargs",
  2203.                     "-Mgnat_main",
  2204.                     "-margs",
  2205.                 ]
  2206.                 cmd.extend(self.adaflags)
  2207.                 cmd.extend(["-I" + i.path_from(cwd) for i in self.adaincludes])
  2208.                 cmd.append("-cargs")
  2209.                 cmd.extend(self.env.ABI_FLAGS)
  2210.                 cmd.append("-largs")
  2211.                 cmd.extend([o.path_from(cwd) for o in self.objs])
  2212.                 cmd.extend(self.env.LINKFLAGS)
  2213.                 cmd.extend(self.ldflags)
  2214.                 cmd.append("-L.")
  2215.                 cmd.extend(["-l" + l for l in self.stlib])
  2216.                 cmd.extend(["-l" + l for l in self.use])
  2217.                 cmd.extend(self.env.LDFLAGS)
  2218.                 cmd.extend(["-margs", "-a"])
  2219.                 cmd.append(self.main.abspath())
  2220.                 cmd.append("-o")
  2221.                 cmd.append(self.outputs[0].abspath())
  2222.                 return self.exec_command(cmd)
  2223.  
  2224.             def scan(self):
  2225.                 return (
  2226.                     [
  2227.                         self.generator.bld.bldnode.make_node("lib" + u + ".a")
  2228.                         for u in self.use
  2229.                     ],
  2230.                     [],
  2231.                 )
  2232.  
  2233.         tsk = gnatmake(bld, bic, objdir, objs, main, target, self)
  2234.         bld.add_to_group(tsk)
  2235.         return target
  2236.  
  2237.     def ar(self, bld, source, target):
  2238.         bld(rule="${AR} ${ARFLAGS} ${TGT} ${SRC}",
  2239.             source=source,
  2240.             target=target)
  2241.         return target
  2242.  
  2243.     def gzip(self, bld, source):
  2244.         target = source + ".gz"
  2245.         bld(rule="${GZIP} -n < ${SRC} > ${TGT}", source=source, target=target)
  2246.         return target
  2247.  
  2248.     def xz(self, bld, source):
  2249.         target = source + ".xz"
  2250.         bld(rule="${XZ} < ${SRC} > ${TGT}", source=source, target=target)
  2251.         return target
  2252.  
  2253.     def tar(self, bld, source, remove, target):
  2254.  
  2255.         def run(task):
  2256.             import tarfile
  2257.  
  2258.             tar = tarfile.TarFile(task.outputs[0].abspath(),
  2259.                                   "w",
  2260.                                   format=tarfile.USTAR_FORMAT)
  2261.             srcpath = bld.path.abspath() + "/"
  2262.             bldpath = bld.bldnode.abspath() + "/"
  2263.             for src in task.inputs:
  2264.                 src = src.abspath()
  2265.                 dst = src
  2266.                 for r in remove:
  2267.                     dst = src.replace(srcpath + r, "").replace(bldpath + r, "")
  2268.                 tar.add(src, dst, filter=make_tar_info_reproducible)
  2269.             tar.close()
  2270.             return 0
  2271.  
  2272.         bld(rule=run, source=source, target=target)
  2273.         return target
  2274.  
  2275.     def bin2c(self, bld, source, name=None, target=None):
  2276.  
  2277.         def run(task):
  2278.             cmd = [bld.env.BIN2C[0]]
  2279.             if name is not None:
  2280.                 cmd.extend(["-N", name])
  2281.             cmd.append(task.inputs[0].abspath())
  2282.             cmd.append(task.outputs[0].abspath())
  2283.             return task.exec_command(cmd)
  2284.  
  2285.         path, base = os.path.split(source)
  2286.         if target is None:
  2287.             target = path + "/" + base.replace(".", "-")
  2288.         target_c = target + ".c"
  2289.         target_h = target + ".h"
  2290.         bld(rule=run, source=source, target=[target_c, target_h])
  2291.         return target_c, target_h
  2292.  
  2293.     def rtems_syms(self, bld, bic, source, target):
  2294.         syms_source = os.path.splitext(target)[0] + ".c"
  2295.         bld(
  2296.             rule='${RTEMS_SYMS} -e -S ${TGT} ${SRC}',
  2297.             source=source,
  2298.             target=syms_source,
  2299.         )
  2300.         return self.cc(bld, bic, syms_source, target)
  2301.  
  2302.     def rtems_rap(self, bld, base, objects, libs, target):
  2303.  
  2304.         def run(task):
  2305.             cmd = [
  2306.                 bld.env.RTEMS_LD[0],
  2307.                 "-C",
  2308.                 bld.env.CC[0],
  2309.                 "-c",
  2310.                 " ".join(bld.env.CFLAGS),
  2311.                 "-O",
  2312.                 "rap",
  2313.                 "-b",
  2314.                 task.inputs[0].abspath(),
  2315.                 "-e",
  2316.                 "rtems_main",
  2317.                 "-s",
  2318.                 "-o",
  2319.             ]
  2320.             cmd.append(task.outputs[0].abspath())
  2321.             cmd.extend([i.abspath() for i in task.inputs[1:]])
  2322.             cmd.extend(["-l" + l for l in libs])
  2323.             return task.exec_command(cmd)
  2324.  
  2325.         bld(rule=run, source=[base] + objects, target=target)
  2326.         return target
  2327.  
  2328.  
  2329. class GroupItem(Item):
  2330.  
  2331.     def __init__(self, uid, data):
  2332.         super(GroupItem, self).__init__(uid, data)
  2333.  
  2334.     def prepare_build(self, bld, bic):
  2335.         return BuildItemContext(
  2336.             bic.includes + self.substitute(bld, self.data["includes"]),
  2337.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  2338.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  2339.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  2340.             self.data["use-before"] + bic.use + self.data["use-after"],
  2341.             bic.ldflags + self.substitute(bld, self.data["ldflags"]),
  2342.             bic.objects,
  2343.         )
  2344.  
  2345.     def do_build(self, bld, bic):
  2346.         self.install_files(bld)
  2347.  
  2348.  
  2349. class ConfigFileItem(Item):
  2350.  
  2351.     def __init__(self, uid, data):
  2352.         super(ConfigFileItem, self).__init__(uid, data)
  2353.  
  2354.     def do_configure(self, conf, cic):
  2355.         content = self.substitute(conf, self.data["content"])
  2356.         f = conf.bldnode.make_node(conf.env.VARIANT + "/" +
  2357.                                    self.get(conf, "target"))
  2358.         f.parent.mkdir()
  2359.         f.write(content)
  2360.         conf.env.append_value("cfg_files", f.abspath())
  2361.  
  2362.     def do_build(self, bld, bic):
  2363.         self.install_target(bld)
  2364.  
  2365.  
  2366. class ConfigHeaderItem(Item):
  2367.  
  2368.     def __init__(self, uid, data):
  2369.         super(ConfigHeaderItem, self).__init__(uid, data)
  2370.  
  2371.     def do_configure(self, conf, cic):
  2372.         conf.env.include_key = self.data["include-headers"]
  2373.         conf.write_config_header(
  2374.             conf.env.VARIANT + "/" + self.get(conf, "target"),
  2375.             guard=self.data["guard"],
  2376.             headers=True,
  2377.         )
  2378.         conf.env.include_key = None
  2379.  
  2380.     def do_build(self, bld, bic):
  2381.         self.install_target(bld)
  2382.  
  2383.  
  2384. class StartFileItem(Item):
  2385.  
  2386.     def __init__(self, uid, data):
  2387.         super(StartFileItem, self).__init__(uid, data)
  2388.  
  2389.     def do_build(self, bld, bic):
  2390.         source = self.data["source"]
  2391.         if os.path.splitext(source[0])[1] == ".S":
  2392.             tgt = self.asm(bld, bic, source, self.get(bld, "target"))
  2393.         else:
  2394.             tgt = self.cc(bld, bic, source, self.get(bld, "target"))
  2395.         node = bld.bldnode.make_node(tgt)
  2396.         try:
  2397.             bld.start_files.append(node)
  2398.         except AttributeError:
  2399.             bld.start_files = [node]
  2400.         self.install_target(bld)
  2401.  
  2402.  
  2403. class ObjectsItem(Item):
  2404.  
  2405.     def __init__(self, uid, data):
  2406.         super(ObjectsItem, self).__init__(uid, data)
  2407.  
  2408.     def prepare_build(self, bld, bic):
  2409.         return BuildItemContext(
  2410.             bic.includes + self.substitute(bld, self.data["includes"]),
  2411.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  2412.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  2413.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  2414.             bic.use,
  2415.             bic.ldflags,
  2416.             bic.objects,
  2417.         )
  2418.  
  2419.     def do_build(self, bld, bic):
  2420.         bld.objects(
  2421.             asflags=bic.cppflags,
  2422.             cflags=bic.cflags,
  2423.             cppflags=bic.cppflags,
  2424.             cxxflags=bic.cxxflags,
  2425.             includes=bic.includes,
  2426.             source=self.data["source"],
  2427.             target=self.uid,
  2428.         )
  2429.         bic.objects.append(self.uid)
  2430.         self.install_files(bld)
  2431.  
  2432.  
  2433. class BSPItem(Item):
  2434.  
  2435.     def __init__(self, uid, data):
  2436.         super(BSPItem, self).__init__(uid, data)
  2437.         arch_bsps = bsps.setdefault(data["arch"].strip(), {})
  2438.         arch_bsps[data["bsp"].strip()] = self
  2439.  
  2440.     def prepare_build(self, bld, bic):
  2441.         return BuildItemContext(
  2442.             bic.includes + bld.env.BSP_INCLUDES +
  2443.             self.substitute(bld, self.data["includes"]),
  2444.             self.substitute(bld, self.data["cppflags"]),
  2445.             bld.env.BSP_CFLAGS + self.substitute(bld, self.data["cflags"]),
  2446.             [],
  2447.             [],
  2448.             [],
  2449.             [],
  2450.         )
  2451.  
  2452.     def do_build(self, bld, bic):
  2453.         bld(
  2454.             cflags=bic.cflags,
  2455.             cppflags=bic.cppflags,
  2456.             features="c cstlib",
  2457.             includes=bic.includes,
  2458.             install_path="${BSP_LIBDIR}",
  2459.             source=self.data["source"],
  2460.             target="rtemsbsp",
  2461.             use=bic.objects,
  2462.         )
  2463.         self.install_files(bld)
  2464.  
  2465.  
  2466. class LibraryItem(Item):
  2467.  
  2468.     def __init__(self, uid, data):
  2469.         super(LibraryItem, self).__init__(uid, data)
  2470.  
  2471.     def prepare_build(self, bld, bic):
  2472.         return BuildItemContext(
  2473.             bic.includes + self.substitute(bld, self.data["includes"]),
  2474.             bic.cppflags + self.substitute(bld, self.data["cppflags"]),
  2475.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  2476.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  2477.             bic.use,
  2478.             bic.ldflags,
  2479.             [],
  2480.         )
  2481.  
  2482.     def do_build(self, bld, bic):
  2483.         bld(
  2484.             cflags=bic.cflags,
  2485.             cppflags=bic.cppflags,
  2486.             cxxflags=bic.cxxflags,
  2487.             features="c cxx cstlib",
  2488.             includes=bic.includes,
  2489.             install_path=self.data["install-path"],
  2490.             source=self.data["source"],
  2491.             target=self.get(bld, "target"),
  2492.             use=bic.objects,
  2493.         )
  2494.         self.install_files(bld)
  2495.  
  2496.  
  2497. class TestProgramItem(Item):
  2498.  
  2499.     def __init__(self, uid, data):
  2500.         super(TestProgramItem, self).__init__(uid, data)
  2501.         name = uid.split("/")[-1].upper().replace("-", "_")
  2502.         self.exclude = "TEST_" + name + "_EXCLUDE"
  2503.         self.cppflags = "TEST_" + name + "_CPPFLAGS"
  2504.  
  2505.     def get_enabled_by(self):
  2506.         return [{"and": [{"not": self.exclude}, self.data["enabled-by"]]}]
  2507.  
  2508.     def prepare_build(self, bld, bic):
  2509.         return BuildItemContext(
  2510.             bic.includes + self.substitute(bld, self.data["includes"]),
  2511.             bic.cppflags + bld.env[self.cppflags] +
  2512.             self.substitute(bld, self.data["cppflags"]),
  2513.             bic.cflags + self.substitute(bld, self.data["cflags"]),
  2514.             bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
  2515.             self.data["use-before"] + bic.use + self.data["use-after"],
  2516.             bic.ldflags + self.substitute(bld, self.data["ldflags"]),
  2517.             [],
  2518.         )
  2519.  
  2520.     def do_build(self, bld, bic):
  2521.         bld(
  2522.             cflags=bic.cflags,
  2523.             cppflags=bic.cppflags,
  2524.             cxxflags=bic.cxxflags,
  2525.             features=self.data["features"],
  2526.             includes=bic.includes,
  2527.             install_path=None,
  2528.             ldflags=bic.ldflags,
  2529.             source=self.data["source"],
  2530.             start_files=True,
  2531.             stlib=self.data["stlib"],
  2532.             target=self.get(bld, "target"),
  2533.             use=bic.objects + bic.use,
  2534.         )
  2535.  
  2536.  
  2537. class AdaTestProgramItem(TestProgramItem):
  2538.  
  2539.     def __init__(self, uid, data):
  2540.         super(AdaTestProgramItem, self).__init__(uid, data)
  2541.  
  2542.     def do_build(self, bld, bic):
  2543.         objs = []
  2544.         for s in self.data["source"]:
  2545.             objs.append(self.cc(bld, bic, s))
  2546.         self.gnatmake(
  2547.             bld,
  2548.             bic,
  2549.             self.data["ada-object-directory"],
  2550.             objs,
  2551.             self.data["ada-main"],
  2552.             self.data["target"],
  2553.         )
  2554.  
  2555.  
  2556. class OptionItem(Item):
  2557.  
  2558.     def __init__(self, uid, data):
  2559.         super(OptionItem, self).__init__(uid, data)
  2560.  
  2561.     def default_value(self, enabled):
  2562.         value = None
  2563.         for default in self.data["default"]:
  2564.             if _is_enabled(enabled, default["enabled-by"]):
  2565.                 value = default["value"]
  2566.                 break
  2567.         if value is None:
  2568.             return value
  2569.         if isinstance(value, list):
  2570.             return " ".join(value)
  2571.         if isinstance(value, bool):
  2572.             return value
  2573.         return self.data["format"].format(value)
  2574.  
  2575.     def do_defaults(self, enabled):
  2576.         value = self.default_value(enabled)
  2577.         if value is None:
  2578.             return
  2579.         description = self.data["description"]
  2580.         if description:
  2581.             import textwrap
  2582.  
  2583.             tw = textwrap.TextWrapper()
  2584.             tw.drop_whitespace = True
  2585.             tw.initial_indent = "# "
  2586.             tw.subsequent_indent = "# "
  2587.             for line in tw.wrap(description):
  2588.                 print(line)
  2589.         print("{} = {}".format(self.data["name"], value))
  2590.  
  2591.     def _do_append_test_cppflags(self, conf, name, state):
  2592.         conf.env.append_value(
  2593.             "TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state)
  2594.  
  2595.     def _append_test_cppflags(self, conf, cic, value, arg):
  2596.         self._do_append_test_cppflags(conf, arg, value)
  2597.         return value
  2598.  
  2599.     def _assert_aligned(self, conf, cic, value, arg):
  2600.         if value is not None and value % arg != 0:
  2601.             conf.fatal(
  2602.                 "Value '{}' for option '{}' is not aligned by '{}'".format(
  2603.                     value, self.data["name"], arg))
  2604.         return value
  2605.  
  2606.     def _assert_eq(self, conf, cic, value, arg):
  2607.         if value is not None and value != arg:
  2608.             conf.fatal("Value '{}' for option '{}' is not equal to {}".format(
  2609.                 value, self.data["name"], arg))
  2610.         return value
  2611.  
  2612.     def _assert_ge(self, conf, cic, value, arg):
  2613.         if value is not None and value < arg:
  2614.             conf.fatal(
  2615.                 "Value '{}' for option '{}' is not greater than or equal to {}"
  2616.                 .format(value, self.data["name"], arg))
  2617.         return value
  2618.  
  2619.     def _assert_gt(self, conf, cic, value, arg):
  2620.         if value is not None and value <= arg:
  2621.             conf.fatal(
  2622.                 "Value '{}' for option '{}' is not greater than {}".format(
  2623.                     value, self.data["name"], arg))
  2624.         return value
  2625.  
  2626.     def _assert_in_set(self, conf, cic, value, arg):
  2627.         if value is not None and value not in arg:
  2628.             conf.fatal(
  2629.                 "Value '{}' for option '{}' is not an element of {}".format(
  2630.                     value, self.data["name"], arg))
  2631.         return value
  2632.  
  2633.     def _assert_in_interval(self, conf, cic, value, arg):
  2634.         if value is not None and (value < arg[0] or value > arg[1]):
  2635.             conf.fatal(
  2636.                 "Value '{}' for option '{}' is not in closed interval [{}, {}]"
  2637.                 .format(value, self.data["name"], arg[0], arg[1]))
  2638.         return value
  2639.  
  2640.     def _assert_int8(self, conf, cic, value, arg):
  2641.         return self._assert_in_interval(conf, cic, value, [-128, 127])
  2642.  
  2643.     def _assert_int16(self, conf, cic, value, arg):
  2644.         return self._assert_in_interval(conf, cic, value, [-32768, 32767])
  2645.  
  2646.     def _assert_int32(self, conf, cic, value, arg):
  2647.         return self._assert_in_interval(conf, cic, value,
  2648.                                         [-2147483648, 2147483647])
  2649.  
  2650.     def _assert_int64(self, conf, cic, value, arg):
  2651.         return self._assert_in_interval(
  2652.             conf, cic, value, [-9223372036854775808, 9223372036854775807])
  2653.  
  2654.     def _assert_le(self, conf, cic, value, arg):
  2655.         if value is not None and value > arg:
  2656.             conf.fatal(
  2657.                 "Value '{}' for option '{}' is not less than or equal to {}".
  2658.                 format(value, self.data["name"], arg))
  2659.         return value
  2660.  
  2661.     def _assert_lt(self, conf, cic, value, arg):
  2662.         if value is not None and value >= arg:
  2663.             conf.fatal("Value '{}' for option '{}' is not less than {}".format(
  2664.                 value, self.data["name"], arg))
  2665.         return value
  2666.  
  2667.     def _assert_ne(self, conf, cic, value, arg):
  2668.         if value is not None and value == arg:
  2669.             conf.fatal(
  2670.                 "Value '{}' for option '{}' is not unequal to {}".format(
  2671.                     value, self.data["name"], arg))
  2672.         return value
  2673.  
  2674.     def _assert_power_of_two(self, conf, cic, value, arg):
  2675.         if value is not None and (value <= 0 or (value & (value - 1)) != 0):
  2676.             conf.fatal(
  2677.                 "Value '{}' for option '{}' is not a power of two".format(
  2678.                     value, self.data["name"]))
  2679.         return value
  2680.  
  2681.     def _assert_uint8(self, conf, cic, value, arg):
  2682.         return self._assert_in_interval(conf, cic, value, [0, 255])
  2683.  
  2684.     def _assert_uint16(self, conf, cic, value, arg):
  2685.         return self._assert_in_interval(conf, cic, value, [0, 65535])
  2686.  
  2687.     def _assert_uint32(self, conf, cic, value, arg):
  2688.         return self._assert_in_interval(conf, cic, value, [0, 4294967295])
  2689.  
  2690.     def _assert_uint64(self, conf, cic, value, arg):
  2691.         return self._assert_in_interval(conf, cic, value,
  2692.                                         [0, 18446744073709551615])
  2693.  
  2694.     def _check_cc(self, conf, cic, value, arg):
  2695.         result = conf.check_cc(
  2696.             fragment=arg["fragment"],
  2697.             cflags=arg["cflags"],
  2698.             msg="Checking for " + arg["message"],
  2699.             mandatory=False,
  2700.         )
  2701.         return value and result
  2702.  
  2703.     def _check_cxx(self, conf, cic, value, arg):
  2704.         result = conf.check_cxx(
  2705.             fragment=arg["fragment"],
  2706.             cxxflags=arg["cxxflags"],
  2707.             msg="Checking for " + arg["message"],
  2708.             mandatory=False,
  2709.         )
  2710.         return value and result
  2711.  
  2712.     def _comment(self, conf, cic, value, arg):
  2713.         return value
  2714.  
  2715.     def _define_condition(self, conf, cic, value, arg):
  2716.         name = self.data["name"] if arg is None else arg
  2717.         conf.define_cond(name, value)
  2718.         return value
  2719.  
  2720.     def _define(self, conf, cic, value, arg):
  2721.         name = self.data["name"] if arg is None else arg
  2722.         if value is not None:
  2723.             conf.define(name, value)
  2724.         else:
  2725.             conf.define_cond(name, False)
  2726.         return value
  2727.  
  2728.     def _define_unquoted(self, conf, cic, value, arg):
  2729.         name = self.data["name"] if arg is None else arg
  2730.         if value is not None:
  2731.             conf.define(name, value, quote=False)
  2732.         else:
  2733.             conf.define_cond(name, False)
  2734.         return value
  2735.  
  2736.     def _env_append(self, conf, cic, value, arg):
  2737.         name = self.data["name"] if arg is None else arg
  2738.         conf.env.append_value(name, value)
  2739.         return value
  2740.  
  2741.     def _env_assign(self, conf, cic, value, arg):
  2742.         name = self.data["name"] if arg is None else arg
  2743.         conf.env[name] = value
  2744.         return value
  2745.  
  2746.     def _env_enable(self, conf, cic, value, arg):
  2747.         if value:
  2748.             name = self.data["name"] if arg is None else arg
  2749.             conf.env.append_value("ENABLE", name)
  2750.         return value
  2751.  
  2752.     def _find_program(self, conf, cic, value, arg):
  2753.         return conf.find_program(value, path_list=cic.path_list)
  2754.  
  2755.     def _format_and_define(self, conf, cic, value, arg):
  2756.         name = self.data["name"] if arg is None else arg
  2757.         if value is not None:
  2758.             conf.define(name, self.data["format"].format(value), quote=False)
  2759.         else:
  2760.             conf.define_cond(name, False)
  2761.         return value
  2762.  
  2763.     def _get_boolean(self, conf, cic, value, arg):
  2764.         name = self.data["name"]
  2765.         try:
  2766.             value = cic.cp.getboolean(conf.variant, name)
  2767.             cic.add_option(name)
  2768.         except configparser.NoOptionError:
  2769.             value = self.default_value(conf.env.ENABLE)
  2770.         except ValueError as ve:
  2771.             conf.fatal("Invalid value for configuration option {}: {}".format(
  2772.                 name, ve))
  2773.         return value
  2774.  
  2775.     def _get_env(self, conf, cic, value, arg):
  2776.         return conf.env[arg]
  2777.  
  2778.     def _get_integer(self, conf, cic, value, arg):
  2779.         name = self.data["name"]
  2780.         try:
  2781.             value = cic.cp.get(conf.variant, name)
  2782.             cic.add_option(name)
  2783.         except configparser.NoOptionError:
  2784.             value = self.default_value(conf.env.ENABLE)
  2785.         if not value:
  2786.             return None
  2787.         try:
  2788.             return eval(value)
  2789.         except Exception as e:
  2790.             conf.fatal(
  2791.                 "Value '{}' for option '{}' is an invalid integer expression: {}"
  2792.                 .format(value, name, e))
  2793.  
  2794.     def _get_string(self, conf, cic, value, arg):
  2795.         name = self.data["name"]
  2796.         try:
  2797.             value = cic.cp.get(conf.variant, name)
  2798.             cic.add_option(name)
  2799.         except configparser.NoOptionError:
  2800.             value = self.default_value(conf.env.ENABLE)
  2801.         return value
  2802.  
  2803.     def _script(self, conf, cic, value, arg):
  2804.         local_variables = {
  2805.             "self": self,
  2806.             "conf": conf,
  2807.             "cic": cic,
  2808.             "value": value
  2809.         }
  2810.         exec(arg, None, local_variables)
  2811.         return local_variables["value"]
  2812.  
  2813.     def _test_state_benchmark(self, conf, name):
  2814.         self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
  2815.  
  2816.     def _test_state_exclude(self, conf, name):
  2817.         conf.env.append_value(
  2818.             "ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE")
  2819.  
  2820.     def _test_state_expected_fail(self, conf, name):
  2821.         self._do_append_test_cppflags(conf, name,
  2822.                                       "-DTEST_STATE_EXPECTED_FAIL=1")
  2823.  
  2824.     def _test_state_indeterminate(self, conf, name):
  2825.         self._do_append_test_cppflags(conf, name,
  2826.                                       "-DTEST_STATE_INDETERMINATE=1")
  2827.  
  2828.     def _test_state_user_input(self, conf, name):
  2829.         self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
  2830.  
  2831.     def _set_test_state(self, conf, cic, value, arg):
  2832.         actions = {
  2833.             "benchmark": self._test_state_benchmark,
  2834.             "exclude": self._test_state_exclude,
  2835.             "expected-fail": self._test_state_expected_fail,
  2836.             "indeterminate": self._test_state_indeterminate,
  2837.             "user-input": self._test_state_user_input,
  2838.         }
  2839.         action = actions[arg["state"]]
  2840.         for test in arg["tests"]:
  2841.             action(conf, test)
  2842.         return value
  2843.  
  2844.     def _set_value(self, conf, cic, value, arg):
  2845.         return arg
  2846.  
  2847.     def _set_value_enabled_by(self, conf, cic, value, arg):
  2848.         for value_enabled_by in arg:
  2849.             if _is_enabled(conf.env.ENABLE, value_enabled_by["enabled-by"]):
  2850.                 return value_enabled_by["value"]
  2851.         return None
  2852.  
  2853.     def _split(self, conf, cic, value, arg):
  2854.         return value.split()
  2855.  
  2856.     def _substitute(self, conf, cic, value, arg):
  2857.         if isinstance(value, list):
  2858.             return [self.substitute(conf, v) for v in value]
  2859.         else:
  2860.             return self.substitute(conf, value)
  2861.  
  2862.     def do_configure(self, conf, cic):
  2863.         actions = {
  2864.             "append-test-cppflags": self._append_test_cppflags,
  2865.             "assert-aligned": self._assert_aligned,
  2866.             "assert-eq": self._assert_eq,
  2867.             "assert-ge": self._assert_ge,
  2868.             "assert-gt": self._assert_gt,
  2869.             "assert-in-set": self._assert_in_set,
  2870.             "assert-int8": self._assert_int8,
  2871.             "assert-int16": self._assert_int16,
  2872.             "assert-int32": self._assert_int32,
  2873.             "assert-int64": self._assert_int64,
  2874.             "assert-le": self._assert_le,
  2875.             "assert-lt": self._assert_lt,
  2876.             "assert-ne": self._assert_ne,
  2877.             "assert-power-of-two": self._assert_power_of_two,
  2878.             "assert-uint8": self._assert_uint8,
  2879.             "assert-uint16": self._assert_uint16,
  2880.             "assert-uint32": self._assert_uint32,
  2881.             "assert-uint64": self._assert_uint64,
  2882.             "check-cc": self._check_cc,
  2883.             "check-cxx": self._check_cxx,
  2884.             "comment": self._comment,
  2885.             "define-condition": self._define_condition,
  2886.             "define": self._define,
  2887.             "define-unquoted": self._define_unquoted,
  2888.             "env-append": self._env_append,
  2889.             "env-assign": self._env_assign,
  2890.             "env-enable": self._env_enable,
  2891.             "find-program": self._find_program,
  2892.             "format-and-define": self._format_and_define,
  2893.             "get-boolean": self._get_boolean,
  2894.             "get-env": self._get_env,
  2895.             "get-integer": self._get_integer,
  2896.             "get-string": self._get_string,
  2897.             "script": self._script,
  2898.             "set-test-state": self._set_test_state,
  2899.             "set-value": self._set_value,
  2900.             "set-value-enabled-by": self._set_value_enabled_by,
  2901.             "split": self._split,
  2902.             "substitute": self._substitute,
  2903.         }
  2904.         value = None
  2905.         for action in self.data["actions"]:
  2906.             for action_arg in action.items():
  2907.                 value = actions[action_arg[0]](conf, cic, value, action_arg[1])
  2908.  
  2909.  
  2910. class ScriptItem(Item):
  2911.  
  2912.     def __init__(self, uid, data):
  2913.         super(ScriptItem, self).__init__(uid, data)
  2914.  
  2915.     def prepare_configure(self, conf, cic):
  2916.         script = self.data["prepare-configure"]
  2917.         if script:
  2918.             exec(script)
  2919.  
  2920.     def do_configure(self, conf, cic):
  2921.         script = self.data["do-configure"]
  2922.         if script:
  2923.             exec(script)
  2924.  
  2925.     def prepare_build(self, bld, bic):
  2926.         script = self.data["prepare-build"]
  2927.         if script:
  2928.             exec(script)
  2929.         return bic
  2930.  
  2931.     def do_build(self, bld, bic):
  2932.         script = self.data["do-build"]
  2933.         if script:
  2934.             exec(script)
  2935.  
  2936.  
  2937. class ConfigItemContext(object):
  2938.  
  2939.     def __init__(self, cp, path_list):
  2940.         self.cp = cp
  2941.         self.options = set()
  2942.         self.path_list = path_list
  2943.  
  2944.     def add_option(self, name):
  2945.         self.options.add(name.upper())
  2946.  
  2947.  
  2948. class BuildItemContext(object):
  2949.  
  2950.     def __init__(self, includes, cppflags, cflags, cxxflags, use, ldflags,
  2951.                  objects):
  2952.         self.includes = includes
  2953.         self.cppflags = cppflags
  2954.         self.cflags = cflags
  2955.         self.cxxflags = cxxflags
  2956.         self.use = use
  2957.         self.ldflags = ldflags
  2958.         self.objects = objects
  2959.  
  2960.  
  2961. def is_one_item_newer(ctx, path, mtime):
  2962.     try:
  2963.         mtime2 = os.path.getmtime(path)
  2964.         if mtime <= mtime2:
  2965.             return True
  2966.         names = os.listdir(path)
  2967.     except Exception as e:
  2968.         ctx.fatal("Cannot access build specification directory: {}".format(e))
  2969.     for name in names:
  2970.         path2 = os.path.join(path, name)
  2971.         if name.endswith(".yml") and not name.startswith("."):
  2972.             mtime2 = os.path.getmtime(path2)
  2973.             if mtime <= mtime2:
  2974.                 return True
  2975.         else:
  2976.             mode = os.lstat(path2).st_mode
  2977.             if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
  2978.                 return True
  2979.     return False
  2980.  
  2981.  
  2982. def must_update_item_cache(ctx, path, cache_file):
  2983.     try:
  2984.         mtime = os.path.getmtime(cache_file)
  2985.     except:
  2986.         return True
  2987.     return is_one_item_newer(ctx, path, mtime)
  2988.  
  2989.  
  2990. def load_from_yaml(load, ctx, data_by_uid, base, path):
  2991.     try:
  2992.         names = os.listdir(path)
  2993.     except Exception as e:
  2994.         ctx.fatal("Cannot list build specification directory: {}".format(e))
  2995.     for name in names:
  2996.         path2 = os.path.join(path, name)
  2997.         if name.endswith(".yml") and not name.startswith("."):
  2998.             uid = "/" + os.path.relpath(path2, base).replace(
  2999.                 ".yml", "").replace("\\", "/")
  3000.             with open(path2, "r") as f:
  3001.                 data_by_uid[uid] = load(f.read())
  3002.         else:
  3003.             mode = os.lstat(path2).st_mode
  3004.             if stat.S_ISDIR(mode):
  3005.                 load_from_yaml(load, ctx, data_by_uid, base, path2)
  3006.  
  3007.  
  3008. def load_items_in_directory(ctx, ctors, path):
  3009.     p = re.sub(r"[^\w]", "_", path) + ".pickle"
  3010.     try:
  3011.         f = ctx.bldnode.make_node(p)
  3012.     except AttributeError:
  3013.         f = ctx.path.make_node("build/" + p)
  3014.     f.parent.mkdir()
  3015.     cache_file = f.abspath()
  3016.     data_by_uid = {}
  3017.     if must_update_item_cache(ctx, path, cache_file):
  3018.         from waflib import Logs
  3019.  
  3020.         Logs.warn(
  3021.             "Regenerate build specification cache (needs a couple of seconds)..."
  3022.         )
  3023.  
  3024.         #
  3025.         # Do not use a system provided yaml module and instead import it from
  3026.         # the project.  This reduces the host system requirements to a simple
  3027.         # Python 3 installation without extra modules.
  3028.         #
  3029.         sys.path.append("yaml/lib3")
  3030.         from yaml import safe_load
  3031.  
  3032.         load_from_yaml(safe_load, ctx, data_by_uid, path, path)
  3033.         with open(cache_file, "wb") as f:
  3034.             pickle.dump(data_by_uid, f)
  3035.     else:
  3036.         with open(cache_file, "rb") as f:
  3037.             data_by_uid = pickle.load(f)
  3038.     for uid, data in data_by_uid.items():
  3039.         if data["type"] == "build":
  3040.             items[uid] = ctors[data["build-type"]](uid, data)
  3041.  
  3042.  
  3043. def load_items(ctx, specs):
  3044.     if items:
  3045.         return
  3046.  
  3047.     ctors = {
  3048.         "ada-test-program": AdaTestProgramItem,
  3049.         "bsp": BSPItem,
  3050.         "config-file": ConfigFileItem,
  3051.         "config-header": ConfigHeaderItem,
  3052.         "test-program": TestProgramItem,
  3053.         "group": GroupItem,
  3054.         "library": LibraryItem,
  3055.         "objects": ObjectsItem,
  3056.         "option": OptionItem,
  3057.         "script": ScriptItem,
  3058.         "start-file": StartFileItem,
  3059.     }
  3060.  
  3061.     for path in specs:
  3062.         load_items_in_directory(ctx, ctors, path)
  3063.  
  3064.  
  3065. def load_items_from_options(ctx):
  3066.     specs = ctx.options.rtems_specs
  3067.     if specs is not None:
  3068.         specs = specs.split(",")
  3069.     else:
  3070.         specs = ["spec/build"]
  3071.     load_items(ctx, specs)
  3072.     return specs
  3073.  
  3074.  
  3075. def options(ctx):
  3076.     load_version(ctx)
  3077.     prefix = ctx.parser.get_option("--prefix")
  3078.     prefix.default = default_prefix
  3079.     prefix.help = "installation prefix [default: '{}']".format(default_prefix)
  3080.     rg = ctx.add_option_group("RTEMS options")
  3081.     rg.add_option(
  3082.         "--rtems-bsps",
  3083.         metavar="REGEX,...",
  3084.         help=
  3085.         "a comma-separated list of Python regular expressions which select the desired BSP variants (e.g. 'sparc/erc32'); it may be used in the bspdefaults and bsps commands",
  3086.     )
  3087.     rg.add_option(
  3088.         "--rtems-compiler",
  3089.         metavar="COMPILER",
  3090.         help=
  3091.         "determines which compiler is used to list the BSP option defaults [default: 'gcc']; it may be used in the bspdefaults command; valid compilers are: {}"
  3092.         .format(", ".join(compilers)),
  3093.     )
  3094.     rg.add_option(
  3095.         "--rtems-config",
  3096.         metavar="CONFIG.INI,...",
  3097.         help=
  3098.         "a comma-separated list of paths to the BSP configuration option files [default: 'config.ini']; default option values can be obtained via the bspdefaults command; it may be used in the configure command",
  3099.     )
  3100.     rg.add_option(
  3101.         "--rtems-specs",
  3102.         metavar="SPECDIRS,...",
  3103.         help=
  3104.         "a comma-separated list of directory paths to build specification items [default: 'spec/build']; it may be used in the bspdefaults, bsps, and configure commands",
  3105.     )
  3106.     rg.add_option(
  3107.         "--rtems-tools",
  3108.         metavar="PREFIX,...",
  3109.         help=
  3110.         "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",
  3111.     )
  3112.     rg.add_option(
  3113.         "--rtems-top-group",
  3114.         metavar="UID",
  3115.         help=
  3116.         "the UID of the top-level group [default: '/grp']; it may be used in the bspdefaults and configure commands",
  3117.     )
  3118.  
  3119.  
  3120. def check_environment(conf):
  3121.     for ev in [
  3122.             "AR",
  3123.             "AS",
  3124.             "ASFLAGS",
  3125.             "CC",
  3126.             "CFLAGS",
  3127.             "CPPFLAGS",
  3128.             "CXX",
  3129.             "CXXFLAGS",
  3130.             "IFLAGS",
  3131.             "LD",
  3132.             "LIB",
  3133.             "LINK_CC",
  3134.             "LINK_CXX",
  3135.             "LINKFLAGS",
  3136.             "MFLAGS",
  3137.             "RFLAGS",
  3138.             "WFLAGS",
  3139.     ]:
  3140.         if ev in os.environ:
  3141.             conf.msg("Environment variable set", ev, color="RED")
  3142.  
  3143.  
  3144. def load_version(ctx):
  3145.     global default_prefix
  3146.  
  3147.     def _check_num(s):
  3148.         try:
  3149.             i = int(s)
  3150.         except:
  3151.             ctx.fatal(
  3152.                 "Invalid VERSION number: version number is not a number: " + s)
  3153.  
  3154.     cp = configparser.ConfigParser()
  3155.     version_file = "VERSION"
  3156.     version_major = None
  3157.     version_minor = None
  3158.     version_revision = None
  3159.     version_label = None
  3160.     prefix = None
  3161.     if cp.read([version_file]):
  3162.         try:
  3163.             value = cp.get("version", "revision")
  3164.             # The revision is <major>.<minor>[-label]
  3165.             # break is up and update the version
  3166.             if "." not in value:
  3167.                 ctx.fatal(
  3168.                     "Invalid VERSION revision: no number (dot) separator")
  3169.             # version-string => major.minor[.revsion][-label]
  3170.             vs = value.split(".", 2)
  3171.             _check_num(vs[0])
  3172.             version_major = vs[0]
  3173.             if "-" in vs[-1]:
  3174.                 ls = vs[-1].split("-", 1)
  3175.                 vs[-1] = ls[0]
  3176.                 version_label = ls[1]
  3177.             _check_num(vs[1])
  3178.             version_minor = vs[1]
  3179.             if len(vs) == 3:
  3180.                 _check_num(vs[2])
  3181.                 version_revision = vs[2]
  3182.             prefix = "/opt/rtems/" + version_major
  3183.         except configparser.NoOptionError:
  3184.             pass
  3185.     if version_label is None and version_revision is not None:
  3186.         ctx.fatal(
  3187.             "Invalid VERSION revision: a revision number requires a label")
  3188.     if version_major is not None:
  3189.         version["__RTEMS_MAJOR__"] = version_major
  3190.     if version_minor is not None:
  3191.         version["__RTEMS_MINOR__"] = version_minor
  3192.     if version_revision is not None:
  3193.         version["__RTEMS_REVISION__"] = version_revision
  3194.     if version_label is not None:
  3195.         version["RTEMS_RELEASE_VERSION_LABEL"] = version_label
  3196.     # Checking minor insures major and minor are valid
  3197.     if version_minor is None:
  3198.         version_label = get_repo_release_label(ctx)
  3199.     return version_label
  3200.  
  3201.  
  3202. def configure_version(conf):
  3203.     version_label = load_version(conf)
  3204.     v_str = version["__RTEMS_MAJOR__"] + "." + version["__RTEMS_MINOR__"]
  3205.     if int(version["__RTEMS_REVISION__"]) != 0:
  3206.         v_str += "." + version["__RTEMS_REVISION__"]
  3207.     if version_label is not None and version_label != "":
  3208.         v_str += "." + version_label
  3209.     conf.msg("Configure RTEMS version", v_str, color="YELLOW")
  3210.  
  3211.  
  3212. def load_config_files(ctx):
  3213.     cp = configparser.ConfigParser()
  3214.     files = ctx.options.rtems_config
  3215.     if files is not None:
  3216.         files = files.split(",")
  3217.     else:
  3218.         files = ["config.ini"]
  3219.     actual_files = cp.read(files)
  3220.     for o in files:
  3221.         if not o in actual_files:
  3222.             ctx.fatal("Option file '{}' was not readable".format(o))
  3223.     return cp
  3224.  
  3225.  
  3226. def inherit(conf, cp, bsp_map, arch, bsp, path):
  3227.     variant = arch + "/" + bsp
  3228.     if variant in path:
  3229.         path = " -> ".join(path + [variant])
  3230.         conf.fatal("Recursion in BSP options inheritance: {}".format(path))
  3231.  
  3232.     try:
  3233.         base = cp.get(variant, "INHERIT")
  3234.         cp.remove_option(variant, "INHERIT")
  3235.         base_variant = arch + "/" + base
  3236.         conf.msg(
  3237.             "Inherit options from '{}'".format(base_variant),
  3238.             variant,
  3239.             color="YELLOW",
  3240.         )
  3241.         if not cp.has_section(base_variant):
  3242.             if (not arch in bsps) or (not base in bsps[arch]):
  3243.                 conf.fatal(
  3244.                     "BSP variant '{}' cannot inherit options from not existing variant '{}'"
  3245.                     .format(variant, base_variant))
  3246.             bsp_map[bsp] = base
  3247.             return base
  3248.         top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
  3249.         for i in cp.items(base_variant):
  3250.             name = i[0]
  3251.             if not cp.has_option(variant, name):
  3252.                 cp.set(variant, name, i[1])
  3253.         bsp_map[bsp] = top
  3254.         return top
  3255.     except configparser.NoOptionError:
  3256.         return bsp_map.get(bsp, bsp)
  3257.  
  3258.  
  3259. def resolve_option_inheritance(conf, cp):
  3260.     bsp_map = {}
  3261.     for variant in cp.sections():
  3262.         try:
  3263.             arch, bsp = variant.split("/")
  3264.         except:
  3265.             conf.fatal(
  3266.                 "Section name '{}' is a malformed 'arch/bsp' tuple".format(
  3267.                     variant))
  3268.         inherit(conf, cp, bsp_map, arch, bsp, [])
  3269.     return bsp_map
  3270.  
  3271.  
  3272. def check_compiler(ctx, compiler):
  3273.     if compiler not in compilers:
  3274.         ctx.fatal("Specified compiler '{}' is not one of {}".format(
  3275.             compiler, compilers))
  3276.  
  3277.  
  3278. def get_compiler(conf, cp, variant):
  3279.     try:
  3280.         value = cp.get(variant, "COMPILER")
  3281.         cp.remove_option(variant, "COMPILER")
  3282.         check_compiler(conf, value)
  3283.     except configparser.NoOptionError:
  3284.         value = "gcc"
  3285.     return value
  3286.  
  3287.  
  3288. def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
  3289.     conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
  3290.  
  3291.     conf.setenv(variant)
  3292.     arch, bsp_name = variant.split("/")
  3293.     bsp_base = bsp_map.get(bsp_name, bsp_name)
  3294.  
  3295.     try:
  3296.         bsp_item = bsps[arch][bsp_base]
  3297.     except KeyError:
  3298.         conf.fatal("No such base BSP: '{}'".format(variant))
  3299.  
  3300.     family = bsp_item.data["family"]
  3301.  
  3302.     arch_bsp = arch + "/" + bsp_base
  3303.     arch_family = arch + "/" + family
  3304.  
  3305.     for key, value in version.items():
  3306.         conf.env[key] = value
  3307.  
  3308.     conf.env["ARCH"] = arch
  3309.     conf.env["ARCH_BSP"] = arch_bsp
  3310.     conf.env["ARCH_FAMILY"] = arch_family
  3311.     conf.env["BSP_BASE"] = bsp_base
  3312.     conf.env["BSP_NAME"] = bsp_name
  3313.     conf.env["BSP_FAMILY"] = family
  3314.     conf.env["DEST_OS"] = "rtems"
  3315.  
  3316.     # For the enabled-by evaluation we have to use the base BSP defined by the
  3317.     # build specification and not the BSP name provided by the user.
  3318.     conf.env["ENABLE"] = [
  3319.         get_compiler(conf, cp, variant),
  3320.         arch,
  3321.         "bsps/" + arch_family,
  3322.         arch_bsp,
  3323.     ]
  3324.  
  3325.     conf.env["TOP"] = conf.path.abspath()
  3326.     conf.env["TOPGROUP"] = top_group
  3327.     conf.env["VARIANT"] = variant
  3328.  
  3329.     cic = ConfigItemContext(cp, path_list)
  3330.     items[conf.env.TOPGROUP].configure(conf, cic)
  3331.     bsp_item.configure(conf, cic)
  3332.  
  3333.     options = set([o[0].upper() for o in cp.items(variant)])
  3334.     for o in options.difference(cic.options):
  3335.         conf.msg("Unknown configuration option", o.upper(), color="RED")
  3336.  
  3337.  
  3338. def check_forbidden_options(ctx, opts):
  3339.     for o in opts:
  3340.         if getattr(ctx.options, "rtems_" + o):
  3341.             ctx.fatal(
  3342.                 "The --rtems-{} command line option is not allowed in the {} command"
  3343.                 .format(o.replace("_", "-"), ctx.cmd))
  3344.  
  3345.  
  3346. def get_path_list(conf):
  3347.     path_list = []
  3348.     tools = conf.options.rtems_tools
  3349.     if tools is not None:
  3350.         for t in tools.split(","):
  3351.             path_list.extend([t + "/bin", t])
  3352.     path_list.append(conf.env.PREFIX + "/bin")
  3353.     path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
  3354.     return path_list
  3355.  
  3356.  
  3357. def get_top_group(ctx):
  3358.     top_group = ctx.options.rtems_top_group
  3359.     if top_group is None:
  3360.         top_group = "/grp"
  3361.     if top_group not in items:
  3362.         ctx.fatal(
  3363.             "There is no top-level group with UID '{}' in the specification".
  3364.             format(top_group))
  3365.     return top_group
  3366.  
  3367.  
  3368. def configure(conf):
  3369.     check_forbidden_options(conf, ["compiler"])
  3370.     configure_version(conf)
  3371.     check_environment(conf)
  3372.     conf.env["SPECS"] = load_items_from_options(conf)
  3373.     top_group = get_top_group(conf)
  3374.     cp = load_config_files(conf)
  3375.     bsp_map = resolve_option_inheritance(conf, cp)
  3376.     path_list = get_path_list(conf)
  3377.     variant_list = []
  3378.     for variant in cp.sections():
  3379.         variant_list.append(variant)
  3380.         configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
  3381.     conf.setenv("")
  3382.     conf.env["VARIANTS"] = variant_list
  3383.  
  3384.  
  3385. def append_variant_builds(bld):
  3386.     import waflib.Options
  3387.     from waflib.Build import (
  3388.         BuildContext,
  3389.         CleanContext,
  3390.         InstallContext,
  3391.         UninstallContext,
  3392.     )
  3393.  
  3394.     for var in bld.env["VARIANTS"]:
  3395.         for c in (BuildContext, CleanContext, InstallContext,
  3396.                   UninstallContext):
  3397.             name = c.__name__.replace("Context", "").lower()
  3398.  
  3399.             class rtems_context(c):
  3400.                 cmd = name + "_" + var
  3401.                 variant = var
  3402.                 last_variant = var == bld.env["VARIANTS"][-1]
  3403.  
  3404.                 def compile(self):
  3405.                     global variant_errors
  3406.                     try:
  3407.                         super().compile()
  3408.                     except:
  3409.                         variant_errors += [self.variant]
  3410.                         if not self.keep:
  3411.                             raise
  3412.                     if self.keep and self.last_variant and len(
  3413.                             variant_errors) > 0:
  3414.                         raise self.fatal('error: BSP(s) in errors: ' +
  3415.                                          ' '.join(variant_errors))
  3416.  
  3417.         waflib.Options.commands.append(bld.cmd + "_" + var)
  3418.  
  3419.  
  3420. def build(bld):
  3421.     if not bld.variant:
  3422.         check_forbidden_options(
  3423.             bld,
  3424.             ["compiler", "config", "specs", "tools", "top_group"],
  3425.         )
  3426.         load_items(bld, bld.env.SPECS)
  3427.         append_variant_builds(bld)
  3428.         return
  3429.     bic = BuildItemContext(bld.env.ARCH_INCLUDES.split(), [], [], [], [], [],
  3430.                            [])
  3431.     bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
  3432.     items[bld.env.TOPGROUP].build(bld, bic)
  3433.  
  3434.  
  3435. def add_log_filter(name):
  3436.     msg = "'" + name + "' finished successfully"
  3437.  
  3438.     class Filter:
  3439.  
  3440.         def filter(self, rec):
  3441.             return not msg in rec.getMessage()
  3442.  
  3443.     import logging
  3444.  
  3445.     logging.getLogger("waflib").addFilter(Filter())
  3446.  
  3447.  
  3448. def get_white_list(ctx):
  3449.     white_list = ctx.options.rtems_bsps
  3450.     if white_list:
  3451.         white_list = white_list.split(",")
  3452.     return white_list
  3453.  
  3454.  
  3455. def is_in_white_list(variant, white_list):
  3456.     if not white_list:
  3457.         return True
  3458.     for pattern in white_list:
  3459.         if re.match(pattern + "$", variant):
  3460.             return True
  3461.     return False
  3462.  
  3463.  
  3464. def no_matches_error(ctx, white_list):
  3465.     if white_list:
  3466.         ctx.fatal("No BSP matches with the specified patterns: '{}'".format(
  3467.             "', '".join(white_list)))
  3468.     else:
  3469.         ctx.fatal("The build specification contains no BSPs")
  3470.  
  3471.  
  3472. def bspdefaults(ctx):
  3473.     """get all options with default values for base BSP variants"""
  3474.     check_forbidden_options(ctx, ["config", "tools"])
  3475.     add_log_filter(ctx.cmd)
  3476.     load_items_from_options(ctx)
  3477.     top_group = get_top_group(ctx)
  3478.     white_list = get_white_list(ctx)
  3479.     compiler = ctx.options.rtems_compiler
  3480.     if compiler is not None:
  3481.         check_compiler(ctx, compiler)
  3482.     else:
  3483.         compiler = "gcc"
  3484.     first = True
  3485.     for arch in sorted(bsps):
  3486.         for bsp in sorted(bsps[arch]):
  3487.             variant = arch + "/" + bsp
  3488.             if is_in_white_list(variant, white_list):
  3489.                 if not first:
  3490.                     print("")
  3491.                 first = False
  3492.                 print("""[{}]
  3493. # Selects the compiler used to build the BSP (allowed values are "gcc" and
  3494. # "clang").  Please note that the values of some options depend on the compiler
  3495. # selection and changing the compiler may lead to unpredictable behaviour if
  3496. # these options are not adjusted as well.  Use the --rtems-compiler command line
  3497. # option to get the default values for a particular compiler via
  3498. # ./waf bspdefaults.
  3499. COMPILER = {}""".format(variant, compiler))
  3500.                 enable = [compiler, arch, variant]
  3501.                 bsp_item = bsps[arch][bsp]
  3502.                 family = "bsps/" + arch + "/" + bsp_item.data["family"]
  3503.                 enabled = [compiler, arch, family, variant]
  3504.                 items[top_group].defaults(enabled)
  3505.                 bsp_item.defaults(enabled)
  3506.     if first:
  3507.         no_matches_error(ctx, white_list)
  3508.  
  3509.  
  3510. def bsplist(ctx):
  3511.     """lists base BSP variants"""
  3512.     check_forbidden_options(ctx, ["compiler", "config", "tools", "top_group"])
  3513.     add_log_filter(ctx.cmd)
  3514.     load_items_from_options(ctx)
  3515.     white_list = get_white_list(ctx)
  3516.     first = True
  3517.     for arch in sorted(bsps):
  3518.         for bsp in sorted(bsps[arch]):
  3519.             variant = arch + "/" + bsp
  3520.             if is_in_white_list(variant, white_list):
  3521.                 first = False
  3522.                 print(variant)
  3523.     if first:
  3524.         no_matches_error(ctx, white_list)
  3525.  
Advertisement
Add Comment
Please, Sign In to add comment