If you have ever tried to check for the presence of a library from a configuration script, you know this is not an easy task. Getting the right compiler (CFLAGS) and linker (LIBS) flags can be very difficult, if not impossible, without manual help from the user.
To detect some libraries, you have to create a little test program and link it against the library you are looking for. If the build is successful, the library is available, and possibly ready to be used. In fact, this is what GNU Autoconf's AC_CHECK_LIB macro does. However, this is not necessarily true, as the detected library may be older than you expected, or may need some extra flags that you didn't think of.
To overcome this difficulty, the *-config scripts were born. These scripts, specific to a library, print the required compiler and linker flags to the standard output, so that the configuration script can get them. For example, consider the glib 1.x library. It includes the glib-config script, which produces the following output:
[dawn jmmv] $ glib-config --cflags
-I/usr/pkg/include/glib/glib-1.2 -I/usr/pkg/lib/glib/include
[dawn jmmv] $ glib-config --libs
-L/usr/pkg/lib -Wl,-R/usr/pkg/lib -lglib
This is certainly nicer than manual detection of libraries, but has a problem: each library has to provide its own script, which may be syntactically incompatible with all other ones (e.g., accepting an -ldflags flag rather than --libs).
And here is where pkg-config comes into play: it generalizes the *-config scripts concept and provides a consistent framework to access library information.
In the pkg-config world, each library installs a special metadata file in a centralized directory (typically, lib/pkgconfig relative to the installation prefix). These files specify the version of the installed library, the compiler and linker flags needed to link to it as well as some other extra information. Then, the pkg-config utility can be used to consistently query this information.
The above glib example becomes the following:
[dawn jmmv] $ pkg-config --cflags glib
-I/usr/pkg/include/glib/glib-1.2 -I/usr/pkg/lib/glib/include
[dawn jmmv] $ pkg-config --libs glib
-Wl,-R/usr/pkg/lib -L/usr/pkg/lib -lglib
We can also ensure that a given version is present:
[dawn jmmv] $ pkg-config --print-errors glib-2.0 ">=" 2.6.0
[dawn jmmv] $ pkg-config --print-errors glib-2.0 ">=" 2.10.0
Requested 'glib-2.0 >= 2.10.0' but version of GLib is 2.6.2
Furthermore, this integrates nicely with configuration scripts generated by GNU Autoconf, as pkg-config provides a macro (PKG_CHECK_MODULES) to trivially query package information.
Unfortunately, not all libraries provide pkg-config metadata files (yet?), so you still need to fallback to the previous mechanisms if available. Furthermore, some people don't like pkg-config, but I can't see why. To me, it's a very neat concept (I'm not talking about its internal implementation, which may be messy) that greatly simplifies things.