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