[PATCH v2 0/6] Kbuild: Fix cleanup of generated sources in tools (and more)

This patch series fixes various unexpected leftovers after running make clean and make mrproper.
The test suggested during review of v1 [0] revealed more cleanup related issues. I thought it's best to fix them all together in a somewhat extended v2, to let the new test pass without cheating.
Changes in v2: - add test for 'make clean' and 'make mrproper' - fix found issues until test passes - move .gitignore changes to separate commit
[0] http://patchwork.ozlabs.org/project/uboot/patch/20230614113954.51812-2-tobia...
Tobias Deiminger (6): Kbuild: Fix cleanup of generated sources in tools Adjust gitignore for tools/generated/ Kbuild: Fix cleanup of VPL Kbuild: Fix cleanup of *.dtb for some archs Kbuild: Fix cleanup of *.dtbo for sandbox test: Find leftovers after clean/mrproper
Makefile | 4 +- arch/sandbox/dts/Makefile | 2 +- dts/Makefile | 4 +- test/py/tests/test_cleanup_build.py | 105 ++++++++++++++++++++++++++++ tools/.gitignore | 1 + tools/Makefile | 64 ++++++++--------- tools/env/.gitignore | 1 - 7 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 test/py/tests/test_cleanup_build.py
base-commit: 50842b217fef505a0ec6662cc2acdc55d0bb23c5

On 'make clean', generated C files in tools/env/ and tools/boot/ are currently not removed, but they should.
Auto-generation for shared sources was first introduced with ad80c4a3220b ("kbuild, tools: generate wrapper C sources automatically by Makefile"). Cleanup later regressed (see Fixes:), because shared files were moved out of lib/ and common/, but 'clean-dirs := lib common' was not adjusted accordingly. Further, the generated tools/env/embedded.c became a sibling to project files, which prevents directory-wise cleanup at all.
To solve it, we establishe tools/generated/ as the sole place for generated sources. Wrappers are now generated as tools/generated/<orig_dirname>/<orig_filename>, and 'make clean' can remove tools/generated/ as a whole (Linux Makefile.asm-generic headers are cleaned similarly). This way we don't have to maintain separate clean-files or clean-dirs entries for each single added or moved wrapper file.
Fixes: 0649cd0d4908 ("Move environment files from common/ to env/") Fixes: 19a91f2464a8 ("Create a new boot/ directory") Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- tools/Makefile | 64 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d793cf3bec..d6835b24b0 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -48,20 +48,20 @@ hostprogs-$(CONFIG_VIDEO_LOGO) += bmp_logo HOSTCFLAGS_bmp_logo.o := -pedantic
hostprogs-$(BUILD_ENVCRC) += envcrc -envcrc-objs := envcrc.o lib/crc32.o env/embedded.o lib/sha1.o +envcrc-objs := envcrc.o generated/lib/crc32.o generated/env/embedded.o generated/lib/sha1.o
hostprogs-$(CONFIG_CMD_NET) += gen_eth_addr HOSTCFLAGS_gen_eth_addr.o := -pedantic
hostprogs-$(CONFIG_CMD_NET) += gen_ethaddr_crc -gen_ethaddr_crc-objs := gen_ethaddr_crc.o lib/crc8.o +gen_ethaddr_crc-objs := gen_ethaddr_crc.o generated/lib/crc8.o HOSTCFLAGS_gen_ethaddr_crc.o := -pedantic
hostprogs-$(CONFIG_CMD_LOADS) += img2srec HOSTCFLAGS_img2srec.o := -pedantic
hostprogs-y += mkenvimage -mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o +mkenvimage-objs := mkenvimage.o os_support.o generated/lib/crc32.o
hostprogs-y += dumpimage mkimage hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign @@ -71,30 +71,30 @@ ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_FWU_MDATA_GPT_BLK),) hostprogs-y += file2include endif
-FIT_OBJS-y := fit_common.o fit_image.o image-host.o boot/image-fit.o -FIT_SIG_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := image-sig-host.o boot/image-fit-sig.o -FIT_CIPHER_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := boot/image-cipher.o +FIT_OBJS-y := fit_common.o fit_image.o image-host.o generated/boot/image-fit.o +FIT_SIG_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := image-sig-host.o generated/boot/image-fit-sig.o +FIT_CIPHER_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := generated/boot/image-cipher.o
# The following files are synced with upstream DTC. # Use synced versions from scripts/dtc/libfdt/. LIBFDT_OBJS := $(addprefix libfdt/, fdt.o fdt_ro.o fdt_wip.o fdt_sw.o fdt_rw.o \ fdt_strerror.o fdt_empty_tree.o fdt_addresses.o fdt_overlay.o)
-RSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/rsa/, \ +RSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix generated/lib/rsa/, \ rsa-sign.o rsa-verify.o \ rsa-mod-exp.o)
-ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.o) +ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix generated/lib/ecdsa/, ecdsa-libcrypto.o)
-AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \ +AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix generated/lib/aes/, \ aes-encrypt.o aes-decrypt.o)
# Cryptographic helpers and image types that depend on openssl/libcrypto LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \ - lib/fdt-libcrypto.o \ + generated/lib/fdt-libcrypto.o \ sunxi_toc0.o
-ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o +ROCKCHIP_OBS = generated/lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
# common objs for dumpimage and mkimage dumpimage-mkimage-objs := aisimage.o \ @@ -102,20 +102,20 @@ dumpimage-mkimage-objs := aisimage.o \ $(FIT_OBJS-y) \ $(FIT_SIG_OBJS-y) \ $(FIT_CIPHER_OBJS-y) \ - boot/fdt_region.o \ - boot/bootm.o \ - lib/crc32.o \ + generated/boot/fdt_region.o \ + generated/boot/bootm.o \ + generated/lib/crc32.o \ default_image.o \ - lib/fdtdec_common.o \ - lib/fdtdec.o \ - boot/image.o \ - boot/image-host.o \ + generated/lib/fdtdec_common.o \ + generated/lib/fdtdec.o \ + generated/boot/image.o \ + generated/boot/image-host.o \ imagetool.o \ imximage.o \ imx8image.o \ imx8mimage.o \ kwbimage.o \ - lib/md5.o \ + generated/lib/md5.o \ lpc32xximage.o \ mxsimage.o \ omapimage.o \ @@ -128,12 +128,12 @@ dumpimage-mkimage-objs := aisimage.o \ $(ROCKCHIP_OBS) \ socfpgaimage.o \ sunxi_egon.o \ - lib/crc16-ccitt.o \ - lib/hash-checksum.o \ - lib/sha1.o \ - lib/sha256.o \ - lib/sha512.o \ - common/hash.o \ + generated/lib/crc16-ccitt.o \ + generated/lib/hash-checksum.o \ + generated/lib/sha1.o \ + generated/lib/sha256.o \ + generated/lib/sha512.o \ + generated/common/hash.o \ ublimage.o \ zynqimage.o \ zynqmpimage.o \ @@ -213,7 +213,7 @@ HOSTCFLAGS_mxsboot.o := -pedantic
hostprogs-$(CONFIG_ARCH_SUNXI) += mksunxiboot hostprogs-$(CONFIG_ARCH_SUNXI) += sunxi-spl-image-builder -sunxi-spl-image-builder-objs := sunxi-spl-image-builder.o lib/bch.o +sunxi-spl-image-builder-objs := sunxi-spl-image-builder.o generated/lib/bch.o
hostprogs-$(CONFIG_NETCONSOLE) += ncb
@@ -221,16 +221,16 @@ hostprogs-$(CONFIG_ARCH_KIRKWOOD) += kwboot hostprogs-$(CONFIG_ARCH_MVEBU) += kwboot
hostprogs-y += proftool -proftool-objs = proftool.o lib/abuf.o +proftool-objs = proftool.o generated/lib/abuf.o
hostprogs-$(CONFIG_STATIC_RELA) += relocate-rela hostprogs-$(CONFIG_RISCV) += prelink-riscv
hostprogs-$(CONFIG_ARCH_OCTEON) += update_octeon_header -update_octeon_header-objs := update_octeon_header.o lib/crc32.o +update_octeon_header-objs := update_octeon_header.o generated/lib/crc32.o
hostprogs-y += fdtgrep -fdtgrep-objs += $(LIBFDT_OBJS) boot/fdt_region.o fdtgrep.o +fdtgrep-objs += $(LIBFDT_OBJS) generated/boot/fdt_region.o fdtgrep.o
ifneq ($(TOOLS_ONLY),y) hostprogs-y += spl_size_limit @@ -262,12 +262,12 @@ HOSTCFLAGS_sha256.o := -pedantic HOSTCFLAGS_sha512.o := -pedantic -DCONFIG_SHA512 -DCONFIG_SHA384
quiet_cmd_wrap = WRAP $@ -cmd_wrap = echo "#include <../$(patsubst $(obj)/%,%,$@)>" >$@ +cmd_wrap = echo "#include <../$(patsubst $(obj)/generated/%,%,$@)>" >$@
-$(obj)/boot/%.c $(obj)/common/%.c $(obj)/env/%.c $(obj)/lib/%.c: +$(obj)/generated/%.c: $(call cmd,wrap)
-clean-dirs := lib common +clean-dirs := generated
always := $(hostprogs-y)

On Tue, Jun 20, 2023 at 12:41:02AM +0200, Tobias Deiminger wrote:
On 'make clean', generated C files in tools/env/ and tools/boot/ are currently not removed, but they should.
Auto-generation for shared sources was first introduced with ad80c4a3220b ("kbuild, tools: generate wrapper C sources automatically by Makefile"). Cleanup later regressed (see Fixes:), because shared files were moved out of lib/ and common/, but 'clean-dirs := lib common' was not adjusted accordingly. Further, the generated tools/env/embedded.c became a sibling to project files, which prevents directory-wise cleanup at all.
To solve it, we establishe tools/generated/ as the sole place for generated sources. Wrappers are now generated as tools/generated/<orig_dirname>/<orig_filename>, and 'make clean' can remove tools/generated/ as a whole (Linux Makefile.asm-generic headers are cleaned similarly). This way we don't have to maintain separate clean-files or clean-dirs entries for each single added or moved wrapper file.
Fixes: 0649cd0d4908 ("Move environment files from common/ to env/") Fixes: 19a91f2464a8 ("Create a new boot/ directory") Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!

On 20/06/2023 00.41, Tobias Deiminger wrote:
quiet_cmd_wrap = WRAP $@ -cmd_wrap = echo "#include <../$(patsubst $(obj)/%,%,$@)>" >$@ +cmd_wrap = echo "#include <../$(patsubst $(obj)/generated/%,%,$@)>" >$@
-$(obj)/boot/%.c $(obj)/common/%.c $(obj)/env/%.c $(obj)/lib/%.c: +$(obj)/generated/%.c: $(call cmd,wrap)
FWIW, this change incidentally also fixes a warning (and future error!) from GNU Make. Building v2023.04 with GNU Make 4.4 produces
WRAP tools/env/embedded.c tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/common/crc32.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/boot/crc32.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/lib/embedded.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/common/embedded.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/boot/embedded.c'. WRAP tools/lib/sha1.c HOSTCC tools/img2srec tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/env/sha1.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/common/sha1.c'. tools/Makefile:264: warning: pattern recipe did not update peer target 'tools/boot/sha1.c'.
etc. See the first "WARNING: Future backward-incompatibility!" bullet in https://lists.gnu.org/archive/html/help-make/2022-10/msg00020.html .
Rasmus

Tell git that auto-generated C sources are now exclusively expected under tools/generated/.
Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- tools/.gitignore | 1 + tools/env/.gitignore | 1 - 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/.gitignore b/tools/.gitignore index cda3ea628c..941d38de21 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -34,6 +34,7 @@ /relocate-rela /spl_size_limit /sunxi-spl-image-builder +/tools/generated/**/*.c /update_octeon_header /version.h /xway-swap-bytes diff --git a/tools/env/.gitignore b/tools/env/.gitignore index 8d28b2b70b..804abacc6e 100644 --- a/tools/env/.gitignore +++ b/tools/env/.gitignore @@ -1,3 +1,2 @@ -embedded.c fw_printenv fw_printenv_unstripped

On Tue, Jun 20, 2023 at 12:41:03AM +0200, Tobias Deiminger wrote:
Tell git that auto-generated C sources are now exclusively expected under tools/generated/.
Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!

VPL artifacts like example vpl/u-boot-vpl are currently not removed by 'make clean'.
We can clean them just as it's already done for SPL and TPL.
Fixes: f86ca5ad8f78 ("Introduce Verifying Program Loader (VPL)") Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index 444baaefd0..febe9c31d9 100644 --- a/Makefile +++ b/Makefile @@ -2148,7 +2148,7 @@ CHANGELOG:
# Directories & files removed with 'make clean' CLEAN_DIRS += $(MODVERDIR) \ - $(foreach d, spl tpl, $(patsubst %,$d/%, \ + $(foreach d, spl tpl vpl, $(patsubst %,$d/%, \ $(filter-out include, $(shell ls -1 $d 2>/dev/null))))
CLEAN_FILES += include/bmp_logo.h include/bmp_logo_data.h \ @@ -2163,7 +2163,7 @@ CLEAN_FILES += include/bmp_logo.h include/bmp_logo_data.h \ idbloader-spi.img lib/efi_loader/helloworld_efi.S
# Directories & files removed with 'make mrproper' -MRPROPER_DIRS += include/config include/generated spl tpl \ +MRPROPER_DIRS += include/config include/generated spl tpl vpl \ .tmp_objdiff doc/output include/asm
# Remove include/asm symlink created by U-Boot before v2014.01

On Tue, Jun 20, 2023 at 12:41:04AM +0200, Tobias Deiminger wrote:
VPL artifacts like example vpl/u-boot-vpl are currently not removed by 'make clean'.
We can clean them just as it's already done for SPL and TPL.
Fixes: f86ca5ad8f78 ("Introduce Verifying Program Loader (VPL)") Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!

'make clean' did not descend into arch/$ARCH/dts for arc, m68k, nios2, sh, xtensa.
Fix it by adding the missing archs to the explicit clean-dirs list.
Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- dts/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dts/Makefile b/dts/Makefile index cb31113829..3437e54033 100644 --- a/dts/Makefile +++ b/dts/Makefile @@ -63,4 +63,6 @@ spl_dtbs: $(obj)/dt-$(SPL_NAME).dtb clean-files := dt.dtb.S
# Let clean descend into dts directories -subdir- += ../arch/arm/dts ../arch/microblaze/dts ../arch/mips/dts ../arch/sandbox/dts ../arch/x86/dts ../arch/powerpc/dts ../arch/riscv/dts +subdir- += ../arch/arc/dts ../arch/arm/dts ../arch/m68k/dts ../arch/microblaze/dts \ + ../arch/mips/dts ../arch/nios2/dts ../arch/powerpc/dts ../arch/riscv/dts \ + ../arch/sandbox/dts ../arch/sh/dts ../arch/x86/dts ../arch/xtensa/dts

On Tue, Jun 20, 2023 at 12:41:05AM +0200, Tobias Deiminger wrote:
'make clean' did not descend into arch/$ARCH/dts for arc, m68k, nios2, sh, xtensa.
Fix it by adding the missing archs to the explicit clean-dirs list.
Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!

sandbox can generate DT overlays, but they were not cleaned.
Extend the explicit clean-files list accordingly.
Fixes: 95300f203f32 ("pytest: add sandbox test for "extension" command") Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- arch/sandbox/dts/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/Makefile b/arch/sandbox/dts/Makefile index b6a88479b2..f810b4752f 100644 --- a/arch/sandbox/dts/Makefile +++ b/arch/sandbox/dts/Makefile @@ -18,4 +18,4 @@ PHONY += dtbs dtbs: $(addprefix $(obj)/, $(dtb-y)) @:
-clean-files := *.dtb +clean-files := *.dtb *.dtbo

On Tue, Jun 20, 2023 at 12:41:06AM +0200, Tobias Deiminger wrote:
sandbox can generate DT overlays, but they were not cleaned.
Extend the explicit clean-files list accordingly.
Fixes: 95300f203f32 ("pytest: add sandbox test for "extension" command") Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!

Docs describe 'make clean' to delete most generated files, 'make mrproper' to delete current configuration and all generated files. This test tries to assert it.
Idea is to search remaining files by patterns in copies of the initial out-of-source build, which has two advantages: - looking in an out-of-source build dir allows to tell generated source code from committed source code - copying is fast (compared to rebuilding each time) which allows to do a "world clean"
Signed-off-by: Tobias Deiminger tdmg@linutronix.de --- test/py/tests/test_cleanup_build.py | 105 ++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 test/py/tests/test_cleanup_build.py
diff --git a/test/py/tests/test_cleanup_build.py b/test/py/tests/test_cleanup_build.py new file mode 100644 index 0000000000..5206ff73ec --- /dev/null +++ b/test/py/tests/test_cleanup_build.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2023 Tobias Deiminger tdmg@linutronix.de + +"""Test for unexpected leftovers after make clean""" + +import itertools +import os +import pathlib +import shutil +import sys + +import pytest + +# pylint: disable=redefined-outer-name + + +@pytest.fixture +def tmp_copy_of_builddir(u_boot_config, tmp_path): + """For each test, provide a temporary copy of the initial build directory.""" + shutil.copytree( + u_boot_config.build_dir, + tmp_path, + symlinks=True, + dirs_exist_ok=True, + ) + return tmp_path + + +@pytest.fixture(scope="module") +def run_make(u_boot_log): + """Provide function to run and log make without connecting to u-boot console.""" + runner = u_boot_log.get_runner("make", sys.stdout) + + def _run_make(build_dir, target): + cmd = ["make", f"O={build_dir}", target] + runner.run(cmd) + + yield _run_make + runner.close() + + +@pytest.fixture(scope="module") +def most_generated_files(): + """Path.glob style patterns to describe what should be removed by 'make clean'.""" + return ( + "**/*.c", + "**/*.dtb", + "**/*.dtbo", + "**/*.o", + "**/*.py", + "**/*.pyc", + "**/*.so", + "**/*.srec", + "u-boot*", + "[svt]pl/u-boot*", + ) + + +@pytest.fixture(scope="module") +def all_generated_files(most_generated_files): + """Path.glob style patterns to describe what should be removed by 'make mrproper'.""" + return most_generated_files + (".config", "**/*.h") + + +def find_files(search_dir, include_patterns, exclude_dirs=None): + """Find files matching include_patterns, unless it's in one of exclude_dirs. + + include_patterns -- Path.glob style pattern relative to search dir + exclude_dir -- directories to exclude, expected relative to search dir + """ + matches = [] + exclude_dirs = [] if exclude_dirs is None else exclude_dirs + for abs_path in itertools.chain.from_iterable( + pathlib.Path(search_dir).glob(pattern) for pattern in include_patterns + ): + if abs_path.is_dir(): + continue + rel_path = pathlib.Path(os.path.relpath(abs_path, search_dir)) + if not any( + rel_path.is_relative_to(exclude_dir) for exclude_dir in exclude_dirs + ): + matches.append(rel_path) + return matches + + +def test_clean(run_make, tmp_copy_of_builddir, most_generated_files): + """Test if 'make clean' deletes most generated files.""" + run_make(tmp_copy_of_builddir, "clean") + leftovers = find_files( + tmp_copy_of_builddir, + most_generated_files, + exclude_dirs=["scripts", "test/overlay"], + ) + assert not leftovers, f"leftovers: {', '.join(map(str, leftovers))}" + + +def test_mrproper(run_make, tmp_copy_of_builddir, all_generated_files): + """Test if 'make mrproper' deletes current configuration and all generated files.""" + run_make(tmp_copy_of_builddir, "mrproper") + leftovers = find_files( + tmp_copy_of_builddir, + all_generated_files, + exclude_dirs=["test/overlay"], + ) + assert not leftovers, f"leftovers: {', '.join(map(str, leftovers))}"

On Tue, Jun 20, 2023 at 12:41:07AM +0200, Tobias Deiminger wrote:
Docs describe 'make clean' to delete most generated files, 'make mrproper' to delete current configuration and all generated files. This test tries to assert it.
Idea is to search remaining files by patterns in copies of the initial out-of-source build, which has two advantages:
- looking in an out-of-source build dir allows to tell generated source code from committed source code
- copying is fast (compared to rebuilding each time) which allows to do a "world clean"
Signed-off-by: Tobias Deiminger tdmg@linutronix.de
Applied to u-boot/next, thanks!
participants (3)
-
Rasmus Villemoes
-
Tobias Deiminger
-
Tom Rini