Repository: conan-io/conan
Pull request #16715 (2024-07-23 19:27:27)

fix Makedeps aggregation of dirs
Changelog: Bugfix: The ``MakeDeps`` generator was missing some aggregated variables when packages have components.
Docs: Omit.


The ``MakeDeps`` generator should probably be simplified, it has some accidental complexity that made very difficult to understand what is happening. I think it should have a lot of overlap with ``AutotoolsDeps`` and ``GnuDepsFlags``, so probably should be refactored to use some common logic.


Note: I need this as part of CPS https://github.com/conan-io/conan/pull/16054



Patch:
diff --git a/conan/internal/api/profile/profile_loader.py b/conan/internal/api/profile/profile_loader.py
index 6f6a83b81..8b36ef61f 100644
--- a/conan/internal/api/profile/profile_loader.py
+++ b/conan/internal/api/profile/profile_loader.py
@@ -319,7 +319,10 @@ class _ProfileValueParser(object):
 
         base_profile.settings.update(settings)
         for pkg_name, values_dict in package_settings.items():
-            base_profile.package_settings[pkg_name].update(values_dict)
+            existing = base_profile.package_settings[pkg_name]
+            existing.update(values_dict)
+            # Make sure they are ordered, so `compiler=xxx` goes before `compiler.cppstd` always
+            base_profile.package_settings[pkg_name] = OrderedDict(sorted(existing.items()))
         for pattern, refs in tool_requires.items():
             # If the same package, different version is added, the latest version prevail
             current = base_profile.tool_requires.setdefault(pattern, [])
diff --git a/conan/tools/gnu/makedeps.py b/conan/tools/gnu/makedeps.py
index d02cc3534..7c61b5ffb 100644
--- a/conan/tools/gnu/makedeps.py
+++ b/conan/tools/gnu/makedeps.py
@@ -49,6 +49,9 @@ def _get_formatted_dirs(folders: list, prefix_path_: str, name: str) -> list:
     """
     ret = []
     for directory in folders:
+        if directory.startswith("$(CONAN"):  # already a variable
+            ret.append(directory)
+            continue
         directory = os.path.normpath(directory).replace("\\", "/")
         prefix = ""
         if not os.path.isabs(directory):
@@ -77,6 +80,7 @@ def _makefy_properties(properties: Optional[dict]) -> dict:
     """
     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")
@@ -84,11 +88,12 @@ def _check_property_value(name, value, output):
     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: 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 {}
 
@@ -572,6 +577,10 @@ class DepGenerator:
         dirs = {}
         for var, prefix in _common_cppinfo_dirs().items():
             cppinfo_value = getattr(dependency.cpp_info, var)
+            if not cppinfo_value:  # The root value is not defined, there might be components
+                cppinfo_value = [f"$(CONAN_{var.replace('dirs', '_dirs').upper()}_{_makefy(dependency.ref.name)}_{_makefy(name)})"
+                                 for name, obj in dependency.cpp_info.components.items() if getattr(obj, var.lower())]
+                prefix = ""
             formatted_dirs = _get_formatted_dirs(cppinfo_value, root, _makefy(dependency.ref.name))
             if formatted_dirs:
                 self._info.dirs_append(var)
@@ -655,21 +664,17 @@ class MakeDeps:
         check_duplicated_generator(self, self._conanfile)
 
         host_req = self._conanfile.dependencies.host
-        build_req = self._conanfile.dependencies.build  # tool_requires
         test_req = self._conanfile.dependencies.test
 
         content_buffer = f"{self._title}\n"
         deps_buffer = ""
 
         # Filter the build_requires not activated for any requirement
-        dependencies = [tup for tup in list(host_req.items()) + list(build_req.items()) + list(test_req.items()) if not tup[0].build]
+        dependencies = list(host_req.items()) + list(test_req.items())
 
         make_infos = []
 
         for require, dep in dependencies:
-            # 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
             output = ConanOutput(scope=f"{self._conanfile} MakeDeps: {dep}:")
             dep_gen = DepGenerator(dep, require, output)
             make_infos.append(dep_gen.makeinfo)


Test patch:
diff --git a/test/integration/configuration/test_profile_priority.py b/test/integration/configuration/test_profile_priority.py
index 3a18ae74f..5fb859766 100644
--- a/test/integration/configuration/test_profile_priority.py
+++ b/test/integration/configuration/test_profile_priority.py
@@ -76,3 +76,43 @@ def test_profile_cli_priority():
     assert "user.myconf:myvalue3={'3': '5', 'a': 'b'}" in c.out
     assert "user.myconf:myvalue4={'1': '2'}" in c.out
     assert "user.myconf:myvalue5={'6': '7'}" in c.out
+
+
+def test_profiles_patterns_include():
+    # https://github.com/conan-io/conan/issues/16718
+    c = TestClient()
+    msvc = textwrap.dedent("""
+        [settings]
+        compiler=msvc
+        compiler.cppstd=14
+        compiler.version=193
+        os=Windows
+
+        test*/*:compiler.cppstd=14
+        """)
+    clang = textwrap.dedent("""
+        include(./msvc)
+        [settings]
+        test*/*:compiler=clang
+        test*/*:compiler.cppstd=17
+        test*/*:compiler.runtime_version=v144
+        test*/*:compiler.version=18
+        """)
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            name = "test_pkg"
+            version = "0.1"
+            settings = "os", "compiler"
+            def generate(self):
+                self.output.info(f"MyCompiler={self.settings.compiler}!!!")
+                self.output.info(f"MyCompilerVersion={self.settings.compiler.version}!!!")
+                self.output.info(f"MyCompilerCpp={self.settings.compiler.cppstd}!!!")
+            """)
+    c.save({"conanfile.py": conanfile,
+            "msvc": msvc,
+            "clang": clang})
+    c.run("install . -pr=clang")
+    assert "conanfile.py (test_pkg/0.1): MyCompiler=clang!!!" in c.out
+    assert "conanfile.py (test_pkg/0.1): MyCompilerVersion=18!!!" in c.out
+    assert "conanfile.py (test_pkg/0.1): MyCompilerCpp=17!!!" in c.out