Home / Alt manpages / pkgconf(1)

  • pkgconf(1)
  • User command
  • linux

Use pkgconf to Inspect and Supply Build Flags

A build fails with a wall of missing header errors, and pkgconf is how you find out whether the library is even installed. pkgconf also tells you which compiler and linker flags a module provides, and which .pc file supplied them. You will test version constraints too, and see how search paths and cross-compilation settings change the result.

Allow about fifteen minutes. You need a shell and the pkgconf package. The examples below were checked with pkgconf 1.8.1-2build1 on this machine. The compatible command name pkg-config is a symlink to the same executable here, but check your own installation before relying on that.

1. Check the installed command

Start with read-only checks. No elevated privileges are needed:

$ command -v pkgconf
/usr/bin/pkgconf
$ pkgconf --version
1.8.1
$ dpkg-query -W -f='${Package} ${Version}\n' pkgconf:amd64
pkgconf:amd64 1.8.1-2build1
$ ls -l "$(command -v pkg-config)"
lrwxrwxrwx ... /usr/bin/pkg-config -> pkgconf

The link metadata varies, so the useful check is the final target: both names accept the option forms in this guide once they resolve to pkgconf. If your distribution ships a different implementation, recheck its manual page before assuming pkgconf's behaviour applies.

Checkpoint

Record the executable and version before debugging a build. Package upgrades can change the resolver's defaults and diagnostics.

2. Find a module and read its flags

A module is normally described by a file ending in .pc. Ask pkgconf for a known installed module, such as zlib on this machine:

$ pkgconf --exists zlib
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ pkgconf --modversion zlib
1.3
$ pkgconf --cflags zlib
$ pkgconf --libs zlib
-lz

--exists is built for scripting: status 0 means the resolver found the module and its dependencies, and it prints nothing on success. --modversion prints the module version; --cflags and --libs print fragments ready for a compiler or linker command.

In a build script, trust the exit status, not human-readable error text:

if ! pkgconf --exists "REQUIRED_MODULE"; then
    printf 'missing pkg-config module: %s\n' "REQUIRED_MODULE" >&2
    exit 1
fi

Keep the module name separate from shell syntax. Never paste untrusted text into a command that gets evaluated as shell code later; build systems normally capture these flags directly, which avoids a second round of shell interpretation.

3. Inspect what the module actually contains

When a flag looks wrong, inspect the resolver instead of guessing. These are all read-only:

$ pkgconf --path zlib
/usr/lib/x86_64-linux-gnu/pkgconfig/zlib.pc
$ pkgconf --variable=prefix zlib
/usr
$ pkgconf --print-requires zlib
$ pkgconf --libs-only-l zlib
z

--path names the .pc file behind the dependency set. --variable reads one named value out of the module. --print-requires lists ordinary dependencies, and --libs-only-l keeps only library-name fragments, dropping directories and other linker options. Empty output can be perfectly correct: this zlib file has no listed required module, and its linker result is already about as simple as it gets.

For a fuller audit, --print-variables, --print-requires-private and --simulate expose more of the resolver's internal state, and --validate MODULE is worth reaching for when you suspect a malformed .pc file. None of these repair it for you.

4. Check version constraints before configuring a build

Use the version predicates when a project needs a minimum or exact release:

$ pkgconf --atleast-version=1.0 zlib
$ printf 'minimum check: %s\n' "$?"
minimum check: 0
$ pkgconf --atleast-version=999.0 zlib
$ printf 'unmet check: %s\n' "$?"
unmet check: 1

No success message here either. A non-zero result means the constraint failed, or the module could not be resolved at all. Reach for --exact-version only when equality is genuinely required, and --max-version when a newer module would actually break things. Pick the least restrictive constraint your source can actually tolerate.

5. Control search paths without touching system files

PKG_CONFIG_PATH adds secondary directories to the .pc lookup; PKG_CONFIG_LIBDIR replaces the primary search directories outright. That distinction matters: adding a project directory is a very different move from swapping out the system directories entirely.

$ PKG_CONFIG_PATH="/path/to/project/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" \
  pkgconf --path YOUR_MODULE
/path/to/project/lib/pkgconfig/YOUR_MODULE.pc

This only changes one command, since the variable is prefixed just to that invocation. Swap in your own YOUR_MODULE and path, and check the returned path before trusting the flags. Do not export a development or build-directory path from your shell profile unless you actually want every future build to see it.

For a cross build, point the primary directory at the target's module directories and add a sysroot prefix:

$ PKG_CONFIG_LIBDIR="/path/to/sysroot/usr/lib/pkgconfig:/path/to/sysroot/usr/share/pkgconfig" \
  PKG_CONFIG_SYSROOT_DIR="/path/to/sysroot" \
  pkgconf --modversion TARGET_MODULE

Paths and module names here are entirely deployment-specific, so there is no honest sample output to show you. Verify with --path TARGET_MODULE and inspect the resulting --cflags and --libs. Do not reach for sudo just to make a missing target module appear: fix the sysroot or its package contents instead.

6. Use static flags deliberately

The default query builds a shared-linking dependency graph. Add --static only when the link is actually static:

$ pkgconf --libs zlib
-lz
$ pkgconf --static --libs zlib
-lz

These two outputs happen to match for zlib on this machine; that is not a promise they will match for every module. Static mode walks a deeper dependency graph and can pull in private dependencies you were not expecting. --shared explicitly selects the simpler graph, and --pure changes how a static graph is treated. Compare the outputs for your own module and toolchain rather than copying one mode blindly into every project.

7. Treat personality files as configuration data

The companion pkgconf-personality(5) page documents a cross-compile personality format: an RFC822-like file with shell-style assignments, comments starting with #, and properties such as Triplet, SysrootDir, DefaultSearchPaths, SystemIncludePaths and SystemLibraryPaths. Fragment-list values use colon-separated paths, and the optional WantDefaultPure and WantDefaultStatic are false unless set to true, yes or 1.

Triplet: x86_64-example-linux-gnu
SysrootDir: /path/to/sysroot
DefaultSearchPaths: /path/to/sysroot/usr/lib/pkgconfig:/path/to/sysroot/usr/share/pkgconfig
SystemIncludePaths: /path/to/sysroot/usr/include
SystemLibraryPaths: /path/to/sysroot/usr/lib
WantDefaultStatic: false

That is a format example, not a complete working toolchain. The personality manual describes the fields but does not document a command-line option for picking a particular file, and creating one will not change an ordinary pkgconf invocation by itself. Start with the documented environment variables, then check how your cross-build system actually loads personality data.

8. Recover from the common failures

If pkgconf says a module was not found, run pkgconf --list-all and check the spelling. Then inspect PKG_CONFIG_PATH and PKG_CONFIG_LIBDIR in the same environment the failing build actually runs in: a successful lookup in your interactive shell proves nothing about a service, container or build runner with different variables or package directories.

If flags point at the wrong architecture, stop the build. Do not paper over it with arbitrary -I or -L options, and do not delete system .pc files. Remove any temporary variable prefixes from the command, or start a clean shell to undo them. If you changed a persistent profile or build configuration along the way, restore its previous line from version control or a backup before retrying.

Done means

  • You confirmed the installed pkgconf version and whether pkg-config points to it.
  • You checked module availability, version, flags and the source .pc path.
  • You used exit status for dependency checks instead of parsing success text.
  • You changed search paths only for the intended command and verified the result.
  • You used static mode only when the link model actually requires it.
  • You can tell documented environment settings apart from personality-file data that still needs build-system integration.