Skip to content

[POC][CMakeConfigDeps] Create CONFIG files per component#19681

Draft
franramirez688 wants to merge 33 commits into
conan-io:develop2from
franramirez688:frm/cmake_file_name_cmp
Draft

[POC][CMakeConfigDeps] Create CONFIG files per component#19681
franramirez688 wants to merge 33 commits into
conan-io:develop2from
franramirez688:frm/cmake_file_name_cmp

Conversation

@franramirez688
Copy link
Copy Markdown
Contributor

@franramirez688 franramirez688 commented Feb 26, 2026

Changelog: Feature: CMakeConfigDeps can create complete CONFIG files per component.
Docs: https://github.com/conan-io/docs/pull/XXXX
Closes: #19407

Checked on CCI repo

spirv-tools

Applying this diff, it should work locally on your native env:

diff --git a/recipes/spirv-tools/all/conanfile.py b/recipes/spirv-tools/all/conanfile.py
index 63ee667a0..a37ee186c 100644
--- a/recipes/spirv-tools/all/conanfile.py
+++ b/recipes/spirv-tools/all/conanfile.py
@@ -167,6 +167,13 @@ class SpirvtoolsConan(ConanFile):
                 self.cpp_info.components["spirv-tools-core"].system_libs.append(libcxx)

         # FIXME: others components should have their own CMake config file
+        self.cpp_info.set_property("cmake_file_component_names", {
+            "SPIRV-Tools-opt": ["spirv-tools-opt"],
+            "SPIRV-Tools-link": ["spirv-tools-link"],
+            "SPIRV-Tools-reduce": ["spirv-tools-reduce"],
+            "SPIRV-Tools-lint": ["spirv-tools-lint"],
+            "SPIRV-Tools-diff": ["spirv-tools-diff"]
+        })
         if not self.options.shared:
             # SPIRV-Tools-opt
             self.cpp_info.components["spirv-tools-opt"].set_property("cmake_target_name", "SPIRV-Tools-opt")
diff --git a/recipes/spirv-tools/all/test_package/CMakeLists.txt b/recipes/spirv-tools/all/test_package/CMakeLists.txt
index 8e5e8c835..8f599056f 100644
--- a/recipes/spirv-tools/all/test_package/CMakeLists.txt
+++ b/recipes/spirv-tools/all/test_package/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.15)
 project(test_package)

 find_package(SPIRV-Tools REQUIRED CONFIG)
+find_package(SPIRV-Tools-opt REQUIRED CONFIG)

 add_executable(${PROJECT_NAME}_c test_package.c)
 if(TARGET SPIRV-Tools-shared)
diff --git a/recipes/spirv-tools/all/test_package/conanfile.py b/recipes/spirv-tools/all/test_package/conanfile.py
index d8eaf2ab1..98a5e384f 100644
--- a/recipes/spirv-tools/all/test_package/conanfile.py
+++ b/recipes/spirv-tools/all/test_package/conanfile.py
@@ -6,7 +6,7 @@ import os

 class TestPackageConan(ConanFile):
     settings = "os", "arch", "compiler", "build_type"
-    generators = "CMakeToolchain", "CMakeDeps", "VirtualRunEnv"
+    generators = "CMakeToolchain", "CMakeConfigDeps", "VirtualRunEnv"
     test_type = "explicit"

     def layout(self):
-- Conan: Configuring Targets for spirv-tools/1.4.313.0
-- Conan: Target declared imported STATIC library 'SPIRV-Tools-static'
-- Conan: Target declared alias 'SPIRV-Tools' for 'SPIRV-Tools-static'
-- Conan: Target declared imported INTERFACE library 'spirv-tools::spirv-tools'
-- Conan: Configuring Targets for spirv-tools/1.4.313.0 (Component: spirv-tools-opt)
-- Conan: Target declared imported STATIC library 'SPIRV-Tools-opt'
-- Configuring done (0.8s)
-- Generating done (0.0s)
-- Build files have been written to: /xxxx/conan-center-index/recipes/spirv-tools/all/test_package/build/apple-clang-17-armv8-gnu17-release

spirv-tools/1.4.313.0 (test package): Running CMake.build()
spirv-tools/1.4.313.0 (test package): RUN: cmake --build "/xxxx/conan-center-index/recipes/spirv-tools/all/test_package/build/apple-clang-17-armv8-gnu17-release" -- -j14
[ 25%] Building C object CMakeFiles/test_package_c.dir/test_package.c.o
[ 50%] Building CXX object CMakeFiles/test_package_cpp.dir/test_package.cpp.o
[ 75%] Linking C executable test_package_c
[ 75%] Built target test_package_c
[100%] Linking CXX executable test_package_cpp
ld: warning: ignoring duplicate libraries: '-lc++'
[100%] Built target test_package_cpp


======== Testing the package: Executing test ========
spirv-tools/1.4.313.0 (test package): Running test()
spirv-tools/1.4.313.0 (test package): RUN: ./test_package_c

spirv-tools/1.4.313.0 (test package): RUN: ./test_package_cpp
OpCapability Linkage
OpCapability Shader
OpMemoryModel Logical GLSL450
%int = OpTypeInt 32 1
%int_42 = OpConstant %int 42
$ tree
tree
.
├── . . . 
├── SPIRV-Tools-diff-Targets-release.cmake
├── SPIRV-Tools-diffConfig.cmake
├── SPIRV-Tools-diffConfigVersion.cmake
├── SPIRV-Tools-diffTargets.cmake
├── SPIRV-Tools-link-Targets-release.cmake
├── SPIRV-Tools-linkConfig.cmake
├── SPIRV-Tools-linkConfigVersion.cmake
├── SPIRV-Tools-linkTargets.cmake
├── SPIRV-Tools-lint-Targets-release.cmake
├── SPIRV-Tools-lintConfig.cmake
├── SPIRV-Tools-lintConfigVersion.cmake
├── SPIRV-Tools-lintTargets.cmake
├── SPIRV-Tools-opt-Targets-release.cmake
├── SPIRV-Tools-optConfig.cmake
├── SPIRV-Tools-optConfigVersion.cmake
├── SPIRV-Tools-optTargets.cmake
├── SPIRV-Tools-reduce-Targets-release.cmake
├── SPIRV-Tools-reduceConfig.cmake
├── SPIRV-Tools-reduceConfigVersion.cmake
├── SPIRV-Tools-reduceTargets.cmake
├── SPIRV-Tools-Targets-release.cmake
├── SPIRV-ToolsConfig.cmake
├── SPIRV-ToolsConfigVersion.cmake
└── SPIRV-ToolsTargets.cmake

@franramirez688 franramirez688 added this to the 2.27.0 milestone Feb 26, 2026
Comment thread conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py
Comment thread conan/tools/cmake/cmakeconfigdeps/target_configuration.py
Comment thread conan/tools/cmake/cmakeconfigdeps/target_configuration.py Outdated
Copy link
Copy Markdown
Member

@memsharded memsharded left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking better, good job!

I still think we might want to do it in 2 stages, and even also some previous refactor over the complicated parts of CMakeConfigDeps core class before the introduction, but lets talk about it.

Comment thread conan/tools/cmake/cmakeconfigdeps/config.py Outdated
Comment thread conan/tools/cmake/cmakeconfigdeps/config_version.py Outdated
foo-config-version.cmake
"""
def __init__(self, cmakedeps, conanfile):
def __init__(self, cmakedeps, conanfile, config_comp_name, cmake_file_name):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic, I think this is really great

Comment thread conan/tools/cmake/cmakeconfigdeps/target_configuration.py
Comment thread conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py Outdated
Copy link
Copy Markdown
Member

@memsharded memsharded left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now struggling a bit to understand the changes and risks. Maybe now a previous PR that is a pure refactor would make change.

Comment thread conan/tools/cmake/cmakeconfigdeps/config.py
Comment thread conan/tools/cmake/cmakeconfigdeps/config_version.py Outdated
@franramirez688 franramirez688 force-pushed the frm/cmake_file_name_cmp branch from ef83505 to 95ca476 Compare March 11, 2026 16:01
components belonging to it. The first one mentioned in the
list will act as the root one. For example:
self.cpp_info.set_property("cmake_file_component_names", {
"CMAKE_FILE_NAME1": ["COMP1", "COMP2"],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be the component name, and let the component define the cmake file name via a property? (Or the default one if the component name can be used as a config name)

Get the name of the files for the find_package(XXX) for the root and the rest of
the components.
This is used by CMakeConfigDeps to determine:
- The filename to generate (XXX-config.cmake or FindXXX.cmake)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- The filename to generate (XXX-config.cmake or FindXXX.cmake)
- The filename to generate (XXX-config.cmake)

no module mode

@franramirez688 franramirez688 force-pushed the frm/cmake_file_name_cmp branch from ed62c75 to 3d83384 Compare March 12, 2026 18:23
def package_info(self):
self.cpp_info.components["core"].libs = ["core"]
self.cpp_info.components["core"].type = "static-library"
self.cpp_info.components["core"].location = "lib/libcore.a"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a requires to extra here and check that the find_dependency appears in pkg-config?

@franramirez688 franramirez688 requested a review from czoido March 16, 2026 09:43
Comment thread conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py Outdated
@franramirez688 franramirez688 modified the milestones: 2.27.0, 2.28.0 Mar 23, 2026
@franramirez688 franramirez688 requested a review from czoido April 17, 2026 06:25
Comment on lines +18 to +20
self._cmake_file_name = cmake_file_name
self._cmake_file_info = cmake_file_info
self._full_cpp_info = cmake_file_info["full_cpp_info"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice for the POC, but it seems a bit of hiper-parametrization, passing too many different and interconnected objects.
But most of it can be abstracted to:

  • self._cmakedeps is needed for:
    • A set of properties for this xxx-config.cmake file
    • The filename computation
    • Access to the consumer conanfile
  • self._conanfile is needed for:
    • The package name and version
    • The cpp_info.components (not sure why)

So it seems that the first thing we want to do is to clearly parameterize the ConfigTemplate2 class (and the other helpers classes) to provide a more direct and explicit parameterization of what is needed. Then the CMakeConfigDeps is free to pass it the information for 1 file or multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done a quick POC of this refactor in #19880.

I know it is not that simple for the other files, but I think the idea is still valid

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds fine.

The cpp_info.components (not sure why)

It's not really the cpp_info.components object; they are the components belonging to each CMake file.

@czoido
Copy link
Copy Markdown
Contributor

czoido commented Apr 20, 2026

This is looking really good in my opinion.

About the naming discussion, maybe instead of cmake_file_component_names it could be renamed to something like cmake_config_files or cmake_config_packages?

About how the root config is resolved, maybe it could be determined from the effective cmake_file_name, instead of only being the fallback file that receives the components not assigned to any other config.

For example, if cmake_file_name is explicitly defined:

    self.cpp_info.set_property("cmake_file_name", "CoreLib")

    self.cpp_info.set_property("cmake_config_files", {
        "CoreLib": {
            "components": ["core", "core-utils"],
        },
        "CoreLibExtra": {
            "components": ["extra"],
        },
    })

Then CoreLib would be considered the root config, because it matches the effective cmake_file_name.

If cmake_file_name is not explicitly declared:

    name = "mypackage"
    ...
    self.cpp_info.set_property("cmake_config_files", {
        "mypackage": {
            "components": ["core"],
        },
        "mypackage-tools": {
            "components": ["tools"],
        },
    })

Then mypackage would be considered the root config, because it matches the default config name derived from the Conan package name.

What do you think?

Copy link
Copy Markdown
Member

@memsharded memsharded left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reviewed now only the tests, and I think from the UX/UI point of view, it is looking good.


def package_info(self):
# Split into separate config files per CMake package name
self.cpp_info.set_property("cmake_file_component_names", {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the cmake_file_name could be reused, and if it is a dict then the meaning is this one?
That would make the recipe doing that breaking for previous Conan versions, but I also think this property won't work for consumers that use an older Conan version, so a required_conan_version would still be necessary?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I like the approach of centralizing the definition of the different files and components in one place.

@franramirez688 franramirez688 modified the milestones: 2.28.0, 2.29.0 Apr 27, 2026
@franramirez688 franramirez688 modified the milestones: 2.29.0, 2.30.0 May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Investigate config file creation per component in CMakeConfigDeps

4 participants