> diff --git a/tools/testing/selftests/bpf/Makefile 
> b/tools/testing/selftests/bpf/Makefile
> index 5c4cdf1dafc8..a305d13409e4 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile

[ ... ]

> @@ -598,47 +602,77 @@ $(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.bpf.o:
>
>  $(TRUNNER_BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
> -     $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@)
> -     $(Q)$$(BPFTOOL) gen object $$(<:.o=.linked1.o) $$<
> -     $(Q)$$(BPFTOOL) gen object $$(<:.o=.linked2.o) $$(<:.o=.linked1.o)
> -     $(Q)$$(BPFTOOL) gen object $$(<:.o=.linked3.o) $$(<:.o=.linked2.o)
> -     $(Q)diff $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)
> -     $(Q)$$(BPFTOOL) gen skeleton $$(<:.o=.linked3.o) name $$(notdir 
> $$(<:.bpf.o=)) > $$@
> -     $(Q)$$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir 
> $$(<:.bpf.o=)) > $$(@:.skel.h=.subskel.h)
> -     $(Q)rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)
> +     $(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then                    \
> +             printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
> +             exit 0;                                                 \
> +     fi;)                                                            \

When the .bpf.o prerequisite is missing during an incremental
permissive build, this exits 0 without removing $@ (.skel.h).
If a stale .skel.h remains from a previous successful build,
downstream targets will silently compile against it.

The commit message says "remove failed outputs so downstream rules
can detect absence" -- would adding $(RM) $$@ before exit 0 match
that intent?

> +     printf '  %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 
> 1>&2; \
> +     $$(BPFTOOL) gen object $$(<:.o=.linked1.o) $$< &&               \
> +     $$(BPFTOOL) gen object $$(<:.o=.linked2.o) $$(<:.o=.linked1.o) && \
> +     $$(BPFTOOL) gen object $$(<:.o=.linked3.o) $$(<:.o=.linked2.o) && \
> +     diff $$(<:.o=.linked2.o) $$(<:.o=.linked3.o) &&         \
> +     $$(BPFTOOL) gen skeleton $$(<:.o=.linked3.o) name $$(notdir 
> $$(<:.bpf.o=)) > $$@ && \
> +     $$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir 
> $$(<:.bpf.o=)) > $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \
> +             $$(RM) $$@ $$(@:.skel.h=.subskel.h); \
> +             printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
> +     }); \
> +     rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)

When PERMISSIVE is not set (strict mode, the default), the $(if)
block expands to nothing and this recipe becomes a single shell
command ending with:

    ... > $$(@:.skel.h=.subskel.h) ; \
    rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)

If bpftool or diff fails anywhere in the && chain, the shell skips
to the semicolon and runs rm -f, which always returns 0.  Make then
sees exit code 0 from the recipe and continues the build as though
skeleton generation succeeded.

The original code used separate $(Q)-prefixed recipe lines, where
make saw each command's exit code individually and would abort on
failure.

The same pattern appears in the TRUNNER_BPF_LSKELS,
TRUNNER_BPF_LSKELS_SIGNED, and TRUNNER_BPF_SKELS_LINKED rules.

One possible fix would be to capture the exit status before the
cleanup, for example:

    ... > subskel.h ; rc=$$?; \
    rm -f linked1.o linked2.o linked3.o; \
    exit $$rc

https://lore.kernel.org/bpf/[email protected]/


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/24478691588

Reply via email to