Repository: conan-io/conan
Pull request #18662 (2025-07-17 22:23:43)

Add `cmake_target_aliases` support for `CMakeConfigDeps`
Changelog: Feature: Add `cmake_target_aliases` support for `CMakeConfigDeps`
Docs: TODO


Closes https://github.com/conan-io/conan/issues/18659, but a few questions remain open that need to be discussed before merging can be performed


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/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py
index 30b71b0ea..cf11208af 100644
--- a/conan/tools/cmake/cmakedeps2/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps2/target_configuration.py
@@ -122,6 +122,23 @@ class TargetConfigurationTemplate2:
 
         prefixes = self._cmakedeps.get_property("cmake_additional_variables_prefixes",
                                                 self._conanfile, check_type=list) or []
+        seen_aliases = set()
+        root_target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile)
+        root_target_name = root_target_name or f"{pkg_name}::{pkg_name}"
+        for lib in libs.values():
+            for alias in lib.get("cmake_target_aliases", []):
+                if alias == root_target_name:
+                    raise ConanException(f"Can't define an alias '{alias}' for the "
+                                         f"root target '{root_target_name}' in {self._conanfile}. "
+                                         f"Changing the default target should be done with the "
+                                         f"'cmake_target_name' property.")
+                if alias in seen_aliases:
+                    raise ConanException(f"Alias '{alias}' already defined in {self._conanfile}. ")
+                seen_aliases.add(alias)
+                if alias in libs:
+                    raise ConanException(f"Alias '{alias}' already defined as a target in "
+                                         f"{self._conanfile}. ")
+
         f = self._cmakedeps.get_cmake_filename(self._conanfile)
         prefixes = [f] + prefixes
         include_dirs = definitions = libraries = None
@@ -164,12 +181,16 @@ class TargetConfigurationTemplate2:
                 target = self._get_cmake_lib(component, cpp_info.components, pkg_folder,
                                              pkg_folder_var)
                 if target is not None:
+                    cmake_target_aliases = self._get_aliases(name)
+                    target["cmake_target_aliases"] = cmake_target_aliases
                     libs[target_name] = target
         else:
             target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile)
             target_name = target_name or f"{pkg_name}::{pkg_name}"
             target = self._get_cmake_lib(cpp_info, None, pkg_folder, pkg_folder_var)
             if target is not None:
+                cmake_target_aliases = self._get_aliases()
+                target["cmake_target_aliases"] = cmake_target_aliases
                 libs[target_name] = target
         return libs
 
@@ -233,11 +254,16 @@ class TargetConfigurationTemplate2:
             target["link_languages"] = link_languages
         return target
 
+    def _get_aliases(self, comp_name=None):
+        aliases = self._cmakedeps.get_property("cmake_target_aliases", self._conanfile,
+                                               comp_name, check_type=list) or []
+        return aliases
+
     def _add_root_lib_target(self, libs, pkg_name, cpp_info):
         """
-        Addd a new pkgname::pkgname INTERFACE target that depends on default_components or
+        Add a new pkgname::pkgname INTERFACE target that depends on default_components or
         on all other library targets (not exes)
-        It will not be added if there exists already a pkgname::pkgname target.
+        It will not be added if there exists already a pkgname::pkgname target (Or an alias exists).
         """
         root_target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile)
         root_target_name = root_target_name or f"{pkg_name}::{pkg_name}"
@@ -253,8 +279,11 @@ class TargetConfigurationTemplate2:
                     all_requires[comp_name] = True  # It is an interface, full link
             else:
                 all_requires = {k: True for k in libs.keys()}
+            # This target might have an alias, so we need to check it
+            cmake_target_aliases = self._get_aliases()
             libs[root_target_name] = {"type": "INTERFACE",
-                                      "requires": all_requires}
+                                      "requires": all_requires,
+                                      "cmake_target_aliases": cmake_target_aliases}
 
     def _get_exes(self, cpp_info, pkg_name, pkg_folder, pkg_folder_var):
         exes = {}
@@ -333,6 +362,12 @@ class TargetConfigurationTemplate2:
             message(STATUS "Conan: Target declared imported {{lib_info["type"]}} library '{{lib}}'")
             add_library({{lib}} {{lib_info["type"]}} IMPORTED)
         endif()
+        {% for alias in lib_info.get("cmake_target_aliases", []) %}
+        if(NOT TARGET {{alias}})
+            message(STATUS "Conan: Target declared alias '{{alias}}' for '{{lib}}'")
+            add_library({{alias}} ALIAS {{lib}})
+        endif()
+        {% endfor %}
         {% if lib_info.get("includedirs") %}
         set_property(TARGET {{lib}} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
                      {{config_wrapper(config, lib_info["includedirs"])}})
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/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_aliases.py b/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_aliases.py
new file mode 100644
index 000000000..b09dd2207
--- /dev/null
+++ b/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_aliases.py
@@ -0,0 +1,261 @@
+import textwrap
+import pytest
+
+from conan.test.utils.tools import TestClient
+
+new_value = "will_break_next"
+
+consumer = textwrap.dedent("""
+from conan import ConanFile
+from conan.tools.cmake import CMake
+
+class Consumer(ConanFile):
+    name = "consumer"
+    version = "1.0"
+    settings = "os", "compiler", "build_type", "arch"
+    generators = "CMakeDeps", "CMakeToolchain"
+    exports_sources = ["CMakeLists.txt"]
+    requires = "hello/1.0"
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+        cmake.build()
+""")
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_global_alias():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            # the default global target is "hello::hello"
+            self.cpp_info.set_property("cmake_target_aliases", ["hello"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    get_target_property(_aliased_target hello ALIASED_TARGET)
+    message("hello aliased target: ${_aliased_target}")
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    assert "hello aliased target: hello::hello" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_component_alias():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            self.cpp_info.components["buy"].set_property("cmake_target_aliases",
+                ["hola::adios"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    get_target_property(_aliased_target hola::adios ALIASED_TARGET)
+    message("hola::adios aliased target: ${_aliased_target}")
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    assert "hola::adios aliased target: hello::buy" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_global_and_component_alias():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            self.cpp_info.set_property("cmake_target_aliases", ["hola::hola"])
+            self.cpp_info.components["buy"].set_property("cmake_target_aliases",
+                ["hola::adios"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    get_target_property(_aliased_target hola::hola ALIASED_TARGET)
+    message("hola::hola aliased target: ${_aliased_target}")
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    assert "hola::hola aliased target: hello::hello" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_custom_name():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            self.cpp_info.set_property("cmake_target_name", "ola::comprar")
+            self.cpp_info.set_property("cmake_target_aliases", ["hello"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    get_target_property(_aliased_target hello ALIASED_TARGET)
+    message("hello aliased target: ${_aliased_target}")
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    assert "hello aliased target: ola::comprar" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_collide_component_alias():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            self.cpp_info.components["buy"].set_property("cmake_target_aliases", ["hello::buy"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}", assert_error=True)
+
+    assert "Alias 'hello::buy' already defined as a target in hello/1.0" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+def test_collide_component_alias_to_alias():
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            self.cpp_info.components["ola"].set_property("cmake_target_aliases", ["hello::foo"])
+            self.cpp_info.components["buy"].set_property("cmake_target_aliases", ["hello::foo"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}", assert_error=True)
+
+    assert "Alias 'hello::foo' already defined in hello/1.0" in client.out
+
+
+@pytest.mark.tool("cmake", "3.27")
+@pytest.mark.parametrize("root_target", ["hello::custom", None])
+def test_skip_global_if_aliased(root_target):
+    target_line = f'self.cpp_info.set_property("cmake_target_name", "{root_target}")' if root_target else ""
+    target_name = root_target or "hello::hello"
+    conanfile = textwrap.dedent(f"""
+    from conan import ConanFile
+
+    class Hello(ConanFile):
+        name = "hello"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+
+        def package_info(self):
+            {target_line}
+            self.cpp_info.components["foo"].set_property("cmake_target_aliases", ["{target_name}"])
+    """)
+
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(test NONE)
+
+    find_package(hello REQUIRED)
+    get_target_property(_aliased_target hello::hello ALIASED_TARGET)
+    message("hello::hello aliased target: ${_aliased_target}")
+    """)
+
+    client = TestClient()
+    client.save({"conanfile.py": conanfile})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}")
+
+    client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
+    client.run(f"create . -c tools.cmake.cmakedeps:new={new_value}", assert_error=True)
+
+    assert f"Can't define an alias '{target_name}' for the root target '{target_name}' in hello/1.0" in client.out
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/cmake/cmakedeps2/test_cmakedeps.py b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
index ef72cc991..0f329d469 100644
--- a/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
+++ b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
@@ -482,3 +482,42 @@ def test_requires_to_application_component():
     assert "automake::automake" not in targets
     assert "# Requirement automake::mylibapp => Full link: True" in targets
     assert "$<$<CONFIG:RELEASE>:automake::mylibapp>" in targets
+
+
+def test_alias_cmakedeps_set_property():
+    tc = TestClient()
+    tc.save({"dep/conanfile.py": textwrap.dedent("""
+
+        from conan import ConanFile
+        class Dep(ConanFile):
+            name = "dep"
+            version = "1.0"
+            settings = "os", "compiler", "build_type", "arch"
+            def package_info(self):
+                self.cpp_info.components["mycomp"].set_property("cmake_target_name", "dep::mycomponent")
+        """),
+             "conanfile.py": textwrap.dedent("""
+             from conan import ConanFile
+             from conan.tools.cmake import CMakeDeps, CMake
+             class Pkg(ConanFile):
+                name = "pkg"
+                version = "1.0"
+                settings = "os", "compiler", "build_type", "arch"
+
+                requires = "dep/1.0"
+
+                def generate(self):
+                    deps = CMakeDeps(self)
+                    deps.set_property("dep", "cmake_target_aliases", ["alias", "dep::other_name"])
+                    deps.set_property("dep::mycomp", "cmake_target_aliases", ["component_alias", "dep::my_aliased_component"])
+                    deps.generate()
+             """)})
+    tc.run("create dep")
+    tc.run(f"install . -c tools.cmake.cmakedeps:new={new_value}")
+    targets_data = tc.load('dep-Targets-release.cmake')
+    assert "add_library(dep::dep" in targets_data
+    assert "add_library(alias" in targets_data
+    assert "add_library(dep::other_name" in targets_data
+
+    assert "add_library(component_alias" in targets_data
+    assert "add_library(dep::my_aliased_component" in targets_data
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