conan icon indicating copy to clipboard operation
conan copied to clipboard

[question] meson generated pkgconfigs in conan2

Open Eugene-Alexeev opened this issue 3 months ago • 2 comments

What is your question?

I have a libA that's defined like usual in meson:

# Create the library
lib_a = shared_library(
  'LibA',
  src_files,
  include_directories: include_directories(public_include_dirs + private_include_dirs),
  cpp_args: compile_args + common_args,
  objc_args: compile_args + common_args,
  install: true,
)

and then wrapped in a conan package ProjA (in reality contains multiple components):

    
class ProjA(ConanFile):
    name = "ProjA"

    def package_info(self):
        inc = ["include/"]
        self.cpp_info.components["libA"].libs = ["LibA"]
        self.cpp_info.components["libA"].includedirs = inc
        self.cpp_info.components["libA"].libdirs = [os.path.join(self.package_folder, "lib")]

it's then pushed to a repo and used in another ProjB. That one has hundreds of components with distinct include directories and dependencies, so defining them in conan the same way is a path I'd like to avoid. I tried to use meson's pkgconfig generation and that works, but how then pick up the generated files properly when dealing with package_info()?

this works:

deps = [ dependency('ProjA-LibA') ]

libB = static_library('LibB',
  sources,
  install: true,
  include_directories: include_directories,
  dependencies: deps,
)

# generate pkg-config file
pkg.generate(
  libB,
  name: 'LibB',
  version: meson.project_version(),
  filebase: 'libB',
)

this doesn't:

class ProjB(ConanFile):
    name = "ProjB"

    def requirements(self):
        self.requires("ProjA/1.0.0", visible=True, transitive_libs=True, transitive_headers=True)

    def package(self):
        meson = Meson(self)
        meson.install()

    def package_info(self):
        inc = ["include"]

        # meson puts them there
        pc_path = os.path.join(self.package_folder, "lib", "pkgconfig")

        # automatically discover all .pc files. is there a better way?
        if os.path.exists(pc_path):
            pc_files = glob.glob(os.path.join(pc_path, "*.pc"))

            for pc_file in pc_files:
                lib_name = os.path.splitext(os.path.basename(pc_file))[0]

                try:
                    pkg_config = PkgConfig(self, lib_name, pkg_config_path=pc_path)
                    pkg_config.fill_cpp_info(
                        self.cpp_info.components[lib_name],
                        is_system=False
                    )
                    self.cpp_info.components[lib_name].set_property(
                        "cmake_target_name",
                        f"{self.name}::{lib_name}"
                    )
                    self.output.info(f"loaded component: {lib_name}")
                except Exception as e:
                    self.output.warning(f"could not load .pc file for {lib_name}: {e}")

the part that doesn't work is connecting that conan dependency with a dependency that meson sees and generates in pkgconfig. its output looks something like this:

➜  pkgconfig cat libB.pc 
prefix=/
includedir=${prefix}/include
libdir=${prefix}/lib

Name: LibB
Description: blabla
Version: 1.0.0
Requires: ProjA-LibA
Libs: -L${libdir} -llibB
Cflags: -I${includedir}

so when I do "conan create" or "conan export-pkg" I get:

ProjA/1.0.0 Already installed! 
ProjB/1.0.0: Loaded component: export-txt
ProjB/1.0.0: WARN: Could not load .pc file for libB: PkgConfig failed. Command: pkg-config --print-provides barcodes-common --print-errors
stderr:
    Package ProjA-LibA was not found in the pkg-config search path.
    Perhaps you should add the directory containing `ProjA-LibA.pc'
    to the PKG_CONFIG_PATH environment variable

which is strange because it looks like conan should provide that since it's stated in self.requires(..) and installed

the question is - how to make conan pick up meson's generated .pc files so it fixes the connection between Conan's 'ProjA' dependency that contains 'LibA' components and meson's 'ProjA-LibA'?

Have you read the CONTRIBUTING guide?

  • [x] I've read the CONTRIBUTING guide

Eugene-Alexeev avatar Dec 10 '25 23:12 Eugene-Alexeev

Hi @Eugene-Alexeev

Thanks for your question.

I think the problem is that those .pc files have a dependency to an external ProjA-LibA.pc file that doesn't exist. Note that such a file doesn't necessarily exist, because the ProjA package is not creating it? The package_info() method of ProjB did package its own xxxx.pc files, but it doesn't have any ProjA-LibA.pc file, it simply doesn't exist.

Maybe you could try calling the PkgConfigDeps generator in the ProjB generate() method, and then also package those files in the package() method so they are available at package_info() time.

In general the pkg_config.fill_cpp_info() flow is intended for packages that do not have any other Conan dependencies, like some of the system package wrappers. But it might not be suitable out of the box for this use case, you can try the above idea, but maybe there is something that make it unfeasible.

memsharded avatar Dec 11 '25 00:12 memsharded

yes PkgConfigDeps is already called in ProjB's generate(), and it's executing successfully, I just omitted it. I can see both ProjA.pc and ProjA-LibA.pc, hence my expectation that conan will pick them up somehow. I'll try to connect them

Eugene-Alexeev avatar Dec 11 '25 16:12 Eugene-Alexeev

yes PkgConfigDeps is already called in ProjB's generate(), and it's executing successfully, I just omitted it. I can see both ProjA.pc and ProjA-LibA.pc, hence my expectation that conan will pick them up somehow. I'll try to connect them

They can be generated in the temporary build folder in the generate() method, somewhere like build/generators. But these files are typically not packaged in the package() method, only the package projB ones, are you sure they are being packaged?

memsharded avatar Dec 11 '25 17:12 memsharded