A checklist for writing pkg-config files

tl;dr: Use AX_PKG_CHECK_MODULES to split public/private dependencies; use AC_CONFIG_FILES to magically include the API version in the .pc file name.

A few tips for creating a pkg-config file which you will never need to think about maintaining again — because one of the most common problems with pkg-config files is that their dependency lists are years out of date compared to the dependencies checked for in configure.ac. See lower down for some example automake snippets.

  • Include the project’s major API version ((Assuming this is the number which will change if backwards-incompatible API/ABI changes are made.)) in the pkg-config file name. e.g. libfoo-1.pc rather than libfoo.pc. This will allow parallel installation of two API-incompatible versions of the library if it becomes necessary in future.
  • Split private and public dependencies between Requires and Requires.private. This eliminates over-linking when dynamically linking against the project, since in that case the private dependencies are not needed. This is easily done using the AX_PKG_CHECK_MODULES macro (and perhaps using an upstream macro in future — see pkg-config bug #87154). A dependency is public when its symbols are exposed in public headers installed by your project; it is private otherwise.
  • Include useful ancillary variables, such as the paths to any utilities, directories or daemons which ship with the project. For example, glib-2.0.pc has variables giving the paths for its utilities: glib-genmarshal, gobject-query and glib-mkenums. libosinfo-1.0.pc has variables for its database directories. Ensure the variables use a variable form of ${prefix}, allowing it to be overridden when invoking pkg-config using pkg-config --define-variable=prefix=/some/other/prefix. This allows use of libraries installed in one (read only) prefix from binaries in another, while installing ancillary files (e.g. D-Bus service files) to the second prefix.
  • Substitute in the Name and Version using @PACKAGE_NAME@ and @PACKAGE_VERSION@ so they don’t fall out of sync.
  • Place the .pc.in template in the source code subdirectory for the library it’s for — so if your project produces multiple libraries (or might do in future), the .pc.in files don’t get mixed up at the top level.

Given all those suggestions, here’s a template libmy-project/my-project.pc.in file (updated to incorporate suggestions by Dan Nicholson):

prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

my_project_utility=my-project-utility-binary-name
my_project_db_dir=@sysconfdir@/my-project/db

Name: @PACKAGE_NAME@
Description: Some brief but informative description
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lmy-project-@API_VERSION@
Cflags: -I${includedir}/my-project-@API_VERSION@
Requires: @AX_PACKAGE_REQUIRES@
Requires.private: @AX_PACKAGE_REQUIRES_PRIVATE@

And here’s a a few snippets from a template configure.ac:

# Release version
m4_define([package_version_major],[1])
m4_define([package_version_minor],[2])
m4_define([package_version_micro],[3])

# API version
m4_define([api_version],[1])

AC_INIT([my-project],
        [package_version_major.package_version_minor.package_version_micro],
        …)

# Dependencies
PKG_PROG_PKG_CONFIG
PKG_INSTALLDIR

glib_reqs=2.40
gio_reqs=2.42
gthread_reqs=2.40
nice_reqs=0.1.6

# The first list on each line is public; the second is private.
# The AX_PKG_CHECK_MODULES macro substitutes AX_PACKAGE_REQUIRES and
# AX_PACKAGE_REQUIRES_PRIVATE.
AX_PKG_CHECK_MODULES([GLIB],
                     [glib-2.0 >= $glib_reqs gio-2.0 >= $gio_reqs],
                     [gthread-2.0 >= $gthread_reqs])
AX_PKG_CHECK_MODULES([NICE],
                     [nice >= $nice_reqs],
                     [])

AC_SUBST([PACKAGE_VERSION_MAJOR],package_version_major)
AC_SUBST([PACKAGE_VERSION_MINOR],package_version_minor)
AC_SUBST([PACKAGE_VERSION_MICRO],package_version_micro)
AC_SUBST([API_VERSION],api_version)

# Output files
# Rename the template .pc file to include the API version on configure
AC_CONFIG_FILES([
libmy-project/my-project-$API_VERSION.pc:libmy-project/my-project.pc.in
…
],[],
[API_VERSION='$API_VERSION'])
AC_OUTPUT

And finally, the top-level Makefile.am:

# Install the pkg-config file; the directory is set using
# PKG_INSTALLDIR in configure.ac.
pkgconfig_DATA = libmy-project/my-project-$(API_VERSION).pc

Once that’s all built, you’ll end up with an installed my-project-1.pc file containing the following (assuming a prefix of /usr; note that by default autoconf substitutes in references to variables rather than the values themselves, so pkg-config can continue to be used with --define-variable to override the prefix):

prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

my_project_utility=my-project-utility-binary-name
my_project_db_dir=${prefix}/etc/my-project/db

Name: my-project
Description: Some brief but informative description
Version: 1.2.3
Libs: -L${libdir} -lmy-project-1
Cflags: -I${includedir}/my-project-1
Requires: glib-2.0 >= 2.40 gio-2.0 >= 2.42 nice >= 0.1.6
Requires.private: gthread-2.0 >= 2.40

All code samples in this post are released into the public domain.

8 thoughts on “A checklist for writing pkg-config files

  1. Matthias

    Include useful ancillary variables, such as the paths to any utilities

    Unfortunately, CMake cannot read them and provides only the standard variables. But still a good advice.

    1. Philip Withnall Post author

      I don’t know much about CMake, so this might be total rubbish, but couldn’t you use execute_command() (or something like it) to run pkg-config --variable=my-variable my-project?

      1. AbdulKareem

        I may be mistaken but OP here talks about creating .pc files which provides flags required for compiling against particular library.

        pkg-config utility writes those flags to stdout to be used with compiler.

  2. Dan Nicholson

    Good stuff! I looked at your macro and left some comments in the bug. Two thoughts on your suggested usage.

    1. The DISTCLEANFILES and EXTRA_DIST settings in Makefile.am are unnecessary since you're using AC_CONFIG_FILES. For the same reason that you don't have to manually dist Makefile.in or cleanup Makefile on distclean, the files passed in AC_CONFIG_FILES shouldn't need this handling.

    2. Using pkg.m4 from pkg-config-0.27 and newer, you can use the PKG_INSTALLDIR/PKG_NOARCH_INSTALLDIR macros to set the installation directory for the pc files. This allows the builder to override them in a standard way if they need to. The variables substituted are pkgconfigdir and noarch_pkgconfigdir, respectively. So, you could include PKG_INSTALLDIR in configure.ac and then drop the pkgconfigdir setting in Makefile.am.

  3. Anonymous

    You shouldn't actually use AC_CONFIG_FILES for a .pc.in file; instead, you should use sed in Makefile.am. That way, someone can "make prefix=/elsewhere" and have that work correctly.

    Also, it's obnoxious that the first of the two lists in AX_PKG_CHECK_MODULES is the *public* list, since that's the one you almost never want, unless you've actually made your library expose implementation details of the other library.

    The rest looks great.

    1. Philip Withnall Post author

      Given that I can only find one library (harfbuzz) which doesn’t use AC_CONFIG_FILES for its .pc file, out of a quick sample of the core git repos I have, I’m not entirely convinced of the practical need to support make prefix=/elsewhere. Using AC_CONFIG_FILES is a lot more convenient. Is there a real use case where make prefix=/elsewhere needs to be supported without re-running configure?

      I’m on the fence about public-vs-private as well. Private should be used more commonly, but I could imagine people _assuming_ public would come first, using the macro that way, and experiencing bugs which completely belie the original point of having the macro (which was to avoid static linking problems). I’ll put it up for consideration in the pkg-config bug report though.

      1. Philip Withnall Post author

        I’ve received a comment by e-mail stating that AC_CONFIG_FILES is correct for pkg-config files, because it substitutes in references to the variables, rather than the values themselves. So @prefix@ is typically expanded to ${prefix}. pkg-config can then do the recursive expansion of the variables itself.

Comments are closed.