Re: [RFC PATCH bpf-next v5 8/8] selftests/bpf: build each test runner instance in its own sub-make

Mykola Lysenko <[email protected]>
Newsgroups org.kernel.vger.bpf
Message-ID <CAMtxOX0PJUEv_05aQ_AQmseREAUMeESCK1NC1Sq0hcDsffo5cw@mail.gmail.com>
On Thu, Aug 6, 2026 at 11:30 AM Eduard Zingerman <[email protected]> wrote:
>
> On Thu, 2026-08-06 at 01:02 -0700, Eduard Zingerman wrote:
>
> ...
>
> > > +# The default flavor's skeletons are also inputs of bench, test_maps,
> > > +# xskxceiver, xdp_* and test_cpp; "default-skels" builds just those
> > > +# without the test_progs test objects and binary.
> > > +.PHONY: default-skels
> > > +default-skels: $(RUNNER_PREREQS)
> > > +   +$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \
> > > +           BPF_CC='$(CLANG)' BPF_CC_MSG=CLNG-BPF BPF_SYS_INCLUDES='$(CLANG_SYS_INCLUDES)'  \
> > > +           BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v3'                \
> > > +           BPF_DEFINES=-DENABLE_ATOMICS_TESTS skels
> > > +
> > > +$(OUTPUT)/test_progs: $(RUNNER_PREREQS) default-skels FORCE
> > > +   +$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \
> > > +           BPF_CC='$(CLANG)' BPF_CC_MSG=CLNG-BPF BPF_SYS_INCLUDES='$(CLANG_SYS_INCLUDES)'  \
> > > +           BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v3'                \
> > > +           BPF_DEFINES=-DENABLE_ATOMICS_TESTS
> >
> > Is there a way to reduce copy-paste between default-skels and
> > test_progs? Why does test_progs depend on default-skels?
>
> Overall, I don't like the default-skels dependency for `bench` and company.
> Previously these depended only on a subset of skel.h/bpf.o files.
> But idk if that could be avoided. One option is to extract Makefile.skel
> with the rules necessary to build bpf object files, and include it from
> Makefile.runner and from the main makefile. Making `bench` and others
> depend on the exact set of skels they want. But I'm not sure I like this
> option either.
>
> Please think a bit about this thing, maybe you can come up with
> something.

I prototyped your Makefile.skel option and I think it is the right
call - it comes out simpler than what it replaces, not more complex.

The observation that convinced me: it is the same move as your
RUNNER_OBJS change, applied to the BPF artifacts. The main Makefile
owns everything shared (now including the default flavor's BPF
objects and skeletons, via the included rule file instantiated with
the default parameters), and a runner instance owns only what is
private to it (the flavored artifacts, the test objects, the binary).
With the main Makefile able to build any individual skeleton:

- bench, xskxceiver, xdp_*, test_cpp and the install list go back to
  depending on exactly the files they consume - the stock per-target
  dependency lines, unchanged;
- default-skels disappears, and with it the delegation rules and the
  test_progs-depends-on-default-skels serialization trick you asked
  about this morning (so the DEFAULT_RUNNER_ARGS deduplication and
  the two pattern rules from your first mail dissolve rather than
  get adopted);
- every output path has exactly one owner again: the main Makefile
  writes $(OUTPUT)'s BPF artifacts, each flavored runner writes only
  its own directory. The unflavored sub-makes include the same rule
  file, and since the delegating rules list the full artifact set as
  prerequisites, a runner sub-make always finds them fresh - same
  file, same rules, so there is no two-lists invariant to maintain.

Cold "make bench" builds 19 BPF objects and 19 skeletons with this,
versus the full default-flavor pass (982 objects, 1,792 skeleton
headers) with default-skels - that was the regression you called out.

While folding it in, the file split settled one step further:
Makefile.skel is self-contained - the hand-maintained skeleton
metadata (the light-skeleton demand lists, LINKED_SKELS with its
-deps map, the skeleton blacklist, the BPF-object header list, the
per-source flag overrides) sits right next to the derivations and
rules that read it, and since both the main Makefile and
Makefile.runner include the file, both instantiations read the same
data with no second copy to keep in sync. Makefile.buildvars keeps
only definitions genuinely shared between files (toolchain, paths,
flags, the permissive-mode helpers, the runner link lists).

The cost is a fourth makefile and ~20 lines of parameter plumbing in
the top Makefile; Makefile.runner shrinks by the extracted section.
Functional checks on the prototype: no-op builds stay no-ops, a
full build settles, touching one bench prog rebuilds exactly its
skeleton, bench object and the binary, and touching a shared prog
rebuilds object, signed lskel, test objects and relinks test_progs.
The folded version passes the same full A/B as the rest of the
series: BPF objects, skeletons and artifact lists match the current
Makefile's output modulo the two changes already accounted for (the
moved header's recorded paths, the flavored duplicate .o removal).

This is folded into v6.

>
> ...
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.