Skip to content

Commit ae9a736

Browse files
nerdCopterclaude
andauthored
fix(make): parallel build races, forced re-link, and CI observability (#1238)
Squash of fix/parallel-build-deps — five fixes: 1. mkdir-p race: TARGET_DIRS + .SECONDEXPANSION + order-only prerequisites 2. ELF double-link race: binary_hex target builds BIN+HEX in one sub-make 3. FORCE → $(SRC) on version.o: no-op incremental builds (BF parity) 4. binary_hex: single sub-make, ELF linked once, matched artifacts 5. CI: -j$(nproc) --output-sync=recurse --keep-going Validated: 389/389 targets clean, 0 errors. Run 2 (no changes): 1m18s vs 12m48s clean. Resolves #1237 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 595a69d commit ae9a736

2 files changed

Lines changed: 21 additions & 14 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ jobs:
125125
# Build HEX
126126
- name: Compile Code
127127
run: |
128-
make ${{ matrix.targets }}
128+
make ${{ matrix.targets }} -j$(nproc) --output-sync=recurse --keep-going
129129
130130
# Upload the Builds to ZIP file with existing SHA in .hex names
131131
- name: Upload Artifacts

Makefile

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -298,17 +298,21 @@ TARGET_LST = $(OBJECT_DIR)/$(FORKNAME)_$(TARGET).lst
298298
TARGET_OBJS = $(addsuffix .o,$(addprefix $(OBJECT_DIR)/$(TARGET)/,$(basename $(SRC))))
299299
TARGET_DEPS = $(addsuffix .d,$(addprefix $(OBJECT_DIR)/$(TARGET)/,$(basename $(SRC))))
300300
TARGET_MAP = $(OBJECT_DIR)/$(FORKNAME)_$(TARGET).map
301+
TARGET_DIRS := $(sort $(dir $(TARGET_OBJS)))
301302

302303
CLEAN_ARTIFACTS := $(TARGET_BIN)
303304
CLEAN_ARTIFACTS += $(TARGET_HEX)
304305
CLEAN_ARTIFACTS += $(TARGET_ELF) $(TARGET_OBJS) $(TARGET_MAP)
305306
CLEAN_ARTIFACTS += $(TARGET_LST)
306307

307-
# Make sure build date and revision is updated on every incremental build
308-
$(OBJECT_DIR)/$(TARGET)/build/version.o : FORCE
308+
# Rebuild version.o whenever any source changes so the embedded timestamp stays current
309+
$(OBJECT_DIR)/$(TARGET)/build/version.o : $(SRC)
309310

310-
.PHONY: FORCE clean clean_test clean_all all_clean
311-
FORCE:
311+
.PHONY: clean clean_test clean_all all_clean binary_hex
312+
313+
# Build each output directory once, not once-per-file, for parallel-safe compilation
314+
$(TARGET_DIRS):
315+
@mkdir -p $@
312316

313317
# List of buildable ELF files and their object dependencies.
314318
# It would be nice to compute these lists, but that seems to be just beyond make.
@@ -329,15 +333,17 @@ $(TARGET_ELF): $(TARGET_OBJS) $(LD_SCRIPT) $(LD_SCRIPTS)
329333
$(V1) $(CROSS_CC) -o $@ $(filter %.o,$^) $(LD_FLAGS)
330334
$(V1) $(SIZE) $(TARGET_ELF)
331335

336+
# .SECONDEXPANSION allows $$(dir $$@) in prerequisites — expands to the target's
337+
# directory at rule instantiation time, wiring each .o to its pre-created dir.
338+
.SECONDEXPANSION:
339+
332340
# Compile
333341
ifeq ($(DEBUG),GDB)
334-
$(OBJECT_DIR)/$(TARGET)/%.o: %.c
335-
$(V1) mkdir -p $(dir $@)
342+
$(OBJECT_DIR)/$(TARGET)/%.o: %.c | $$(dir $$@)
336343
$(V1) echo "%% (debug) $(notdir $<)" "$(STDOUT)" && \
337344
$(CROSS_CC) -c -o $@ $(CFLAGS) $(CC_DEBUG_OPTIMISATION) $<
338345
else
339-
$(OBJECT_DIR)/$(TARGET)/%.o: %.c
340-
$(V1) mkdir -p $(dir $@)
346+
$(OBJECT_DIR)/$(TARGET)/%.o: %.c | $$(dir $$@)
341347
$(V1) $(if $(findstring $(subst ./src/main/,,$<),$(SPEED_OPTIMISED_SRC)), \
342348
echo "%% (speed optimised) $(notdir $<)" "$(STDOUT)" && \
343349
$(CROSS_CC) -c -o $@ $(CFLAGS) $(CC_SPEED_OPTIMISATION) $<, \
@@ -349,13 +355,11 @@ $(OBJECT_DIR)/$(TARGET)/%.o: %.c
349355
endif
350356

351357
# Assemble
352-
$(OBJECT_DIR)/$(TARGET)/%.o: %.s
353-
$(V1) mkdir -p $(dir $@)
358+
$(OBJECT_DIR)/$(TARGET)/%.o: %.s | $$(dir $$@)
354359
@echo "%% $(notdir $<)" "$(STDOUT)"
355360
$(V1) $(CROSS_CC) -c -o $@ $(ASFLAGS) $<
356361

357-
$(OBJECT_DIR)/$(TARGET)/%.o: %.S
358-
$(V1) mkdir -p $(dir $@)
362+
$(OBJECT_DIR)/$(TARGET)/%.o: %.S | $$(dir $$@)
359363
@echo "%% $(notdir $<)" "$(STDOUT)"
360364
$(V1) $(CROSS_CC) -c -o $@ $(ASFLAGS) $<
361365

@@ -410,7 +414,7 @@ targets-group-rest: $(GROUP_OTHER_TARGETS)
410414

411415
$(VALID_TARGETS):
412416
$(V0) @echo "Building $@" && \
413-
$(MAKE) binary hex TARGET=$@ && \
417+
$(MAKE) binary_hex TARGET=$@ && \
414418
echo "Building $@ succeeded."
415419

416420
$(NOBUILD_TARGETS):
@@ -481,6 +485,9 @@ binary:
481485
hex:
482486
$(V0) $(MAKE) $(TARGET_HEX)
483487

488+
## binary_hex : build both binary and hex in one sub-make (ELF linked once; BIN+HEX via parallel objcopy)
489+
binary_hex: $(TARGET_BIN) $(TARGET_HEX)
490+
484491
unbrick_$(TARGET): $(TARGET_HEX)
485492
$(V0) stty -F $(SERIAL_DEVICE) raw speed 115200 -crtscts cs8 -parenb -cstopb -ixon
486493
$(V0) stm32flash -w $(TARGET_HEX) -v -g 0x0 -b 115200 $(SERIAL_DEVICE)

0 commit comments

Comments
 (0)