Skip to content

ADR-0200: Move volk -include flag off of volk_dep.compile_args (libvmaf.pc leak fix)

  • Status: Accepted
  • Date: 2026-04-27
  • Deciders: Lusoris, Claude (Anthropic)
  • Tags: vulkan, build, fork-local, abi
  • Supersedes: nothing — bug-fix follow-up to ADR-0198

Context

ADR-0198 renamed volk's vk* PFN dispatcher symbols to vmaf_priv_vk* via a force-included header (volk_priv_remap.h), generated by the volk subproject's gen_priv_remap.py from upstream volk.h. The -include flag was attached to volk_dep's compile_args so every libvmaf TU pulling in volk_dep (i.e., libvmaf's vulkan kernels) picked it up.

That works for shared libvmaf.so builds — meson's pkg-config generator drops dependency compile_args from the public Cflags: on shared targets. It does not work for static libvmaf.a builds: meson includes compile_args from all dependencies in the generated libvmaf.pc's Cflags: so downstream consumers know how to link against the static archive's transitively-required deps.

Net effect on libvmaf.pc from a default_library=static -Denable_vulkan=enabled build:

Cflags: -I${includedir} -I${includedir}/libvmaf -DVK_NO_PROTOTYPES \
        -include /<libvmaf-build-dir>/subprojects/volk-vulkan-sdk-1.4.341.0/volk_priv_remap.h \
        -pthread

Lawrence's BtbN-style fully-static FFmpeg build (cross-toolchain, x86_64-ffbuild-linux-gnu-gcc, glibc-2.28) ran:

$ pkg-config --cflags libvmaf
-I/opt/ffbuild/include/libvmaf -DVK_NO_PROTOTYPES \
  -include /45-vmaf/lusoris/core/build/subprojects/volk-vulkan-sdk-1.4.341.0/volk_priv_remap.h \
  -pthread

After libvmaf was installed to /opt/ffbuild/, the build-dir path was gone. FFmpeg's configure ran check_func_headers aom/aom_codec.h with the leaked Cflags, hit:

<command-line>: fatal error: \
  /45-vmaf/lusoris/core/build/subprojects/volk-vulkan-sdk-1.4.341.0/volk_priv_remap.h: \
  No such file or directory
compilation terminated.

The whole FFmpeg build cascaded: every subsequent feature probe that included libvmaf's Cflags failed the same way. lawrence's chat report 2026-04-27 22:19: "thats a bug for sure" / 22:20: "yeah because it's broken the compile flags probably lol".

Decision

Move the -include volk_priv_remap.h flag off of volk_dep.compile_args and onto libvmaf's own targets as private c_args. The volk subproject continues to expose volk_priv_remap_h_path as a meson variable; the parent project (libvmaf) reads it via subproject('volk').get_variable('volk_priv_remap_h_path') and appends ['-include', volk_priv_remap_h_path] to vmaf_cflags_common, which is the c_args: argument of libvmaf's library() call (and the various sub-libraries that compile vulkan TUs).

Per meson's pkg-config generator: c_args: on a library() is private to the target and does not appear in the generated .pc Cflags:. Only compile_args: declared on a dependency that the target consumes appears in Cflags:. By moving the -include from a dependency to private c_args, the symbol-rename behaviour stays identical at libvmaf's compile time while the leaked path is removed from the public Cflags surface.

volk_dep keeps -DVK_NO_PROTOTYPES in compile_args because that flag is harmless to consumers — it's a behaviour switch for volk's header that just disables auto-prototype declarations. Consumers don't include volk.h directly, so it has no effect on them either way.

Alternatives considered

Option Pros Cons Why not chosen
Move -include to libvmaf's c_args (chosen) Surgical; keeps the rename behaviour byte-for-byte; no consumer-visible change to libvmaf.pc on shared builds; static libvmaf.pc Cflags now have no leaked paths Requires libvmaf to know about the volk subproject's variable Cleanest meson-idiomatic fix
Manually edit libvmaf.pc post-generation to strip the -include line Minimal volk-side change Brittle (regex-based); fragile across meson versions; works for Cflags: but not for Cflags.private: Hack — easier to fix at the source
Generate volk's volk_priv_remap.h at install time instead of build time Avoids build-dir paths in pkg-config volk's wrap is a build artefact, not an install artefact; no place to install the generated header on the target system; would also leak -include /usr/include/vmaf/volk_priv_remap.h to consumers who never need it Architecturally wrong — the rename is a private libvmaf detail, never a consumer concern
Bake the rename into volk.c source via a script-edit at configure time, drop the runtime -include entirely No -include flag anywhere Fragile — regenerating volk.c on every wrap bump is a maintenance burden; loses the regex-based generator's coverage signal (gen script prints rename count) Over-engineering for a flag-leak bug
Drop the per-build-mode distinction by always producing shared libvmaf Avoids the static-build pkg-config mechanic BtbN-style FFmpeg builds REQUIRE static libraries; that's the whole point of the fork-local Vulkan support for that workflow Out of scope — static support is a load-bearing feature

Consequences

  • Positive: lawrence's BtbN-style fully-static FFmpeg build works again. pkg-config --cflags libvmaf returns -I${includedir} -I${includedir}/libvmaf -DVK_NO_PROTOTYPES -pthread (no leaked build-dir paths) on both shared and static builds.
  • Positive: the symbol-rename behaviour is unchanged — verified via nm libvmaf.a | grep -cE '^[0-9a-f]* (T|D|B|R) vk[A-Z]' = 0 (no GLOBAL vk*) and nm libvmaf.a | grep -cE '^[0-9a-f]* (T|D|B|R) vmaf_priv_vk' = 719 post-fix; identical to ADR-0198's pre-leak-fix numbers.
  • Negative: the libvmaf side now has a 3-line block coupling it to a volk-subproject internal variable. Documented at the declaration site so future maintainers see the dependency.
  • Neutral / follow-ups:
  • Add a CI assertion that the static-build libvmaf.pc does not contain volk_priv_remap or any ${meson.build_root()} substring. Cheap regression gate.
  • If a future libvmaf restructuring splits the vulkan kernels into a separate static_library('vmaf_vulkan_internal'), move the -include onto that library's c_args: instead of the umbrella vmaf_cflags_common to scope it more tightly. Not load-bearing today.

References

  • Source: lawrence's chat report 2026-04-27 22:19 — pasted FFmpeg configure log showing the leaked path.
  • Pre-fix verification (this dev box):
$ grep Cflags core/build-vk-static-test/meson-private/libvmaf.pc
Cflags: -I${includedir} -I${includedir}/libvmaf -DVK_NO_PROTOTYPES \
        -include /home/kilian/dev/vmaf/core/build-vk-static-test/subprojects/.../volk_priv_remap.h \
        -pthread
  • Post-fix verification:
$ grep Cflags core/build-vk-static-test/meson-private/libvmaf.pc
Cflags: -I${includedir} -I${includedir}/libvmaf -DVK_NO_PROTOTYPES -pthread
  • Parent: ADR-0198 — introduced the rename mechanism whose flag we're now relocating.
  • Original fix: ADR-0185 — shared-library --exclude-libs,ALL predecessor.
  • User direction 2026-04-27: paraphrased — "this is a real bug; fix the build flags so downstream consumers don't see broken paths."