Skip to content

Commit f71718f

Browse files
committed
Improve Makefile bootstrap verification
1 parent f265042 commit f71718f

File tree

1 file changed

+149
-91
lines changed

1 file changed

+149
-91
lines changed

Makefile

Lines changed: 149 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
CFLAGS := -O -g \
2-
-std=c99 -pedantic
1+
# Compiler and flags
2+
CC ?= gcc
3+
CFLAGS := -O -g -std=c99 -pedantic
34

5+
# Flags to check for compiler support
46
CFLAGS_TO_CHECK := \
57
-fwrapv \
68
-Wall -Wextra \
@@ -16,122 +18,119 @@ CFLAGS_TO_CHECK := \
1618
-Wno-format-pedantic \
1719
-Wno-overflow
1820

19-
SUPPORTED_CFLAGS :=
20-
# Check if a specific compiler flag is supported, attempting a dummy compilation
21-
# with flags. If successful, it returns the flag string; otherwise, it returns
22-
# an empty string.
23-
# Usage: $(call check_flag, -some-flag)
24-
check_flag = $(shell $(CC) $(1) -S -o /dev/null -xc /dev/null 2>/dev/null; \
25-
if test $$? -eq 0; then echo "$(1)"; fi)
21+
# Check if a specific compiler flag is supported
22+
check_flag = $(shell $(CC) $(1) -S -o /dev/null -xc /dev/null 2>/dev/null && echo "$(1)")
2623

27-
# Iterate through the list of all potential flags, effectively filtering out all
28-
# unsupported flags.
29-
$(foreach flag, $(CFLAGS_TO_CHECK), $(eval CFLAGS += $(call check_flag, $(flag))))
24+
# Add supported flags
25+
$(foreach flag,$(CFLAGS_TO_CHECK),$(eval CFLAGS += $(call check_flag,$(flag))))
3026

27+
# Build configuration
3128
BUILD_SESSION := .session.mk
32-
3329
-include $(BUILD_SESSION)
3430

31+
# Stage definitions
3532
STAGE0 := shecc
3633
STAGE1 := shecc-stage1.elf
3734
STAGE2 := shecc-stage2.elf
3835

36+
# Directory structure
3937
OUT ?= out
40-
ARCHS = arm riscv
41-
ARCH ?= $(firstword $(ARCHS))
42-
HOST_ARCH = $(shell arch 2>/dev/null)
4338
SRCDIR := $(shell find src -type d)
4439
LIBDIR := $(shell find lib -type d)
4540

46-
SRCS := $(wildcard $(patsubst %,%/main.c, $(SRCDIR)))
41+
# Architecture configuration
42+
ARCHS := arm riscv
43+
ARCH ?= $(firstword $(ARCHS))
44+
HOST_ARCH := $(shell arch 2>/dev/null)
45+
46+
# Source files
47+
SRCS := $(wildcard $(patsubst %,%/main.c,$(SRCDIR)))
4748
OBJS := $(SRCS:%.c=$(OUT)/%.o)
48-
deps := $(OBJS:%.o=%.o.d)
49+
DEPS := $(OBJS:%.o=%.o.d)
50+
51+
# Test files
4952
TESTS := $(wildcard tests/*.c)
5053
TESTBINS := $(TESTS:%.c=$(OUT)/%.elf)
51-
SNAPSHOTS := $(foreach SNAPSHOT_ARCH,$(ARCHS), $(patsubst tests/%.c, tests/snapshots/%-$(SNAPSHOT_ARCH).json, $(TESTS)))
54+
SNAPSHOTS := $(foreach SNAPSHOT_ARCH,$(ARCHS),$(patsubst tests/%.c,tests/snapshots/%-$(SNAPSHOT_ARCH).json,$(TESTS)))
5255

53-
all: config bootstrap
56+
# Build targets
57+
.PHONY: all config bootstrap clean distclean
58+
.PHONY: check check-stage0 check-stage2 check-sanitizer check-snapshots check-snapshot
59+
.PHONY: update-snapshots update-snapshot sanitizer help
5460

55-
sanitizer: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -O0
56-
sanitizer: LDFLAGS += -fsanitize=address -fsanitize=undefined
57-
sanitizer: config $(OUT)/$(STAGE0)-sanitizer
58-
$(VECHO) " Built stage 0 compiler with sanitizers\n"
61+
.DEFAULT_GOAL := all
62+
63+
all: config bootstrap
5964

65+
help:
66+
@echo "Available targets:"
67+
@echo " all - Build with bootstrap (default)"
68+
@echo " bootstrap - Build and verify self-hosting compiler"
69+
@echo " config - Configure target architecture"
70+
@echo " sanitizer - Build with address/undefined sanitizers"
71+
@echo " check - Run all tests"
72+
@echo " check-stage0 - Test stage 0 compiler"
73+
@echo " check-stage2 - Test stage 2 compiler"
74+
@echo " check-sanitizer - Test with sanitizers"
75+
@echo " check-snapshots - Verify IR snapshots"
76+
@echo " update-snapshots - Update IR snapshots"
77+
@echo " clean - Remove build artifacts"
78+
@echo " distclean - Remove all generated files"
79+
@echo ""
80+
@echo "Variables:"
81+
@echo " ARCH={arm,riscv} - Target architecture (default: $(ARCH))"
82+
@echo " OUT=<dir> - Output directory (default: $(OUT))"
83+
@echo " CC=<compiler> - C compiler (default: $(CC))"
84+
85+
# Validate architecture selection
6086
ifeq (,$(filter $(ARCH),$(ARCHS)))
61-
$(error Support ARM and RISC-V only. Select the target with "ARCH=arm" or "ARCH=riscv")
87+
$(error Unsupported architecture '$(ARCH)'. Use ARCH=arm or ARCH=riscv)
6288
endif
89+
6390
include mk/$(ARCH).mk
6491
include mk/common.mk
6592

93+
# Configuration target
6694
config:
67-
$(Q)ln -s $(PWD)/$(SRCDIR)/$(ARCH)-codegen.c $(SRCDIR)/codegen.c
95+
$(Q)mkdir -p $(OUT) $(OUT)/$(SRCDIR) $(OUT)/tests
96+
$(Q)ln -sf $(PWD)/$(SRCDIR)/$(ARCH)-codegen.c $(SRCDIR)/codegen.c
6897
$(Q)$(PRINTF) $(ARCH_DEFS) > $@
69-
$(VECHO) "Target machine code switch to %s\n" $(ARCH)
70-
$(Q)$(MAKE) $(BUILD_SESSION) --silent
98+
$(VECHO) "Target architecture: %s\n" $(ARCH)
99+
$(Q)$(MAKE) $(BUILD_SESSION) --no-print-directory
71100
$(Q)$(CONFIG_CHECK_CMD)
72101

73-
$(OUT)/tests/%.elf: tests/%.c $(OUT)/$(STAGE0)
74-
$(VECHO) " SHECC\t$@\n"
75-
$(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $< > $(basename $@).log ; \
76-
chmod +x $@ ; $(PRINTF) "Running $@ ...\n"
77-
$(Q)$(TARGET_EXEC) $@ && $(call pass)
78-
79-
check: check-stage0 check-stage2
80-
81-
check-stage0: $(OUT)/$(STAGE0) $(TESTBINS) tests/driver.sh
82-
$(VECHO) " TEST STAGE 0\n"
83-
tests/driver.sh 0
84-
85-
check-stage2: $(OUT)/$(STAGE2) $(TESTBINS) tests/driver.sh
86-
$(VECHO) " TEST STAGE 2\n"
87-
tests/driver.sh 2
88-
89-
check-sanitizer: $(OUT)/$(STAGE0)-sanitizer tests/driver.sh
90-
$(VECHO) " TEST STAGE 0 (with sanitizers)\n"
91-
$(Q)cp $(OUT)/$(STAGE0)-sanitizer $(OUT)/shecc
92-
tests/driver.sh 0
93-
$(Q)rm $(OUT)/shecc
94-
95-
check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh
96-
$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) --silent;)
97-
$(VECHO) "Switching backend back to %s\n" $(ARCH)
98-
$(Q)$(MAKE) distclean config ARCH=$(ARCH) --silent
99-
100-
check-snapshot: $(OUT)/$(STAGE0) tests/check-snapshots.sh
101-
$(VECHO) "Checking snapshot for %s\n" $(ARCH)
102-
tests/check-snapshots.sh $(ARCH)
103-
$(VECHO) " OK\n"
104-
105-
update-snapshots: tests/update-snapshots.sh
106-
$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) --silent;)
107-
$(VECHO) "Switching backend back to %s\n" $(ARCH)
108-
$(Q)$(MAKE) distclean config ARCH=$(ARCH) --silent
102+
$(BUILD_SESSION):
103+
@$(PRINTF) "ARCH=$(ARCH)\n" > $@
109104

110-
update-snapshot: $(OUT)/$(STAGE0) tests/update-snapshots.sh
111-
$(VECHO) "Updating snapshot for %s\n" $(ARCH)
112-
tests/update-snapshots.sh $(ARCH)
113-
$(VECHO) " OK\n"
105+
# Sanitizer build
106+
sanitizer: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -O0
107+
sanitizer: LDFLAGS += -fsanitize=address -fsanitize=undefined
108+
sanitizer: config $(OUT)/$(STAGE0)-sanitizer
109+
$(VECHO) "Built stage 0 compiler with sanitizers\n"
114110

115-
$(OUT)/%.o: %.c
111+
# Object file compilation
112+
$(OUT)/%.o: %.c | $(OUT)
116113
$(VECHO) " CC\t$@\n"
114+
$(Q)mkdir -p $(dir $@)
117115
$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $<
118116

119-
SHELL_HACK := $(shell mkdir -p $(OUT) $(OUT)/$(SRCDIR) $(OUT)/tests)
117+
# Tool binaries
118+
$(OUT)/norm-lf: tools/norm-lf.c | $(OUT)
119+
$(VECHO) " CC+LD\t$@\n"
120+
$(Q)$(CC) $(CFLAGS) -o $@ $^
120121

121-
$(OUT)/norm-lf: tools/norm-lf.c
122+
$(OUT)/inliner: tools/inliner.c | $(OUT)
122123
$(VECHO) " CC+LD\t$@\n"
123124
$(Q)$(CC) $(CFLAGS) -o $@ $^
124125

125-
$(OUT)/libc.inc: $(OUT)/inliner $(OUT)/norm-lf $(LIBDIR)/c.c
126+
# Generate libc include
127+
$(OUT)/libc.inc: $(OUT)/inliner $(OUT)/norm-lf $(LIBDIR)/c.c | $(OUT)
126128
$(VECHO) " GEN\t$@\n"
127129
$(Q)$(OUT)/norm-lf $(LIBDIR)/c.c $(OUT)/c.normalized.c
128130
$(Q)$(OUT)/inliner $(OUT)/c.normalized.c $@
129131
$(Q)$(RM) $(OUT)/c.normalized.c
130132

131-
$(OUT)/inliner: tools/inliner.c
132-
$(VECHO) " CC+LD\t$@\n"
133-
$(Q)$(CC) $(CFLAGS) -o $@ $^
134-
133+
# Stage 0: Host compiler build
135134
$(OUT)/$(STAGE0): $(OUT)/libc.inc $(OBJS)
136135
$(VECHO) " LD\t$@\n"
137136
$(Q)$(CC) $(OBJS) $(LDFLAGS) -o $@
@@ -140,35 +139,94 @@ $(OUT)/$(STAGE0)-sanitizer: $(OUT)/libc.inc $(OBJS)
140139
$(VECHO) " LD\t$@ (with sanitizers)\n"
141140
$(Q)$(CC) $(OBJS) $(LDFLAGS) -o $@
142141

142+
# Stage 1: Self-compiled compiler
143143
$(OUT)/$(STAGE1): $(OUT)/$(STAGE0)
144144
$(Q)$(STAGE1_CHECK_CMD)
145145
$(VECHO) " SHECC\t$@\n"
146146
$(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $(SRCDIR)/main.c > $(OUT)/shecc-stage1.log
147147
$(Q)chmod a+x $@
148148

149+
# Stage 2: Verify bootstrap
149150
$(OUT)/$(STAGE2): $(OUT)/$(STAGE1)
150151
$(VECHO) " SHECC\t$@\n"
151152
$(Q)$(TARGET_EXEC) $(OUT)/$(STAGE1) -o $@ $(SRCDIR)/main.c
153+
$(Q)chmod a+x $@
152154

155+
# Bootstrap verification
153156
bootstrap: $(OUT)/$(STAGE2)
154-
$(Q)chmod 775 $(OUT)/$(STAGE2)
155-
$(Q)if ! diff -q $(OUT)/$(STAGE1) $(OUT)/$(STAGE2); then \
156-
echo "Unable to bootstrap. Aborting"; false; \
157+
$(Q)if cmp -s $(OUT)/$(STAGE1) $(OUT)/$(STAGE2); then \
158+
echo "Bootstrap successful: Stage 1 and Stage 2 are identical"; \
159+
else \
160+
echo "ERROR: Bootstrap failed - Stage 1 and Stage 2 differ"; \
161+
exit 1; \
157162
fi
158163

159-
$(BUILD_SESSION):
160-
$(PRINTF) "ARCH=$(ARCH)" > $@
164+
# Test compilation
165+
$(OUT)/tests/%.elf: tests/%.c $(OUT)/$(STAGE0)
166+
$(VECHO) " SHECC\t$@\n"
167+
$(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $< > $(basename $@).log
168+
$(Q)chmod +x $@
169+
$(Q)$(PRINTF) "Running $@ ...\n"
170+
$(Q)$(TARGET_EXEC) $@ && $(call pass)
171+
172+
# Test targets
173+
check: check-stage0 check-stage2
161174

162-
.PHONY: clean
175+
check-stage0: $(OUT)/$(STAGE0) $(TESTBINS) tests/driver.sh
176+
$(VECHO) "Testing Stage 0 compiler\n"
177+
@tests/driver.sh 0
178+
179+
check-stage2: $(OUT)/$(STAGE2) $(TESTBINS) tests/driver.sh
180+
$(VECHO) "Testing Stage 2 compiler\n"
181+
@tests/driver.sh 2
182+
183+
check-sanitizer: $(OUT)/$(STAGE0)-sanitizer tests/driver.sh
184+
$(VECHO) "Testing Stage 0 with sanitizers\n"
185+
$(Q)cp $(OUT)/$(STAGE0)-sanitizer $(OUT)/shecc
186+
@tests/driver.sh 0
187+
$(Q)rm $(OUT)/shecc
188+
189+
# Snapshot testing
190+
check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh
191+
$(Q)$(foreach SNAPSHOT_ARCH,$(ARCHS),$(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) --no-print-directory;)
192+
$(VECHO) "Switching backend back to %s\n" $(ARCH)
193+
$(Q)$(MAKE) distclean config ARCH=$(ARCH) --no-print-directory
194+
195+
check-snapshot: $(OUT)/$(STAGE0) tests/check-snapshots.sh
196+
$(VECHO) "Checking snapshot for %s\n" $(ARCH)
197+
@tests/check-snapshots.sh $(ARCH)
198+
$(VECHO) " OK\n"
199+
200+
update-snapshots: tests/update-snapshots.sh
201+
$(Q)$(foreach SNAPSHOT_ARCH,$(ARCHS),$(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) --no-print-directory;)
202+
$(VECHO) "Switching backend back to %s\n" $(ARCH)
203+
$(Q)$(MAKE) distclean config ARCH=$(ARCH) --no-print-directory
204+
205+
update-snapshot: $(OUT)/$(STAGE0) tests/update-snapshots.sh
206+
$(VECHO) "Updating snapshot for %s\n" $(ARCH)
207+
@tests/update-snapshots.sh $(ARCH)
208+
$(VECHO) " OK\n"
209+
210+
# Directory creation
211+
$(OUT):
212+
@mkdir -p $(OUT) $(OUT)/$(SRCDIR) $(OUT)/tests
213+
214+
# Clean targets
163215
clean:
164-
-$(RM) $(OUT)/$(STAGE0) $(OUT)/$(STAGE1) $(OUT)/$(STAGE2)
165-
-$(RM) $(OBJS) $(deps)
166-
-$(RM) $(TESTBINS) $(OUT)/tests/*.log $(OUT)/tests/*.lst
167-
-$(RM) $(OUT)/shecc*.log
168-
-$(RM) $(OUT)/libc.inc
216+
$(VECHO) "Cleaning build artifacts\n"
217+
-$(Q)$(RM) -f $(OUT)/$(STAGE0) $(OUT)/$(STAGE1) $(OUT)/$(STAGE2)
218+
-$(Q)$(RM) -f $(OUT)/$(STAGE0)-sanitizer
219+
-$(Q)$(RM) -f $(OBJS) $(DEPS)
220+
-$(Q)$(RM) -f $(TESTBINS) $(OUT)/tests/*.log $(OUT)/tests/*.lst
221+
-$(Q)$(RM) -f $(OUT)/shecc*.log
222+
-$(Q)$(RM) -f $(OUT)/libc.inc $(OUT)/c.normalized.c
169223

170224
distclean: clean
171-
-$(RM) $(OUT)/inliner $(OUT)/norm-lf $(OUT)/target $(SRCDIR)/codegen.c config $(BUILD_SESSION)
172-
-$(RM) DOM.dot CFG.dot
173-
174-
-include $(deps)
225+
$(VECHO) "Removing all generated files\n"
226+
-$(Q)$(RM) -f $(OUT)/inliner $(OUT)/norm-lf $(OUT)/target
227+
-$(Q)$(RM) -f $(SRCDIR)/codegen.c config $(BUILD_SESSION)
228+
-$(Q)$(RM) -f DOM.dot CFG.dot
229+
-$(Q)$(RM) -rf $(OUT)
230+
231+
# Include dependencies
232+
-include $(DEPS)

0 commit comments

Comments
 (0)