Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature][PkgConfigDeps] Warn about duplicated *.pc names #10263

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions conan/tools/gnu/pkgconfigdeps/pc_files_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,32 @@
get_single_package_info, get_aliases_info


def _get_aliases_pc_files_and_content(dep, aliases_info):
def _get_aliases_pc_files_and_content(conanfile, dep, aliases_info, root_pc_files):
"""
Get all the PC files and content for the aliases defined previously
for package and components names
"""
pc_files = {}
for name, aliases in aliases_info.items():
for alias in aliases:
pc_alias_file = get_alias_pc_filename_and_content(
pc_name, pc_content = get_alias_pc_filename_and_content(
dep,
alias,
[name], # require is the own name which is being used the aliases for.
description="Alias %s for %s" % (alias, name),
)
pc_files.update(pc_alias_file)
if pc_name in pc_files:
conanfile.output.warn("[%s] The PC alias name %s already exists and it matches with "
"another alias one. Please, review all the "
"pkg_config_aliases defined. Skipping it!"
% (dep, pc_name))
elif pc_name in root_pc_files:
conanfile.output.warn("[%s] The PC alias name %s already exists and it matches with "
"another package/component one. Please, review all the "
"pkg_config_aliases defined. Skipping it!"
% (dep, pc_name))
else:
pc_files[pc_name] = pc_content
return pc_files


Expand All @@ -31,14 +42,20 @@ def _get_components_pc_files_and_content(conanfile, dep, components_info):
for pc_info_component in components_info:
# Get the *.pc file content for each component
description = "Conan component: %s" % pc_info_component.name
pc_file = get_pc_filename_and_content(
pc_name, pc_content = get_pc_filename_and_content(
conanfile,
dep,
pc_info_component.name,
pc_info_component.requires,
description,
cpp_info=pc_info_component.cpp_info)
pc_files.update(pc_file)
if pc_name in pc_files:
conanfile.output.warn("[%s] The PC component name %s already exists and it matches with "
"another component one. Please, review all the "
"component's pkg_config_name defined. Skipping it!"
% (dep, pc_name))
else:
pc_files[pc_name] = pc_content
return pc_files


Expand All @@ -49,13 +66,18 @@ def _get_package_with_components_pc_files_and_content(conanfile, dep, package_in
pc_files = {}
pc_files.update(_get_components_pc_files_and_content(conanfile, dep, components_info))
description = "Conan package: %s" % package_info.name
pc_alias_file_pkg = get_alias_pc_filename_and_content(
pc_name, pc_content = get_alias_pc_filename_and_content(
dep,
package_info.name,
package_info.requires,
description
)
pc_files.update(pc_alias_file_pkg)
if pc_name in pc_files:
conanfile.output.warn("[%s] The PC package name %s already exists and it matches with "
"another component one. Please, review all the "
"component's pkg_config_name defined. Skipping it!" % (dep, pc_name))
else:
pc_files[pc_name] = pc_content
return pc_files


Expand All @@ -64,9 +86,9 @@ def _get_single_package_pc_file_and_content(conanfile, dep, package_info):
Get the PC files for dependencies without components
"""
description = "Conan package: %s" % package_info.name
pc_file = get_pc_filename_and_content(conanfile, dep, package_info.name,
package_info.requires, description)
return pc_file
pc_name, pc_content = get_pc_filename_and_content(conanfile, dep, package_info.name,
package_info.requires, description)
return {pc_name: pc_content}


def get_pc_files_and_content(conanfile, dep):
Expand All @@ -85,5 +107,5 @@ def get_pc_files_and_content(conanfile, dep):
pc_files.update(_get_single_package_pc_file_and_content(conanfile, dep, pkg_info))
# Package and components names aliases
aliases_info = get_aliases_info(dep, pkg_info, components_info)
pc_files.update(_get_aliases_pc_files_and_content(dep, aliases_info))
pc_files.update(_get_aliases_pc_files_and_content(conanfile, dep, aliases_info, pc_files))
return pc_files
4 changes: 2 additions & 2 deletions conan/tools/gnu/pkgconfigdeps/pc_files_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def get_pc_filename_and_content(conanfile, dep, name, requires, description, cpp
}
template = Template(_get_pc_file_template(), trim_blocks=True, lstrip_blocks=True,
undefined=StrictUndefined)
return {name + ".pc": template.render(context)}
return name + ".pc", template.render(context)


def get_alias_pc_filename_and_content(dep, name, requires, description):
Expand All @@ -122,4 +122,4 @@ def get_alias_pc_filename_and_content(dep, name, requires, description):
}
template = Template(_get_alias_pc_file_template(), trim_blocks=True,
lstrip_blocks=True, undefined=StrictUndefined)
return {name + ".pc": template.render(context)}
return name + ".pc", template.render(context)
14 changes: 12 additions & 2 deletions conan/tools/gnu/pkgconfigdeps/pkgconfigdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ def content(self):
pc_files = {}
host_req = self._conanfile.dependencies.host
for _, dep in host_req.items():
pc_files.update(get_pc_files_and_content(self._conanfile, dep))
dep_name = str(dep)
for pc_name, pc_content in get_pc_files_and_content(self._conanfile, dep).items():
if pc_name in pc_files:
_, analyzed_dep_name = pc_files[pc_name]
self._conanfile.output.warn(
"[%s] The PC file name %s already exists and it matches with another "
"name/alias declared in %s package. Please, review all the "
"pkg_config_name/pkg_config_aliases defined. Skipping it!"
% (dep_name, pc_name, analyzed_dep_name))
else:
pc_files[pc_name] = (pc_content, dep_name)
return pc_files

def generate(self):
"""Save all the *.pc files"""
# Current directory is the generators_folder
generator_files = self.content
for generator_file, content in generator_files.items():
for generator_file, (content, _) in generator_files.items():
save(generator_file, content)
85 changes: 85 additions & 0 deletions conans/test/functional/toolchains/gnu/test_pkgconfigdeps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import glob
import os
import textwrap

Expand Down Expand Up @@ -462,3 +463,87 @@ def package_info(self):

pc_content = client.load("second-mycomponent.pc")
assert "Requires: compo1" == get_requires_from_content(pc_content)


def test_duplicated_names_warnings():
"""
Testing some WARN messages if there are duplicated pkg_config_name/pkg_config_aliases defined

Scenario: consumer -> pkgA/1.0 -> pkgB/1.0
Expected WARN cases:
- Duplicated aliases.
- Duplicated names, alias and component name
- Duplicated components names.
- Duplicated package and component name.
- Duplicated names between different dependencies.
"""
client = TestClient()
conanfile = textwrap.dedent("""
from conans import ConanFile

class Recipe(ConanFile):

def package_info(self):
self.cpp_info.set_property("pkg_config_name", "libpkg")
# Duplicated components
self.cpp_info.components["cmp1"].set_property("pkg_config_name", "component1")
self.cpp_info.components["cmp2"].set_property("pkg_config_name", "component1")
# Duplicated package and component name
self.cpp_info.components["cmp3"].set_property("pkg_config_name", "libpkg")
""")
client.save({"conanfile.py": conanfile})
client.run("create . pkgB/1.0@")

conanfile = textwrap.dedent("""
from conans import ConanFile

class PkgConfigConan(ConanFile):
requires = "pkgB/1.0"

def package_info(self):
# Duplicated name as pkgB
self.cpp_info.set_property("pkg_config_name", "libpkg")
self.cpp_info.components["cmp1"].requires.append("pkgB::cmp1")
self.cpp_info.components["cmp1"].set_property("pkg_config_name", "component1")
# Duplicated aliases
self.cpp_info.components["cmp2"].set_property("pkg_config_aliases", ["alias1"])
self.cpp_info.components["cmp3"].set_property("pkg_config_aliases", ["alias1"])
# Duplicated names, alias and component name
self.cpp_info.components["cmp2"].set_property("pkg_config_name", "libcmp")
self.cpp_info.components["cmp4"].set_property("pkg_config_aliases", ["libcmp"])
""")
client.save({"conanfile.py": conanfile}, clean_first=True)
client.run("create . pkgA/1.0@")

conanfile = textwrap.dedent("""
[requires]
pkgA/1.0

[generators]
PkgConfigDeps
""")
client.save({"conanfile.txt": conanfile}, clean_first=True)
client.run("install .")
output = client.out
# Duplicated aliases from pkgA
assert "WARN: [pkgA/1.0] The PC alias name alias1.pc already exists and it matches with " \
"another alias one" in output
# Duplicated names, alias and component name from pkgA
assert "WARN: [pkgA/1.0] The PC alias name libcmp.pc already exists and it matches with " \
"another package/component one" in output
# Duplicated components from pkgB
assert "WARN: [pkgB/1.0] The PC component name component1.pc already exists and it matches " \
"with another component one" in output
# Duplicated package and component name from pkgB
assert "WARN: [pkgB/1.0] The PC package name libpkg.pc already exists and it matches with " \
"another component one" in output
# Duplicated names between pkgB and pkgA
assert "WARN: [pkgB/1.0] The PC file name component1.pc already exists and it matches with " \
"another name/alias declared in pkgA/1.0 package" in output
assert "WARN: [pkgB/1.0] The PC file name libpkg.pc already exists and it matches with " \
"another name/alias declared in pkgA/1.0 package" in output
pc_files = [os.path.basename(i) for i in glob.glob(os.path.join(client.current_folder, '*.pc'))]
pc_files.sort()
# Let's check all the PC file names created just in case
assert pc_files == ['alias1.pc', 'component1.pc', 'libcmp.pc', 'libpkg-cmp3.pc',
'libpkg-cmp4.pc', 'libpkg.pc']