PkgConfigDeps

Warning

These tools are still experimental (so subject to breaking changes) but with very stable syntax. We encourage their usage to be prepared for Conan 2.0.

PkgConfigDeps

Available since: 1.38.0

The PkgConfigDeps is the dependencies generator for pkg-config. Generates pkg-config files named <PKG-NAME>.pc (where <PKG-NAME is the name declared by dependencies in cpp_info.names["pkg_config"] if specified), containing a valid pkg-config file syntax. Indeed, it can also be defined using set_property and the property pkg_config_name (available since Conan 1.36), for instance:

self.cpp_info.components["mycomponent"].set_property("pkg_config_name", "mypkg-config-name")

Note

In Conan 2.0 that will be the default way of setting those properties and also passing custom properties to generators. Check the cpp_info attributes reference for more information.

The prefix variable is automatically adjusted to the package_folder.

The PkgConfigDeps generator can be used by name in conanfiles:

conanfile.py
class Pkg(ConanFile):
    generators = "PkgConfigDeps"
conanfile.txt
[generators]
PkgConfigDeps

And it can also be fully instantiated in the conanfile generate() method:

from conan import ConanFile
from conan.tools.gnu import PkgConfigDeps

class App(ConanFile):
    settings = "os", "arch", "compiler", "build_type"
    requires = "zlib/1.2.11"

    def generate(self):
        pc = PkgConfigDeps(self)
        pc.generate()

The PkgConfigDeps will generate the *.pc file after a conan install command:

$ conan install .
# Check the [PC_FILE_NAME].pc created in your current folder

Now, running this command using the previous conanfile.py, you can check the zlib.pc file created into your current folder:

prefix=/Users/YOUR_USER/.conan/data/zlib/1.2.11/_/_/package/647afeb69d3b0a2d3d316e80b24d38c714cc6900
libdir=${prefix}/lib
includedir=${prefix}/include

Name: zlib
Description: Conan package: zlib
Version: 1.2.11
Libs: -L"${libdir}" -lz -F Frameworks
Cflags: -I"${includedir}"

build_context_activated

Available since: 1.52.0

When you have a build-require, by default, the *.pc files are not generated. But you can activate it using the build_context_activated attribute:

tool_requires = ["my_tool/0.0.1"]

def generate(self):
    pc = PkgConfigDeps(self)
    # generate the *.pc file for the tool require
    pc.build_context_activated = ["my_tool"]
    pc.generate()

Warning

The build_context_activated feature will fail if no “build” profile is used. This feature only work when using the two host and build profiles.

build_context_suffix

Available since: 1.52.0

When you have the same package as a build-require and as a regular require it will cause a conflict in the generator because the file names of the *.pc files will collide as well as the names, requires names, etc.

For example, this is a typical situation with some requirements (capnproto, protobuf…) that contain a tool used to generate source code at build time (so it is a build_require), but also providing a library to link to the final application, so you also have a regular require. Solving this conflict is specially important when we are cross-building because the tool (that will run in the building machine) belongs to a different binary package than the library, that will “run” in the host machine.

You can use the build_context_suffix attribute to specify a suffix for a requirement, so the files/requires/names of the requirement in the build context (tool require) will be renamed:

tool_requires = ["my_tool/0.0.1"]
requires = ["my_tool/0.0.1"]

def generate(self):
    pc = PkgConfigDeps(self)
    # generate the *.pc file for the tool require
    pc.build_context_activated = ["my_tool"]
    # disambiguate the files, requires, names, etc
    pc.build_context_suffix = {"my_tool": "_BUILD"}
    pc.generate()

Warning

The build_context_suffix feature will fail if no “build” profile is used. This feature only work when using the two host and build profiles.

Components

If a recipe uses components, the files generated will be <[PKG-NAME]-[COMP-NAME]>.pc with their corresponding flags and require relations.

Additionally, a <PKG-NAME>.pc is generated to maintain compatibility for consumers with recipes that start supporting components. This <PKG-NAME>.pc file will declare all the components of the package as requires while the rest of the fields will be empty, relying on the propagation of flags coming from the components <[PKG-NAME]-[COMP-NAME]>.pc files.

Properties

The following properties affect the PkgConfigDeps generator:

  • pkg_config_name property will define the name of the generated *.pc file (xxxxx.pc)

  • pkg_config_aliases property sets some aliases of any package/component name for pkg_config generator. This property only accepts list-like Python objects.

  • pkg_config_custom_content property will add user defined content to the .pc files created by this generator.

  • component_version property sets a custom version to be used in the Version field belonging to the created *.pc file for that component.

These properties can be defined at global cpp_info level or at component level.

Example:

def package_info(self):
    custom_content = "datadir=${prefix}/share"
    self.cpp_info.set_property("pkg_config_custom_content", custom_content)
    self.cpp_info.set_property("pkg_config_name", "myname")
    self.cpp_info.components["mycomponent"].set_property("pkg_config_name", "componentname")
    self.cpp_info.components["mycomponent"].set_property("pkg_config_aliases", ["alias1", "alias2"])
    self.cpp_info.components["mycomponent"].set_property("component_version", "1.14.12")

Names and aliases

Aliases are available since: 1.43.0

By default, the *.pc files will be named following these rules:

  • For packages, it uses the package name, e.g., package zlib/1.2.11 -> zlib.pc.

  • For components, the package name + hyphen + component name, e.g., openssl/3.0.0 with self.cpp_info.components["crypto"] -> openssl-crypto.pc.

You can change that default behavior with the pkg_config_name and pkg_config_aliases properties. For instance, openssl/3.0.0` recipe has these pkg_config_name properties already declared:

from conan import ConanFile

class OpenSSLConan(ConanFile):
    name = "openssl"

    # any code here

    def package_info(self):
        self.cpp_info.set_property("pkg_config_name", "openssl")
        self.cpp_info.components["crypto"].set_property("pkg_config_name", "libcrypto")
        self.cpp_info.components["ssl"].set_property("pkg_config_name", "libssl")

Run conan install openssl/3.0.0@ -g PkgConfigDeps and check the *.pc files created:

  • libcrypto.pc

  • libssl.pc

  • openssl.pc

  • zlib.pc (openssl requires zlib)

Their pkg_config_name properties are used as the final *.pc file names:

openssl.pc
Name: openssl
Description: Conan package: openssl
Version: 3.0.0
Requires: libcrypto libssl
libcrypto.pc
prefix=/Users/conan_user/.conan/data/openssl/3.0.0/_/_/package/88955cec2844f731470e07bd44ce5a3a24ec88b7
libdir1=${prefix}/lib
includedir1=${prefix}/include

Name: libcrypto
Description: Conan component: libcrypto
Version: 3.0.0
Libs: -L"${libdir1}" -lcrypto -F Frameworks
Cflags: -I"${includedir1}"
Requires: zlib

A special mention when a component shares the same *.pc file name as the root package one:

from conan import ConanFile

class OpenCLConan(ConanFile):

    # ...

    def package_info(self):
        self.cpp_info.set_property("pkg_config_name", "OpenCL")  # -> OpenCL.pc
        self.cpp_info.components["_opencl-headers"].set_property("pkg_config_name", "OpenCL")  # -> OpenCL.pc

The only *.pc file created will be the one belonging to the component:

  • OpenCL.pc (from component)

Now, let’s see how pkg_config_aliases property works step by step.

Let’s create our own myopenssl/1.0.0 recipe and define several aliases like the following:

from conan import ConanFile

class MyOpenSSLConan(ConanFile):
    name = "myopenssl"
    version = "1.0.0"

    def package_info(self):
        # Aliases
        self.cpp_info.set_property("pkg_config_aliases", ["myopenssl_alias"])
        self.cpp_info.components["mycrypto"].set_property("pkg_config_aliases", ["mycrypto", "crp"])
        self.cpp_info.components["myssl"].set_property("pkg_config_aliases", ["myssl"])

Then, after creating the package locally with conan create . and consuming it conan install myopenssl/1.0.0@ -g PkgConfigDeps, the files created will be:

  • myopenssl-mycrypto.pc

  • myopenssl-myssl.pc

  • myopenssl.pc

  • crp.pc (alias of myopenssl-mycrypto)

  • mycrypto.pc (alias of myopenssl-mycrypto)

  • myssl.pc (alias of myopenssl-myssl)

  • myopenssl_alias.pc (alias of myopenssl)

Where any of those aliases files contains something like this:

mycrypto.pc
Name: mycrypto
Description: Alias mycrypto for myopenssl-mycrypto
Version: 1.0.0
Requires: myopenssl-mycrypto

It’s also possible to use both properties together:

from conan import ConanFile

class MyOpenSSLConan(ConanFile):
    name = "myopenssl"
    version = "1.0.0"

    # any code here

    def package_info(self):
        self.cpp_info.set_property("pkg_config_name", "myopenssl")
        self.cpp_info.components["mycrypto"].set_property("pkg_config_name", "libmycrypto")
        self.cpp_info.components["myssl"].set_property("pkg_config_name", "libmyssl")
        # Aliases
        self.cpp_info.set_property("pkg_config_aliases", ["myopenssl_alias"])
        self.cpp_info.components["mycrypto"].set_property("pkg_config_aliases", ["mycrypto", "crp"])
        self.cpp_info.components["myssl"].set_property("pkg_config_aliases", ["myssl"])

After executing the commands mentioned above, the files are:

  • libmycrypto.pc

  • libmyssl.pc

  • myopenssl.pc

  • crp.pc (alias of libmycrypto)

  • mycrypto.pc (alias of libmycrypto)

  • myssl.pc (alias of libmyssl)

  • myopenssl_alias.pc (alias of myopenssl)

The only change is which name the alias is pointing to:

mycrypto.pc
Name: mycrypto
Description: Alias mycrypto for libmycrypto
Version: 1.0.0
Requires: libmycrypto