From 5ffeb3d16aa5fd5b535083286afbe875a7fbadc5 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:46:03 +0200 Subject: [PATCH 1/3] build: make the static-file build repeatable Two things kept the same source from producing the same image. fileadder walked the input directory with readdir(), which returns entries in whatever order the filesystem hands them over. The same tree therefore embedded the files in a different order on a different filesystem, and the image differed byte for byte. scandir() with alphasort() fixes the order. version.h was listed in .PHONY, which makes it permanently out of date and propagates that to everything including it, so a plain build recompiled the four largest translation units even when nothing had changed. Dropping it from .PHONY alone would go too far the other way: with no prerequisites the recipe would never run again and the version string would freeze at its first value. A FORCE prerequisite keeps the recipe running every time, and the generated header is only moved into place when its content actually differs, so the timestamp survives an unchanged rebuild. Measured on this tree: with FORCE the version follows GIT_VERSION and an unchanged rebuild leaves version.h untouched; without it the header keeps the value from the first build. --- Makefile | 9 ++++++--- tools/fileadder.c | 12 ++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 67fd42d7..9a4a1236 100644 --- a/Makefile +++ b/Makefile @@ -112,11 +112,14 @@ html_min: $(HTML) html_data.c html_data.h &: $(HTML) | tools html_min tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data -$(VERSION_HEADER): +$(VERSION_HEADER): FORCE @printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \ "#define VERSION_SW \"$(VERSION_EXTENSION)\"" \ "#define BUILD_DATE \"$(BUILD_DATE)\"" \ - "#endif" > $(VERSION_HEADER) + "#endif" > $(VERSION_HEADER).tmp + @cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \ + && rm -f $(VERSION_HEADER).tmp \ + || mv $(VERSION_HEADER).tmp $(VERSION_HEADER) httpd: html_data.h @@ -166,7 +169,7 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i tools/output/crc_calculator -u $@ ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin -.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) $(VERSION_HEADER) create_build_dir +.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir .PHONY: machine_check: diff --git a/tools/fileadder.c b/tools/fileadder.c index 1da276ec..eec14d23 100644 --- a/tools/fileadder.c +++ b/tools/fileadder.c @@ -338,15 +338,16 @@ int main(int argc, char **argv) if (stat(arguments.data_file, &s) == 0 && s.st_mode & S_IFDIR) { printf("Adding entries in directory %s\n", arguments.data_file); addsDir = true; - DIR *dirptr = opendir(arguments.data_file); - if (!dirptr) { + struct dirent **entries; + int n_entries = scandir(arguments.data_file, &entries, NULL, alphasort); + if (n_entries < 0) { fprintf(stderr, "%s: ", arguments.data_file); perror("Error opening directory"); return 5; } - struct dirent *in_file; int addr = arguments.address; - while ( (in_file = readdir(dirptr)) ) { + for (int e = 0; e < n_entries; e++) { + struct dirent *in_file = entries[e]; snprintf(pathbuffer, PATH_SIZE, "%s/%s", arguments.data_file, in_file->d_name); if (stat(pathbuffer, &s) < 0) { fprintf(stderr, "%s: ", pathbuffer); @@ -377,6 +378,9 @@ int main(int argc, char **argv) addidx(in_file->d_name, addr, data_read); addr += data_read; } + for (int e = 0; e < n_entries; e++) + free(entries[e]); + free(entries); } else { size_t data_read = addfile(arguments.data_file, arguments.address); From 3a924591901a92e33eda255fb43e7a80bab9d335 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Wed, 16 Sep 2026 01:56:42 +0200 Subject: [PATCH 2/3] build: don't regenerate html_data from the image rule The .bin rule passes -p html_data to fileadder, which rewrites html_data.c and html_data.h after the objects that include them have already been compiled and linked. The next make therefore rebuilds html_data.c, httpd.c and page_impl.c even when nothing changed, and so does the one after that. The rule only needs to embed the files into the image; the header comes from the html_data.c rule above. Spotted by plaes in #358, which cannot be merged as it stands. With this and the previous commit, a second make after a full build compiles nothing, and the image is byte for byte the same as before. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9a4a1236..aa1839c4 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i tools/output/imagebuilder -i $^ $@ tools/output/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@ tools/output/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@ - tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -p html_data -b BANK1 $@ + tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@ tools/output/crc_calculator -u $@ ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin From 068ec4c5106ddacc772076e8824abaa4f88602e6 Mon Sep 17 00:00:00 2001 From: DrDoof <8052722+DrDoof@users.noreply.github.com> Date: Tue, 29 Sep 2026 05:27:16 +0200 Subject: [PATCH 3/3] fileadder: print the generated files only when there is no image to write The image rule no longer passes -p, so fileadder fell through to printing html_data.h and html_data.c on stdout on every image build. The listing is only useful when fileadder is run without an image, to see what it would generate. --- tools/fileadder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/fileadder.c b/tools/fileadder.c index eec14d23..8eeafbb2 100644 --- a/tools/fileadder.c +++ b/tools/fileadder.c @@ -441,7 +441,7 @@ int main(int argc, char **argv) fputs(xbuf, ofile); fputs(fbuf, ofile); fclose(ofile); - } else if (addsDir){ + } else if (addsDir && !argv[arg_index]) { puts(dbuf); puts(xbuf); puts(ibuf);