Repository: conan-io/conan
Pull request #18661 (2025-07-17 16:48:31)

serialize conandata information
Changelog: Feature: Add ``self.conan_data`` to the information serialized by ConanFile, so it is printed in ``conan graph info`` and other commands
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/18658

We might want to talk again about enabling ``conan inspect`` to inspect cache and remote references (downloading them first)



Patch:
diff --git a/conan/cli/commands/new.py b/conan/cli/commands/new.py
index d316bf5a3..e6aab3c35 100644
--- a/conan/cli/commands/new.py
+++ b/conan/cli/commands/new.py
@@ -12,7 +12,7 @@ def new(conan_api, parser, *args):
                         "either a predefined built-in or a user-provided one. "
                         "Available built-in templates: basic, cmake_lib, cmake_exe, header_lib, "
                         "meson_lib, meson_exe, msbuild_lib, msbuild_exe, bazel_lib, bazel_exe, "
-                        "autotools_lib, autotools_exe, local_recipes_index, workspace. "
+                        "autotools_lib, autotools_exe, premake_lib, premake_exe, local_recipes_index, workspace. "
                         "E.g. 'conan new cmake_lib -d name=hello -d version=0.1'. "
                         "You can define your own templates too by inputting an absolute path "
                         "as your template, or a path relative to your conan home folder."
diff --git a/conan/internal/model/conan_file.py b/conan/internal/model/conan_file.py
index 5b2c595bf..a68c32775 100644
--- a/conan/internal/model/conan_file.py
+++ b/conan/internal/model/conan_file.py
@@ -75,6 +75,7 @@ class ConanFile:
     runenv_info = None
     conf_info = None
     generator_info = None
+    conan_data = None
 
     def __init__(self, display_name=""):
         self.display_name = display_name
@@ -176,6 +177,8 @@ class ConanFile:
         if self.info is not None:
             result["info"] = self.info.serialize()
         result["vendor"] = self.vendor
+        if self.conan_data:
+            result["conandata"] = self.conan_data
         return result
 
     @property
diff --git a/conan/tools/premake/premakedeps.py b/conan/tools/premake/premakedeps.py
index 80b291ac2..c98a6377e 100644
--- a/conan/tools/premake/premakedeps.py
+++ b/conan/tools/premake/premakedeps.py
@@ -8,16 +8,27 @@ from conan.tools.premake.constants import CONAN_TO_PREMAKE_ARCH
 
 # Filename format strings
 PREMAKE_VAR_FILE = "conan_{pkgname}_vars_{config}.premake5.lua"
-PREMAKE_CONF_FILE = "conan_{pkgname}{config}.premake5.lua"
 PREMAKE_PKG_FILE = "conan_{pkgname}.premake5.lua"
 PREMAKE_ROOT_FILE = "conandeps.premake5.lua"
 
+PREMAKE_CONFIG_FILE = "conanconfig_{config}.premake5.lua"
+PREMAKE_CONFIG_ROOT_FILE = "conanconfig.premake5.lua"
+
 # File template format strings
+PREMAKE_TEMPLATE_CONFIG = """
+include "conanutils.premake5.lua"
+
+t_conan_deps_order = {{}}
+t_conan_deps_order["{config}"] = {{{order}}}
+
+if conan_deps_order == nil then conan_deps_order = {{}} end
+conan_premake_tmerge(conan_deps_order, t_conan_deps_order)
+"""
 PREMAKE_TEMPLATE_UTILS = """
 function conan_premake_tmerge(dst, src)
     for k, v in pairs(src) do
         if type(v) == "table" then
-            if type(conandeps[k] or 0) == "table" then
+            if type(dst[k] or 0) == "table" then
                 conan_premake_tmerge(dst[k] or {}, src[k] or {})
             else
                 dst[k] = v
@@ -66,8 +77,9 @@ function {function_name}(conf, pkg)
     if conf == nil then
 {filter_call}
     elseif pkg == nil then
-        for k,v in pairs(conandeps[conf]) do
-            {function_name}(conf, k)
+        local order = conan_deps_order[conf]
+        for index, lib in ipairs(order) do
+            {function_name}(conf, lib)
         end
     else
 {lua_content}
@@ -183,10 +195,13 @@ class PremakeDeps:
         # Global utility file
         self._output_lua_file("conanutils.premake5.lua", [PREMAKE_TEMPLATE_UTILS])
 
-        # Extract all dependencies
-        host_req = self._conanfile.dependencies.host
-        test_req = self._conanfile.dependencies.test
-        build_req = self._conanfile.dependencies.build
+        # Extract all dependencies in topological order: some linkers like ld or gold prunes the
+        # functions which are not being used in the lookup table. If the less dependant libraries are
+        # passed first, the linker will not be able to resolve the symbols in the dependent libraries
+        # as they will have been removed
+        host_req = self._conanfile.dependencies.host.topological_sort
+        test_req = self._conanfile.dependencies.test.topological_sort
+        build_req = self._conanfile.dependencies.build.topological_sort
 
         # Merge into one list
         full_req = list(host_req.items()) + list(test_req.items()) + list(build_req.items())
@@ -212,13 +227,13 @@ class PremakeDeps:
             # Create list of all available profiles by searching on disk
             file_pattern = PREMAKE_VAR_FILE.format(pkgname=dep_name, config="*")
             file_regex = PREMAKE_VAR_FILE.format(pkgname=re.escape(dep_name), config="(([^_]*)_(.*))")
-            available_files = glob.glob(file_pattern)
+            available_config_files = glob.glob(file_pattern)
             # Add filename of current generations var file if not already present
-            if var_filename not in available_files:
-                available_files.append(var_filename)
+            if var_filename not in available_config_files:
+                available_config_files.append(var_filename)
             profiles = [
                 (regex_res[0], regex_res.group(1), regex_res.group(2), regex_res.group(3)) for regex_res in [
-                    re.search(file_regex, file_name) for file_name in available_files
+                    re.search(file_regex, file_name) for file_name in available_config_files
                 ]
             ]
             config_sets = [profile[1] for profile in profiles]
@@ -235,6 +250,8 @@ class PremakeDeps:
         self._output_lua_file(PREMAKE_ROOT_FILE, [
             # Includes
             *[f'include "{pkg_file}"' for pkg_file in pkg_files],
+            # Global order for each configuration
+            'include "conanconfig.premake5.lua"',
             # Functions
             PREMAKE_TEMPLATE_ROOT_FUNCTION.format(
                 function_name="conan_setup_build",
@@ -257,4 +274,19 @@ class PremakeDeps:
             PREMAKE_TEMPLATE_ROOT_GLOBAL
         ])
 
+        # Output configuration file for the current build configuration
+        self._output_lua_file(PREMAKE_CONFIG_FILE.format(config=conf_name), [
+            PREMAKE_TEMPLATE_CONFIG.format(
+                config=conf_name, order=", ".join(f'"{name}"' for name in reversed(dep_names))
+            )
+        ])
+
+        # Output root configuration file
+        available_config_files = glob.glob(PREMAKE_CONFIG_FILE.format(config="*"))
+        available_configs = [file_name.split("_", 1)[1].split(".")[0] for file_name in available_config_files]
+        available_configs.append(conf_name)
+        self._output_lua_file(PREMAKE_CONFIG_ROOT_FILE, [
+            *['include "{}"'.format(PREMAKE_CONFIG_FILE.format(config=config)) for config in available_configs],
+        ])
+
         return self.output_files


Test patch:
diff --git a/test/integration/conanfile/conan_data_test.py b/test/integration/conanfile/conan_data_test.py
index e05d4d977..d1abb4ffd 100644
--- a/test/integration/conanfile/conan_data_test.py
+++ b/test/integration/conanfile/conan_data_test.py
@@ -1,3 +1,4 @@
+import json
 import os
 import shutil
 import sys
@@ -193,6 +194,22 @@ class Lib(ConanFile):
         client.run("export-pkg . --name=name --version=version")
         self.assertIn("My URL: this url", client.out)
 
+    def test_conan_data_serialize(self):
+        c = TestClient(light=True)
+        conandata = textwrap.dedent("""
+            src:
+                url: "this url"
+        """)
+        c.save({"conanfile.py": GenConanfile("pkg", "0.1"),
+                "conandata.yml": conandata})
+        c.run("graph info . --format=json")
+        graph = json.loads(c.stdout)
+        assert graph["graph"]["nodes"]["0"]["conandata"]["src"] == {"url": "this url"}
+
+        c.run("inspect . --format=json")
+        result = json.loads(c.stdout)
+        assert result["conandata"]["src"] == {"url": "this url"}
+
 
 class TestConanDataUpdate:
     """
diff --git a/test/integration/toolchains/premake/test_premakedeps.py b/test/integration/toolchains/premake/test_premakedeps.py
index 9e42410b8..e4a8c9b35 100644
--- a/test/integration/toolchains/premake/test_premakedeps.py
+++ b/test/integration/toolchains/premake/test_premakedeps.py
@@ -1,5 +1,6 @@
 import textwrap
 
+from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
 
 
@@ -63,3 +64,73 @@ def test_premakedeps():
     assert_vars_file(client, 'debug')
     assert_vars_file(client, 'release')
 
+
+def test_premakedeps_link_order():
+    client = TestClient()
+    profile = textwrap.dedent(
+        """
+        [settings]
+        os=Linux
+        arch=x86_64
+        compiler=gcc
+        compiler.version=9
+        compiler.cppstd=17
+        compiler.libcxx=libstdc++
+        build_type=Release
+        """)
+    client.save(
+        {
+            "liba/conanfile.py": GenConanfile("liba")
+            .with_settings("os", "compiler", "build_type", "arch")
+            .with_version("1.0")
+            .with_generator("PremakeDeps")
+            .with_generator("PremakeToolchain")
+            .with_package_info(cpp_info={"libs": ["liba"]}),
+
+            "libb/conanfile.py": GenConanfile("libb")
+            .with_settings("os", "compiler", "build_type", "arch")
+            .with_version("1.0")
+            .with_requires("liba/1.0")
+            .with_generator("PremakeDeps")
+            .with_package_info(
+                cpp_info={
+                    "components": {
+                        "libb1": {"requires": ["liba::liba"]},
+                        "libb2": {"requires": ["libb1"]},
+                    }
+                }
+            ),
+
+            "libc/conanfile.py": GenConanfile("libc")
+            .with_settings("os", "compiler", "build_type", "arch")
+            .with_version("1.0")
+            .with_requires("libb/1.0")
+            .with_generator("PremakeDeps")
+            .with_package_info(cpp_info={"libs": ["libc"]}),
+
+            "consumer/conanfile.py": GenConanfile("consumer")
+            .with_settings("os", "compiler", "build_type", "arch")
+            .with_version("1.0")
+            .with_requires("libc/1.0")
+            .with_generator("PremakeDeps"),
+
+            "profile": profile
+        }
+    )
+    client.run("create liba -pr profile")
+    client.run("create libb -pr profile")
+    client.run("create libc -pr profile")
+    client.run("install consumer -pr profile")
+    contents = client.load("consumer/conanconfig.premake5.lua")
+    assert 'include "conanconfig_release_x86_64.premake5.lua"' in contents
+    contents = client.load("consumer/conanconfig_release_x86_64.premake5.lua")
+    # Check correct order of dependencies: more dependent libs should be linked first
+    assert 't_conan_deps_order["release_x86_64"] = {"libc", "libb", "liba"}' in contents
+
+    # Check previous configurations are preserved when installing new configuration
+    client.run("install consumer -pr profile -s build_type=Debug --build=missing")
+    contents = client.load("consumer/conanconfig.premake5.lua")
+    assert 'include "conanconfig_release_x86_64.premake5.lua"' in contents
+    assert 'include "conanconfig_debug_x86_64.premake5.lua"' in contents
+    contents = client.load("consumer/conanconfig_debug_x86_64.premake5.lua")
+    assert 't_conan_deps_order["debug_x86_64"] = {"libc", "libb", "liba"}' in contents