Home / Alt manpages / pkgconf(1)

  • pkgconf(1)
  • User command
  • linux

Use pkgconf to inspect and supply build flags

You will use the installed pkgconf command to answer three practical questions: is a development module available, which compiler and linker flags does it provide, and which .pc file supplied them. You will also test version constraints 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 used in this guide when they resolve to pkgconf. If your distribution supplies a different implementation, recheck its manual page before assuming that pkgconf-specific 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 intended for a test: status 0 means the resolver found the requested module and its dependencies. It does not print a success message. --modversion prints the module version, while --cflags and --libs print fragments suitable for a compiler or linker command.

In a build script, preserve the exit status rather than matching 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. Do not paste untrusted text into a command that is later evaluated as shell code. 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 surprising, inspect the resolver rather than guessing. These queries are 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 identifies the .pc file used for the dependency set. --variable reads a named value from the module. --print-requires lists ordinary dependencies, and --libs-only-l keeps only library-name fragments, excluding library directories and other linker options. Empty output can be correct: this zlib file has no listed required module and its linker result is already simple.

For a fuller audit, --print-variables, --print-requires-private and --simulate expose more of the resolver state. Use --validate MODULE when you suspect a malformed .pc file. These diagnostics do not repair the file.

4. Check version constraints before configuring a build

Use module-specific 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

The command prints no success message. A non-zero result means the constraint was not satisfied, or that the module could not be resolved. Use --exact-version when equality is genuinely required and --max-version when a newer module is unacceptable. Prefer the least restrictive constraint that your source actually supports.

5. Control search paths without changing system files

PKG_CONFIG_PATH adds secondary directories for .pc lookup. PKG_CONFIG_LIBDIR supplies the primary search directories. The distinction matters: adding a project directory is different from replacing the normal system directories.

$ 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 changes only one command because the variable is prefixed to that command. Replace YOUR_MODULE and the path with real values. Check the returned path before using the flags. Do not globally export a development or build-directory path in a shell profile unless you intend every later build to see it.

For a cross build, set the primary directory to the target's module directories and use 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 are deployment-specific, so there is no honest universal output for that example. Verify with --path TARGET_MODULE and inspect the resulting --cflags and --libs. Do not use sudo merely to make a missing target module appear. Fix the target sysroot or its package contents.

6. Use static flags deliberately

The default query is for 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 the installed zlib metadata. That is not a promise that they match for another module. Static mode computes a deeper graph and can add private dependencies. --shared explicitly selects the simpler shared-linking graph, while --pure affects how a static graph is treated. Compare the outputs for the module and toolchain you are building instead of copying one mode into every project.

7. Treat personality files as configuration data

The companion pkgconf-personality(5) page documents a cross-compile personality format. It is an RFC822-like file with shell-style assignments, comments beginning with #, and properties such as Triplet, SysrootDir, DefaultSearchPaths, SystemIncludePaths and SystemLibraryPaths. Fragment-list values use colon-separated paths. Optional WantDefaultPure and WantDefaultStatic values 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

This is a format example, not a complete working toolchain. The local personality manual describes the file's fields but does not document a command-line option for selecting a particular file. Do not assume that creating this file changes an ordinary pkgconf invocation. Start with the documented environment variables, then consult the integration used by your cross-build system for how it 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 as the failing build. A successful lookup in your interactive shell does not prove that a service, container or build runner has the same variables or package directories.

If flags point into the wrong architecture, stop the build. Do not compensate by adding arbitrary -I or -L options, and do not delete system .pc files. Remove temporary variable prefixes from the command or start a clean shell to undo them. If you changed a persistent profile or build configuration outside these examples, restore its previous line from version control or its 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 selected path.
  • You used static mode only when the link model requires it.
  • You can distinguish documented environment settings from personality-file data that still needs build-system integration.