forked from Imagelibrary/rtems
Merge the "default" and "default-by-variant" attributes. Use an "enabled-by" expression to select the default value based on the enabled set. This makes it possible to select default values depending on other options. For example you could choose memory settings based on whether RTEMS_SMP is enabled or disabled. The change was tested by comparing the output of ./waf bspdefaults before and after the change.
1676 lines
54 KiB
Python
Executable File
1676 lines
54 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
#
|
|
# Copyright (C) 2020 Hesham Almatary <Hesham.Almatary@cl.cam.ac.uk>
|
|
# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
from __future__ import print_function
|
|
|
|
import pickle
|
|
import os
|
|
import re
|
|
import stat
|
|
import string
|
|
import sys
|
|
|
|
try:
|
|
import configparser
|
|
except:
|
|
import ConfigParser as configparser
|
|
|
|
from waflib.TaskGen import after, before_method, feature
|
|
|
|
is_windows_host = os.name == "nt" or sys.platform in ["msys", "cygwin"]
|
|
default_prefix = "/opt/rtems/6"
|
|
compilers = ["gcc", "clang"]
|
|
items = {}
|
|
bsps = {}
|
|
|
|
|
|
def no_unicode(value):
|
|
if sys.version_info[0] > 2:
|
|
return value
|
|
if isinstance(value, unicode):
|
|
return str(value)
|
|
return value
|
|
|
|
|
|
class VersionControlKeyHeader:
|
|
_content = None
|
|
|
|
@staticmethod
|
|
def write(bld, filename):
|
|
if VersionControlKeyHeader._content is None:
|
|
from waflib.Build import Context
|
|
from waflib.Errors import WafError
|
|
|
|
content = """/*
|
|
* Automatically generated. Do not edit.
|
|
*/
|
|
#if !defined(_RTEMS_VERSION_VC_KEY_H_)
|
|
#define _RTEMS_VERSION_VC_KEY_H_
|
|
"""
|
|
try:
|
|
rev = bld.cmd_and_log("git rev-parse HEAD",
|
|
quiet=Context.STDOUT).strip()
|
|
content += """#define RTEMS_VERSION_VC_KEY "{}"
|
|
""".format(rev)
|
|
except WafError:
|
|
content += """/* No version control key found; release? */
|
|
"""
|
|
content += """#endif
|
|
"""
|
|
VersionControlKeyHeader._content = content
|
|
f = bld.bldnode.make_node(filename)
|
|
f.parent.mkdir()
|
|
try:
|
|
if content != f.read():
|
|
f.write(VersionControlKeyHeader._content)
|
|
except:
|
|
f.write(VersionControlKeyHeader._content)
|
|
|
|
|
|
class EnvWrapper(object):
|
|
|
|
def __init__(self, env):
|
|
self._env = env
|
|
|
|
def __getitem__(self, name):
|
|
fields = name.split(":")
|
|
v = self._env[fields[0]]
|
|
try:
|
|
fmt = "{:" + fields[1] + "}"
|
|
except IndexError:
|
|
fmt = "{}"
|
|
if isinstance(v, list):
|
|
return " ".join([fmt.format(w) for w in v])
|
|
return fmt.format(v)
|
|
|
|
|
|
class Template(string.Template):
|
|
idpattern = "[_A-Za-z][_A-Za-z0-9:#]*"
|
|
|
|
|
|
_VAR_PATTERN = re.compile("\$\{?(" + Template.idpattern + ")\}?$")
|
|
|
|
|
|
def _is_enabled_op_and(enabled, enabled_by):
|
|
for next_enabled_by in enabled_by:
|
|
if not _is_enabled(enabled, next_enabled_by):
|
|
return False
|
|
return True
|
|
|
|
|
|
def _is_enabled_op_not(enabled, enabled_by):
|
|
return not _is_enabled(enabled, enabled_by)
|
|
|
|
|
|
def _is_enabled_op_or(enabled, enabled_by):
|
|
for next_enabled_by in enabled_by:
|
|
if _is_enabled(enabled, next_enabled_by):
|
|
return True
|
|
return False
|
|
|
|
|
|
_IS_ENABLED_OP = {
|
|
"and": _is_enabled_op_and,
|
|
"not": _is_enabled_op_not,
|
|
"or": _is_enabled_op_or,
|
|
}
|
|
|
|
|
|
def _is_enabled(enabled, enabled_by):
|
|
if isinstance(enabled_by, bool):
|
|
return enabled_by
|
|
if isinstance(enabled_by, list):
|
|
return _is_enabled_op_or(enabled, enabled_by)
|
|
if isinstance(enabled_by, dict):
|
|
key, value = next(iter(enabled_by.items()))
|
|
return _IS_ENABLED_OP[key](enabled, value)
|
|
return enabled_by in enabled
|
|
|
|
|
|
def _asm_explicit_target(self, node):
|
|
task = self.create_task("asm", node,
|
|
self.bld.bldnode.make_node(self.target))
|
|
try:
|
|
self.compiled_tasks.append(task)
|
|
except AttributeError:
|
|
self.compiled_tasks = [task]
|
|
return task
|
|
|
|
|
|
@feature("asm_explicit_target")
|
|
@before_method("process_source")
|
|
def _enable_asm_explicit_target(self):
|
|
self.mappings = dict(self.mappings) # Copy
|
|
self.mappings[".S"] = _asm_explicit_target
|
|
|
|
|
|
@after("apply_link")
|
|
@feature("cprogram", "cxxprogram")
|
|
def process_start_files(self):
|
|
if getattr(self, "start_files", False):
|
|
self.link_task.dep_nodes.extend(self.bld.start_files)
|
|
|
|
|
|
class Item(object):
|
|
|
|
def __init__(self, uid, data):
|
|
self.uid = uid
|
|
self.data = data
|
|
self.links = self._init_links
|
|
|
|
def _init_links(self):
|
|
self._links = []
|
|
for link in self.data["links"]:
|
|
if link["role"] == "build-dependency":
|
|
uid = link["uid"]
|
|
if not os.path.isabs(uid):
|
|
uid = os.path.normpath(
|
|
os.path.join(os.path.dirname(self.uid), uid))
|
|
self._links.append(items[uid])
|
|
self.links = self._yield_links
|
|
for link in self._links:
|
|
yield link
|
|
|
|
def _yield_links(self):
|
|
for link in self._links:
|
|
yield link
|
|
|
|
def get_enabled_by(self):
|
|
return self.data["enabled-by"]
|
|
|
|
def defaults(self, enabled):
|
|
if _is_enabled(enabled, self.get_enabled_by()):
|
|
for p in self.links():
|
|
p.defaults(enabled)
|
|
self.do_defaults(enabled)
|
|
|
|
def configure(self, conf, cic):
|
|
if _is_enabled(conf.env.ENABLE, self.get_enabled_by()):
|
|
self.prepare_configure(conf, cic)
|
|
for p in self.links():
|
|
p.configure(conf, cic)
|
|
self.do_configure(conf, cic)
|
|
|
|
def build(self, bld, bic):
|
|
if _is_enabled(bld.env.ENABLE, self.get_enabled_by()):
|
|
bic = self.prepare_build(bld, bic)
|
|
for p in self.links():
|
|
p.build(bld, bic)
|
|
self.do_build(bld, bic)
|
|
|
|
def do_defaults(self, enabled):
|
|
return
|
|
|
|
def prepare_configure(self, conf, cic):
|
|
return
|
|
|
|
def do_configure(self, conf, cic):
|
|
return
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return bic
|
|
|
|
def do_build(self, bld, bic):
|
|
return
|
|
|
|
def substitute(self, ctx, value):
|
|
if isinstance(value, str):
|
|
try:
|
|
return Template(value).substitute(EnvWrapper(ctx.env))
|
|
except Exception as e:
|
|
ctx.fatal(
|
|
"In item '{}' substitution in '{}' failed: {}".format(
|
|
self.uid, value, e))
|
|
if isinstance(value, list):
|
|
more = []
|
|
for item in value:
|
|
if isinstance(item, str):
|
|
m = _VAR_PATTERN.match(item)
|
|
else:
|
|
m = None
|
|
if m:
|
|
more.extend(ctx.env[m.group(1).strip("{}")])
|
|
else:
|
|
more.append(self.substitute(ctx, item))
|
|
return more
|
|
return value
|
|
|
|
def get(self, ctx, name):
|
|
return self.substitute(ctx, self.data[name])
|
|
|
|
def install_target(self, bld):
|
|
install_path = self.data["install-path"]
|
|
if install_path:
|
|
bld.install_files(install_path, self.get(bld, "target"))
|
|
|
|
def install_files(self, bld):
|
|
for install in self.data["install"]:
|
|
bld.install_files(install["destination"], install["source"])
|
|
|
|
def asm(self, bld, bic, source, target=None):
|
|
if target is None:
|
|
target = os.path.splitext(source)[0] + ".o"
|
|
bld(
|
|
asflags=self.substitute(bld, self.data["asflags"]),
|
|
cppflags=self.substitute(bld, self.data["cppflags"]),
|
|
features="asm_explicit_target asm c",
|
|
includes=bic.includes +
|
|
self.substitute(bld, self.data["includes"]),
|
|
source=[source],
|
|
target=target,
|
|
)
|
|
return target
|
|
|
|
def cc(self, bld, bic, source, target=None, deps=[], cppflags=[]):
|
|
if target is None:
|
|
target = os.path.splitext(source)[0] + ".o"
|
|
bld(
|
|
cflags=self.substitute(bld, self.data["cflags"]),
|
|
cppflags=cppflags + self.substitute(bld, self.data["cppflags"]),
|
|
features="c",
|
|
includes=bic.includes +
|
|
self.substitute(bld, self.data["includes"]),
|
|
rule=
|
|
"${CC} ${CFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
|
|
source=[source] + deps,
|
|
target=target,
|
|
)
|
|
return target
|
|
|
|
def cxx(self, bld, bic, source, target=None, deps=[], cppflags=[]):
|
|
if target is None:
|
|
target = os.path.splitext(source)[0] + ".o"
|
|
bld(
|
|
cppflags=cppflags + self.substitute(bld, self.data["cppflags"]),
|
|
cxxflags=self.substitute(bld, self.data["cxxflags"]),
|
|
features="cxx",
|
|
includes=bic.includes +
|
|
self.substitute(bld, self.data["includes"]),
|
|
rule=
|
|
"${CXX} ${CXXFLAGS} ${CPPFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} -c ${SRC[0]} -o ${TGT}",
|
|
source=[source] + deps,
|
|
target=target,
|
|
)
|
|
return target
|
|
|
|
def link(self, bld, bic, cmd, source, target):
|
|
from waflib.Task import Task
|
|
|
|
class link(Task):
|
|
|
|
def __init__(self, item, bic, cmd, env):
|
|
super(link, self).__init__(self, env=env)
|
|
self.cmd = cmd
|
|
self.ldflags = bic.ldflags + item.data["ldflags"]
|
|
self.stlib = item.data["stlib"]
|
|
self.use = (item.data["use-before"] + bic.use +
|
|
item.data["use-after"])
|
|
|
|
def run(self):
|
|
cmd = [self.cmd]
|
|
cmd.extend(self.env.LINKFLAGS)
|
|
cmd.extend([i.abspath() for i in self.inputs])
|
|
cmd.append("-o" + self.outputs[0].abspath())
|
|
cmd.append("-L.")
|
|
cmd.extend(["-l" + l for l in self.stlib])
|
|
cmd.extend(["-l" + l for l in self.use])
|
|
cmd.extend(self.env.LDFLAGS)
|
|
cmd.extend(self.ldflags)
|
|
return self.exec_command(cmd)
|
|
|
|
def scan(self):
|
|
return (
|
|
[
|
|
self.generator.bld.bldnode.make_node("lib" + u + ".a")
|
|
for u in self.use
|
|
],
|
|
[],
|
|
)
|
|
|
|
tsk = link(self, bic, cmd, bld.env)
|
|
tsk.set_inputs([bld.bldnode.make_node(s) for s in source])
|
|
tsk.set_outputs(bld.bldnode.make_node(target))
|
|
bld.add_to_group(tsk)
|
|
return target
|
|
|
|
def link_cc(self, bld, bic, source, target):
|
|
return self.link(bld, bic, bld.env.LINK_CC[0], source, target)
|
|
|
|
def link_cxx(self, bld, bic, source, target):
|
|
return self.link(bld, bic, bld.env.LINK_CXX[0], source, target)
|
|
|
|
def gnatmake(self, bld, bic, objdir, objs, main, target):
|
|
from waflib.Task import Task
|
|
|
|
class gnatmake(Task):
|
|
|
|
def __init__(self, bld, bic, objdir, objs, main, target, item):
|
|
super(gnatmake, self).__init__(self, env=bld.env)
|
|
self.objdir = objdir
|
|
self.objs = [bld.bldnode.make_node(o) for o in objs]
|
|
self.main = bld.path.make_node(main)
|
|
self.set_inputs(self.objs + [self.main])
|
|
self.set_outputs(bld.bldnode.make_node(target))
|
|
self.adaflags = item.data["adaflags"]
|
|
self.adaincludes = []
|
|
for i in item.data["adaincludes"]:
|
|
self.adaincludes.append(bld.bldnode.make_node(i))
|
|
self.adaincludes.append(bld.path.make_node(i))
|
|
self.ldflags = bic.ldflags + item.data["ldflags"]
|
|
self.stlib = item.data["stlib"]
|
|
self.use = (item.data["use-before"] + bic.use +
|
|
item.data["use-after"])
|
|
|
|
def run(self):
|
|
cwd = self.get_cwd()
|
|
cmd = [
|
|
self.env.GNATMAKE[0],
|
|
"-D",
|
|
self.objdir,
|
|
"-bargs",
|
|
"-Mgnat_main",
|
|
"-margs",
|
|
]
|
|
cmd.extend(self.adaflags)
|
|
cmd.extend(["-I" + i.path_from(cwd) for i in self.adaincludes])
|
|
cmd.append("-cargs")
|
|
cmd.extend(self.env.ABI_FLAGS)
|
|
cmd.append("-largs")
|
|
cmd.extend([o.path_from(cwd) for o in self.objs])
|
|
cmd.extend(self.env.LINKFLAGS)
|
|
cmd.extend(self.ldflags)
|
|
cmd.append("-L.")
|
|
cmd.extend(["-l" + l for l in self.stlib])
|
|
cmd.extend(["-l" + l for l in self.use])
|
|
cmd.extend(self.env.LDFLAGS)
|
|
cmd.extend(["-margs", "-a"])
|
|
cmd.append(self.main.abspath())
|
|
cmd.append("-o")
|
|
cmd.append(self.outputs[0].abspath())
|
|
return self.exec_command(cmd)
|
|
|
|
def scan(self):
|
|
return (
|
|
[
|
|
self.generator.bld.bldnode.make_node("lib" + u + ".a")
|
|
for u in self.use
|
|
],
|
|
[],
|
|
)
|
|
|
|
tsk = gnatmake(bld, bic, objdir, objs, main, target, self)
|
|
bld.add_to_group(tsk)
|
|
return target
|
|
|
|
def ar(self, bld, source, target):
|
|
bld(rule="${AR} ${ARFLAGS} ${TGT} ${SRC}",
|
|
source=source,
|
|
target=target)
|
|
return target
|
|
|
|
def gzip(self, bld, source):
|
|
target = source + ".gz"
|
|
bld(rule="${GZIP} < ${SRC} > ${TGT}", source=source, target=target)
|
|
return target
|
|
|
|
def xz(self, bld, source):
|
|
target = source + ".xz"
|
|
bld(rule="${XZ} < ${SRC} > ${TGT}", source=source, target=target)
|
|
return target
|
|
|
|
def tar(self, bld, source, remove, target):
|
|
|
|
def run(task):
|
|
import tarfile
|
|
|
|
tar = tarfile.TarFile(task.outputs[0].abspath(),
|
|
"w",
|
|
format=tarfile.USTAR_FORMAT)
|
|
srcpath = bld.path.abspath() + "/"
|
|
bldpath = bld.bldnode.abspath() + "/"
|
|
for src in task.inputs:
|
|
src = src.abspath()
|
|
dst = src
|
|
for r in remove:
|
|
dst = src.replace(srcpath + r, "").replace(bldpath + r, "")
|
|
tar.add(src, dst)
|
|
tar.close()
|
|
return 0
|
|
|
|
bld(rule=run, source=source, target=target)
|
|
return target
|
|
|
|
def bin2c(self, bld, source, name=None, target=None):
|
|
|
|
def run(task):
|
|
cmd = [bld.env.BIN2C[0]]
|
|
if name is not None:
|
|
cmd.extend(["-N", name])
|
|
cmd.append(task.inputs[0].abspath())
|
|
cmd.append(task.outputs[0].abspath())
|
|
return task.exec_command(cmd)
|
|
|
|
path, base = os.path.split(source)
|
|
if target is None:
|
|
target = path + "/" + base.replace(".", "-")
|
|
target_c = target + ".c"
|
|
target_h = target + ".h"
|
|
bld(rule=run, source=source, target=[target_c, target_h])
|
|
return target_c, target_h
|
|
|
|
def rtems_syms(self, bld, source, target):
|
|
bld(
|
|
rule='${RTEMS_SYMS} -e -C ${CC} -c "${CFLAGS}" -o ${TGT} ${SRC}',
|
|
source=source,
|
|
target=target,
|
|
)
|
|
return target
|
|
|
|
def rtems_rap(self, bld, base, objects, libs, target):
|
|
|
|
def run(task):
|
|
cmd = [
|
|
bld.env.RTEMS_LD[0],
|
|
"-C",
|
|
bld.env.CC[0],
|
|
"-c",
|
|
" ".join(bld.env.CFLAGS),
|
|
"-O",
|
|
"rap",
|
|
"-b",
|
|
task.inputs[0].abspath(),
|
|
"-e",
|
|
"rtems_main",
|
|
"-s",
|
|
"-o",
|
|
]
|
|
cmd.append(task.outputs[0].abspath())
|
|
cmd.extend([i.abspath() for i in task.inputs[1:]])
|
|
cmd.extend(["-l" + l for l in libs])
|
|
return task.exec_command(cmd)
|
|
|
|
bld(rule=run, source=[base] + objects, target=target)
|
|
return target
|
|
|
|
|
|
class GroupItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(GroupItem, self).__init__(uid, data)
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return BuildItemContext(
|
|
bic.includes + self.substitute(bld, self.data["includes"]),
|
|
bic.cppflags + self.substitute(bld, self.data["cppflags"]),
|
|
bic.cflags + self.substitute(bld, self.data["cflags"]),
|
|
bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
|
|
self.data["use-before"] + bic.use + self.data["use-after"],
|
|
bic.ldflags + self.substitute(bld, self.data["ldflags"]),
|
|
bic.objects,
|
|
)
|
|
|
|
def do_build(self, bld, bic):
|
|
self.install_files(bld)
|
|
|
|
|
|
class ConfigFileItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(ConfigFileItem, self).__init__(uid, data)
|
|
|
|
def do_configure(self, conf, cic):
|
|
content = self.substitute(conf, self.data["content"])
|
|
f = conf.bldnode.make_node(conf.env.VARIANT + "/" +
|
|
self.get(conf, "target"))
|
|
f.parent.mkdir()
|
|
f.write(content)
|
|
conf.env.append_value("cfg_files", f.abspath())
|
|
|
|
def do_build(self, bld, bic):
|
|
self.install_target(bld)
|
|
|
|
|
|
class ConfigHeaderItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(ConfigHeaderItem, self).__init__(uid, data)
|
|
|
|
def do_configure(self, conf, cic):
|
|
conf.env.include_key = self.data["include-headers"]
|
|
conf.write_config_header(
|
|
conf.env.VARIANT + "/" + self.get(conf, "target"),
|
|
guard=self.data["guard"],
|
|
headers=True,
|
|
)
|
|
conf.env.include_key = None
|
|
|
|
def do_build(self, bld, bic):
|
|
self.install_target(bld)
|
|
|
|
|
|
class StartFileItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(StartFileItem, self).__init__(uid, data)
|
|
|
|
def do_build(self, bld, bic):
|
|
source = self.data["source"]
|
|
if os.path.splitext(source[0])[1] == ".S":
|
|
tgt = self.asm(bld, bic, source, self.get(bld, "target"))
|
|
else:
|
|
tgt = self.cc(bld, bic, source, self.get(bld, "target"))
|
|
node = bld.bldnode.make_node(tgt)
|
|
try:
|
|
bld.start_files.append(node)
|
|
except AttributeError:
|
|
bld.start_files = [node]
|
|
self.install_target(bld)
|
|
|
|
|
|
class ObjectsItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(ObjectsItem, self).__init__(uid, data)
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return BuildItemContext(
|
|
bic.includes + self.substitute(bld, self.data["includes"]),
|
|
bic.cppflags + self.substitute(bld, self.data["cppflags"]),
|
|
bic.cflags + self.substitute(bld, self.data["cflags"]),
|
|
bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
|
|
bic.use,
|
|
bic.ldflags,
|
|
bic.objects,
|
|
)
|
|
|
|
def do_build(self, bld, bic):
|
|
bld.objects(
|
|
asflags=bic.cppflags,
|
|
cflags=bic.cflags,
|
|
cppflags=bic.cppflags,
|
|
cxxflags=bic.cxxflags,
|
|
includes=bic.includes,
|
|
source=self.data["source"],
|
|
target=self.uid,
|
|
)
|
|
bic.objects.append(self.uid)
|
|
self.install_files(bld)
|
|
|
|
|
|
class BSPItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(BSPItem, self).__init__(uid, data)
|
|
arch_bsps = bsps.setdefault(data["arch"].strip(), {})
|
|
arch_bsps[data["bsp"].strip()] = self
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return BuildItemContext(
|
|
bic.includes + bld.env.BSP_INCLUDES +
|
|
self.substitute(bld, self.data["includes"]),
|
|
self.substitute(bld, self.data["cppflags"]),
|
|
bld.env.BSP_CFLAGS + self.substitute(bld, self.data["cflags"]),
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
)
|
|
|
|
def do_build(self, bld, bic):
|
|
bld(
|
|
cflags=bic.cflags,
|
|
cppflags=bic.cppflags,
|
|
features="c cstlib",
|
|
includes=bic.includes,
|
|
install_path="${BSP_LIBDIR}",
|
|
source=self.data["source"],
|
|
target="rtemsbsp",
|
|
use=bic.objects,
|
|
)
|
|
self.install_files(bld)
|
|
|
|
|
|
class LibraryItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(LibraryItem, self).__init__(uid, data)
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return BuildItemContext(
|
|
bic.includes + self.substitute(bld, self.data["includes"]),
|
|
bic.cppflags + self.substitute(bld, self.data["cppflags"]),
|
|
bic.cflags + self.substitute(bld, self.data["cflags"]),
|
|
bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
|
|
bic.use,
|
|
bic.ldflags,
|
|
[],
|
|
)
|
|
|
|
def do_build(self, bld, bic):
|
|
bld(
|
|
cflags=bic.cflags,
|
|
cppflags=bic.cppflags,
|
|
cxxflags=bic.cxxflags,
|
|
features="c cxx cstlib",
|
|
includes=bic.includes,
|
|
install_path=self.data["install-path"],
|
|
source=self.data["source"],
|
|
target=self.get(bld, "target"),
|
|
use=bic.objects,
|
|
)
|
|
self.install_files(bld)
|
|
|
|
|
|
class TestProgramItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(TestProgramItem, self).__init__(uid, data)
|
|
name = uid.split("/")[-1].upper().replace("-", "_")
|
|
self.exclude = "TEST_" + name + "_EXCLUDE"
|
|
self.cppflags = "TEST_" + name + "_CPPFLAGS"
|
|
|
|
def get_enabled_by(self):
|
|
return [{"and": [{"not": self.exclude}, self.data["enabled-by"]]}]
|
|
|
|
def prepare_build(self, bld, bic):
|
|
return BuildItemContext(
|
|
bic.includes + self.substitute(bld, self.data["includes"]),
|
|
bic.cppflags + bld.env[self.cppflags] +
|
|
self.substitute(bld, self.data["cppflags"]),
|
|
bic.cflags + self.substitute(bld, self.data["cflags"]),
|
|
bic.cxxflags + self.substitute(bld, self.data["cxxflags"]),
|
|
self.data["use-before"] + bic.use + self.data["use-after"],
|
|
bic.ldflags + self.substitute(bld, self.data["ldflags"]),
|
|
[],
|
|
)
|
|
|
|
def do_build(self, bld, bic):
|
|
bld(
|
|
cflags=bic.cflags,
|
|
cppflags=bic.cppflags,
|
|
cxxflags=bic.cxxflags,
|
|
features=self.data["features"],
|
|
includes=bic.includes,
|
|
install_path=None,
|
|
ldflags=bic.ldflags,
|
|
source=self.data["source"],
|
|
start_files=True,
|
|
stlib=self.data["stlib"],
|
|
target=self.get(bld, "target"),
|
|
use=bic.objects + bic.use,
|
|
)
|
|
|
|
|
|
class AdaTestProgramItem(TestProgramItem):
|
|
|
|
def __init__(self, uid, data):
|
|
super(AdaTestProgramItem, self).__init__(uid, data)
|
|
|
|
def do_build(self, bld, bic):
|
|
objs = []
|
|
for s in self.data["source"]:
|
|
objs.append(self.cc(bld, bic, s))
|
|
self.gnatmake(
|
|
bld,
|
|
bic,
|
|
self.data["ada-object-directory"],
|
|
objs,
|
|
self.data["ada-main"],
|
|
self.data["target"],
|
|
)
|
|
|
|
|
|
class OptionItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(OptionItem, self).__init__(uid, data)
|
|
|
|
def default_value(self, enabled):
|
|
value = None
|
|
for default in self.data["default"]:
|
|
if _is_enabled(enabled, default["enabled-by"]):
|
|
value = default["value"]
|
|
break
|
|
if value is None:
|
|
return value
|
|
if isinstance(value, list):
|
|
return " ".join(value)
|
|
if isinstance(value, bool):
|
|
return value
|
|
return self.data["format"].format(value)
|
|
|
|
def do_defaults(self, enabled):
|
|
value = self.default_value(enabled)
|
|
if value is None:
|
|
return
|
|
description = self.data["description"]
|
|
if description:
|
|
import textwrap
|
|
|
|
tw = textwrap.TextWrapper()
|
|
tw.drop_whitespace = True
|
|
tw.initial_indent = "# "
|
|
tw.subsequent_indent = "# "
|
|
for line in tw.wrap(description):
|
|
print(line)
|
|
print("{} = {}".format(self.data["name"], value))
|
|
|
|
def _do_append_test_cppflags(self, conf, name, state):
|
|
conf.env.append_value(
|
|
"TEST_" + name.upper().replace("-", "_") + "_CPPFLAGS", state)
|
|
|
|
def _append_test_cppflags(self, conf, cic, value, arg):
|
|
self._do_append_test_cppflags(conf, arg, value)
|
|
return value
|
|
|
|
def _assert_aligned(self, conf, cic, value, arg):
|
|
if value is not None and value % arg != 0:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not aligned by '{}'".format(
|
|
value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_eq(self, conf, cic, value, arg):
|
|
if value is not None and value != arg:
|
|
conf.fatal("Value '{}' for option '{}' is not equal to {}".format(
|
|
value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_ge(self, conf, cic, value, arg):
|
|
if value is not None and value < arg:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not greater than or equal to {}"
|
|
.format(value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_gt(self, conf, cic, value, arg):
|
|
if value is not None and value <= arg:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not greater than {}".format(
|
|
value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_in_interval(self, conf, cic, value, arg):
|
|
if value is not None and (value < arg[0] or value > arg[1]):
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not in closed interval [{}, {}]"
|
|
.format(value, self.data["name"], arg[0], arg[1]))
|
|
return value
|
|
|
|
def _assert_int8(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value, [-128, 127])
|
|
|
|
def _assert_int16(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value, [-32768, 32767])
|
|
|
|
def _assert_int32(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value,
|
|
[-2147483648, 2147483647])
|
|
|
|
def _assert_int64(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(
|
|
conf, cic, value, [-9223372036854775808, 9223372036854775807])
|
|
|
|
def _assert_le(self, conf, cic, value, arg):
|
|
if value is not None and value > arg:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not less than or equal to {}".
|
|
format(value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_lt(self, conf, cic, value, arg):
|
|
if value is not None and value >= arg:
|
|
conf.fatal("Value '{}' for option '{}' is not less than {}".format(
|
|
value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_ne(self, conf, cic, value, arg):
|
|
if value is not None and value == arg:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not unequal to {}".format(
|
|
value, self.data["name"], arg))
|
|
return value
|
|
|
|
def _assert_power_of_two(self, conf, cic, value, arg):
|
|
if value is not None and (value <= 0 or (value & (value - 1)) != 0):
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is not a power of two".format(
|
|
value, self.data["name"]))
|
|
return value
|
|
|
|
def _assert_uint8(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value, [0, 255])
|
|
|
|
def _assert_uint16(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value, [0, 65535])
|
|
|
|
def _assert_uint32(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value, [0, 4294967295])
|
|
|
|
def _assert_uint64(self, conf, cic, value, arg):
|
|
return self._assert_in_interval(conf, cic, value,
|
|
[0, 18446744073709551615])
|
|
|
|
def _check_cc(self, conf, cic, value, arg):
|
|
result = conf.check_cc(
|
|
fragment=arg["fragment"],
|
|
cflags=arg["cflags"],
|
|
msg="Checking for " + arg["message"],
|
|
mandatory=False,
|
|
)
|
|
return value and result
|
|
|
|
def _check_cxx(self, conf, cic, value, arg):
|
|
result = conf.check_cxx(
|
|
fragment=arg["fragment"],
|
|
cxxflags=arg["cxxflags"],
|
|
msg="Checking for " + arg["message"],
|
|
mandatory=False,
|
|
)
|
|
return value and result
|
|
|
|
def _define_condition(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
conf.define_cond(name, value)
|
|
return value
|
|
|
|
def _define(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
if value is not None:
|
|
conf.define(name, value)
|
|
else:
|
|
conf.define_cond(name, False)
|
|
return value
|
|
|
|
def _define_unquoted(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
if value is not None:
|
|
conf.define(name, value, quote=False)
|
|
else:
|
|
conf.define_cond(name, False)
|
|
return value
|
|
|
|
def _env_append(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
conf.env.append_value(name, value)
|
|
return value
|
|
|
|
def _env_assign(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
conf.env[name] = value
|
|
return value
|
|
|
|
def _env_enable(self, conf, cic, value, arg):
|
|
if value:
|
|
name = self.data["name"] if arg is None else arg
|
|
conf.env.append_value("ENABLE", name)
|
|
return value
|
|
|
|
def _find_program(self, conf, cic, value, arg):
|
|
return conf.find_program(value, path_list=cic.path_list)
|
|
|
|
def _format_and_define(self, conf, cic, value, arg):
|
|
name = self.data["name"] if arg is None else arg
|
|
if value is not None:
|
|
conf.define(name, self.data["format"].format(value), quote=False)
|
|
else:
|
|
conf.define_cond(name, False)
|
|
return value
|
|
|
|
def _get_boolean(self, conf, cic, value, arg):
|
|
name = self.data["name"]
|
|
try:
|
|
value = cic.cp.getboolean(conf.variant, name)
|
|
cic.add_option(name)
|
|
except configparser.NoOptionError:
|
|
value = self.default_value(conf.env.ENABLE)
|
|
except ValueError as ve:
|
|
conf.fatal("Invalid value for configuration option {}: {}".format(
|
|
name, ve))
|
|
return value
|
|
|
|
def _get_env(self, conf, cic, value, arg):
|
|
return conf.env[arg]
|
|
|
|
def _get_integer(self, conf, cic, value, arg):
|
|
name = self.data["name"]
|
|
try:
|
|
value = cic.cp.get(conf.variant, name)
|
|
cic.add_option(name)
|
|
except configparser.NoOptionError:
|
|
value = self.default_value(conf.env.ENABLE)
|
|
if value is None:
|
|
return value
|
|
try:
|
|
return eval(value)
|
|
except Exception as e:
|
|
conf.fatal(
|
|
"Value '{}' for option '{}' is an invalid integer expression: {}"
|
|
.format(value, name, e))
|
|
|
|
def _get_string(self, conf, cic, value, arg):
|
|
name = self.data["name"]
|
|
try:
|
|
value = cic.cp.get(conf.variant, name)
|
|
cic.add_option(name)
|
|
value = no_unicode(value)
|
|
except configparser.NoOptionError:
|
|
value = self.default_value(conf.env.ENABLE)
|
|
return value
|
|
|
|
def _get_string_command_line(self, conf, cic, value, arg):
|
|
name = self.data["name"]
|
|
try:
|
|
value = conf.rtems_options[name]
|
|
del conf.rtems_options[name]
|
|
except KeyError:
|
|
value = arg[0]
|
|
return value
|
|
|
|
def _script(self, conf, cic, value, arg):
|
|
exec(arg)
|
|
return value
|
|
|
|
def _test_state_benchmark(self, conf, name):
|
|
self._do_append_test_cppflags(conf, name, "-DTEST_STATE_BENCHMARK=1")
|
|
|
|
def _test_state_exclude(self, conf, name):
|
|
conf.env.append_value(
|
|
"ENABLE", "TEST_" + name.upper().replace("-", "_") + "_EXCLUDE")
|
|
|
|
def _test_state_expected_fail(self, conf, name):
|
|
self._do_append_test_cppflags(conf, name,
|
|
"-DTEST_STATE_EXPECTED_FAIL=1")
|
|
|
|
def _test_state_indeterminate(self, conf, name):
|
|
self._do_append_test_cppflags(conf, name,
|
|
"-DTEST_STATE_INDETERMINATE=1")
|
|
|
|
def _test_state_user_input(self, conf, name):
|
|
self._do_append_test_cppflags(conf, name, "-DTEST_STATE_USER_INPUT=1")
|
|
|
|
def _set_test_state(self, conf, cic, value, arg):
|
|
actions = {
|
|
"benchmark": self._test_state_benchmark,
|
|
"exclude": self._test_state_exclude,
|
|
"expected-fail": self._test_state_expected_fail,
|
|
"indeterminate": self._test_state_indeterminate,
|
|
"user-input": self._test_state_user_input,
|
|
}
|
|
action = actions[arg["state"]]
|
|
for test in arg["tests"]:
|
|
action(conf, test)
|
|
return value
|
|
|
|
def _set_value(self, conf, cic, value, arg):
|
|
return arg
|
|
|
|
def _split(self, conf, cic, value, arg):
|
|
return value.split()
|
|
|
|
def _substitute(self, conf, cic, value, arg):
|
|
if isinstance(value, list):
|
|
return [self.substitute(conf, v) for v in value]
|
|
else:
|
|
return self.substitute(conf, value)
|
|
|
|
def do_configure(self, conf, cic):
|
|
actions = {
|
|
"append-test-cppflags": self._append_test_cppflags,
|
|
"assert-aligned": self._assert_aligned,
|
|
"assert-eq": self._assert_eq,
|
|
"assert-ge": self._assert_ge,
|
|
"assert-gt": self._assert_gt,
|
|
"assert-int8": self._assert_int8,
|
|
"assert-int16": self._assert_int16,
|
|
"assert-int32": self._assert_int32,
|
|
"assert-int64": self._assert_int64,
|
|
"assert-le": self._assert_le,
|
|
"assert-lt": self._assert_lt,
|
|
"assert-ne": self._assert_ne,
|
|
"assert-power-of-two": self._assert_power_of_two,
|
|
"assert-uint8": self._assert_uint8,
|
|
"assert-uint16": self._assert_uint16,
|
|
"assert-uint32": self._assert_uint32,
|
|
"assert-uint64": self._assert_uint64,
|
|
"check-cc": self._check_cc,
|
|
"check-cxx": self._check_cxx,
|
|
"define-condition": self._define_condition,
|
|
"define": self._define,
|
|
"define-unquoted": self._define_unquoted,
|
|
"env-append": self._env_append,
|
|
"env-assign": self._env_assign,
|
|
"env-enable": self._env_enable,
|
|
"find-program": self._find_program,
|
|
"format-and-define": self._format_and_define,
|
|
"get-boolean": self._get_boolean,
|
|
"get-env": self._get_env,
|
|
"get-integer": self._get_integer,
|
|
"get-string": self._get_string,
|
|
"get-string-command-line": self._get_string_command_line,
|
|
"script": self._script,
|
|
"set-test-state": self._set_test_state,
|
|
"set-value": self._set_value,
|
|
"split": self._split,
|
|
"substitute": self._substitute,
|
|
}
|
|
value = None
|
|
for action in self.data["actions"]:
|
|
for action_arg in action.items():
|
|
value = actions[action_arg[0]](conf, cic, value, action_arg[1])
|
|
|
|
|
|
class ScriptItem(Item):
|
|
|
|
def __init__(self, uid, data):
|
|
super(ScriptItem, self).__init__(uid, data)
|
|
|
|
def prepare_configure(self, conf, cic):
|
|
script = self.data["prepare-configure"]
|
|
if script:
|
|
exec(script)
|
|
|
|
def do_configure(self, conf, cic):
|
|
script = self.data["do-configure"]
|
|
if script:
|
|
exec(script)
|
|
|
|
def prepare_build(self, bld, bic):
|
|
script = self.data["prepare-build"]
|
|
if script:
|
|
exec(script)
|
|
return bic
|
|
|
|
def do_build(self, bld, bic):
|
|
script = self.data["do-build"]
|
|
if script:
|
|
exec(script)
|
|
|
|
|
|
class ConfigItemContext(object):
|
|
|
|
def __init__(self, cp, path_list):
|
|
self.cp = cp
|
|
self.options = set()
|
|
self.path_list = path_list
|
|
|
|
def add_option(self, name):
|
|
self.options.add(name.upper())
|
|
|
|
|
|
class BuildItemContext(object):
|
|
|
|
def __init__(self, includes, cppflags, cflags, cxxflags, use, ldflags,
|
|
objects):
|
|
self.includes = includes
|
|
self.cppflags = cppflags
|
|
self.cflags = cflags
|
|
self.cxxflags = cxxflags
|
|
self.use = use
|
|
self.ldflags = ldflags
|
|
self.objects = objects
|
|
|
|
|
|
def is_one_item_newer(ctx, path, mtime):
|
|
try:
|
|
mtime2 = os.path.getmtime(path)
|
|
if mtime <= mtime2:
|
|
return True
|
|
names = os.listdir(path)
|
|
except Exception as e:
|
|
ctx.fatal("Cannot access build specification directory: {}".format(e))
|
|
for name in names:
|
|
path2 = os.path.join(path, name)
|
|
if name.endswith(".yml") and not name.startswith("."):
|
|
mtime2 = os.path.getmtime(path2)
|
|
if mtime <= mtime2:
|
|
return True
|
|
else:
|
|
mode = os.lstat(path2).st_mode
|
|
if stat.S_ISDIR(mode) and is_one_item_newer(ctx, path2, mtime):
|
|
return True
|
|
return False
|
|
|
|
|
|
def must_update_item_cache(ctx, path, cache_file):
|
|
try:
|
|
mtime = os.path.getmtime(cache_file)
|
|
except:
|
|
return True
|
|
return is_one_item_newer(ctx, path, mtime)
|
|
|
|
|
|
def load_from_yaml(load, ctx, data_by_uid, base, path):
|
|
try:
|
|
names = os.listdir(path)
|
|
except Exception as e:
|
|
ctx.fatal("Cannot list build specification directory: {}".format(e))
|
|
for name in names:
|
|
path2 = os.path.join(path, name)
|
|
if name.endswith(".yml") and not name.startswith("."):
|
|
uid = "/" + os.path.relpath(path2, base).replace(".yml", "")
|
|
with open(path2, "r") as f:
|
|
data_by_uid[uid] = load(f.read())
|
|
else:
|
|
mode = os.lstat(path2).st_mode
|
|
if stat.S_ISDIR(mode):
|
|
load_from_yaml(load, ctx, data_by_uid, base, path2)
|
|
|
|
|
|
def load_items_in_directory(ctx, ctors, path):
|
|
p = "c4che/" + re.sub(r"[^\w]", "_", path) + ".pickle"
|
|
try:
|
|
f = ctx.bldnode.make_node(p)
|
|
except AttributeError:
|
|
f = ctx.path.make_node("build/" + p)
|
|
f.parent.mkdir()
|
|
cache_file = f.abspath()
|
|
data_by_uid = {}
|
|
if must_update_item_cache(ctx, path, cache_file):
|
|
from waflib import Logs
|
|
|
|
Logs.warn(
|
|
"Regenerate build specification cache (needs a couple of seconds)..."
|
|
)
|
|
|
|
#
|
|
# Do not use a system provided yaml module and instead import it from
|
|
# the project. This reduces the host system requirements to a simple
|
|
# Python 2.7 or 3 installation without extra modules.
|
|
#
|
|
if sys.version_info[0] == 2:
|
|
yaml_path = "yaml/lib"
|
|
else:
|
|
yaml_path = "yaml/lib3"
|
|
sys.path += [yaml_path]
|
|
from yaml import safe_load
|
|
|
|
load_from_yaml(safe_load, ctx, data_by_uid, path, path)
|
|
with open(cache_file, "wb") as f:
|
|
pickle.dump(data_by_uid, f)
|
|
else:
|
|
with open(cache_file, "rb") as f:
|
|
data_by_uid = pickle.load(f)
|
|
for uid, data in data_by_uid.items():
|
|
if data["type"] == "build":
|
|
items[uid] = ctors[data["build-type"]](uid, data)
|
|
|
|
|
|
def load_items(ctx, specs):
|
|
if items:
|
|
return
|
|
|
|
ctors = {
|
|
"ada-test-program": AdaTestProgramItem,
|
|
"bsp": BSPItem,
|
|
"config-file": ConfigFileItem,
|
|
"config-header": ConfigHeaderItem,
|
|
"test-program": TestProgramItem,
|
|
"group": GroupItem,
|
|
"library": LibraryItem,
|
|
"objects": ObjectsItem,
|
|
"option": OptionItem,
|
|
"script": ScriptItem,
|
|
"start-file": StartFileItem,
|
|
}
|
|
|
|
for path in specs:
|
|
load_items_in_directory(ctx, ctors, path)
|
|
|
|
|
|
def load_items_from_options(ctx):
|
|
specs = ctx.options.rtems_specs
|
|
if specs is not None:
|
|
specs = specs.split(",")
|
|
else:
|
|
specs = ["spec/build"]
|
|
load_items(ctx, specs)
|
|
return specs
|
|
|
|
|
|
def options(ctx):
|
|
prefix = ctx.parser.get_option("--prefix")
|
|
prefix.default = default_prefix
|
|
prefix.help = "installation prefix [default: '{}']".format(default_prefix)
|
|
rg = ctx.add_option_group("RTEMS options")
|
|
rg.add_option(
|
|
"--rtems-bsps",
|
|
metavar="REGEX,...",
|
|
help=
|
|
"a comma-separated list of Python regular expressions which select the desired BSP variants (e.g. 'sparc/erc32'); it may be used in the bspdefaults and bsps commands",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-compiler",
|
|
metavar="COMPILER",
|
|
help=
|
|
"determines which compiler is used to list the BSP option defaults [default: 'gcc']; it may be used in the bspdefaults command; valid compilers are: {}"
|
|
.format(", ".join(compilers)),
|
|
)
|
|
rg.add_option(
|
|
"--rtems-config",
|
|
metavar="CONFIG.INI,...",
|
|
help=
|
|
"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",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-specs",
|
|
metavar="SPECDIRS,...",
|
|
help=
|
|
"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",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-tools",
|
|
metavar="PREFIX,...",
|
|
help=
|
|
"a comma-separated list of prefix paths to tools, e.g. compiler, linker, etc. [default: the installation prefix]; tools are searched in the prefix path and also in a 'bin' subdirectory of the prefix path; it may be used in the configure command",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-top-group",
|
|
metavar="UID",
|
|
help=
|
|
"the UID of the top-level group [default: '/grp']; it may be used in the bspdefaults and configure commands",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-version",
|
|
metavar="VALUE",
|
|
help=
|
|
"sets the RTEMS major version number; it is intended for RTEMS maintainers and may be used in the bspdefaults and configure commands",
|
|
)
|
|
rg.add_option(
|
|
"--rtems-option",
|
|
metavar="KEY=VALUE",
|
|
action="append",
|
|
dest="rtems_options",
|
|
default=[],
|
|
help=
|
|
"sets the option identified by KEY to the VALUE in the build specification; it is intended for RTEMS maintainers and may be used in the bspdefaults and configure commands",
|
|
)
|
|
|
|
|
|
def check_environment(conf):
|
|
for ev in [
|
|
"AR",
|
|
"AS",
|
|
"ASFLAGS",
|
|
"CC",
|
|
"CFLAGS",
|
|
"CPPFLAGS",
|
|
"CXX",
|
|
"CXXFLAGS",
|
|
"IFLAGS",
|
|
"LD",
|
|
"LIB",
|
|
"LINK_CC",
|
|
"LINK_CXX",
|
|
"LINKFLAGS",
|
|
"MFLAGS",
|
|
"RFLAGS",
|
|
"WFLAGS",
|
|
]:
|
|
if ev in os.environ:
|
|
conf.msg("Environment variable set", ev, color="RED")
|
|
|
|
|
|
def load_config_files(ctx):
|
|
cp = configparser.ConfigParser()
|
|
files = ctx.options.rtems_config
|
|
if files is not None:
|
|
files = files.split(",")
|
|
else:
|
|
files = ["config.ini"]
|
|
actual_files = cp.read(files)
|
|
for o in files:
|
|
if not o in actual_files:
|
|
ctx.fatal("Option file '{}' was not readable".format(o))
|
|
return cp
|
|
|
|
|
|
def inherit(conf, cp, bsp_map, arch, bsp, path):
|
|
variant = arch + "/" + bsp
|
|
if variant in path:
|
|
path = " -> ".join(path + [variant])
|
|
conf.fatal("Recursion in BSP options inheritance: {}".format(path))
|
|
|
|
try:
|
|
base = cp.get(variant, "INHERIT")
|
|
cp.remove_option(variant, "INHERIT")
|
|
base = no_unicode(base)
|
|
base_variant = arch + "/" + base
|
|
conf.msg(
|
|
"Inherit options from '{}'".format(base_variant),
|
|
variant,
|
|
color="YELLOW",
|
|
)
|
|
if not cp.has_section(base_variant):
|
|
if (not arch in bsps) or (not base in bsps[arch]):
|
|
conf.fatal(
|
|
"BSP variant '{}' cannot inherit options from not existing variant '{}'"
|
|
.format(variant, base_variant))
|
|
bsp_map[bsp] = base
|
|
return base
|
|
top = inherit(conf, cp, bsp_map, arch, base, path + [variant])
|
|
for i in cp.items(base_variant):
|
|
name = i[0]
|
|
if not cp.has_option(variant, name):
|
|
cp.set(variant, name, i[1])
|
|
bsp_map[bsp] = top
|
|
return top
|
|
except configparser.NoOptionError:
|
|
return bsp_map.get(bsp, bsp)
|
|
|
|
|
|
def resolve_option_inheritance(conf, cp):
|
|
bsp_map = {}
|
|
for variant in cp.sections():
|
|
variant = no_unicode(variant)
|
|
try:
|
|
arch, bsp = variant.split("/")
|
|
except:
|
|
conf.fatal(
|
|
"Section name '{}' is a malformed 'arch/bsp' tuple".format(
|
|
variant))
|
|
inherit(conf, cp, bsp_map, arch, bsp, [])
|
|
return bsp_map
|
|
|
|
|
|
def check_compiler(ctx, compiler):
|
|
if compiler not in compilers:
|
|
ctx.fatal("Specified compiler '{}' is not one of {}".format(
|
|
compiler, compilers))
|
|
|
|
|
|
def get_compiler(conf, cp, variant):
|
|
try:
|
|
value = cp.get(variant, "COMPILER")
|
|
cp.remove_option(variant, "COMPILER")
|
|
value = no_unicode(value)
|
|
check_compiler(conf, value)
|
|
except configparser.NoOptionError:
|
|
value = "gcc"
|
|
return value
|
|
|
|
|
|
def configure_variant(conf, cp, bsp_map, path_list, top_group, variant):
|
|
conf.msg("Configure board support package (BSP)", variant, color="YELLOW")
|
|
|
|
conf.setenv(variant)
|
|
arch, bsp_name = variant.split("/")
|
|
bsp_base = bsp_map.get(bsp_name, bsp_name)
|
|
|
|
try:
|
|
bsp_item = bsps[arch][bsp_base]
|
|
except KeyError:
|
|
conf.fatal("No such base BSP: '{}'".format(variant))
|
|
|
|
family = bsp_item.data["family"]
|
|
|
|
arch_bsp = arch + "/" + bsp_base
|
|
arch_family = arch + "/" + family
|
|
|
|
conf.env["ARCH"] = arch
|
|
conf.env["ARCH_BSP"] = arch_bsp
|
|
conf.env["ARCH_FAMILY"] = arch_family
|
|
conf.env["BSP_BASE"] = bsp_base
|
|
conf.env["BSP_NAME"] = bsp_name
|
|
conf.env["BSP_FAMILY"] = family
|
|
conf.env["DEST_OS"] = "rtems"
|
|
|
|
# For the enabled-by evaluation we have to use the base BSP defined by the
|
|
# build specification and not the BSP name provided by the user.
|
|
conf.env["ENABLE"] = [
|
|
get_compiler(conf, cp, variant),
|
|
arch,
|
|
"bsps/" + arch_family,
|
|
arch_bsp,
|
|
]
|
|
|
|
conf.env["TOP"] = conf.path.abspath()
|
|
conf.env["TOPGROUP"] = top_group
|
|
conf.env["VARIANT"] = variant
|
|
|
|
prepare_rtems_options(conf)
|
|
cic = ConfigItemContext(cp, path_list)
|
|
items[conf.env.TOPGROUP].configure(conf, cic)
|
|
bsp_item.configure(conf, cic)
|
|
|
|
options = set([o[0].upper() for o in cp.items(variant)])
|
|
for o in options.difference(cic.options):
|
|
conf.msg("Unknown configuration option", o.upper(), color="RED")
|
|
for key in conf.rtems_options:
|
|
conf.msg("Unknown command line RTEMS option", key, color="RED")
|
|
|
|
|
|
def check_forbidden_options(ctx, opts):
|
|
for o in opts:
|
|
if getattr(ctx.options, "rtems_" + o):
|
|
ctx.fatal(
|
|
"The --rtems-{} command line option is not allowed in the {} command"
|
|
.format(o.replace("_", "-"), ctx.cmd))
|
|
|
|
|
|
def get_path_list(conf):
|
|
path_list = []
|
|
tools = conf.options.rtems_tools
|
|
if tools is not None:
|
|
for t in tools.split(","):
|
|
path_list.extend([t + "/bin", t])
|
|
path_list.append(conf.env.PREFIX + "/bin")
|
|
path_list.extend(os.environ.get("PATH", "").split(os.pathsep))
|
|
return path_list
|
|
|
|
|
|
def get_top_group(ctx):
|
|
top_group = ctx.options.rtems_top_group
|
|
if top_group is None:
|
|
top_group = "/grp"
|
|
if top_group not in items:
|
|
ctx.fatal(
|
|
"There is no top-level group with UID '{}' in the specification".
|
|
format(top_group))
|
|
return top_group
|
|
|
|
|
|
def prepare_rtems_options(conf):
|
|
conf.rtems_options = {}
|
|
for x in conf.options.rtems_options:
|
|
try:
|
|
k, v = x.split("=", 1)
|
|
conf.rtems_options[k] = v
|
|
except:
|
|
conf.fatal(
|
|
"The RTEMS option '{}' is not in KEY=VALUE format".format(x))
|
|
version = conf.options.rtems_version
|
|
if version is not None:
|
|
key = "__RTEMS_MAJOR__"
|
|
if conf.rtems_options.get(key, version) != version:
|
|
conf.fatal(
|
|
"Conflicting RTEMS major versions specified at the command line"
|
|
)
|
|
conf.rtems_options[key] = version
|
|
|
|
|
|
def configure(conf):
|
|
check_forbidden_options(conf, ["compiler"])
|
|
check_environment(conf)
|
|
conf.env["SPECS"] = load_items_from_options(conf)
|
|
top_group = get_top_group(conf)
|
|
cp = load_config_files(conf)
|
|
bsp_map = resolve_option_inheritance(conf, cp)
|
|
path_list = get_path_list(conf)
|
|
variant_list = []
|
|
for variant in cp.sections():
|
|
variant = no_unicode(variant)
|
|
variant_list.append(variant)
|
|
configure_variant(conf, cp, bsp_map, path_list, top_group, variant)
|
|
conf.setenv("")
|
|
conf.env["VARIANTS"] = variant_list
|
|
|
|
|
|
def append_variant_builds(bld):
|
|
import waflib.Options
|
|
from waflib.Build import (
|
|
BuildContext,
|
|
CleanContext,
|
|
InstallContext,
|
|
UninstallContext,
|
|
)
|
|
|
|
for var in bld.env["VARIANTS"]:
|
|
for c in (BuildContext, CleanContext, InstallContext,
|
|
UninstallContext):
|
|
name = c.__name__.replace("Context", "").lower()
|
|
|
|
class magic(c):
|
|
cmd = name + "_" + var
|
|
variant = var
|
|
|
|
waflib.Options.commands.append(bld.cmd + "_" + var)
|
|
|
|
|
|
def long_command_line_workaround(bld):
|
|
if is_windows_host:
|
|
bld.load("long_gcc")
|
|
|
|
|
|
def build(bld):
|
|
if not bld.variant:
|
|
check_forbidden_options(
|
|
bld,
|
|
[
|
|
"compiler",
|
|
"config",
|
|
"options",
|
|
"specs",
|
|
"tools",
|
|
"top_group",
|
|
"version",
|
|
],
|
|
)
|
|
load_items(bld, bld.env.SPECS)
|
|
append_variant_builds(bld)
|
|
return
|
|
long_command_line_workaround(bld)
|
|
bic = BuildItemContext(bld.env.ARCH_INCLUDES.split(), [], [], [], [], [],
|
|
[])
|
|
bsps[bld.env.ARCH][bld.env.BSP_BASE].build(bld, bic)
|
|
items[bld.env.TOPGROUP].build(bld, bic)
|
|
|
|
|
|
def add_log_filter(name):
|
|
msg = "'" + name + "' finished successfully"
|
|
|
|
class Filter:
|
|
|
|
def filter(self, rec):
|
|
return not msg in rec.getMessage()
|
|
|
|
import logging
|
|
|
|
logging.getLogger("waflib").addFilter(Filter())
|
|
|
|
|
|
def get_white_list(ctx):
|
|
white_list = ctx.options.rtems_bsps
|
|
if white_list:
|
|
white_list = white_list.split(",")
|
|
return white_list
|
|
|
|
|
|
def is_in_white_list(variant, white_list):
|
|
if not white_list:
|
|
return True
|
|
for pattern in white_list:
|
|
if re.match(pattern + "$", variant):
|
|
return True
|
|
return False
|
|
|
|
|
|
def no_matches_error(ctx, white_list):
|
|
if white_list:
|
|
ctx.fatal("No BSP matches with the specified patterns: '{}'".format(
|
|
"', '".join(white_list)))
|
|
else:
|
|
ctx.fatal("The build specification contains no BSPs")
|
|
|
|
|
|
def bspdefaults(ctx):
|
|
"""get all options with default values for base BSP variants"""
|
|
check_forbidden_options(ctx, ["config", "tools"])
|
|
add_log_filter(ctx.cmd)
|
|
load_items_from_options(ctx)
|
|
top_group = get_top_group(ctx)
|
|
white_list = get_white_list(ctx)
|
|
compiler = ctx.options.rtems_compiler
|
|
if compiler is not None:
|
|
check_compiler(ctx, compiler)
|
|
else:
|
|
compiler = "gcc"
|
|
first = True
|
|
for arch in sorted(bsps):
|
|
for bsp in sorted(bsps[arch]):
|
|
variant = arch + "/" + bsp
|
|
if is_in_white_list(variant, white_list):
|
|
if not first:
|
|
print("")
|
|
first = False
|
|
print("""[{}]
|
|
# Selects the compiler used to build the BSP (allowed values are "gcc" and
|
|
# "clang"). Please note that the values of some options depend on the compiler
|
|
# selection and changing the compiler may lead to unpredictable behaviour if
|
|
# these options are not adjusted as well. Use the --rtems-compiler command line
|
|
# option to get the default values for a particular compiler via
|
|
# ./waf bspdefaults.
|
|
COMPILER = {}""".format(variant, compiler))
|
|
enable = [compiler, arch, variant]
|
|
bsp_item = bsps[arch][bsp]
|
|
family = "bsps/" + arch + "/" + bsp_item.data["family"]
|
|
enabled = [compiler, arch, family, variant]
|
|
items[top_group].defaults(enabled)
|
|
bsp_item.defaults(enabled)
|
|
if first:
|
|
no_matches_error(ctx, white_list)
|
|
|
|
|
|
def bsplist(ctx):
|
|
"""lists base BSP variants"""
|
|
check_forbidden_options(
|
|
ctx,
|
|
["compiler", "config", "options", "tools", "top_group", "version"])
|
|
add_log_filter(ctx.cmd)
|
|
load_items_from_options(ctx)
|
|
white_list = get_white_list(ctx)
|
|
first = True
|
|
for arch in sorted(bsps):
|
|
for bsp in sorted(bsps[arch]):
|
|
variant = arch + "/" + bsp
|
|
if is_in_white_list(variant, white_list):
|
|
first = False
|
|
print(variant)
|
|
if first:
|
|
no_matches_error(ctx, white_list)
|