mbv: use new ninja-based build
This commit is contained in:
parent
922f258884
commit
a5c14f089b
@ -2,10 +2,13 @@ ARG TARGET
|
||||
|
||||
FROM debian:bookworm AS deps
|
||||
|
||||
# possible values: x64, arm64
|
||||
ARG ARCH=x64
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
||||
apt-get install -y make clang libgmock-dev gdb curl && \
|
||||
apt-get install -y make clang libgmock-dev gdb curl ninja-build && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists
|
||||
RUN cd /opt && curl -L https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-x64.tar.gz | tar -xz
|
||||
RUN cd /opt && curl -L https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-${ARCH}.tar.gz | tar -xz
|
||||
|
||||
|
||||
FROM deps AS dev
|
||||
@ -17,10 +20,10 @@ ARG TARGET=${TARGET}
|
||||
ADD . /workspace
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN make ${TARGET}
|
||||
RUN ./configure --version=${VERSION} && ninja -C build ../out/${TARGET}
|
||||
|
||||
|
||||
FROM scratch AS export
|
||||
ARG TARGET=${TARGET}
|
||||
|
||||
COPY --from=build /workspace/${TARGET} /
|
||||
COPY --from=build /workspace/out/${TARGET} /
|
||||
|
81
mbv/Makefile
81
mbv/Makefile
@ -1,84 +1,3 @@
|
||||
TOOLCHAIN_PATH = /opt/xpack-riscv-none-elf-gcc-14.2.0-3/bin/
|
||||
CC = $(TOOLCHAIN_PATH)riscv-none-elf-gcc
|
||||
LD = $(TOOLCHAIN_PATH)riscv-none-elf-g++
|
||||
CXX = $(TOOLCHAIN_PATH)riscv-none-elf-g++
|
||||
OBJCOPY = $(TOOLCHAIN_PATH)riscv-none-elf-objcopy
|
||||
|
||||
linker_script = bootloader.ld
|
||||
includes =
|
||||
sources =
|
||||
|
||||
CFLAGS = -march=rv32i -g -ffunction-sections -fdata-sections -Os -Werror -Wall -flto
|
||||
CXXFLAGS = $(CFLAGS) -std=c++20 -fno-exceptions -fcoroutines
|
||||
CPPFLAGS = -MD -MP
|
||||
LDFLAGS = -march=rv32i \
|
||||
-g \
|
||||
-Wl,--gc-sections -Os \
|
||||
-Wl,--print-memory-usage -flto
|
||||
|
||||
sources += hal/lib/common/xil_assert.c
|
||||
includes += -Ihal/lib/common
|
||||
|
||||
sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c hal/uart/xuartlite_intr.c
|
||||
includes += -Ihal/uart
|
||||
|
||||
bootloader_objects = bootloader.o start.o $(sources:.c=.o)
|
||||
helloworld_objects = helloworld.o start.o $(sources:.c=.o)
|
||||
|
||||
all_objects = $(bootloader_objects) $(helloworld_objects)
|
||||
deps = $(all_objects:.o=.d)
|
||||
|
||||
CPPFLAGS += $(includes)
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
%.elf: app.ld
|
||||
$(LD) -Wl,-Tapp.ld $(LDFLAGS) -o $@ $(objects)
|
||||
|
||||
bootloader.elf: $(bootloader_objects) bootloader.ld
|
||||
$(LD) -Wl,-Tbootloader.ld $(LDFLAGS) -o $@ $(bootloader_objects)
|
||||
|
||||
helloworld.elf: objects = $(helloworld_objects)
|
||||
helloworld.elf: $(helloworld_objects)
|
||||
|
||||
HOSTCXX = clang++
|
||||
HOSTLDFLAGS = -lgmock -lgtest -lgtest_main -L/usr/local/opt/llvm/lib -L/usr/local/lib
|
||||
HOSTCFLAGS = -std=c++20 -g\
|
||||
-I/usr/local/opt/llvm/include \
|
||||
-I/usr/local/include \
|
||||
-I/usr/local/include \
|
||||
-MP -MD
|
||||
|
||||
TSAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=thread
|
||||
ASAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=address -fsanitize=leak
|
||||
|
||||
tests =
|
||||
|
||||
.PHONY: test
|
||||
test: $(tests) ## Run tests
|
||||
|
||||
%.run: test/%
|
||||
TSAN_OPTIONS='suppressions=tsan.suppressions' ASAN_OPTIONS=detect_leaks=1 ./$<
|
||||
|
||||
%.host.o: %.cc
|
||||
$(HOSTCXX) $(HOSTCFLAGS) -c -o $@ $<
|
||||
|
||||
test/%_test: | mktest
|
||||
$(HOSTCXX) $(HOSTCFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
||||
|
||||
test/%_asan: | mktest
|
||||
$(HOSTCXX) $(ASAN_CFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
||||
|
||||
test/%_tsan: | mktest
|
||||
$(HOSTCXX) $(TSAN_CFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
||||
|
||||
.PHONY: mktest
|
||||
mktest:
|
||||
mkdir -p test
|
||||
|
||||
test_deps =
|
||||
|
||||
.PHONY: bootloader
|
||||
bootloader: ## Build the bootloader in docker
|
||||
docker build -o . --target export --build-arg TARGET=bootloader.elf .
|
||||
|
0
mbv/README.md
Normal file
0
mbv/README.md
Normal file
300
mbv/configure
vendored
Executable file
300
mbv/configure
vendored
Executable file
@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import glob
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
# global config
|
||||
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"
|
||||
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",
|
||||
"-Wextra",
|
||||
"-flto",
|
||||
"-march=rv32i",
|
||||
"-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",
|
||||
"-Wl,--gc-sections",
|
||||
"-Wl,--print-memory-usage",
|
||||
"-flto",
|
||||
"-march=rv32i",
|
||||
]
|
||||
|
||||
|
||||
def get_cxx_flags():
|
||||
return cpp_flags + cxx_flags
|
||||
|
||||
|
||||
def get_cc_flags():
|
||||
return cpp_flags + cc_flags
|
||||
|
||||
|
||||
def get_ldflags():
|
||||
return ldflags
|
||||
|
||||
|
||||
def add_cpp_flag(flag):
|
||||
cpp_flags.append(flag)
|
||||
|
||||
|
||||
def gen_rules():
|
||||
tools = {"cxx": "g++", "cc": "gcc", "as": "as", "objcopy": "objcopy"}
|
||||
|
||||
tc_path = toolchain_path
|
||||
tc_prefix = toolchain_prefix
|
||||
|
||||
rules = f"""
|
||||
rule cxx
|
||||
command = $cxx -MMD -MT $out -MF $out.d {' '.join(get_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
|
||||
description = CC $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule as
|
||||
command = $as $in -o $out
|
||||
description = AS $out
|
||||
|
||||
rule link
|
||||
command = $cxx {' '.join(ldflags)} -Wl,-T$linker_script -o $out $in $libs
|
||||
description = LINK $out
|
||||
|
||||
rule objcopy
|
||||
command = $objcopy -O binary $in $out
|
||||
description = OBJCOPY $out
|
||||
|
||||
rule hostcxx
|
||||
command = clang++ -MMD -MT $out -MF $out.d {hostcflags} -c $in -o $out
|
||||
description = HOSTCXX $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule hostlink
|
||||
command = clang++ {hostldflags} -o $out $in {hostlibs}
|
||||
description = HOSTLINK $out
|
||||
|
||||
rule profdata
|
||||
command = llvm-profdata merge -sparse $profraw -o $out
|
||||
description = PROFDATA
|
||||
|
||||
rule cov
|
||||
command = llvm-cov show --output-dir cov -format html --instr-profile $profdata $objects && touch $out
|
||||
description = COV
|
||||
|
||||
rule hosttest
|
||||
command = LLVM_PROFILE_FILE=$in.profraw ./$in && touch $out
|
||||
"""
|
||||
|
||||
for var, tool in tools.items():
|
||||
toolpath = os.path.join(tc_path, "bin", f"{tc_prefix}{tool}")
|
||||
yield f"{var} = {toolpath}"
|
||||
|
||||
for line in rules.splitlines():
|
||||
yield line
|
||||
|
||||
|
||||
def get_suffix_rule(filename, cxx_rule="cxx", cc_rule="cc"):
|
||||
suffix = filename.split(".")[-1]
|
||||
return collections.defaultdict(
|
||||
lambda: None,
|
||||
{
|
||||
"c": cc_rule,
|
||||
"cc": cxx_rule,
|
||||
"cpp": cxx_rule,
|
||||
"s": "as",
|
||||
},
|
||||
)[suffix]
|
||||
|
||||
|
||||
def make_cxx_rule(name, cflags=()):
|
||||
cflags = " ".join(cflags + get_cxx_flags())
|
||||
rule = f"""
|
||||
rule {name}
|
||||
command = $cxx -MMD -MT $out -MF $out.d {cflags} -c $in -o $out
|
||||
description = CXX $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
"""
|
||||
return rule.splitlines()
|
||||
|
||||
|
||||
def make_cc_rule(name, cflags=()):
|
||||
cflags = " ".join(cflags + get_cc_flags())
|
||||
rule = f"""
|
||||
rule {name}
|
||||
command = $cc -MMD -MT $out -MF $out.d {cflags} -c $in -o $out
|
||||
description = CC $out
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
"""
|
||||
return rule.splitlines()
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
for line in lines:
|
||||
yield line
|
||||
for objs, _ in dependencies:
|
||||
objects += objs
|
||||
objects = " ".join(objects)
|
||||
|
||||
yield f"build {elf_out}: link {objects} | {linker_script}"
|
||||
yield f" linker_script = {linker_script}"
|
||||
if bin_out is not None:
|
||||
bin_out = os.path.relpath(bin_out, builddir)
|
||||
yield f"build {bin_out}: objcopy {elf_out}"
|
||||
|
||||
|
||||
def build_test(name, sources):
|
||||
builds = [
|
||||
(os.path.relpath(s, builddir), f"{name}_" + re.sub(r"\.\w+", ".o", s))
|
||||
for s in sources
|
||||
]
|
||||
|
||||
out = name
|
||||
|
||||
for i, o in builds:
|
||||
rule = "hostcxx"
|
||||
yield f"build {o}: {rule} {i}"
|
||||
|
||||
objects = " ".join(b[1] for b in builds)
|
||||
|
||||
yield f"build {out}: hostlink {objects}"
|
||||
yield f"build {out}.run: hosttest {out}"
|
||||
|
||||
|
||||
def make_coverage(binaries):
|
||||
bins = " ".join(binaries)
|
||||
profraw = " ".join(f"{x}.profraw" for x in binaries)
|
||||
objects = " ".join(f"--object {x}" for x in binaries)
|
||||
testruns = " ".join(f"{x}.run" for x in binaries)
|
||||
yield f"build profdata: profdata | {testruns}"
|
||||
yield f" profraw = {profraw}"
|
||||
yield f"build cov/index.html: cov {bins} | profdata"
|
||||
yield f" profdata = profdata"
|
||||
yield f" objects = {objects}"
|
||||
|
||||
|
||||
hal = source_set("hal", [
|
||||
"hal/start.cc",
|
||||
"hal/lib/common/xil_assert.c",
|
||||
"hal/uart/xuartlite.c",
|
||||
"hal/uart/xuartlite_stats.c",
|
||||
"hal/uart/xuartlite_intr.c",
|
||||
])
|
||||
|
||||
bootloader = source_set("bootloader", glob.glob("./bootloader/**/*.cc", recursive=True))
|
||||
helloworld = source_set("helloworld", glob.glob("./apps/helloworld/**/*.cc", recursive=True))
|
||||
|
||||
bootloader_image = build_image(
|
||||
bootloader,
|
||||
dependencies=[hal],
|
||||
elf_out="out/bootloader.elf",
|
||||
linker_script="bootloader/bootloader.ld",
|
||||
)
|
||||
|
||||
helloworld_image = build_image(
|
||||
helloworld,
|
||||
dependencies=[hal],
|
||||
elf_out="out/helloworld.elf",
|
||||
bin_out="out/helloworld.bin",
|
||||
)
|
||||
|
||||
all = [build_source_set(hal), bootloader_image, helloworld_image]
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Generate ninja build files.')
|
||||
parser.add_argument('--version', required=True,
|
||||
help='version tag (typically from `git describe`)')
|
||||
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)
|
||||
|
||||
if not os.path.exists(builddir):
|
||||
os.mkdir(builddir)
|
||||
|
||||
with open(os.path.join(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}/'
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user