Warning, /wscript is written in an unsupported language. File is not indexed.
0001 #!/usr/bin/env python
0002
0003 # SPDX-License-Identifier: BSD-2-Clause
0004 #
0005 # Copyright (C) 2020 Hesham Almatary <Hesham.Almatary@cl.cam.ac.uk>
0006 # Copyright (C) 2019, 2020 embedded brains GmbH & Co. KG
0007 # Copyright (C) 2024 Contemporary Software <chris@contemporary.software>
0008 #
0009 # Redistribution and use in source and binary forms, with or without
0010 # modification, are permitted provided that the following conditions
0011 # are met:
0012 # 1. Redistributions of source code must retain the above copyright
0013 # notice, this list of conditions and the following disclaimer.
0014 # 2. Redistributions in binary form must reproduce the above copyright
0015 # notice, this list of conditions and the following disclaimer in the
0016 # documentation and/or other materials provided with the distribution.
0017 #
0018 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0028 # POSSIBILITY OF SUCH DAMAGE.
0029
0030 import pickle
0031 import os
0032 import re
0033 import stat
0034 import string
0035 import sys
0036
0037 try:
0038 import configparser
0039 except:
0040 import ConfigParser as configparser
0041
0042 from waflib.TaskGen import after, before_method, feature
0043
0044 version = {
0045 "__RTEMS_MAJOR__": "6", # version
0046 "__RTEMS_MINOR__": "0", # revision
0047 "__RTEMS_REVISION__": "0", # not used
0048 "RTEMS_RELEASE_VERSION_LABEL": "not-released"
0049 }
0050 default_prefix = "/opt/rtems/" + version["__RTEMS_MAJOR__"]
0051 compilers = ["gcc", "clang"]
0052 items = {}
0053 bsps = {}
0054
0055
0056 def get_repo_release_label(ctx):
0057 from waflib.Build import Context
0058 from waflib.Errors import WafError
0059 release_label = "not-released"
0060 try:
0061 modified = ctx.cmd_and_log("git ls-files --modified",
0062 quiet=Context.BOTH).strip()
0063 release_label = ctx.cmd_and_log("git rev-parse HEAD",
0064 quiet=Context.BOTH).strip()
0065 if modified:
0066 release_label += "-modified"
0067 except WafError:
0068 pass
0069 return release_label
0070
0071
0072 #
0073 # This class is not aligned the project's release labelling. It will
0074 # not be changed for RTEMS 6. A VC key is a type of release label and
0075 # there are more types.
0076 #
0077 # The term `key` and a repo normally relates to the signing key of
0078 # repository which is security related and it is important we do not
0079 # imply this here. They are unrelated.
0080 #
0081 class VersionControlKeyHeader:
0082 _content = None
0083
0084 @staticmethod
0085 def write(bld, filename):
0086 content = VersionControlKeyHeader._content
0087 if content is None:
0088 content = """/*
0089 * Automatically generated. Do not edit.
0090 *
0091 * Warning: This interface will be replaced with release labels
0092 * after RTEMS 6.
0093 */
0094 #if !defined(_RTEMS_VERSION_VC_KEY_H_)
0095 #define _RTEMS_VERSION_VC_KEY_H_
0096 """
0097 release_label = bld.env.RTEMS_RELEASE_VERSION_LABEL
0098 if release_label == "not-released":
0099 release_label = get_repo_release_label(bld)
0100 if release_label:
0101 content += """#define RTEMS_VERSION_CONTROL_KEY "{}"
0102 """.format(release_label)
0103 content += """#endif
0104 """
0105 VersionControlKeyHeader._content = content
0106 f = bld.bldnode.make_node(filename)
0107 f.parent.mkdir()
0108 try:
0109 if content != f.read():
0110 f.write(content)
0111 except:
0112 f.write(content)
0113
0114
0115 class EnvWrapper(object):
0116
0117 def __init__(self, env):
0118 self._env = env
0119
0120 def __getitem__(self, name):
0121 k, c, f = name.partition(":")
0122 v = self._env[k]
0123 fmt = "{" + c + f + "}"
0124 if isinstance(v, list):
0125 return " ".join([fmt.format(w) for w in v])
0126 return fmt.format(v)
0127
0128
0129 class Template(string.Template):
0130 idpattern = "[_A-Za-z][_A-Za-z0-9:#]*"
0131
0132
0133 _VAR_PATTERN = re.compile("\\$\\{?(" + Template.idpattern + ")\\}?$")
0134
0135
0136 def _is_enabled_op_and(enabled, enabled_by):
0137 for next_enabled_by in enabled_by:
0138 if not _is_enabled(enabled, next_enabled_by):
0139 return False
0140 return True
0141
0142
0143 def _is_enabled_op_not(enabled, enabled_by):
0144 return not _is_enabled(enabled, enabled_by)
0145
0146
0147 def _is_enabled_op_or(enabled, enabled_by):
0148 for next_enabled_by in enabled_by:
0149 if _is_enabled(enabled, next_enabled_by):
0150 return True
0151 return False
0152
0153
0154 _IS_ENABLED_OP = {
0155 "and": _is_enabled_op_and,
0156 "not": _is_enabled_op_not,
0157 "or": _is_enabled_op_or,
0158 }
0159
0160
0161 def _is_enabled(enabled, enabled_by):
0162 if isinstance(enabled_by, bool):
0163 return enabled_by
0164 if isinstance(enabled_by, list):
0165 return _is_enabled_op_or(enabled, enabled_by)
0166 if isinstance(enabled_by, dict):
0167 key, value = next(iter(enabled_by.items()))
0168 return _IS_ENABLED_OP[key](enabled, value)
0169 return enabled_by in enabled
0170
0171
0172 def _asm_explicit_target(self, node):
0173 task = self.create_task("asm", node,
0174 self.bld.bldnode.make_node(self.target))
0175 try:
0176 self.compiled_tasks.append(task)
0177 except AttributeError:
0178 self.compiled_tasks = [task]
0179 return task
0180
0181
0182 @feature("asm_explicit_target")
0183 @before_method("process_source")
0184 def _enable_asm_explicit_target(self):
0185 self.mappings = dict(self.mappings) # Copy
0186 self.mappings[".S"] = _asm_explicit_target
0187
0188
0189 @after("apply_link")
0190 @feature("cprogram", "cxxprogram")
0191 def process_start_files(self):
0192 if getattr(self, "start_files", False):
0193 self.link_task.dep_nodes.extend(self.bld.start_files)
0194
0195
0196 def make_tar_info_reproducible(info):
0197 info.uid = 0
0198 info.gid = 0
0199 info.mtime = 0
0200 info.uname = "root"
0201 info.gname = "root"
0202 return info
0203
0204
0205 class Item(object):
0206
0207 def __init__(self, uid, data):
0208 self.uid = uid
0209 self.data = data
0210 self.links = self._init_links
0211
0212 def _init_links(self):
0213 self._links = []
0214 for link in self.data["links"]:
0215 if link["role"] == "build-dependency":
0216 uid = link["uid"]
0217 if not os.path.isabs(uid):
0218 uid = os.path.normpath(
0219 os.path.join(os.path.dirname(self.uid),
0220 uid)).replace("\\", "/")
0221 self._links.append(items[uid])
0222 self.links = self._yield_links
0223 for link in self._links:
0224 yield link
0225
0226 def _yield_links(self):
0227 for link in self._links:
0228 yield link
0229
0230 def get_enabled_by(self):
0231 return self.data["enabled-by"]
0232
0233 def defaults(self, enabled):
0234 if _is_enabled(enabled, self.get_enabled_by()):
0235 for p in self.links():
0236 p.defaults(enabled)
0237 self.do_defaults(enabled)
0238
0239 def configure(self, conf, cic):
0240 if _is_enabled(conf.env.ENABLE, self.get_enabled_by()):
0241 self.prepare_configure(conf, cic)
0242 for p in self.links():
0243 p.configure(conf, cic)
0244 try:
0245 self.do_configure(conf, cic)
0246 except Exception as e:
0247 raise type(e)(
0248 "Configuration error related to item spec:{}: {}".format(
0249 self.uid, str(e)))
0250
0251 def build(self, bld, bic):
0252 if _is_enabled(bld.env.ENABLE, self.get_enabled_by()):
0253 bic = self.prepare_build(bld, bic)
0254 for p in self.links():
0255 p.build(bld, bic)
0256 try:
0257 self.do_build(bld, bic)
0258 except Exception as e:
0259 raise type(e)("Build error related to item spec:{}: {}".format(
0260 self.uid, str(e)))
0261
0262 def do_defaults(self, enabled):
0263 return
0264
0265 def prepare_configure(self, conf, cic):
0266 return
0267
0268 def do_configure(self, conf, cic):
0269 return
0270
0271 def prepare_build(self, bld, bic):
0272 return bic
0273
0274 def do_build(self, bld, bic):
0275 return
0276
0277 def substitute(self, ctx, value):
0278 if isinstance(value, str):
0279 try:
0280 return Template(value).substitute(EnvWrapper(ctx.env))
0281 except Exception as e:
0282 ctx.fatal(
0283 "In item '{}' substitution in '{}' failed: {}".format(
0284 self.uid, value, e))
0285 if isinstance(value, list):
0286 more = []
0287 for item in value:
0288 if isinstance(item, str):
0289 m = _VAR_PATTERN.match(item)
0290 else:
0291 m = None
0292 if m:
0293 more.extend(ctx.env[m.group(1).strip("{}")])
0294 else:
0295 more.append(self.substitute(ctx, item))
0296 return more
0297 return value
0298
0299 def get(self, ctx, name):
0300 return self.substitute(ctx, self.data[name])
0301
0302 def install_target(self, bld):
0303 install_path = self.data["install-path"]
0304 if install_path:
0305 bld.install_files(install_path, self.get(bld, "target"))
0306
0307 def install_files(self, bld):
0308 for install in self.data["install"]:
0309 bld.install_files(install["destination"], install["source"])
0310
0311 def asm(self, bld, bic, source, target=None):
0312 if target is None:
0313 target = os.path.splitext(source)[0] + ".o"
0314 bld(
0315 asflags=self.substitute(bld, self.data["asflags"]),
0316 cppflags=bic.cppflags +
0317 self.substitute(bld, self.data["cppflags"]),
0318 features="asm_explicit_target asm c",
0319 includes=bic.includes +
0320 self.substitute(bld, self.data["includes"]),
0321 source=[source],
0322 target=target,
0323 )
0324 return target
0325
0326 def cc(self, bld, bic, source, target=None, deps=[], cppflags=[]):
0327 if target is None:
0328 target = os.path.splitext(source)[0] + ".o"
0329 bld(
0330 cflags=bic.cflags + self.substitute(bld, self.data["cflags"]),
0331 cppflags=bic.cppflags + cppflags +
0332 self.substitute(bld, self.data["cppflags"]),
0333 features="c",
0334 includes=bic.includes +
0335 self.substitute(bld, self.data["includes"]),
0336 rule=
0337 "${CC} ${CFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
0338 source=[source] + deps,
0339 target=target,
0340 )
0341 return target
0342
0343 def cxx(self, bld, bic, source, target=None, deps=[], cppflags=[]):
0344 if target is None:
0345 target = os.path.splitext(source)[0] + ".o"
0346 bld(
0347 cppflags=bic.cppflags + cppflags +
0348 self.substitute(bld, self.data["cppflags"]),
0349 cxxflags=bic.cxxflags +
0350 self.substitute(bld, self.data["cxxflags"]),
0351 features="cxx",
0352 includes=bic.includes +
0353 self.substitute(bld, self.data["includes"]),
0354 rule=
0355 "${CXX} ${CXXFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
0356 source=[source] + deps,
0357 target=target,
0358 )
0359 return target
0360
0361 def link(self, bld, bic, cmd, source, target):
0362 from waflib.Task import Task
0363
0364 class link(Task):
0365
0366 def __init__(self, item, bic, cmd, env, ldflags):
0367 super(link, self).__init__(self, env=env)
0368 self.cmd = cmd
0369 self.ldflags = ldflags
0370 self.stlib = item.data["stlib"]
0371 self.use = (item.data["use-before"] + bic.use +
0372 item.data["use-after"])
0373
0374 def run(self):
0375 cmd = [self.cmd]
0376 cmd.extend(self.env.LINKFLAGS)
0377 cmd.extend([i.abspath() for i in self.inputs])
0378 cmd.append("-o" + self.outputs[0].abspath())
0379 cmd.append("-L.")
0380 cmd.extend(["-l" + l for l in self.stlib])
0381 cmd.extend(["-l" + l for l in self.use])
0382 cmd.extend(self.env.LDFLAGS)
0383 cmd.extend(self.ldflags)
0384 return self.exec_command(cmd)
0385
0386 def scan(self):
0387 return (
0388 [
0389 self.generator.bld.bldnode.make_node("lib" + u + ".a")
0390 for u in self.use
0391 ],
0392 [],
0393 )
0394
0395 tsk = link(self, bic, cmd, bld.env,
0396 bic.ldflags + self.substitute(bld, self.data["ldflags"]))
0397 tsk.set_inputs([bld.bldnode.make_node(s) for s in source])
0398 tsk.set_outputs(bld.bldnode.make_node(target))
0399 bld.add_to_group(tsk)
0400 return target
0401
0402 def link_cc(self, bld, bic, source, target):
0403 return self.link(bld, bic, bld.env.LINK_CC[0], source, target)
0404
0405 def link_cxx(self, bld, bic, source, target):
0406 return self.link(bld, bic, bld.env.LINK_CXX[0], source, target)
0407
0408 def gnatmake(self, bld, bic, objdir, objs, main, target):
0409 from waflib.Task import Task
0410
0411 class gnatmake(Task):
0412
0413 def __init__(self, bld, bic, objdir, objs, main, target, item):
0414 super(gnatmake, self).__init__(self, env=bld.env)
0415 self.objdir = objdir
0416 self.objs = [bld.bldnode.make_node(o) for o in objs]
0417 self.main = bld.path.make_node(main)
0418 self.set_inputs(self.objs + [self.main])
0419 self.set_outputs(bld.bldnode.make_node(target))
0420 self.adaflags = item.data["adaflags"]
0421 self.adaincludes = []
0422 for i in item.data["adaincludes"]:
0423 self.adaincludes.append(bld.bldnode.make_node(i))
0424 self.adaincludes.append(bld.path.make_node(i))
0425 self.ldflags = bic.ldflags + item.data["ldflags"]
0426 self.stlib = item.data["stlib"]
0427 self.use = (item.data["use-before"] + bic.use +
0428 item.data["use-after"])
0429
0430 def run(self):
0431 cwd = self.get_cwd()
0432 cmd = [
0433 self.env.GNATMAKE[0],
0434 "-D",
0435 self.objdir,
0436 "-bargs",
0437 "-Mgnat_main",
0438 "-margs",
0439 ]
0440 cmd.extend(self.adaflags)
0441 cmd.extend(["-I" + i.path_from(cwd) for i in self.adaincludes])
0442 cmd.append("-cargs")
0443 cmd.extend(self.env.ABI_FLAGS)
0444 cmd.append("-largs")
0445 cmd.extend([o.path_from(cwd) for o in self.objs])
0446 cmd.extend(self.env.LINKFLAGS)
0447 cmd.extend(self.ldflags)
0448 cmd.append("-L.")
0449 cmd.extend(["-l" + l for l in self.stlib])
0450 cmd.extend(["-l" + l for l in self.use])
0451 cmd.extend(self.env.LDFLAGS)
0452 cmd.extend(["-margs", "-a"])
0453 cmd.append(self.main.abspath())
0454 cmd.append("-o")
0455 cmd.append(self.outputs[0].abspath())
0456 return self.exec_command(cmd)
0457
0458 def scan(self):
0459 return (
0460 [
0461 self.generator.bld.bldnode.make_node("lib" + u + ".a")
0462 for u in self.use
0463 ],
0464 [],
0465 )
0466
0467 tsk = gnatmake(bld, bic, objdir, objs, main, target, self)
0468 bld.add_to_group(tsk)
0469 return target
0470
0471 def ar(self, bld, source, target):
0472 bld(rule="${AR} ${ARFLAGS} ${TGT} ${SRC}",
0473 source=source,
0474 target=target)
0475 return target
0476
0477 def gzip(self, bld, source):
0478 target = source + ".gz"
0479 bld(rule="${GZIP} -n < ${SRC} > ${TGT}", source=source, target=target)
0480 return target
0481
0482 def xz(self, bld, source):
0483 target = source + ".xz"
0484 bld(rule="${XZ} < ${SRC} > ${TGT}", source=source, target=target)
0485 return target
0486
0487 def tar(self, bld, source, remove, target):
0488
0489 def run(task):
0490 import tarfile
0491
0492 tar = tarfile.TarFile(task.outputs[0].abspath(),
0493 "w",
0494 format=tarfile.USTAR_FORMAT)
0495 srcpath = bld.path.abspath() + "/"
0496 bldpath = bld.bldnode.abspath() + "/"
0497 for src in task.inputs:
0498 src = src.abspath()
0499 dst = src
0500 for r in remove:
0501 dst = src.replace(srcpath + r, "").replace(bldpath + r, "")
0502 tar.add(src, dst, filter=make_tar_info_reproducible)
0503 tar.close()
0504 return 0
0505
0506 bld(rule=run, source=source, target=target)
0507 return target
0508
0509 def bin2c(self, bld, source, name=None, target=None):
0510
0511 def run(task):
0512 cmd = [bld.env.BIN2C[0]]
0513 if name is not None:
0514 cmd.extend(["-N", name])
0515 cmd.append(task.inputs[0].abspath())
0516 cmd.append(task.outputs[0].abspath())
0517 return task.exec_command(cmd)
0518
0519 path, base = os.path.split(source)
0520 if target is None:
0521 target = path + "/" + base.replace(".", "-")
0522 target_c = target + ".c"
0523 target_h = target + ".h"
0524 bld(rule=run, source=source, target=[target_c, target_h])
0525 return target_c, target_h
0526
0527 def rtems_syms(self, bld, bic, source, target):
0528 syms_source = os.path.splitext(target)[0] + ".c"
0529 bld(
0530 rule='${RTEMS_SYMS} -e -S ${TGT} ${SRC}',
0531 source=source,
0532 target=syms_source,
0533 )
0534 return self.cc(bld, bic, syms_source, target)
0535
0536 def rtems_rap(self, bld, base, objects, libs, target):
0537
0538 def run(task):
0539 cmd = [
0540 bld.env.RTEMS_LD[0],
0541 "-C",
0542 bld.env.CC[0],
0543 "-c",
0544 " ".join(bld.env.CFLAGS),
0545 "-O",
0546 "rap",
0547 "-b",
0548 task.inputs[0].abspath(),
0549 "-e",
0550 "rtems_main",
0551 "-s",
0552 "-o",
0553 ]
0554 cmd.append(task.outputs[0].abspath())
0555 cmd.extend([i.abspath() for i in task.inputs[1:]])
0556 cmd.extend(["-l" + l for l in libs])
0557 return task.exec_command(cmd)
0558
0559 bld(rule=run, source=[base] + objects, target=target)
0560 return target
0561
0562
0563 class GroupItem(Item):
0564
0565 def __init__(self, uid, data):
0566 super(GroupItem, self).__init__(uid, data)
0567
0568 def prepare_build(self, bld, bic):
0569 return BuildItemContext(
0570 bic.includes + self.substitute(bld, self.data["includes"]),
0571 bic.cppflags + self.substitute(bld, self.data["cppflags"]),
0572 bic.cflags + self.substitute(bld, self.data["cflags"]),
0573 bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
0574 self.data["use-before"] + bic.use + self.data["use-after"],
0575 bic.ldflags + self.substitute(bld, self.data["ldflags"]),
0576 bic.objects,
0577 )
0578
0579 def do_build(self, bld, bic):
0580 self.install_files(bld)
0581
0582
0583 class ConfigFileItem(Item):
0584
0585 def __init__(self, uid, data):
0586 super(ConfigFileItem, self).__init__(uid, data)
0587
0588 def do_configure(self, conf, cic):
0589 content = self.substitute(conf, self.data["content"])
0590 f = conf.bldnode.make_node(conf.env.VARIANT + "/" +
0591 self.get(conf, "target"))
0592 f.parent.mkdir()
0593 f.write(content)
0594 conf.env.append_value("cfg_files", f.abspath())
0595
0596 def do_build(self, bld, bic):
0597 self.install_target(bld)
0598
0599
0600 class ConfigHeaderItem(Item):
0601
0602 def __init__(self, uid, data):
0603 super(ConfigHeaderItem, self).__init__(uid, data)
0604
0605 def do_configure(self, conf, cic):
0606 conf.env.include_key = self.data["include-headers"]
0607 conf.write_config_header(
0608 conf.env.VARIANT + "/" + self.get(conf, "target"),
0609 guard=self.data["guard"],
0610 headers=True,
0611 )
0612 conf.env.include_key = None
0613
0614 def do_build(self, bld, bic):
0615 self.install_target(bld)
0616
0617
0618 class StartFileItem(Item):
0619
0620 def __init__(self, uid, data):
0621 super(StartFileItem, self).__init__(uid, data)
0622
0623 def do_build(self, bld, bic):
0624 source = self.data["source"]
0625 if os.path.splitext(source[0])[1] == ".S":
0626 tgt = self.asm(bld, bic, source, self.get(bld, "target"))
0627 else:
0628 tgt = self.cc(bld, bic, source, self.get(bld, "target"))
0629 node = bld.bldnode.make_node(tgt)
0630 try:
0631 bld.start_files.append(node)
0632 except AttributeError:
0633 bld.start_files = [node]
0634 self.install_target(bld)
0635
0636
0637 class ObjectsItem(Item):
0638
0639 def __init__(self, uid, data):
0640 super(ObjectsItem, self).__init__(uid, data)
0641
0642 def prepare_build(self, bld, bic):
0643 return BuildItemContext(
0644 bic.includes + self.substitute(bld, self.data["includes"]),
0645 bic.cppflags + self.substitute(bld, self.data["cppflags"]),
0646 bic.cflags + self.substitute(bld, self.data["cflags"]),
0647 bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
0648 bic.use,
0649 bic.ldflags,
0650 bic.objects,
0651 )
0652
0653 def do_build(self, bld, bic):
0654 bld.objects(
0655 asflags=bic.cppflags,
0656 cflags=bic.cflags,
0657 cppflags=bic.cppflags,
0658 cxxflags=bic.cxxflags,
0659 includes=bic.includes,
0660 source=self.data["source"],
0661 target=self.uid,
0662 )
0663 bic.objects.append(self.uid)
0664 self.install_files(bld)
0665
0666
0667 class BSPItem(Item):
0668
0669 def __init__(self, uid, data):
0670 super(BSPItem, self).__init__(uid, data)
0671 arch_bsps = bsps.setdefault(data["arch"].strip(), {})
0672 arch_bsps[data["bsp"].strip()] = self
0673
0674 def prepare_build(self, bld, bic):
0675 return BuildItemContext(
0676 bic.includes + bld.env.BSP_INCLUDES +
0677 self.substitute(bld, self.data["includes"]),
0678 self.substitute(bld, self.data["cppflags"]),
0679 bld.env.BSP_CFLAGS + self.substitute(bld, self.data["cflags"]),
0680 [],
0681 [],
0682 [],
0683 [],
0684 )
0685
0686 def do_build(self, bld, bic):
0687 bld(
0688 cflags=bic.cflags,
0689 cppflags=bic.cppflags,
0690 features="c cstlib",
0691 includes=bic.includes,
0692 install_path="${BSP_LIBDIR}",
0693 source=self.data["source"],
0694 target="rtemsbsp",
0695 use=bic.objects,
0696 )
0697 self.install_files(bld)
0698
0699
0700 class LibraryItem(Item):
0701
0702 def __init__(self, uid, data):
0703 super(LibraryItem, self).__init__(uid, data)
0704
0705 def prepare_build(self, bld, bic):
0706 return BuildItemContext(
0707 bic.includes + self.substitute(bld, self.data["includes"]),
0708 bic.cppflags + self.substitute(bld, self.data["cppflags"]),
0709 bic.cflags + self.substitute(bld, self.data["cflags"]),
0710 bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
0711 bic.use,
0712 bic.ldflags,
0713 [],
0714 )
0715
0716 def do_build(self, bld, bic):
0717 bld(
0718 cflags=bic.cflags,
0719 cppflags=bic.cppflags,
0720 cxxflags=bic.cxxflags,
0721 features="c cxx cstlib",
0722 includes=bic.includes,
0723 install_path=self.data["install-path"],
0724 source=self.data["source"],
0725 target=self.get(bld, "target"),
0726 use=bic.objects,
0727 )
0728 self.install_files(bld)
0729
0730
0731 class TestProgramItem(Item):
0732
0733 def __init__(self, uid, data):
0734 super(TestProgramItem, self).__init__(uid, data)
0735 name = uid.split("/")[-1].upper().replace("-", "_")
0736 self.exclude = "TEST_" + name + "_EXCLUDE"
0737 self.cppflags = "TEST_" + name + "_CPPFLAGS"
0738
0739 def get_enabled_by(self):
0740 return [{"and": [{"not": self.exclude}, self.data["enabled-by"]]}]
0741
0742 def prepare_build(self, bld, bic):
0743 return BuildItemContext(
0744 bic.includes + self.substitute(bld, self.data["includes"]),
0745 bic.cppflags + bld.env[self.cppflags] +
0746 self.substitute(bld, self.data["cppflags"]),
0747 bic.cflags + self.substitute(bld, self.data["cflags"]),
0748 bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
0749 self.data["use-before"] + bic.use + self.data["use-after"],
0750 bic.ldflags + self.substitute(bld, self.data["ldflags"]),
0751 [],
0752 )
0753
0754 def do_build(self, bld, bic):
0755 bld(
0756 cflags=bic.cflags,
0757 cppflags=bic.cppflags,
0758 cxxflags=bic.cxxflags,
0759 features=self.data["features"],
0760 includes=bic.includes,
0761 install_path=None,
0762 ldflags=bic.ldflags,
0763 source=self.data["source"],
0764 start_files=True,
0765 stlib=self.data["stlib"],
0766 target=self.get(bld, "target"),
0767 use=bic.objects + bic.use,
0768 )
0769
0770
0771 class AdaTestProgramItem(TestProgramItem):
0772
0773 def __init__(self, uid, data):
0774 super(AdaTestProgramItem, self).__init__(uid, data)
0775
0776 def do_build(self, bld, bic):
0777 objs = []
0778 for s in self.data["source"]:
0779 objs.append(self.cc(bld, bic, s))
0780 self.gnatmake(
0781 bld,
0782 bic,
0783 self.data["ada-object-directory"],
0784 objs,
0785 self.data["ada-main"],
0786 self.data["target"],
0787 )
0788
0789
0790 class OptionItem(Item):
0791
0792 def __init__(self, uid, data):
0793 super(OptionItem, self).__init__(uid, data)
0794
0795 def default_value(self, enabled):
0796 value = None
0797 for default in self.data["default"]:
0798 if _is_enabled(enabled, default["enabled-by"]):
0799 value = default["value"]
0800 break
0801 if value is None:
0802 return value
0803 if isinstance(value, list):
0804 return " ".join(value)
0805 if isinstance(value, bool):
0806 return value
0807 return self.data["format"].format(value)
0808
0809 def do_defaults(self, enabled):
0810 value = self.default_value(enabled)
0811 if value is None:
0812 return
0813 description = self.data["description"]
0814 if description:
0815 import textwrap
0816
0817 tw = textwrap.TextWrapper()
0818 tw.drop_whitespace = True
0819 tw.initial_indent = "# "
0820 tw.subsequent_indent = "# "
0821 for line in tw.wrap(description):
0822 print(line)
0823 print("{} = {}".format(self.data["name"], value))
0824
0825 def _do_append_test_cppflags(self, conf, name, state):
0826 conf.env.append_value(
0827 "TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state)
0828
0829 def _append_test_cppflags(self, conf, cic, value, arg):
0830 self._do_append_test_cppflags(conf, arg, value)
0831 return value
0832
0833 def _assert_aligned(self, conf, cic, value, arg):
0834 if value is not None and value % arg != 0:
0835 conf.fatal(
0836 "Value '{}' for option '{}' is not aligned by '{}'".format(
0837 value, self.data["name"], arg))
0838 return value
0839
0840 def _assert_eq(self, conf, cic, value, arg):
0841 if value is not None and value != arg:
0842 conf.fatal("Value '{}' for option '{}' is not equal to {}".format(
0843 value, self.data["name"], arg))
0844 return value
0845
0846 def _assert_ge(self, conf, cic, value, arg):
0847 if value is not None and value < arg:
0848 conf.fatal(
0849 "Value '{}' for option '{}' is not greater than or equal to {}"
0850 .format(value, self.data["name"], arg))
0851 return value
0852
0853 def _assert_gt(self, conf, cic, value, arg):
0854 if value is not None and value <= arg:
0855 conf.fatal(
0856 "Value '{}' for option '{}' is not greater than {}".format(
0857 value, self.data["name"], arg))
0858 return value
0859
0860 def _assert_in_set(self, conf, cic, value, arg):
0861 if value is not None and value not in arg:
0862 conf.fatal(
0863 "Value '{}' for option '{}' is not an element of {}".format(
0864 value, self.data["name"], arg))
0865 return value
0866
0867 def _assert_in_interval(self, conf, cic, value, arg):
0868 if value is not None and (value < arg[0] or value > arg[1]):
0869 conf.fatal(
0870 "Value '{}' for option '{}' is not in closed interval [{}, {}]"
0871 .format(value, self.data["name"], arg[0], arg[1]))
0872 return value
0873
0874 def _assert_int8(self, conf, cic, value, arg):
0875 return self._assert_in_interval(conf, cic, value, [-128, 127])
0876
0877 def _assert_int16(self, conf, cic, value, arg):
0878 return self._assert_in_interval(conf, cic, value, [-32768, 32767])
0879
0880 def _assert_int32(self, conf, cic, value, arg):
0881 return self._assert_in_interval(conf, cic, value,
0882 [-2147483648, 2147483647])
0883
0884 def _assert_int64(self, conf, cic, value, arg):
0885 return self._assert_in_interval(
0886 conf, cic, value, [-9223372036854775808, 9223372036854775807])
0887
0888 def _assert_le(self, conf, cic, value, arg):
0889 if value is not None and value > arg:
0890 conf.fatal(
0891 "Value '{}' for option '{}' is not less than or equal to {}".
0892 format(value, self.data["name"], arg))
0893 return value
0894
0895 def _assert_lt(self, conf, cic, value, arg):
0896 if value is not None and value >= arg:
0897 conf.fatal("Value '{}' for option '{}' is not less than {}".format(
0898 value, self.data["name"], arg))
0899 return value
0900
0901 def _assert_ne(self, conf, cic, value, arg):
0902 if value is not None and value == arg:
0903 conf.fatal(
0904 "Value '{}' for option '{}' is not unequal to {}".format(
0905 value, self.data["name"], arg))
0906 return value
0907
0908 def _assert_power_of_two(self, conf, cic, value, arg):
0909 if value is not None and (value <= 0 or (value & (value - 1)) != 0):
0910 conf.fatal(
0911 "Value '{}' for option '{}' is not a power of two".format(
0912 value, self.data["name"]))
0913 return value
0914
0915 def _assert_uint8(self, conf, cic, value, arg):
0916 return self._assert_in_interval(conf, cic, value, [0, 255])
0917
0918 def _assert_uint16(self, conf, cic, value, arg):
0919 return self._assert_in_interval(conf, cic, value, [0, 65535])
0920
0921 def _assert_uint32(self, conf, cic, value, arg):
0922 return self._assert_in_interval(conf, cic, value, [0, 4294967295])
0923
0924 def _assert_uint64(self, conf, cic, value, arg):
0925 return self._assert_in_interval(conf, cic, value,
0926 [0, 18446744073709551615])
0927
0928 def _check_cc(self, conf, cic, value, arg):
0929 result = conf.check_cc(
0930 fragment=arg["fragment"],
0931 cflags=arg["cflags"],
0932 msg="Checking for " + arg["message"],
0933 mandatory=False,
0934 )
0935 return value and result
0936
0937 def _check_cxx(self, conf, cic, value, arg):
0938 result = conf.check_cxx(
0939 fragment=arg["fragment"],
0940 cxxflags=arg["cxxflags"],
0941 msg="Checking for " + arg["message"],
0942 mandatory=False,
0943 )
0944 return value and result
0945
0946 def _comment(self, conf, cic, value, arg):
0947 return value
0948
0949 def _define_condition(self, conf, cic, value, arg):
0950 name = self.data["name"] if arg is None else arg
0951 conf.define_cond(name, value)
0952 return value
0953
0954 def _define(self, conf, cic, value, arg):
0955 name = self.data["name"] if arg is None else arg
0956 if value is not None:
0957 conf.define(name, value)
0958 else:
0959 conf.define_cond(name, False)
0960 return value
0961
0962 def _define_unquoted(self, conf, cic, value, arg):
0963 name = self.data["name"] if arg is None else arg
0964 if value is not None:
0965 conf.define(name, value, quote=False)
0966 else:
0967 conf.define_cond(name, False)
0968 return value
0969
0970 def _env_append(self, conf, cic, value, arg):
0971 name = self.data["name"] if arg is None else arg
0972 conf.env.append_value(name, value)
0973 return value
0974
0975 def _env_assign(self, conf, cic, value, arg):
0976 name = self.data["name"] if arg is None else arg
0977 conf.env[name] = value
0978 return value
0979
0980 def _env_enable(self, conf, cic, value, arg):
0981 if value:
0982 name = self.data["name"] if arg is None else arg
0983 conf.env.append_value("ENABLE", name)
0984 return value
0985
0986 def _find_program(self, conf, cic, value, arg):
0987 return conf.find_program(value, path_list=cic.path_list)
0988
0989 def _format_and_define(self, conf, cic, value, arg):
0990 name = self.data["name"] if arg is None else arg
0991 if value is not None:
0992 conf.define(name, self.data["format"].format(value), quote=False)
0993 else:
0994 conf.define_cond(name, False)
0995 return value
0996
0997 def _get_boolean(self, conf, cic, value, arg):
0998 name = self.data["name"]
0999 try:
1000 value = cic.cp.getboolean(conf.variant, name)
1001 cic.add_option(name)
1002 except configparser.NoOptionError:
1003 value = self.default_value(conf.env.ENABLE)
1004 except ValueError as ve:
1005 conf.fatal("Invalid value for configuration option {}: {}".format(
1006 name, ve))
1007 return value
1008
1009 def _get_env(self, conf, cic, value, arg):
1010 return conf.env[arg]
1011
1012 def _get_integer(self, conf, cic, value, arg):
1013 name = self.data["name"]
1014 try:
1015 value = cic.cp.get(conf.variant, name)
1016 cic.add_option(name)
1017 except configparser.NoOptionError:
1018 value = self.default_value(conf.env.ENABLE)
1019 if not value:
1020 return None
1021 try:
1022 return eval(value)
1023 except Exception as e:
1024 conf.fatal(
1025 "Value '{}' for option '{}' is an invalid integer expression: {}"
1026 .format(value, name, e))
1027
1028 def _get_string(self, conf, cic, value, arg):
1029 name = self.data["name"]
1030 try:
1031 value = cic.cp.get(conf.variant, name)
1032 cic.add_option(name)
1033 except configparser.NoOptionError:
1034 value = self.default_value(conf.env.ENABLE)
1035 return value
1036
1037 def _script(self, conf, cic, value, arg):
1038 local_variables = {
1039 "self": self,
1040 "conf": conf,
1041 "cic": cic,
1042 "value": value
1043 }
1044 exec(arg, None, local_variables)
1045 return local_variables["value"]
1046
1047 def _test_state_benchmark(self, conf, name):
1048 self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
1049
1050 def _test_state_exclude(self, conf, name):
1051 conf.env.append_value(
1052 "ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE")
1053
1054 def _test_state_expected_fail(self, conf, name):
1055 self._do_append_test_cppflags(conf, name,
1056 "-DTEST_STATE_EXPECTED_FAIL=1")
1057
1058 def _test_state_indeterminate(self, conf, name):
1059 self._do_append_test_cppflags(conf, name,
1060 "-DTEST_STATE_INDETERMINATE=1")
1061
1062 def _test_state_user_input(self, conf, name):
1063 self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
1064
1065 def _set_test_state(self, conf, cic, value, arg):
1066 actions = {
1067 "benchmark": self._test_state_benchmark,
1068 "exclude": self._test_state_exclude,
1069 "expected-fail": self._test_state_expected_fail,
1070 "indeterminate": self._test_state_indeterminate,
1071 "user-input": self._test_state_user_input,
1072 }
1073 action = actions[arg["state"]]
1074 for test in arg["tests"]:
1075 action(conf, test)
1076 return value
1077
1078 def _set_value(self, conf, cic, value, arg):
1079 return arg
1080
1081 def _set_value_enabled_by(self, conf, cic, value, arg):
1082 for value_enabled_by in arg:
1083 if _is_enabled(conf.env.ENABLE, value_enabled_by["enabled-by"]):
1084 return value_enabled_by["value"]
1085 return None
1086
1087 def _split(self, conf, cic, value, arg):
1088 return value.split()
1089
1090 def _substitute(self, conf, cic, value, arg):
1091 if isinstance(value, list):
1092 return [self.substitute(conf, v) for v in value]
1093 else:
1094 return self.substitute(conf, value)
1095
1096 def do_configure(self, conf, cic):
1097 actions = {
1098 "append-test-cppflags": self._append_test_cppflags,
1099 "assert-aligned": self._assert_aligned,
1100 "assert-eq": self._assert_eq,
1101 "assert-ge": self._assert_ge,
1102 "assert-gt": self._assert_gt,
1103 "assert-in-set": self._assert_in_set,
1104 "assert-int8": self._assert_int8,
1105 "assert-int16": self._assert_int16,
1106 "assert-int32": self._assert_int32,
1107 "assert-int64": self._assert_int64,
1108 "assert-le": self._assert_le,
1109 "assert-lt": self._assert_lt,
1110 "assert-ne": self._assert_ne,
1111 "assert-power-of-two": self._assert_power_of_two,
1112 "assert-uint8": self._assert_uint8,
1113 "assert-uint16": self._assert_uint16,
1114 "assert-uint32": self._assert_uint32,
1115 "assert-uint64": self._assert_uint64,
1116 "check-cc": self._check_cc,
1117 "check-cxx": self._check_cxx,
1118 "comment": self._comment,
1119 "define-condition": self._define_condition,
1120 "define": self._define,
1121 "define-unquoted": self._define_unquoted,
1122 "env-append": self._env_append,
1123 "env-assign": self._env_assign,
1124 "env-enable": self._env_enable,
1125 "find-program": self._find_program,
1126 "format-and-define": self._format_and_define,
1127 "get-boolean": self._get_boolean,
1128 "get-env": self._get_env,
1129 "get-integer": self._get_integer,
1130 "get-string": self._get_string,
1131 "script": self._script,
1132 "set-test-state": self._set_test_state,
1133 "set-value": self._set_value,
1134 "set-value-enabled-by": self._set_value_enabled_by,
1135 "split": self._split,
1136 "substitute": self._substitute,
1137 }
1138 value = None
1139 for action in self.data["actions"]:
1140 for action_arg in action.items():
1141 value = actions[action_arg[0]](conf, cic, value, action_arg[1])
1142
1143
1144 class ScriptItem(Item):
1145
1146 def __init__(self, uid, data):
1147 super(ScriptItem, self).__init__(uid, data)
1148
1149 def prepare_configure(self, conf, cic):
1150 script = self.data["prepare-configure"]
1151 if script:
1152 exec(script)
1153
1154 def do_configure(self, conf, cic):
1155 script = self.data["do-configure"]
1156 if script:
1157 exec(script)
1158
1159 def prepare_build(self, bld, bic):
1160 script = self.data["prepare-build"]
1161 if script:
1162 exec(script)
1163 return bic
1164
1165 def do_build(self, bld, bic):
1166 script = self.data["do-build"]
1167 if script:
1168 exec(script)
1169
1170
1171 class ConfigItemContext(object):
1172
1173 def __init__(self, cp, path_list):
1174 self.cp = cp
1175 self.options = set()
1176 self.path_list = path_list
1177
1178 def add_option(self, name):
1179 self.options.add(name.upper())
1180
1181
1182 class BuildItemContext(object):
1183
1184 def __init__(self, includes, cppflags, cflags, cxxflags, use, ldflags,
1185 objects):
1186 self.includes = includes
1187 self.cppflags = cppflags
1188 self.cflags = cflags
1189 self.cxxflags = cxxflags
1190 self.use = use
1191 self.ldflags = ldflags
1192 self.objects = objects
1193
1194
1195 def is_one_item_newer(ctx, path, mtime):
1196 try:
1197 mtime2 = os.path.getmtime(path)
1198 if mtime <= mtime2:
1199 return True
1200 names = os.listdir(path)
1201 except Exception as e:
1202 ctx.fatal("Cannot access build specification directory: {}".format(e))
1203 for name in names:
1204 path2 = os.path.join(path, name)
1205 if name.endswith(".yml") and not name.startswith("."):
1206 mtime2 = os.path.getmtime(path2)
1207 if mtime <= mtime2:
1208 return True
1209 else:
1210 mode = os.lstat(path2).st_mode
1211 if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
1212 return True
1213 return False
1214
1215
1216 def must_update_item_cache(ctx, path, cache_file):
1217 try:
1218 mtime = os.path.getmtime(cache_file)
1219 except:
1220 return True
1221 return is_one_item_newer(ctx, path, mtime)
1222
1223
1224 def load_from_yaml(load, ctx, data_by_uid, base, path):
1225 try:
1226 names = os.listdir(path)
1227 except Exception as e:
1228 ctx.fatal("Cannot list build specification directory: {}".format(e))
1229 for name in names:
1230 path2 = os.path.join(path, name)
1231 if name.endswith(".yml") and not name.startswith("."):
1232 uid = "/" + os.path.relpath(path2, base).replace(
1233 ".yml", "").replace("\\", "/")
1234 with open(path2, "r") as f:
1235 data_by_uid[uid] = load(f.read())
1236 else:
1237 mode = os.lstat(path2).st_mode
1238 if stat.S_ISDIR(mode):
1239 load_from_yaml(load, ctx, data_by_uid, base, path2)
1240
1241
1242 def load_items_in_directory(ctx, ctors, path):
1243 p = re.sub(r"[^\w]", "_", path) + ".pickle"
1244 try:
1245 f = ctx.bldnode.make_node(p)
1246 except AttributeError:
1247 f = ctx.path.make_node("build/" + p)
1248 f.parent.mkdir()
1249 cache_file = f.abspath()
1250 data_by_uid = {}
1251 if must_update_item_cache(ctx, path, cache_file):
1252 from waflib import Logs
1253
1254 Logs.warn(
1255 "Regenerate build specification cache (needs a couple of seconds)..."
1256 )
1257
1258 #
1259 # Do not use a system provided yaml module and instead import it from
1260 # the project. This reduces the host system requirements to a simple
1261 # Python 3 installation without extra modules.
1262 #
1263 sys.path.append("yaml/lib3")
1264 from yaml import safe_load
1265
1266 load_from_yaml(safe_load, ctx, data_by_uid, path, path)
1267 with open(cache_file, "wb") as f:
1268 pickle.dump(data_by_uid, f)
1269 else:
1270 with open(cache_file, "rb") as f:
1271 data_by_uid = pickle.load(f)
1272 for uid, data in data_by_uid.items():
1273 if data["type"] == "build":
1274 items[uid] = ctors[data["build-type"]](uid, data)
1275
1276
1277 def load_items(ctx, specs):
1278 if items:
1279 return
1280
1281 ctors = {
1282 "ada-test-program": AdaTestProgramItem,
1283 "bsp": BSPItem,
1284 "config-file": ConfigFileItem,
1285 "config-header": ConfigHeaderItem,
1286 "test-program": TestProgramItem,
1287 "group": GroupItem,
1288 "library": LibraryItem,
1289 "objects": ObjectsItem,
1290 "option": OptionItem,
1291 "script": ScriptItem,
1292 "start-file": StartFileItem,
1293 }
1294
1295 for path in specs:
1296 load_items_in_directory(ctx, ctors, path)
1297
1298
1299 def load_items_from_options(ctx):
1300 specs = ctx.options.rtems_specs
1301 if specs is not None:
1302 specs = specs.split(",")
1303 else:
1304 specs = ["spec/build"]
1305 load_items(ctx, specs)
1306 return specs
1307
1308
1309 def options(ctx):
1310 load_version(ctx)
1311 prefix = ctx.parser.get_option("--prefix")
1312 prefix.default = default_prefix
1313 prefix.help = "installation prefix [default: '{}']".format(default_prefix)
1314 rg = ctx.add_option_group("RTEMS options")
1315 rg.add_option(
1316 "--rtems-bsps",
1317 metavar="REGEX,...",
1318 help=
1319 "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",
1320 )
1321 rg.add_option(
1322 "--rtems-compiler",
1323 metavar="COMPILER",
1324 help=
1325 "determines which compiler is used to list the BSP option defaults [default: 'gcc']; it may be used in the bspdefaults command; valid compilers are: {}"
1326 .format(", ".join(compilers)),
1327 )
1328 rg.add_option(
1329 "--rtems-config",
1330 metavar="CONFIG.INI,...",
1331 help=
1332 "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",
1333 )
1334 rg.add_option(
1335 "--rtems-specs",
1336 metavar="SPECDIRS,...",
1337 help=
1338 "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",
1339 )
1340 rg.add_option(
1341 "--rtems-tools",
1342 metavar="PREFIX,...",
1343 help=
1344 "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",
1345 )
1346 rg.add_option(
1347 "--rtems-top-group",
1348 metavar="UID",
1349 help=
1350 "the UID of the top-level group [default: '/grp']; it may be used in the bspdefaults and configure commands",
1351 )
1352
1353
1354 def check_environment(conf):
1355 for ev in [
1356 "AR",
1357 "AS",
1358 "ASFLAGS",
1359 "CC",
1360 "CFLAGS",
1361 "CPPFLAGS",
1362 "CXX",
1363 "CXXFLAGS",
1364 "IFLAGS",
1365 "LD",
1366 "LIB",
1367 "LINK_CC",
1368 "LINK_CXX",
1369 "LINKFLAGS",
1370 "MFLAGS",
1371 "RFLAGS",
1372 "WFLAGS",
1373 ]:
1374 if ev in os.environ:
1375 conf.msg("Environment variable set", ev, color="RED")
1376
1377
1378 def load_version(ctx):
1379 global default_prefix
1380
1381 def _check_num(s):
1382 try:
1383 i = int(s)
1384 except:
1385 ctx.fatal(
1386 "Invalid VERSION number: version number is not a number: " + s)
1387
1388 cp = configparser.ConfigParser()
1389 version_file = "VERSION"
1390 version_major = None
1391 version_minor = None
1392 version_revision = None
1393 version_label = None
1394 prefix = None
1395 if cp.read([version_file]):
1396 try:
1397 value = cp.get("version", "revision")
1398 # The revision is <major>.<minor>[-label]
1399 # break is up and update the version
1400 if "." not in value:
1401 ctx.fatal(
1402 "Invalid VERSION revision: no number (dot) separator")
1403 # version-string => major.minor[.revsion][-label]
1404 vs = value.split(".", 2)
1405 _check_num(vs[0])
1406 version_major = vs[0]
1407 if "-" in vs[-1]:
1408 ls = vs[-1].split("-", 1)
1409 vs[-1] = ls[0]
1410 version_label = ls[1]
1411 _check_num(vs[1])
1412 version_minor = vs[1]
1413 if len(vs) == 3:
1414 _check_num(vs[2])
1415 version_revision = vs[2]
1416 prefix = "/opt/rtems/" + version_major
1417 except configparser.NoOptionError:
1418 pass
1419 if version_label is None and version_revision is not None:
1420 ctx.fatal(
1421 "Invalid VERSION revision: a revision number requires a label")
1422 if version_major is not None:
1423 version["__RTEMS_MAJOR__"] = version_major
1424 if version_minor is not None:
1425 version["__RTEMS_MINOR__"] = version_minor
1426 if version_revision is not None:
1427 version["__RTEMS_REVISION__"] = version_revision
1428 if version_label is not None:
1429 version["RTEMS_RELEASE_VERSION_LABEL"] = version_label
1430 # Checking minor insures major and minor are valid
1431 if version_minor is None:
1432 version_label = get_repo_release_label(ctx)
1433 return version_label
1434
1435
1436 def configure_version(conf):
1437 version_label = load_version(conf)
1438 v_str = version["__RTEMS_MAJOR__"] + "." + version["__RTEMS_MINOR__"]
1439 if int(version["__RTEMS_REVISION__"]) != 0:
1440 v_str += "." + version["__RTEMS_REVISION__"]
1441 if version_label is not None and version_label != "":
1442 v_str += "." + version_label
1443 conf.msg("Configure RTEMS version", v_str, color="YELLOW")
1444
1445
1446 def load_config_files(ctx):
1447 cp = configparser.ConfigParser()
1448 files = ctx.options.rtems_config
1449 if files is not None:
1450 files = files.split(",")
1451 else:
1452 files = ["config.ini"]
1453 actual_files = cp.read(files)
1454 for o in files:
1455 if not o in actual_files:
1456 ctx.fatal("Option file '{}' was not readable".format(o))
1457 return cp
1458
1459
1460 def inherit(conf, cp, bsp_map, arch, bsp, path):
1461 variant = arch + "/" + bsp
1462 if variant in path:
1463 path = " -> ".join(path + [variant])
1464 conf.fatal("Recursion in BSP options inheritance: {}".format(path))
1465
1466 try:
1467 base = cp.get(variant, "INHERIT")
1468 cp.remove_option(variant, "INHERIT")
1469 base_variant = arch + "/" + base
1470 conf.msg(
1471 "Inherit options from '{}'".format(base_variant),
1472 variant,
1473 color="YELLOW",
1474 )
1475 if not cp.has_section(base_variant):
1476 if (not arch in bsps) or (not base in bsps[arch]):
1477 conf.fatal(
1478 "BSP variant '{}' cannot inherit options from not existing variant '{}'"
1479 .format(variant, base_variant))
1480 bsp_map[bsp] = base
1481 return base
1482 top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
1483 for i in cp.items(base_variant):
1484 name = i[0]
1485 if not cp.has_option(variant, name):
1486 cp.set(variant, name, i[1])
1487 bsp_map[bsp] = top
1488 return top
1489 except configparser.NoOptionError:
1490 return bsp_map.get(bsp, bsp)
1491
1492
1493 def resolve_option_inheritance(conf, cp):
1494 bsp_map = {}
1495 for variant in cp.sections():
1496 try:
1497 arch, bsp = variant.split("/")
1498 except:
1499 conf.fatal(
1500 "Section name '{}' is a malformed 'arch/bsp' tuple".format(
1501 variant))
1502 inherit(conf, cp, bsp_map, arch, bsp, [])
1503 return bsp_map
1504
1505
1506 def check_compiler(ctx, compiler):
1507 if compiler not in compilers:
1508 ctx.fatal("Specified compiler '{}' is not one of {}".format(
1509 compiler, compilers))
1510
1511
1512 def get_compiler(conf, cp, variant):
1513 try:
1514 value = cp.get(variant, "COMPILER")
1515 cp.remove_option(variant, "COMPILER")
1516 check_compiler(conf, value)
1517 except configparser.NoOptionError:
1518 value = "gcc"
1519 return value
1520
1521
1522 def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
1523 conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
1524
1525 conf.setenv(variant)
1526 arch, bsp_name = variant.split("/")
1527 bsp_base = bsp_map.get(bsp_name, bsp_name)
1528
1529 try:
1530 bsp_item = bsps[arch][bsp_base]
1531 except KeyError:
1532 conf.fatal("No such base BSP: '{}'".format(variant))
1533
1534 family = bsp_item.data["family"]
1535
1536 arch_bsp = arch + "/" + bsp_base
1537 arch_family = arch + "/" + family
1538
1539 for key, value in version.items():
1540 conf.env[key] = value
1541
1542 conf.env["ARCH"] = arch
1543 conf.env["ARCH_BSP"] = arch_bsp
1544 conf.env["ARCH_FAMILY"] = arch_family
1545 conf.env["BSP_BASE"] = bsp_base
1546 conf.env["BSP_NAME"] = bsp_name
1547 conf.env["BSP_FAMILY"] = family
1548 conf.env["DEST_OS"] = "rtems"
1549
1550 # For the enabled-by evaluation we have to use the base BSP defined by the
1551 # build specification and not the BSP name provided by the user.
1552 conf.env["ENABLE"] = [
1553 get_compiler(conf, cp, variant),
1554 arch,
1555 "bsps/" + arch_family,
1556 arch_bsp,
1557 ]
1558
1559 conf.env["TOP"] = conf.path.abspath()
1560 conf.env["TOPGROUP"] = top_group
1561 conf.env["VARIANT"] = variant
1562
1563 cic = ConfigItemContext(cp, path_list)
1564 items[conf.env.TOPGROUP].configure(conf, cic)
1565 bsp_item.configure(conf, cic)
1566
1567 options = set([o[0].upper() for o in cp.items(variant)])
1568 for o in options.difference(cic.options):
1569 conf.msg("Unknown configuration option", o.upper(), color="RED")
1570
1571
1572 def check_forbidden_options(ctx, opts):
1573 for o in opts:
1574 if getattr(ctx.options, "rtems_" + o):
1575 ctx.fatal(
1576 "The --rtems-{} command line option is not allowed in the {} command"
1577 .format(o.replace("_", "-"), ctx.cmd))
1578
1579
1580 def get_path_list(conf):
1581 path_list = []
1582 tools = conf.options.rtems_tools
1583 if tools is not None:
1584 for t in tools.split(","):
1585 path_list.extend([t + "/bin", t])
1586 path_list.append(conf.env.PREFIX + "/bin")
1587 path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
1588 return path_list
1589
1590
1591 def get_top_group(ctx):
1592 top_group = ctx.options.rtems_top_group
1593 if top_group is None:
1594 top_group = "/grp"
1595 if top_group not in items:
1596 ctx.fatal(
1597 "There is no top-level group with UID '{}' in the specification".
1598 format(top_group))
1599 return top_group
1600
1601
1602 def configure(conf):
1603 check_forbidden_options(conf, ["compiler"])
1604 configure_version(conf)
1605 check_environment(conf)
1606 conf.env["SPECS"] = load_items_from_options(conf)
1607 top_group = get_top_group(conf)
1608 cp = load_config_files(conf)
1609 bsp_map = resolve_option_inheritance(conf, cp)
1610 path_list = get_path_list(conf)
1611 variant_list = []
1612 for variant in cp.sections():
1613 variant_list.append(variant)
1614 configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
1615 conf.setenv("")
1616 conf.env["VARIANTS"] = variant_list
1617
1618
1619 def append_variant_builds(bld):
1620 import waflib.Options
1621 from waflib.Build import (
1622 BuildContext,
1623 CleanContext,
1624 InstallContext,
1625 UninstallContext,
1626 )
1627
1628 for var in bld.env["VARIANTS"]:
1629 for c in (BuildContext, CleanContext, InstallContext,
1630 UninstallContext):
1631 name = c.__name__.replace("Context", "").lower()
1632
1633 class magic(c):
1634 cmd = name + "_" + var
1635 variant = var
1636
1637 waflib.Options.commands.append(bld.cmd + "_" + var)
1638
1639
1640 def build(bld):
1641 if not bld.variant:
1642 check_forbidden_options(
1643 bld,
1644 ["compiler", "config", "specs", "tools", "top_group"],
1645 )
1646 load_items(bld, bld.env.SPECS)
1647 append_variant_builds(bld)
1648 return
1649 bic = BuildItemContext(bld.env.ARCH_INCLUDES.split(), [], [], [], [], [],
1650 [])
1651 bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
1652 items[bld.env.TOPGROUP].build(bld, bic)
1653
1654
1655 def add_log_filter(name):
1656 msg = "'" + name + "' finished successfully"
1657
1658 class Filter:
1659
1660 def filter(self, rec):
1661 return not msg in rec.getMessage()
1662
1663 import logging
1664
1665 logging.getLogger("waflib").addFilter(Filter())
1666
1667
1668 def get_white_list(ctx):
1669 white_list = ctx.options.rtems_bsps
1670 if white_list:
1671 white_list = white_list.split(",")
1672 return white_list
1673
1674
1675 def is_in_white_list(variant, white_list):
1676 if not white_list:
1677 return True
1678 for pattern in white_list:
1679 if re.match(pattern + "$", variant):
1680 return True
1681 return False
1682
1683
1684 def no_matches_error(ctx, white_list):
1685 if white_list:
1686 ctx.fatal("No BSP matches with the specified patterns: '{}'".format(
1687 "', '".join(white_list)))
1688 else:
1689 ctx.fatal("The build specification contains no BSPs")
1690
1691
1692 def bspdefaults(ctx):
1693 """get all options with default values for base BSP variants"""
1694 check_forbidden_options(ctx, ["config", "tools"])
1695 add_log_filter(ctx.cmd)
1696 load_items_from_options(ctx)
1697 top_group = get_top_group(ctx)
1698 white_list = get_white_list(ctx)
1699 compiler = ctx.options.rtems_compiler
1700 if compiler is not None:
1701 check_compiler(ctx, compiler)
1702 else:
1703 compiler = "gcc"
1704 first = True
1705 for arch in sorted(bsps):
1706 for bsp in sorted(bsps[arch]):
1707 variant = arch + "/" + bsp
1708 if is_in_white_list(variant, white_list):
1709 if not first:
1710 print("")
1711 first = False
1712 print("""[{}]
1713 # Selects the compiler used to build the BSP (allowed values are "gcc" and
1714 # "clang"). Please note that the values of some options depend on the compiler
1715 # selection and changing the compiler may lead to unpredictable behaviour if
1716 # these options are not adjusted as well. Use the --rtems-compiler command line
1717 # option to get the default values for a particular compiler via
1718 # ./waf bspdefaults.
1719 COMPILER = {}""".format(variant, compiler))
1720 enable = [compiler, arch, variant]
1721 bsp_item = bsps[arch][bsp]
1722 family = "bsps/" + arch + "/" + bsp_item.data["family"]
1723 enabled = [compiler, arch, family, variant]
1724 items[top_group].defaults(enabled)
1725 bsp_item.defaults(enabled)
1726 if first:
1727 no_matches_error(ctx, white_list)
1728
1729
1730 def bsplist(ctx):
1731 """lists base BSP variants"""
1732 check_forbidden_options(ctx, ["compiler", "config", "tools", "top_group"])
1733 add_log_filter(ctx.cmd)
1734 load_items_from_options(ctx)
1735 white_list = get_white_list(ctx)
1736 first = True
1737 for arch in sorted(bsps):
1738 for bsp in sorted(bsps[arch]):
1739 variant = arch + "/" + bsp
1740 if is_in_white_list(variant, white_list):
1741 first = False
1742 print(variant)
1743 if first:
1744 no_matches_error(ctx, white_list)