PkgConfigDeps

Important

This feature is still under development, while it is recommended and usable and we will try not to break them in future releases, some breaking changes might still happen if necessary to prepare for the Conan 2.0 release.

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