Repository: conan-io/conan

Issue:
[feature] Allow specifying an install component for the CMake helper
One is capable of passing `--component` to CMake's `--install` command, however the same functionality is missing from the CMake helper's `install()` method: https://docs.conan.io/en/latest/reference/build_helpers/cmake.html#install

Since Conan does not package CMake configs generated by recipes, in many cases the recipe can just define the runtime component and there won't be a need to `rmdir` additional files.

I suggest adding a `component=None` argument to this method and restructuring it in terms of the cmake generated `cmake_install.cmake` script. The code would look something like this:

```py
def install(... component=None):
    if component is not None:
        if isinstance(component, str):
            component_args = [f"-DCOMPONENT={component}"]
        else:
            component_args = [f"-DCOMPONENT={c}" for c in component]
    else:
        component_args = [""]
    for comp in component_args:
        self.run(f"cmake -DBUILD_TYPE={build_type} {comp} -P cmake_install.cmake", cwd=bindir)
```

or maybe a new method like `install_component()` with a required positional argument, if such a break for v2 is not desirable.  
`find -name conanfile.py -exec grep -nHEe '\.install\([^)]' {} \;` shows no usage for this method being used with any arguments in CCI though.

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).



Discussion:
> Since Conan does not package CMake configs generated by recipes, in many cases the recipe can just define the runtime component and there won't be a need to rmdir additional files.

Just to clarify, this is a policy to ConanCenter, because it is important that recipes there work for any build system, not only CMake, and the general use of the packaged config.cmake files is broken (they might find dependencies in the system, for example, instead of Conan). Users with their own packages they can package the config.cmake files.

It is not clear what the implementation approach would be. The current implementation uses cmake ``--install`` feature:

```python
        bt = build_type or self._conanfile.settings.get_safe("build_type")
        if not bt:
            raise ConanException("build_type setting should be defined.")
        is_multi = is_multi_configuration(self._generator)
        build_config = "--config {}".format(bt) if bt and is_multi else ""

        pkg_folder = args_to_string([self._conanfile.package_folder.replace("\\", "/")])
        build_folder = args_to_string([self._conanfile.build_folder])
        arg_list = ["--install", build_folder, build_config, "--prefix", pkg_folder]
        arg_list = " ".join(filter(None, arg_list))
        command = "%s %s" % (self._cmake_program, arg_list)
        self._conanfile.output.info("CMake command: %s" % command)
        self._conanfile.run(command)
```

Note, this is the ``from conan.tools.cmake import CMake`` modern integration, we are not modifying anymore the previous integration.

Oh, I meant the CMake build helper. That one just does this for `install()`:

```py
self._build(args=args, build_dir=build_dir, target="install")
```

Is this modern integration intended for v2? I see that it still does not use components.
> Is this modern integration intended for v2? I see that it still does not use components.

Yes, only the integrations in https://docs.conan.io/en/latest/reference/conanfile/tools.html will be available in 2.0. All the others have been already removed in released 2.0-alpha. The on that you are referring to has been removed, we will only evolve now the new ones, which are imported from ``from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps``
I think this is still a pertinent issue for the new `CMake` build helper as projects may use component-based installs and the default install component may not install the necessary files. Adding the ability to specify a `component` argument to the `CMake.install()` command would be ideal. This could just translate to `cmake --install ... --component <specified_component>`. I have a use case right now where this is necessary as the default component actually installs a bunch of things that should not be installed but are because they are included from various subprojects.
Hi,

I'm looking for a way to package CMake components into different subfolders. This is in turn needed to simplify product packaging in the downstream package. The downstream package takes build artifacts from different upstream packages that use different toolchains, not just C/C++ components built with CMake. So I cannot use `cmake --install --component` in the downstream package. I'd rather rely on the folder structure of the upstream packages.

Our current approach is to install CMake targets into subfolders using `install(TARGETS ...)`, but his approach is verbose and not quite convenient for local development.

So, I've started looking into using CMake components and hoped that I could rely on Conan CMake helper to install different CMake components into different Conan package subfolders.

I'll try to workaround this with self.run("cmake --install --component")
Here is a workaround that seems to work:
```python
    def package(self):
        self.run(f"cmake --install {self.build_folder} --component component1"
                 f" --prefix {os.path.join(self.package_folder, 'component1')}")
    
    def package_info(self):
        self.cpp_info.builddirs.append("component1")
```
This feature would be of use in the case of a library that provides a `header_only` option.

We would like to be able to do the following in our `conanfile.py`

```python
    def package(self):
        cmake = self.configure_cmake()
        if self.options.header_only:
            cmake.install(component="public_header")
        else:
            cmake.install()
```

Based on what is currently in `conan.tools.cmake` it seems that this kind of add would be realtively small and easy to implement.

Patch:
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -132,7 +132,7 @@ def _build(self, build_type=None, target=None, cli_args=None, build_tool_args=No
     def build(self, build_type=None, target=None, cli_args=None, build_tool_args=None):
         self._build(build_type, target, cli_args, build_tool_args)
 
-    def install(self, build_type=None):
+    def install(self, build_type=None, component=None):
         mkdir(self._conanfile, self._conanfile.package_folder)
 
         bt = build_type or self._conanfile.settings.get_safe("build_type")
@@ -144,6 +144,8 @@ def install(self, build_type=None):
         pkg_folder = '"{}"'.format(self._conanfile.package_folder.replace("\\", "/"))
         build_folder = '"{}"'.format(self._conanfile.build_folder)
         arg_list = ["--install", build_folder, build_config, "--prefix", pkg_folder]
+        if component:
+            arg_list.extend(["--component", component])
         arg_list = " ".join(filter(None, arg_list))
         command = "%s %s" % (self._cmake_program, arg_list)
         self._conanfile.output.info("CMake command: %s" % command)


Test patch:
diff --git a/conans/test/unittests/tools/cmake/test_cmake_install.py b/conans/test/unittests/tools/cmake/test_cmake_install.py
new file mode 100644
--- /dev/null
+++ b/conans/test/unittests/tools/cmake/test_cmake_install.py
@@ -0,0 +1,39 @@
+import platform
+import pytest
+
+from conan.tools.cmake import CMake
+from conan.tools.cmake.presets import write_cmake_presets
+from conans.client.conf import get_default_settings_yml
+from conans.model.conf import Conf
+from conans.model.settings import Settings
+from conans.test.utils.mocks import ConanFileMock
+from conans.test.utils.test_files import temp_folder
+
+
+def test_run_install_component():
+    """
+    Testing that the proper component is installed.
+    Issue related: https://github.com/conan-io/conan/issues/10359
+    """
+    # Load some generic windows settings
+    settings = Settings.loads(get_default_settings_yml())
+    settings.os = "Windows"
+    settings.arch = "x86"
+    settings.build_type = "Release"
+    settings.compiler = "Visual Studio"
+    settings.compiler.runtime = "MDd"
+    settings.compiler.version = "14"
+
+    conanfile = ConanFileMock()
+    conanfile.conf = Conf()
+    conanfile.folders.generators = "."
+    conanfile.folders.set_base_generators(temp_folder())
+    conanfile.settings = settings
+    conanfile.folders.set_base_package(temp_folder())
+
+    # Choose generator to match generic setttings
+    write_cmake_presets(conanfile, "toolchain", "Visual Studio 14 2015", {})
+    cmake = CMake(conanfile)
+    cmake.install(component="foo")
+
+    assert "--component foo" in conanfile.command