Skip to content

Commit 5ab37ed

Browse files
authored
fix PkgConfigDeps in build context (#15763)
fix PkgConfigDeps
1 parent 2a794ba commit 5ab37ed

2 files changed

Lines changed: 116 additions & 16 deletions

File tree

conan/tools/gnu/pkgconfigdeps.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,22 @@ def _get_component_aliases(dep, comp_name):
4343
return comp_aliases or []
4444

4545

46-
def _get_package_name(dep, build_context_suffix=None):
46+
def _get_package_name(req, dep, build_context_suffix=None):
4747
pkg_name = dep.cpp_info.get_property("pkg_config_name") or _get_package_reference_name(dep)
48-
suffix = _get_suffix(dep, build_context_suffix)
48+
suffix = _get_suffix(req, build_context_suffix)
4949
return f"{pkg_name}{suffix}"
5050

5151

52-
def _get_component_name(dep, comp_name, build_context_suffix=None):
52+
def _get_component_name(req, dep, comp_name, build_context_suffix=None):
5353
if comp_name not in dep.cpp_info.components:
5454
# foo::foo might be referencing the root cppinfo
5555
if _get_package_reference_name(dep) == comp_name:
56-
return _get_package_name(dep, build_context_suffix)
56+
return _get_package_name(req, dep, build_context_suffix)
5757
raise ConanException("Component '{name}::{cname}' not found in '{name}' "
5858
"package requirement".format(name=_get_package_reference_name(dep),
5959
cname=comp_name))
6060
comp_name = dep.cpp_info.components[comp_name].get_property("pkg_config_name")
61-
suffix = _get_suffix(dep, build_context_suffix)
61+
suffix = _get_suffix(req, build_context_suffix)
6262
return f"{comp_name}{suffix}" if comp_name else None
6363

6464

@@ -67,11 +67,11 @@ def _get_suffix(req, build_context_suffix=None):
6767
Get the package name suffix coming from PkgConfigDeps.build_context_suffix attribute, but only
6868
for requirements declared as build requirement.
6969
70-
:param req: requirement ConanFile instance
70+
:param req: requirement
7171
:param build_context_suffix: `dict` with all the suffixes
7272
:return: `str` with the suffix
7373
"""
74-
if not build_context_suffix or not req.is_build_context:
74+
if not build_context_suffix or not req.build:
7575
return ""
7676
return build_context_suffix.get(req.ref.name, "")
7777

@@ -201,8 +201,9 @@ def content(self, info):
201201

202202
class _PCGenerator:
203203

204-
def __init__(self, conanfile, dep, build_context_suffix=None):
204+
def __init__(self, conanfile, require, dep, build_context_suffix=None):
205205
self._conanfile = conanfile
206+
self._require = require
206207
self._build_context_suffix = build_context_suffix or {}
207208
self._dep = dep
208209
self._content_generator = _PCContentGenerator(self._conanfile, self._dep)
@@ -240,9 +241,9 @@ def package_info(self):
240241
continue # If the dependency is not in the transitive, might be skipped
241242
else: # For instance, dep == "hello/1.0" and req == "hello::cmp1" -> hello == hello
242243
req_conanfile = self._dep
243-
comp_name = _get_component_name(req_conanfile, comp_ref_name, self._build_context_suffix)
244+
comp_name = _get_component_name(self._require, req_conanfile, comp_ref_name, self._build_context_suffix)
244245
if not comp_name:
245-
pkg_name = _get_package_name(req_conanfile, self._build_context_suffix)
246+
pkg_name = _get_package_name(self._require, req_conanfile, self._build_context_suffix)
246247
# Creating a component name with namespace, e.g., dep-comp1
247248
comp_name = _get_name_with_namespace(pkg_name, comp_ref_name)
248249
ret.append(comp_name)
@@ -256,13 +257,14 @@ def components_info(self):
256257
257258
:return: `list` of `_PCInfo` objects with all the components information
258259
"""
259-
pkg_name = _get_package_name(self._dep, self._build_context_suffix)
260+
pkg_name = _get_package_name(self._require, self._dep, self._build_context_suffix)
260261
components_info = []
261262
# Loop through all the package's components
262263
for comp_ref_name, cpp_info in self._dep.cpp_info.get_sorted_components().items():
263264
# At first, let's check if we have defined some components requires, e.g., "dep::cmp1"
264265
comp_requires_names = self._get_cpp_info_requires_names(cpp_info)
265-
comp_name = _get_component_name(self._dep, comp_ref_name, self._build_context_suffix)
266+
comp_name = _get_component_name(self._require, self._dep, comp_ref_name,
267+
self._build_context_suffix)
266268
if not comp_name:
267269
comp_name = _get_name_with_namespace(pkg_name, comp_ref_name)
268270
comp_description = f"Conan component: {comp_name}"
@@ -281,14 +283,14 @@ def package_info(self):
281283
282284
:return: `_PCInfo` object with the package information
283285
"""
284-
pkg_name = _get_package_name(self._dep, self._build_context_suffix)
286+
pkg_name = _get_package_name(self._require, self._dep, self._build_context_suffix)
285287
# At first, let's check if we have defined some global requires, e.g., "other::cmp1"
286288
requires = self._get_cpp_info_requires_names(self._dep.cpp_info)
287289
# If we have found some component requires it would be enough
288290
if not requires:
289291
# If no requires were found, let's try to get all the direct visible dependencies,
290292
# e.g., requires = "other_pkg/1.0"
291-
requires = [_get_package_name(req, self._build_context_suffix)
293+
requires = [_get_package_name(self._require, req, self._build_context_suffix)
292294
for req in self._transitive_reqs.values()]
293295
description = "Conan package: %s" % pkg_name
294296
aliases = _get_package_aliases(self._dep)
@@ -337,7 +339,7 @@ def _update_pc_files(info):
337339
# Second, let's load the root package's PC file ONLY
338340
# if it does not already exist in components one
339341
# Issue related: https://github.com/conan-io/conan/issues/10341
340-
pkg_name = _get_package_name(self._dep, self._build_context_suffix)
342+
pkg_name = _get_package_name(self._require, self._dep, self._build_context_suffix)
341343
if f"{pkg_name}.pc" not in pc_files:
342344
package_info = _PCInfo(pkg_name, pkg_requires, f"Conan package: {pkg_name}",
343345
self._dep.cpp_info, _get_package_aliases(self._dep))
@@ -400,7 +402,8 @@ def content(self):
400402
if require.build and dep.ref.name not in self.build_context_activated:
401403
continue
402404

403-
pc_generator = _PCGenerator(self._conanfile, dep, build_context_suffix=self.build_context_suffix)
405+
pc_generator = _PCGenerator(self._conanfile, require, dep,
406+
build_context_suffix=self.build_context_suffix)
404407
pc_files.update(pc_generator.pc_files)
405408
return pc_files
406409

conans/test/integration/toolchains/gnu/test_pkgconfigdeps.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,103 @@ def build(self):
808808
c.assert_listed_require({"example/1.0": "Cache"}, build=True)
809809

810810

811+
class TestPCGenerationBuildContext:
812+
"""
813+
https://github.com/conan-io/conan/issues/14920
814+
"""
815+
def test_pc_generate(self):
816+
c = TestClient()
817+
tool = textwrap.dedent("""
818+
import os
819+
from conan import ConanFile
820+
from conan.tools.gnu import PkgConfigDeps
821+
822+
class Example(ConanFile):
823+
name = "tool"
824+
version = "1.0"
825+
requires = "wayland/1.0"
826+
tool_requires = "wayland/1.0"
827+
828+
def generate(self):
829+
deps = PkgConfigDeps(self)
830+
deps.build_context_activated = ["wayland", "dep"]
831+
deps.build_context_suffix = {"wayland": "_BUILD", "dep": "_BUILD"}
832+
deps.generate()
833+
834+
def build(self):
835+
assert os.path.exists("wayland.pc")
836+
assert os.path.exists("wayland_BUILD.pc")
837+
assert os.path.exists("dep.pc")
838+
assert os.path.exists("dep_BUILD.pc")
839+
""")
840+
c.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_package_type("shared-library"),
841+
"wayland/conanfile.py": GenConanfile("wayland", "1.0").with_requires("dep/1.0"),
842+
"tool/conanfile.py": tool,
843+
"app/conanfile.py": GenConanfile().with_tool_requires("tool/1.0")})
844+
c.run("export dep")
845+
c.run("export wayland")
846+
c.run("export tool")
847+
c.run("install app --build=missing")
848+
assert "Install finished successfully" in c.out # the asserts in build() didn't fail
849+
# Now make sure we can actually build with build!=host context
850+
c.run("install app -s:h build_type=Debug --build=missing")
851+
assert "Install finished successfully" in c.out # the asserts in build() didn't fail
852+
853+
def test_pc_generate_components(self):
854+
c = TestClient()
855+
tool = textwrap.dedent("""
856+
import os
857+
from conan import ConanFile
858+
from conan.tools.gnu import PkgConfigDeps
859+
860+
class Example(ConanFile):
861+
name = "tool"
862+
version = "1.0"
863+
requires = "wayland/1.0"
864+
tool_requires = "wayland/1.0"
865+
866+
def generate(self):
867+
deps = PkgConfigDeps(self)
868+
deps.build_context_activated = ["wayland", "dep"]
869+
deps.build_context_suffix = {"wayland": "_BUILD", "dep": "_BUILD"}
870+
deps.generate()
871+
872+
def build(self):
873+
assert os.path.exists("wayland.pc")
874+
assert os.path.exists("wayland-client.pc")
875+
assert os.path.exists("wayland-server.pc")
876+
assert os.path.exists("wayland_BUILD.pc")
877+
assert os.path.exists("wayland_BUILD-client.pc")
878+
assert os.path.exists("wayland_BUILD-server.pc")
879+
assert os.path.exists("dep.pc")
880+
assert os.path.exists("dep_BUILD.pc")
881+
""")
882+
wayland = textwrap.dedent("""
883+
from conan import ConanFile
884+
885+
class Pkg(ConanFile):
886+
name = "wayland"
887+
version = "1.0"
888+
requires = "dep/1.0"
889+
890+
def package_info(self):
891+
self.cpp_info.components["client"].libs = []
892+
self.cpp_info.components["server"].libs = []
893+
""")
894+
c.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_package_type("shared-library"),
895+
"wayland/conanfile.py": wayland,
896+
"tool/conanfile.py": tool,
897+
"app/conanfile.py": GenConanfile().with_tool_requires("tool/1.0")})
898+
c.run("export dep")
899+
c.run("export wayland")
900+
c.run("export tool")
901+
c.run("install app --build=missing")
902+
assert "Install finished successfully" in c.out # the asserts in build() didn't fail
903+
# Now make sure we can actually build with build!=host context
904+
c.run("install app -s:h build_type=Debug --build=missing")
905+
assert "Install finished successfully" in c.out # the asserts in build() didn't fail
906+
907+
811908
def test_pkg_config_deps_and_private_deps():
812909
"""
813910
Testing that no errors are raised when the dependency tree has a private one in the middle

0 commit comments

Comments
 (0)