Skip to content

Commit cf507ae

Browse files
committed
Modularize build system with Kconfig integration
This splits monolithic Makefile into mk/ fragments (toolchain, features, deps, tests, format) and add Kconfiglib-based configuration via 'make config' (interactive menuconfig) or 'make defconfig'. Features (SLIRP networking, web observatory) are enabled by default and toggled via CONFIG_HAS_WEB in .config. The build mode (debug/release) is also configurable through Kconfig. Kconfiglib is fetched on demand from sysprog21/Kconfiglib via shallow clone into tools/kconfig/, cleaned by 'make distclean'. Change-Id: I719856353c77c9c69872b9180fa6d6296b732a6c
1 parent 906cfbb commit cf507ae

11 files changed

Lines changed: 442 additions & 234 deletions

File tree

.github/workflows/build-kbox.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ jobs:
5555
deps/
5656
key: rootfs-${{ hashFiles('scripts/alpine-sha256.txt', 'scripts/mkrootfs.sh', 'tests/guest/*.c', 'tests/stress/*.c', 'Makefile') }}
5757

58+
- name: Configure (defconfig)
59+
run: make defconfig
60+
5861
- name: Build kbox (debug + ASAN/UBSAN)
5962
run: make -j$(nproc)
6063

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*.o
2+
*.d
23
*.dSYM/
34
*.ext4
45
/kbox
@@ -7,6 +8,9 @@ deps/
78
lkl-x86_64/
89
lkl-aarch64/
910
externals/minislirp/
11+
tools/kconfig/
12+
.config
13+
.config.old
1014
target/
1115
tests/guest/*-test
1216
!tests/guest/*-test.c

Makefile

Lines changed: 86 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,264 +1,116 @@
1-
# kbox - Linux kernel as a library, rootless chroot via seccomp-unotify
2-
# Build: make [BUILD=release]
3-
# Test: make check
4-
5-
CC ?= gcc
6-
CFLAGS ?=
7-
LDFLAGS ?=
8-
BUILD ?= debug
9-
10-
ARCH ?= $(shell uname -m)
11-
12-
CFLAGS += -std=gnu11 -D_GNU_SOURCE -Wall -Wextra -Wpedantic -Wshadow
13-
CFLAGS += -Wno-unused-parameter
14-
CFLAGS += -Iinclude -Isrc
15-
16-
ifeq ($(BUILD),release)
17-
CFLAGS += -O2 -DNDEBUG
18-
else
19-
CFLAGS += -O0 -g3 -fsanitize=address,undefined -fno-omit-frame-pointer
20-
LDFLAGS += -fsanitize=address,undefined
21-
endif
22-
23-
# LKL library
24-
LKL_DIR ?= lkl-$(ARCH)
25-
LKL_LIB = $(LKL_DIR)/liblkl.a
26-
27-
LDFLAGS += -L$(LKL_DIR) -L$(LKL_DIR)/lib
28-
LDLIBS = -llkl -lpthread -ldl -lm -lrt
29-
30-
# Optional: SLIRP networking (set KBOX_HAS_SLIRP=1 to enable)
31-
ifdef KBOX_HAS_SLIRP
32-
SLIRP_DIR = externals/minislirp
33-
SLIRP_HDR = $(SLIRP_DIR)/src/libslirp.h
34-
CFLAGS += -DKBOX_HAS_SLIRP -I$(SLIRP_DIR)/src
35-
SLIRP_SRCS = $(wildcard $(SLIRP_DIR)/src/*.c)
36-
SLIRP_OBJS = $(SLIRP_SRCS:.c=.o)
37-
endif
38-
39-
# Optional: Web observatory (set KBOX_HAS_WEB=1 to enable)
40-
ifdef KBOX_HAS_WEB
41-
CFLAGS += -DKBOX_HAS_WEB
42-
WEB_ASSET_SRC = $(SRC_DIR)/web-assets.c
1+
# Build: make [BUILD=release]
2+
# Config: make config (interactive menuconfig)
3+
# make defconfig (all features enabled)
4+
# Test: make check
5+
6+
.DEFAULT_GOAL := all
7+
8+
# Kconfig integration
9+
10+
KCONFIG_DIR := tools/kconfig
11+
KCONFIG_CONF := configs/Kconfig
12+
13+
# Load configuration (safe include -- no error if .config is absent)
14+
-include .config
15+
16+
# Targets that don't require .config
17+
CONFIG_TARGETS := config defconfig oldconfig savedefconfig clean distclean indent \
18+
check-unit fetch-lkl fetch-minislirp install-hooks \
19+
guest-bins stress-bins rootfs
20+
CONFIG_GENERATORS := config defconfig oldconfig
21+
22+
# Require .config for build targets.
23+
# Note: 'make defconfig && make' is the correct two-step sequence.
24+
# 'make defconfig all' does NOT work because .config is parsed at
25+
# Make startup, before any recipe runs.
26+
BUILD_GOALS := $(filter-out $(CONFIG_TARGETS),$(or $(MAKECMDGOALS),all))
27+
HAS_CONFIG_GEN := $(filter $(CONFIG_GENERATORS),$(MAKECMDGOALS))
28+
ifneq ($(BUILD_GOALS),)
29+
ifeq ($(HAS_CONFIG_GEN),)
30+
ifeq ($(wildcard .config),)
31+
$(info )
32+
$(info *** Configuration file ".config" not found!)
33+
$(info *** Please run 'make config' or 'make defconfig' first.)
34+
$(info )
35+
$(error Configuration required)
4336
endif
44-
45-
# Source files
46-
SRC_DIR = src
47-
SRCS = $(SRC_DIR)/main.c \
48-
$(SRC_DIR)/cli.c \
49-
$(SRC_DIR)/syscall-nr.c \
50-
$(SRC_DIR)/lkl-wrap.c \
51-
$(SRC_DIR)/fd-table.c \
52-
$(SRC_DIR)/procmem.c \
53-
$(SRC_DIR)/path.c \
54-
$(SRC_DIR)/identity.c \
55-
$(SRC_DIR)/elf.c \
56-
$(SRC_DIR)/mount.c \
57-
$(SRC_DIR)/probe.c \
58-
$(SRC_DIR)/image.c \
59-
$(SRC_DIR)/seccomp-bpf.c \
60-
$(SRC_DIR)/seccomp-notify.c \
61-
$(SRC_DIR)/shadow-fd.c \
62-
$(SRC_DIR)/seccomp-dispatch.c \
63-
$(SRC_DIR)/seccomp-supervisor.c \
64-
$(SRC_DIR)/net-slirp.c \
65-
$(SRC_DIR)/web-telemetry.c \
66-
$(SRC_DIR)/web-events.c \
67-
$(SRC_DIR)/web-server.c
68-
69-
ifdef KBOX_HAS_WEB
70-
SRCS += $(WEB_ASSET_SRC)
7137
endif
72-
73-
ifdef KBOX_HAS_SLIRP
74-
SRCS += $(SLIRP_SRCS)
7538
endif
7639

77-
OBJS = $(SRCS:.c=.o)
78-
TARGET = kbox
79-
80-
# Unit test files (no LKL dependency)
81-
TEST_DIR = tests/unit
82-
TEST_SRCS = $(TEST_DIR)/test-runner.c \
83-
$(TEST_DIR)/test-fd-table.c \
84-
$(TEST_DIR)/test-path.c \
85-
$(TEST_DIR)/test-identity.c \
86-
$(TEST_DIR)/test-syscall-nr.c \
87-
$(TEST_DIR)/test-elf.c
88-
89-
# Unit tests link only the pure-computation sources (no LKL)
90-
TEST_SUPPORT_SRCS = $(SRC_DIR)/fd-table.c \
91-
$(SRC_DIR)/path.c \
92-
$(SRC_DIR)/identity.c \
93-
$(SRC_DIR)/syscall-nr.c \
94-
$(SRC_DIR)/elf.c
95-
96-
TEST_OBJS = $(TEST_SRCS:.c=.o) $(TEST_SUPPORT_SRCS:.c=.o)
97-
TEST_TARGET = tests/unit/test-runner
98-
99-
# Guest test programs (compiled statically, run inside kbox)
100-
GUEST_DIR = tests/guest
101-
GUEST_SRCS = $(wildcard $(GUEST_DIR)/*-test.c)
102-
GUEST_BINS = $(GUEST_SRCS:.c=)
103-
104-
# Stress test programs (compiled statically, run inside kbox)
105-
STRESS_DIR = tests/stress
106-
STRESS_SRCS = $(wildcard $(STRESS_DIR)/*.c)
107-
STRESS_BINS = $(STRESS_SRCS:.c=)
40+
# Include modular build fragments
41+
include mk/toolchain.mk
42+
include mk/features.mk
43+
include mk/deps.mk
44+
include mk/tests.mk
45+
include mk/format.mk
10846

109-
# Rootfs image
110-
ROOTFS = alpine.ext4
111-
112-
# ---- Top-level targets ----
113-
114-
.PHONY: all clean check check-unit check-integration check-stress guest-bins stress-bins rootfs fetch-lkl fetch-minislirp install-hooks web-assets indent
47+
# Top-level targets
48+
.PHONY: all clean distclean config defconfig oldconfig savedefconfig install-hooks
11549

11650
all: $(TARGET)
117-
ifneq ($(wildcard .git),)
51+
ifneq ($(wildcard .git/hooks),)
11852
all: | .git/hooks/pre-commit
11953
endif
12054

121-
$(TARGET): $(OBJS) | $(LKL_LIB)
122-
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
55+
$(TARGET): $(OBJS) $(LKL_LIB)
56+
@echo " LD $@"
57+
$(Q)$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
58+
59+
# Rebuild all objects when .config changes (CFLAGS may differ).
60+
$(OBJS): $(wildcard .config)
12361

12462
%.o: %.c
125-
$(CC) $(CFLAGS) -c -o $@ $<
63+
@echo " CC $<"
64+
$(Q)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $<
12665

12766
# Auto-install git hooks on first build (skipped in worktrees where .git is a file).
12867
.git/hooks/pre-commit: scripts/pre-commit.hook
129-
@if [ -d .git/hooks ]; then $(MAKE) -s install-hooks; fi
130-
131-
# Auto-fetch LKL if missing
132-
$(LKL_LIB):
133-
@echo "LKL library not found at $(LKL_DIR). Fetching..."
134-
./scripts/fetch-lkl.sh $(ARCH)
135-
136-
# Auto-fetch minislirp if missing (shallow clone, no submodule).
137-
# $(wildcard) evaluates at parse time, so if minislirp has not been
138-
# fetched yet SLIRP_SRCS is empty. Guard: fetch and re-exec make so
139-
# the wildcard picks up the newly-cloned sources.
140-
ifdef KBOX_HAS_SLIRP
141-
# Auto-fetch minislirp if the header is missing and a build target
142-
# was requested (skip for clean/fetch-only goals).
143-
ifneq ($(filter clean,$(MAKECMDGOALS)),clean)
144-
ifeq ($(wildcard $(SLIRP_HDR)),)
145-
$(shell ./scripts/fetch-minislirp.sh >&2)
146-
SLIRP_SRCS = $(wildcard $(SLIRP_DIR)/src/*.c)
147-
SLIRP_OBJS = $(SLIRP_SRCS:.c=.o)
148-
endif
149-
endif
150-
151-
fetch-minislirp:
152-
./scripts/fetch-minislirp.sh
153-
endif
154-
155-
# ---- Test targets ----
156-
157-
check: check-unit check-integration check-stress
158-
159-
check-unit: $(TEST_TARGET)
160-
./$(TEST_TARGET)
161-
162-
# Unit tests are built WITHOUT linking LKL.
163-
# We define LKL stubs for functions referenced by test support code.
164-
$(TEST_TARGET): $(TEST_SRCS) $(TEST_SUPPORT_SRCS)
165-
$(CC) $(CFLAGS) -DKBOX_UNIT_TEST -o $@ $^ $(LDFLAGS)
166-
167-
check-integration: $(TARGET) guest-bins stress-bins $(ROOTFS)
168-
./scripts/run-tests.sh ./$(TARGET) $(ROOTFS)
169-
170-
check-stress: $(TARGET) stress-bins $(ROOTFS)
171-
./scripts/run-stress.sh ./$(TARGET) $(ROOTFS) || \
172-
echo "(stress test failures are non-blocking -- see TODO.md)"
173-
174-
# ---- Guest / stress binaries (static, no ASAN) ----
175-
# These are cross-compiled on Linux and placed into the rootfs.
176-
# They must be statically linked and cannot use sanitizers.
177-
178-
guest-bins: $(GUEST_BINS)
179-
180-
$(GUEST_DIR)/%-test: $(GUEST_DIR)/%-test.c
181-
$(CC) -std=gnu11 -Wall -Wextra -O2 -static -o $@ $<
68+
$(Q)if [ -d .git/hooks ]; then $(MAKE) install-hooks; fi
18269

183-
stress-bins: $(STRESS_BINS)
184-
185-
$(STRESS_DIR)/%: $(STRESS_DIR)/%.c
186-
$(CC) -std=gnu11 -Wall -Wextra -O2 -static -pthread -o $@ $<
187-
188-
# ---- Rootfs ----
189-
190-
rootfs: $(ROOTFS)
191-
192-
$(ROOTFS): scripts/mkrootfs.sh scripts/alpine-sha256.txt $(GUEST_BINS) $(STRESS_BINS)
193-
ALPINE_ARCH=$(ARCH) ./scripts/mkrootfs.sh
194-
195-
# ---- Utilities ----
196-
197-
# Fetch LKL from nightly release if not cached locally.
198-
# To force re-download: rm -rf lkl-x86_64 && make fetch-lkl
199-
fetch-lkl:
200-
./scripts/fetch-lkl.sh $(ARCH)
201-
202-
# Install git hooks from scripts/*.hook into .git/hooks/.
203-
# Skips hooks that already exist (preserves user customizations).
20470
install-hooks:
205-
@for hook in scripts/*.hook; do \
71+
$(Q)for hook in scripts/*.hook; do \
20672
name=$$(basename "$$hook" .hook); \
20773
if [ ! -e .git/hooks/"$$name" ] && [ ! -L .git/hooks/"$$name" ]; then \
20874
ln -s ../../"$$hook" .git/hooks/"$$name"; \
20975
echo "Installed $$name hook"; \
21076
fi; \
21177
done
21278

213-
# Generate compiled-in web assets from web/ directory.
214-
# Re-run when any web/ file changes.
215-
ifdef KBOX_HAS_WEB
216-
WEB_SRCS_ALL = $(shell find web -type f \( -name '*.html' -o -name '*.css' -o -name '*.js' -o -name '*.svg' \) 2>/dev/null)
217-
$(WEB_ASSET_SRC): $(WEB_SRCS_ALL) scripts/gen-web-assets.sh
218-
./scripts/gen-web-assets.sh
219-
web-assets: $(WEB_ASSET_SRC)
220-
endif
79+
# Kconfig targets
22180

222-
# ---- Formatting ----
81+
# Fetch Kconfiglib on demand, then run the appropriate tool.
82+
config: | $(KCONFIG_DIR)/menuconfig.py
83+
$(Q)KCONFIG_CONFIG=.config python3 $(KCONFIG_DIR)/menuconfig.py $(KCONFIG_CONF)
84+
@echo " CONFIG .config"
22385

224-
CLANG_FORMAT := $(shell command -v clang-format-20 2>/dev/null || \
225-
command -v clang-format 2>/dev/null)
226-
SHFMT := shfmt
227-
BLACK := black
86+
defconfig: | $(KCONFIG_DIR)/menuconfig.py
87+
@echo " CONFIG defconfig"
88+
$(Q)KCONFIG_CONFIG=.config python3 $(KCONFIG_DIR)/defconfig.py --kconfig $(KCONFIG_CONF) configs/defconfig
22889

229-
C_SRCS_FMT := $(wildcard src/*.c src/*.h include/kbox/*.h \
230-
tests/unit/*.c tests/unit/*.h \
231-
tests/guest/*.c tests/stress/*.c)
232-
SH_SRCS_FMT := $(wildcard scripts/*.sh)
233-
PY_SRCS_FMT := $(wildcard scripts/gdb/*.py)
90+
oldconfig: | $(KCONFIG_DIR)/menuconfig.py
91+
@echo " CONFIG oldconfig"
92+
$(Q)KCONFIG_CONFIG=.config python3 $(KCONFIG_DIR)/oldconfig.py $(KCONFIG_CONF)
23493

235-
indent:
236-
ifeq ($(CLANG_FORMAT),)
237-
$(error clang-format not found; install clang-format or clang-format-20)
238-
endif
239-
@$(CLANG_FORMAT) --version | grep -q 'version 20' || \
240-
{ echo "error: clang-format version 20 required"; exit 1; }
241-
$(CLANG_FORMAT) -i $(C_SRCS_FMT)
242-
$(SHFMT) -i 4 -fn -ci -sr -bn -w $(SH_SRCS_FMT)
243-
$(BLACK) -q $(PY_SRCS_FMT)
94+
savedefconfig: | $(KCONFIG_DIR)/menuconfig.py
95+
@echo " CONFIG savedefconfig"
96+
$(Q)KCONFIG_CONFIG=.config python3 $(KCONFIG_DIR)/savedefconfig.py --kconfig $(KCONFIG_CONF) --out configs/defconfig
24497

245-
clean:
246-
rm -f $(OBJS) $(TARGET) $(TEST_TARGET) $(TEST_DIR)/*.o
247-
rm -f src/*.o src/web-assets.c
248-
rm -f $(GUEST_BINS) $(STRESS_BINS)
98+
$(KCONFIG_DIR)/menuconfig.py:
99+
@echo " FETCH kconfiglib"
100+
$(Q)scripts/fetch-kconfiglib.sh
101+
102+
# Clean
249103

250-
# ---- Dependencies ----
251-
# Auto-generate with gcc -MM if needed; keep it simple for now.
252-
$(SRC_DIR)/main.o: include/kbox/cli.h include/kbox/image.h
253-
$(SRC_DIR)/cli.o: include/kbox/cli.h
254-
$(SRC_DIR)/probe.o: include/kbox/probe.h src/seccomp-defs.h
255-
$(SRC_DIR)/image.o: include/kbox/image.h include/kbox/mount.h include/kbox/identity.h include/kbox/probe.h src/lkl-wrap.h src/net.h src/seccomp.h src/shadow-fd.h
256-
$(SRC_DIR)/shadow-fd.o: src/shadow-fd.h src/lkl-wrap.h src/syscall-nr.h
257-
$(SRC_DIR)/seccomp-dispatch.o: src/seccomp.h src/seccomp-defs.h src/fd-table.h src/lkl-wrap.h src/procmem.h include/kbox/path.h include/kbox/identity.h src/shadow-fd.h src/net.h
258-
$(SRC_DIR)/seccomp-supervisor.o: src/seccomp.h src/seccomp-defs.h src/syscall-nr.h
259-
$(SRC_DIR)/seccomp-bpf.o: src/seccomp.h src/seccomp-defs.h src/syscall-nr.h
260-
$(SRC_DIR)/seccomp-notify.o: src/seccomp.h src/seccomp-defs.h
261-
$(SRC_DIR)/net-slirp.o: src/net.h src/lkl-wrap.h src/syscall-nr.h
262-
$(SRC_DIR)/web-telemetry.o: src/web.h src/lkl-wrap.h src/syscall-nr.h
263-
$(SRC_DIR)/web-events.o: src/web.h
264-
$(SRC_DIR)/web-server.o: src/web.h src/fd-table.h src/lkl-wrap.h src/syscall-nr.h
104+
clean:
105+
@echo " CLEAN"
106+
$(Q)rm -f $(OBJS) $(OBJS:.o=.d) $(TARGET) $(TEST_TARGET) $(TEST_DIR)/*.o
107+
$(Q)rm -f src/*.o src/*.d src/web-assets.c
108+
$(Q)rm -f $(GUEST_BINS) $(STRESS_BINS)
109+
110+
distclean: clean
111+
@echo " CLEAN distclean"
112+
$(Q)rm -f .config .config.old
113+
$(Q)rm -rf $(KCONFIG_DIR)
114+
115+
# Auto-generated header dependencies (-MMD -MP writes .d alongside .o)
116+
-include $(OBJS:.o=.d)

0 commit comments

Comments
 (0)