What's the benefit of pkgconfig-depends?

Cabal User Guide says:

pkgconfig-depends: *package list*

A list of pkg-config packages, needed to build this package. They can be annotated with versions, e.g. gtk+-2.0 >= 2.10, cairo >= 1.0. If no version constraint is specified, any version is assumed to be acceptable. Cabal uses pkg-config to find if the packages are available on the system and to find the extra compilation and linker options needed to use the packages.

If you need to bind to a C library that supports pkg-config then it is much preferable to use this field rather than hard code options into the other fields. pkg-config --list-all will show you all supported libraries. Depending on your system you may need to adjust PKG_CONFIG_PATH.

Why exactly is pkg-config preferable to raw extra-libraries? What’s the difference between

pkgconfig-depends: zlib

and

extra-libraries: z

unless one needs to constrain supported versions of a foreign package?

1 Like

There are a few other benefits:

  • Packages can report CFLAGS as well as linking flags, so even in the Haskell context, you can find headers installed into non-standard locations (e.g., alternative prefixes, or when using Nix);
  • Packages can declare dependencies between each other, so that you don’t have to know the transitive closure of the libraries you’re linking to construct a working linker command line.
  • pkg-config can also return different flags depending on whether the final link is being done statically or dynamically. Cabal does not currently take advantage of this, but I wish that it did.
8 Likes

pkg-config is a better interface. There are cases where compilation will likely just outright fail if you try to use extra-libraries (libraries might not be in the standard locations gcc searches for or the package requires more link-flags).

It also allows easier specification about which version bounds you accept for the given library (as you noted), since the version is recorded in the .pc file. You don’t need a test program to read and interpret CPP macros that define the version, so it can save autotools shenanigans.

However, pkg-config is not a standard. And some linux distributions (like debian) wrongly add .pc files downstream. Which causes cross-distro breakage: your cabal package may now only build on Debian. So you have to research:

  • does upstream provide .pc files?
  • do all the major distributions install them?
  • do all versions of the library have them or just a couple?

So to be truly portable, you should allow to fall back to non-pkgconfig logic.

Related tickets:

So: you should provide an auto cabal flag to switch between pkg-config and extra-libraries.

(Note: the entire pkg-config “database” is passed to the cabal solver)

4 Likes

Excellent point. And for those who erroneously depend on such packages via pkgconfig-depends, it might also be worth providing ways to override information that cabal would discover from pkg-config; an autotooled ./configure script, for example, will respect overrides to the FOO_CFLAGS and FOO_LIBS variables. This provides an out if the script finds the wrong pkg-config is used or it gives the wrong answers.

1 Like

Additionally the solver uses pkgconfig db to make the install plan. If a candidate dependency requires a pkgconfig module that is not available, that dependency is discarded.
I wonder if a similar mechanism could work with extra-libraries but in the current state of affairs I am not sure whether this would be an improvement or not.

4 Likes

But if you still believe those distributions are the problem, then go start a new topic - I though this one was about the relative benefits of pkgconfig-depends, not your opinions regarding certain decisions made by other (largely) volunteer-based organisations…

Is this all that useful in practice? Cabal packages overwhelmingly only have one maintained version (a.k.a. the most recent one), and there is no culture of packaging multiple external backends for things, so a library lookup failure should pretty much always result in the entire build failing. While an implicit failure in this case is not all that confusing, it does limit design space in terms of error messages.

I think we want the failure behaviour because, as @hasufell points out, that allows cabal to try other assignments of automatic flags, some of which might avoid the native dependency.

e.g. you might have an automatic flag to choose between a fast C library implementation of an algorithm or a slow, Haskell implementation of an algorithm.

Even if cabal cannot fall back on another version, I much prefer getting an error in the first few seconds than coming back to my build and find out it ran into an error after 10 minutes. Everything is cached, but I’ve still lost time (and I guess in CI the cache may not actually get saved in such failing cases).

1 Like

Just adding my 2 cents here, I have recently discovered this myself mucking about with CI, so this thread is of interest to me re: how to use pkgconfig-depends properly.

2 Likes

Kind of a niche circumstance, but I’ve found pkgconfig-depends to work much better with cabal2nix derivations involving c2hs (vs. specifying any/several/all of extra-libraries/extra-lib-dirs/extra-include-dirs).

2 Likes

May I ask what I think is a related question? I note the reference to the entire pkg-config ‘database’ being passed to Cabal’s solver. I also note that the pkgconfig project explains in its README.md:

comparison of pkgconf and pkg-config dependency resolvers

pkgconf builds an acyclic directed dependency graph. This allows for the user to more conservatively link their binaries – which may be helpful in some environments, such as when prelink(1) is being used. As a result of building a directed dependency graph designed for the specific problem domain provided by the user, more accurate dependencies can be determined.

Current release versions of pkg-config, on the other hand, build a database of all known pkg-config files on the system before attempting to resolve dependencies, which is a considerably slower and less efficient design. Efforts have been made recently to improve this behaviour.

pkgconfig provides MSYS2’s default pkg-config.exe implementation.

Does the difference between pkgconf and pkg-config described above have any implications for what Cabal’s solver seeks to do with the ’pkg-config database’?

cabal-install’s solver does not actually care about the interdependencies between pkg-config files. It assumes that the system it lives in is consistent in this respect.

On a side note: this makes me think that it would be easier and faster to parse the .pc directly in Haskell. They are reasonably standard and we only need name and version, not the full graph.

3 Likes