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

Add properties to MakeDeps generator #16613

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
60 changes: 50 additions & 10 deletions conan/tools/gnu/makedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import textwrap

from jinja2 import Template, StrictUndefined
from typing import Optional

from conan.api.output import ConanOutput
from conan.internal import check_duplicated_generator
from conan.tools.files import save

Expand Down Expand Up @@ -67,6 +69,30 @@ def _makefy(name: str) -> str:
return re.sub(r'[^0-9A-Z_]', '_', name.upper())


def _makefy_properties(properties: Optional[dict]) -> dict:
"""
Convert property dictionary keys to Make-variable-friendly syntax
:param properties: The property dictionary to be converted (None is also accepted)
:return: Modified property dictionary with keys not including bad characters that are not parsed correctly
"""
return {_makefy(name): value for name, value in properties.items()} if properties else {}

def _check_property_value(name, value, output):
if "\n" in value:
output.warning(f"Skipping propery '{name}' because it contains newline")
return False
else:
return True

def _filter_properties(properties: Optional[dict], output) -> dict:
"""
Filter out properties whose values contain newlines, because they would break the generated makefile
:param properties: A property dictionary (None is also accepted)
:return: A property dictionary without the properties containing newlines
"""
return {name: value for name, value in properties.items() if _check_property_value(name, value, output)} if properties else {}


def _conan_prefix_flag(variable: str) -> str:
"""
Return a global flag to be used as prefix to any value in the makefile
Expand Down Expand Up @@ -131,6 +157,12 @@ def _jinja_format_list_values() -> str:
{%- endif -%}
{%- endmacro %}

{%- macro define_multiple_variable_value(var, values) -%}
{% for property_name, value in values.items() %}
{{ var }}_{{ property_name }} = {{ value }}
{% endfor %}
{%- endmacro %}

{%- macro define_variable_value(var, values) -%}
{%- if values is not none -%}
{%- if values|length > 0 -%}
Expand Down Expand Up @@ -332,9 +364,10 @@ class DepComponentContentGenerator:
{{- define_variable_value_safe("CONAN_FRAMEWORKS_{}_{}".format(dep_name, name), cpp_info_flags, 'frameworks') -}}
{{- define_variable_value_safe("CONAN_REQUIRES_{}_{}".format(dep_name, name), cpp_info_flags, 'requires') -}}
{{- define_variable_value_safe("CONAN_SYSTEM_LIBS_{}_{}".format(dep_name, name), cpp_info_flags, 'system_libs') -}}
{{- define_multiple_variable_value("CONAN_PROPERTY_{}_{}".format(dep_name, name), properties) -}}
""")

def __init__(self, dependency, component_name: str, dirs: dict, flags: dict):
def __init__(self, dependency, component_name: str, dirs: dict, flags: dict, output):
"""
:param dependency: The dependency object that owns the component
:param component_name: component raw name e.g. poco::poco_json
Expand All @@ -345,6 +378,7 @@ def __init__(self, dependency, component_name: str, dirs: dict, flags: dict):
self._name = component_name
self._dirs = dirs or {}
self._flags = flags or {}
self._output = output

def content(self) -> str:
"""
Expand All @@ -356,7 +390,8 @@ def content(self) -> str:
"dep_name": _makefy(self._dep.ref.name),
"name": _makefy(self._name),
"cpp_info_dirs": self._dirs,
"cpp_info_flags": self._flags
"cpp_info_flags": self._flags,
"properties": _makefy_properties(_filter_properties(self._dep.cpp_info.components[self._name]._properties, self._output)),
}
template = Template(_jinja_format_list_values() + self.template, trim_blocks=True,
lstrip_blocks=True, undefined=StrictUndefined)
Expand Down Expand Up @@ -397,15 +432,17 @@ class DepContentGenerator:
{{- define_variable_value_safe("CONAN_REQUIRES_{}".format(name), cpp_info_flags, 'requires') -}}
{{- define_variable_value_safe("CONAN_SYSTEM_LIBS_{}".format(name), cpp_info_flags, 'system_libs') -}}
{{- define_variable_value("CONAN_COMPONENTS_{}".format(name), components) -}}
{{- define_multiple_variable_value("CONAN_PROPERTY_{}".format(name), properties) -}}
""")

def __init__(self, dependency, require, root: str, sysroot, dirs: dict, flags: dict):
def __init__(self, dependency, require, root: str, sysroot, dirs: dict, flags: dict, output):
self._dep = dependency
self._req = require
self._root = root
self._sysroot = sysroot
self._dirs = dirs or {}
self._flags = flags or {}
self._output = output

def content(self) -> str:
"""
Expand All @@ -420,6 +457,7 @@ def content(self) -> str:
"components": list(self._dep.cpp_info.get_sorted_components().keys()),
"cpp_info_dirs": self._dirs,
"cpp_info_flags": self._flags,
"properties": _makefy_properties(_filter_properties(self._dep.cpp_info._properties, self._output)),
}
template = Template(_jinja_format_list_values() + self.template, trim_blocks=True,
lstrip_blocks=True, undefined=StrictUndefined)
Expand All @@ -431,7 +469,7 @@ class DepComponentGenerator:
Generates Makefile content for a dependency component
"""

def __init__(self, dependency, makeinfo: MakeInfo, component_name: str, component, root: str):
def __init__(self, dependency, makeinfo: MakeInfo, component_name: str, component, root: str, output):
"""
:param dependency: The dependency object that owns the component
:param makeinfo: Makeinfo to store component variables
Expand All @@ -444,6 +482,7 @@ def __init__(self, dependency, makeinfo: MakeInfo, component_name: str, componen
self._comp = component
self._root = root
self._makeinfo = makeinfo
self._output = output

def _get_component_dirs(self) -> dict:
"""
Expand Down Expand Up @@ -500,7 +539,7 @@ def generate(self) -> str:
"""
dirs = self._get_component_dirs()
flags = self._get_component_flags()
comp_content_gen = DepComponentContentGenerator(self._dep, self._name, dirs, flags)
comp_content_gen = DepComponentContentGenerator(self._dep, self._name, dirs, flags, self._output)
comp_content = comp_content_gen.content()
return comp_content

Expand All @@ -510,10 +549,11 @@ class DepGenerator:
Process a dependency cpp_info variables and generate its Makefile content
"""

def __init__(self, dependency, require):
def __init__(self, dependency, require, output):
self._dep = dependency
self._req = require
self._info = MakeInfo(self._dep.ref.name, [], [])
self._output = output

@property
def makeinfo(self) -> MakeInfo:
Expand Down Expand Up @@ -585,11 +625,11 @@ def generate(self) -> str:
sysroot = self._get_sysroot(root)
dirs = self._get_dependency_dirs(root, self._dep)
flags = self._get_dependency_flags(self._dep)
dep_content_gen = DepContentGenerator(self._dep, self._req, root, sysroot, dirs, flags)
dep_content_gen = DepContentGenerator(self._dep, self._req, root, sysroot, dirs, flags, self._output)
content = dep_content_gen.content()

for comp_name, comp in self._dep.cpp_info.get_sorted_components().items():
component_gen = DepComponentGenerator(self._dep, self._info, comp_name, comp, root)
component_gen = DepComponentGenerator(self._dep, self._info, comp_name, comp, root, self._output)
content += component_gen.generate()

return content
Expand Down Expand Up @@ -630,8 +670,8 @@ def generate(self) -> None:
# Require is not used at the moment, but its information could be used, and will be used in Conan 2.0
if require.build:
continue

dep_gen = DepGenerator(dep, require)
output = ConanOutput(scope=f"{self._conanfile} MakeDeps: {dep}:")
dep_gen = DepGenerator(dep, require, output)
make_infos.append(dep_gen.makeinfo)
deps_buffer += dep_gen.generate()

Expand Down
12 changes: 12 additions & 0 deletions test/integration/toolchains/gnu/test_makedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def package_info(self):
lib_dir2 = os.path.join(self.package_folder, "lib2")
self.cpp_info.includedirs = [include_dir]
self.cpp_info.libdirs = [lib_dir, lib_dir2]
self.cpp_info.set_property("my_prop", "my prop value")
self.cpp_info.set_property("my_prop_with_newline", "my\\nprop")
""")
client = TestClient()
client.save({"conanfile.py": conanfile})
Expand All @@ -44,6 +46,9 @@ def package_info(self):
assert f'CONAN_LIB_DIRS_MYLIB = \\\n\t$(CONAN_LIB_DIR_FLAG){prefix}/my_absoulte_path/fake/mylib/lib \\\n\t$(CONAN_LIB_DIR_FLAG)$(CONAN_ROOT_MYLIB)/lib2' in makefile_content
assert f'CONAN_INCLUDE_DIRS_MYLIB = $(CONAN_INCLUDE_DIR_FLAG){prefix}/my_absoulte_path/fake/mylib/include' in makefile_content
assert 'CONAN_BIN_DIRS_MYLIB = $(CONAN_BIN_DIR_FLAG)$(CONAN_ROOT_MYLIB)/bin' in makefile_content
assert 'CONAN_PROPERTY_MYLIB_MY_PROP = my prop value' in makefile_content
memsharded marked this conversation as resolved.
Show resolved Hide resolved
assert 'CONAN_PROPERTY_MYLIB_MY_PROP_WITH_NEWLINE' not in makefile_content
assert "WARN: Skipping propery 'my_prop_with_newline' because it contains newline" in client.stderr

lines = makefile_content.splitlines()
for line_no, line in enumerate(lines):
Expand Down Expand Up @@ -90,6 +95,7 @@ def package_info(self):
assert 'CONAN_BIN_DIRS' not in makefile_content
assert 'CONAN_LIBS' not in makefile_content
assert 'CONAN_FRAMEWORK_DIRS' not in makefile_content
assert 'CONAN_PROPERTY' not in makefile_content


def test_libs_and_system_libs():
Expand Down Expand Up @@ -197,6 +203,8 @@ class TestMakeDepsConan(ConanFile):
def package_info(self):
self.cpp_info.components["mycomponent"].requires.append("lib::cmp1")
self.cpp_info.components["myfirstcomp"].requires.append("mycomponent")
self.cpp_info.components["myfirstcomp"].set_property("my_prop", "my prop value")
self.cpp_info.components["myfirstcomp"].set_property("my_prop_with_newline", "my\\nprop")

""")
client.save({"conanfile.py": conanfile}, clean_first=True)
Expand Down Expand Up @@ -236,6 +244,10 @@ def package_info(self):
assert 'CONAN_REQUIRES = $(CONAN_REQUIRES_SECOND)\n' in makefile_content
assert 'CONAN_LIBS = $(CONAN_LIBS_LIB)\n' in makefile_content

assert 'CONAN_PROPERTY_SECOND_MYFIRSTCOMP_MY_PROP = my prop value\n' in makefile_content
assert 'CONAN_PROPERTY_SECOND_MYFIRSTCOMP_MY_PROP_WITH_NEWLINE' not in makefile_content
assert "WARN: Skipping propery 'my_prop_with_newline' because it contains newline" in client2.stderr


def test_make_with_public_deps_and_component_requires_second():
"""
Expand Down