mbv: now with out of source builds
This commit is contained in:
parent
af768cbb09
commit
e23cf79aa0
191
mbv/configure
vendored
191
mbv/configure
vendored
@ -2,18 +2,25 @@
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import dataclasses
|
||||
import glob
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
# global config
|
||||
class Config:
|
||||
def __init__(self, builddir, outdir):
|
||||
self.builddir = builddir
|
||||
self.outdir = outdir
|
||||
|
||||
if not os.path.exists(builddir):
|
||||
os.mkdir(builddir)
|
||||
if not os.path.exists(outdir):
|
||||
os.mkdir(outdir)
|
||||
|
||||
toolchain_path = f"/opt/xpack-riscv-none-elf-gcc-14.2.0-3"
|
||||
toolchain_prefix = "riscv-none-elf-"
|
||||
builddir = "build"
|
||||
outdir = "out"
|
||||
linker_script = "apps/app.ld"
|
||||
hostcflags = "-g -std=c++20 -fprofile-instr-generate -fcoverage-mapping"
|
||||
hostlibs = "-lgtest -lgmock -lgtest_main"
|
||||
hostldflags = "-fprofile-instr-generate -fcoverage-mapping"
|
||||
@ -22,11 +29,6 @@ include_dirs = [
|
||||
"hal/uart",
|
||||
"hal/lib/common",
|
||||
]
|
||||
|
||||
include_flags = [f"-I{os.path.relpath(i, builddir)}" for i in include_dirs]
|
||||
project_flags = [
|
||||
]
|
||||
cpp_flags = ["-DNDEBUG"] + include_flags + project_flags
|
||||
common_flags = [
|
||||
"-g",
|
||||
"-Wall",
|
||||
@ -36,9 +38,6 @@ common_flags = [
|
||||
"-ffunction-sections",
|
||||
"-Oz",
|
||||
]
|
||||
|
||||
cc_flags = common_flags
|
||||
cxx_flags = common_flags + ["-std=c++20", "-fno-rtti", "-fno-exceptions", "-Wno-missing-field-initializers"]
|
||||
ldflags = [
|
||||
"-Oz",
|
||||
"-g",
|
||||
@ -47,39 +46,48 @@ ldflags = [
|
||||
"-flto",
|
||||
"-march=rv32i_zicsr",
|
||||
]
|
||||
project_flags = [
|
||||
]
|
||||
|
||||
@property
|
||||
def include_flags(self):
|
||||
return [f"-I{os.path.relpath(i, self.builddir)}" for i in self.include_dirs]
|
||||
|
||||
def get_cxx_flags():
|
||||
return cpp_flags + cxx_flags
|
||||
@property
|
||||
def cpp_flags(self):
|
||||
return ["-DNDEBUG"] + self.include_flags + self.project_flags
|
||||
|
||||
@property
|
||||
def cc_flags(self):
|
||||
return self.common_flags + self.cpp_flags
|
||||
|
||||
def get_cc_flags():
|
||||
return cpp_flags + cc_flags
|
||||
@property
|
||||
def cxx_flags(self):
|
||||
return self.common_flags + [
|
||||
"-std=c++20",
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
"-Wno-missing-field-initializers",
|
||||
] + self.cpp_flags
|
||||
|
||||
def relbuild(self, path):
|
||||
return os.path.relpath(path, self.builddir)
|
||||
|
||||
def get_ldflags():
|
||||
return ldflags
|
||||
|
||||
|
||||
def add_cpp_flag(flag):
|
||||
cpp_flags.append(flag)
|
||||
|
||||
|
||||
def gen_rules():
|
||||
def gen_rules(config):
|
||||
tools = {"cxx": "g++", "cc": "gcc", "as": "as", "objcopy": "objcopy"}
|
||||
|
||||
tc_path = toolchain_path
|
||||
tc_prefix = toolchain_prefix
|
||||
tc_path = config.toolchain_path
|
||||
tc_prefix = config.toolchain_prefix
|
||||
|
||||
rules = f"""
|
||||
rule cxx
|
||||
command = $cxx -MMD -MT $out -MF $out.d {' '.join(get_cxx_flags())} -c $in -o $out
|
||||
command = $cxx -MMD -MT $out -MF $out.d {' '.join(config.cxx_flags)} -c $in -o $out
|
||||
description = CXX $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule cc
|
||||
command = $cc -MMD -MT $out -MF $out.d {' '.join(get_cc_flags())} -c $in -o $out
|
||||
command = $cc -MMD -MT $out -MF $out.d {' '.join(config.cc_flags)} -c $in -o $out
|
||||
description = CC $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
@ -89,7 +97,7 @@ rule as
|
||||
description = AS $out
|
||||
|
||||
rule link
|
||||
command = $cxx {' '.join(ldflags)} -Wl,-T$linker_script -o $out $in $libs
|
||||
command = $cxx {' '.join(config.ldflags)} -Wl,-T$linker_script -o $out $in $libs
|
||||
description = LINK $out
|
||||
|
||||
rule objcopy
|
||||
@ -97,13 +105,13 @@ rule objcopy
|
||||
description = OBJCOPY $out
|
||||
|
||||
rule hostcxx
|
||||
command = clang++ -MMD -MT $out -MF $out.d {hostcflags} -c $in -o $out
|
||||
command = clang++ -MMD -MT $out -MF $out.d {config.hostcflags} -c $in -o $out
|
||||
description = HOSTCXX $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule hostlink
|
||||
command = clang++ {hostldflags} -o $out $in {hostlibs}
|
||||
command = clang++ {config.hostldflags} -o $out $in {config.hostlibs}
|
||||
description = HOSTLINK $out
|
||||
|
||||
rule profdata
|
||||
@ -140,7 +148,7 @@ def get_suffix_rule(filename, cxx_rule="cxx", cc_rule="cc"):
|
||||
|
||||
|
||||
def make_cxx_rule(name, cflags=()):
|
||||
cflags = " ".join(cflags + get_cxx_flags())
|
||||
cflags = " ".join(cflags)
|
||||
rule = f"""
|
||||
rule {name}
|
||||
command = $cxx -MMD -MT $out -MF $out.d {cflags} -c $in -o $out
|
||||
@ -152,7 +160,7 @@ rule {name}
|
||||
|
||||
|
||||
def make_cc_rule(name, cflags=()):
|
||||
cflags = " ".join(cflags + get_cc_flags())
|
||||
cflags = " ".join(cflags)
|
||||
rule = f"""
|
||||
rule {name}
|
||||
command = $cc -MMD -MT $out -MF $out.d {cflags} -c $in -o $out
|
||||
@ -163,55 +171,67 @@ rule {name}
|
||||
return rule.splitlines()
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SourceSet:
|
||||
name: str
|
||||
sources: list[str]
|
||||
cflags: list[str]
|
||||
|
||||
def get_objects(self, config):
|
||||
for s in self.sources:
|
||||
yield (config.relbuild(s), re.sub(r"\.\w+", ".o", s))
|
||||
|
||||
def get_targets(self, config):
|
||||
return list(zip(*self.get_objects(config)))[1]
|
||||
|
||||
|
||||
def source_set(name, sources, cflags=()):
|
||||
builds = [
|
||||
(os.path.relpath(s, builddir), re.sub(r"\.\w+", ".o", s))
|
||||
for s in sources
|
||||
if get_suffix_rule(s) is not None
|
||||
]
|
||||
|
||||
lines = []
|
||||
|
||||
cxx_rule = "cxx"
|
||||
cc_rule = "cc"
|
||||
if cflags:
|
||||
cxx_rule = f"cxx_{name}"
|
||||
lines += make_cxx_rule(cxx_rule, cflags=cflags)
|
||||
cc_rule = f"cc_{name}"
|
||||
lines += make_cc_rule(cc_rule, cflags=cflags)
|
||||
|
||||
for i, o in builds:
|
||||
rule = get_suffix_rule(i, cxx_rule=cxx_rule, cc_rule=cc_rule)
|
||||
if rule is None:
|
||||
continue
|
||||
lines.append(f"build {o}: {rule} {i}")
|
||||
|
||||
return [b[1] for b in builds], lines
|
||||
return SourceSet(name, sources, cflags)
|
||||
|
||||
|
||||
def build_source_set(source_set):
|
||||
for line in source_set[1]:
|
||||
yield line
|
||||
|
||||
|
||||
def build_image(source_set, elf_out, dependencies=(), bin_out=None, linker_script=linker_script):
|
||||
# to make it builddir-relative
|
||||
linker_script = os.path.relpath(linker_script, builddir)
|
||||
|
||||
elf_out = os.path.relpath(elf_out, builddir)
|
||||
|
||||
objects, lines = source_set
|
||||
def __f(config):
|
||||
cxx_rule = "cxx"
|
||||
cc_rule = "cc"
|
||||
lines = []
|
||||
if source_set.cflags:
|
||||
cxx_rule = f"cxx_{name}"
|
||||
lines += make_cxx_rule(cxx_rule, cflags=cflags + config.cxx_flags)
|
||||
cc_rule = f"cc_{name}"
|
||||
lines += make_cc_rule(cc_rule, cflags=cflags + config.cc_flags)
|
||||
for line in lines:
|
||||
yield line
|
||||
for objs, _ in dependencies:
|
||||
objects += objs
|
||||
|
||||
for i, o in source_set.get_objects(config):
|
||||
rule = get_suffix_rule(i, cxx_rule=cxx_rule, cc_rule=cc_rule)
|
||||
if rule is None:
|
||||
continue
|
||||
yield f"build {o}: {rule} {i}"
|
||||
|
||||
return __f
|
||||
|
||||
|
||||
def build_image(source_set, elf_out, linker_script, dependencies=(), bin_out=None):
|
||||
def __f(config):
|
||||
# to make it builddir-relative
|
||||
lscript = config.relbuild(linker_script)
|
||||
elfout = config.relbuild(os.path.join(config.outdir, elf_out))
|
||||
|
||||
for l in build_source_set(source_set)(config):
|
||||
yield l
|
||||
|
||||
objects = source_set.get_targets(config)
|
||||
for dep in dependencies:
|
||||
objects += dep.get_targets(config)
|
||||
objects = " ".join(objects)
|
||||
|
||||
yield f"build {elf_out}: link {objects} | {linker_script}"
|
||||
yield f" linker_script = {linker_script}"
|
||||
yield f"build {elfout}: link {objects} | {lscript}"
|
||||
yield f" linker_script = {lscript}"
|
||||
if bin_out is not None:
|
||||
bin_out = os.path.relpath(bin_out, builddir)
|
||||
yield f"build {bin_out}: objcopy {elf_out}"
|
||||
binout = config.relbuild(os.path.join(config.outdir, bin_out))
|
||||
yield f"build {binout}: objcopy {elfout}"
|
||||
|
||||
return __f
|
||||
|
||||
|
||||
def build_test(name, sources):
|
||||
@ -258,16 +278,17 @@ bootloader = source_set("bootloader", glob.glob("./bootloader/**/*.cc", recursiv
|
||||
bootloader_image = build_image(
|
||||
bootloader,
|
||||
dependencies=[hal],
|
||||
elf_out="out/bootloader.elf",
|
||||
elf_out="bootloader.elf",
|
||||
linker_script="bootloader/bootloader.ld",
|
||||
)
|
||||
|
||||
def app_image(app):
|
||||
return build_image(
|
||||
source_set(app, glob.glob(f"./apps/{app}/**/*.cc", recursive=True)),
|
||||
linker_script="apps/app.ld",
|
||||
dependencies=[hal],
|
||||
elf_out=f"out/{app}.elf",
|
||||
bin_out=f"out/{app}.bin",
|
||||
elf_out=f"{app}.elf",
|
||||
bin_out=f"{app}.bin",
|
||||
)
|
||||
|
||||
all = [
|
||||
@ -284,24 +305,24 @@ def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Generate ninja build files.')
|
||||
parser.add_argument('--version', required=True,
|
||||
help='version tag (typically from `git describe`)')
|
||||
parser.add_argument('--build-dir', default='build', help='build directory')
|
||||
parser.add_argument('--out-dir', default='out', help='output directory')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
add_cpp_flag(f'-DGIT_VERSION_TAG=\\"{args.version}\\"')
|
||||
header = gen_rules()
|
||||
lines = itertools.chain(header, *all)
|
||||
config = Config(builddir=args.build_dir, outdir=args.out_dir)
|
||||
config.project_flags.append(f'-DGIT_VERSION_TAG=\\"{args.version}\\"')
|
||||
header = gen_rules(config)
|
||||
lines = itertools.chain(header, *(f(config) for f in all))
|
||||
|
||||
if not os.path.exists(builddir):
|
||||
os.mkdir(builddir)
|
||||
|
||||
with open(os.path.join(builddir, "build.ninja"), "w") as f:
|
||||
with open(os.path.join(config.builddir, "build.ninja"), "w") as f:
|
||||
f.write("\n".join(lines))
|
||||
f.write("\n")
|
||||
|
||||
print(
|
||||
f'Configure done. Build with "ninja -C {builddir}". Output will be in {outdir}/'
|
||||
f'Configure done. Build with "ninja -C {config.builddir}". Output will be in {config.outdir}/'
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user