Repository: conan-io/conan

Issue:
[feature] Components: POC components in the cmake generator
After #6716 and #6717, we should be able to do a POC with the CMake generator and the usage of the "smart targets": Targets that depend only on the components needed (not the whole library. 

With the possibility of linking just with the needed components of other requirements depending on the implementation of #6717 


Discussion:
I have a very poc for cmake/cmake_find_package, and some questions on this feature [here](https://github.com/syoliver/conan/tree/add-components-to-generators). How can I help @danimtb ?
Hi @syoliver and thanks a lot for pointing me to your branch!

In fact, there are some good ideas there.

I have opened a new PR #7108 with my implementation using a jinja2 template. It will be great if you could review it or even try my branch from your side 😄
@danimtb Your PR #7108 seems to work for the cmake_find_package generator part of my recipe :+1:
@syoliver thanks a lot for testing!! We are in the review process right now and we will hopefully have components experimental implementation for the `cmake_find_package`` generator in the next Conan release!
> @syoliver thanks a lot for testing!! We are in the review process right now and we will hopefully have components experimental implementation for the `cmake_find_package`` generator in the next Conan release!

Will you work on cmake generator after? If I can help you... 
We have to consider the implementation for the `cmake` generator. Currently we are pushing towards promoting generators such as `cmake_find_package/_multi` generators as we think the interface using `find_package()` is cleaner.

Moreover, we are implementing a new "toolchain" concept #7076 that will cover the functionality of macros such as `conan_basic_setup()`.

Anyway, as I see you are interested we can ping you in the PRs related to components and CMake so we can discuss it.
#7108 only provided ``cmake_find_package`` support, not ``cmake``. Reopening this.

Resolving patch:
diff --git a/conans/client/generators/cmake_find_package_multi.py b/conans/client/generators/cmake_find_package_multi.py
--- a/conans/client/generators/cmake_find_package_multi.py
+++ b/conans/client/generators/cmake_find_package_multi.py
@@ -1,20 +1,25 @@
+from jinja2 import Template
 import textwrap
 
+from conans.client.generators import CMakeFindPackageGenerator
 from conans.client.generators.cmake import DepsCppCmake
 from conans.client.generators.cmake_find_package_common import (find_transitive_dependencies,
                                                                 target_template,
                                                                 CMakeFindPackageCommonMacros)
 from conans.client.generators.cmake_multi import extend
+from conans.errors import ConanException
 from conans.model import Generator
+from conans.model.build_info import COMPONENT_SCOPE
 
 
-class CMakeFindPackageMultiGenerator(Generator):
+class CMakeFindPackageMultiGenerator(CMakeFindPackageGenerator):
+    name = "cmake_find_package_multi"
     config_template = textwrap.dedent("""
         {macros_and_functions}
 
         # Requires CMake > 3.0
         if(${{CMAKE_VERSION}} VERSION_LESS "3.0")
-           message(FATAL_ERROR "The 'cmake_find_package_multi' generator only works with CMake > 3.0" )
+            message(FATAL_ERROR "The 'cmake_find_package_multi' generator only works with CMake > 3.0")
         endif()
 
         include(${{CMAKE_CURRENT_LIST_DIR}}/{name}Targets.cmake)
@@ -33,9 +38,8 @@ class CMakeFindPackageMultiGenerator(Generator):
         file(GLOB CONFIG_FILES "${{_DIR}}/{name}Target-*.cmake")
 
         foreach(f ${{CONFIG_FILES}})
-          include(${{f}})
+            include(${{f}})
         endforeach()
-
         """)
 
     target_properties = """
@@ -91,6 +95,169 @@ class CMakeFindPackageMultiGenerator(Generator):
         endif()
         """)
 
+    components_target_build_type_tpl = Template(textwrap.dedent("""\
+        ########## MACROS ###########################################################################
+        #############################################################################################
+        {{ conan_message }}
+        {{ conan_find_apple_frameworks }}
+        {{ conan_package_library_targets }}
+
+        ########### VARIABLES #######################################################################
+        #############################################################################################
+
+        {{ global_target_variables }}
+        set({{ pkg_name }}_COMPONENTS_{{ build_type }} {{ pkg_components }})
+
+        {%- for comp_name, comp in components %}
+
+        ########### COMPONENT {{ comp_name }} VARIABLES #############################################
+
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDE_DIRS_{{ build_type }} {{ comp.include_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDE_DIR_{{ build_type }} {{ comp.include_path }})
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDES_{{ build_type }} {{ comp.include_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_LIB_DIRS_{{ build_type }} {{ comp.lib_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_RES_DIRS_{{ build_type }} {{ comp.res_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_DEFINITIONS_{{ build_type }} {{ comp.defines }})
+        set({{ pkg_name }}_{{ comp_name }}_COMPILE_DEFINITIONS_{{ build_type }} {{ comp.compile_definitions }})
+        set({{ pkg_name }}_{{ comp_name }}_COMPILE_OPTIONS_LIST_{{ build_type }} "{{ comp.cxxflags_list }}" "{{ comp.cflags_list }}")
+        set({{ pkg_name }}_{{ comp_name }}_LIBS_{{ build_type }} {{ comp.libs }})
+        set({{ pkg_name }}_{{ comp_name }}_SYSTEM_LIBS_{{ build_type }} {{ comp.system_libs }})
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORK_DIRS_{{ build_type }} {{ comp.framework_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS_{{ build_type }} {{ comp.frameworks }})
+        set({{ pkg_name }}_{{ comp_name }}_BUILD_MODULES_PATHS_{{ build_type }} {{ comp.build_modules_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_DEPENDENCIES_{{ build_type }} {{ comp.public_deps }})
+        set({{ pkg_name }}_{{ comp_name }}_LINKER_FLAGS_LIST_{{ build_type }}
+                $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:{{ comp.sharedlinkflags_list }}>
+                $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,MODULE_LIBRARY>:{{ comp.sharedlinkflags_list }}>
+                $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:{{ comp.exelinkflags_list }}>
+        )
+
+        ########## COMPONENT {{ comp_name }} FIND LIBRARIES & FRAMEWORKS / DYNAMIC VARS #############
+
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS_FOUND_{{ build_type }} "")
+        conan_find_apple_frameworks({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS_FOUND_{{ build_type }} "{{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORKS}' }}" "{{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORK_DIRS}' }}")
+
+        set({{ pkg_name }}_{{ comp_name }}_LIB_TARGETS_{{ build_type }} "")
+        set({{ pkg_name }}_{{ comp_name }}_NOT_USED_{{ build_type }} "")
+        set({{ pkg_name }}_{{ comp_name }}_LIBS_FRAMEWORKS_DEPS_{{ build_type }} {{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORKS_FOUND_'+build_type+'}' }} {{ '${'+pkg_name+'_'+comp_name+'_SYSTEM_LIBS_'+build_type+'}' }} {{ '${'+pkg_name+'_'+comp_name+'_DEPENDENCIES_'+build_type+'}' }})
+        conan_package_library_targets("{{ '${'+pkg_name+'_'+comp_name+'_LIBS_'+build_type+'}' }}"
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LIB_DIRS_'+build_type+'}' }}"
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LIBS_FRAMEWORKS_DEPS_'+build_type+'}' }}"
+                                      {{ pkg_name }}_{{ comp_name }}_NOT_USED_{{ build_type }}
+                                      {{ pkg_name }}_{{ comp_name }}_LIB_TARGETS_{{ build_type }}
+                                      ""
+                                      "{{ pkg_name }}_{{ comp_name }}")
+
+        set({{ pkg_name }}_{{ comp_name }}_LINK_LIBS_{{ build_type }} {{ '${'+pkg_name+'_'+comp_name+'_LIB_TARGETS_'+build_type+'}' }} {{ '${'+pkg_name+'_'+comp_name+'_DEPENDENCIES_'+build_type+'}' }})
+
+        {%- endfor %}
+        """))
+
+    components_targets_tpl = Template(textwrap.dedent("""\
+        {%- for comp_name, comp in components %}
+
+        if(NOT TARGET {{ pkg_name }}::{{ comp_name }})
+            add_library({{ pkg_name }}::{{ comp_name }} INTERFACE IMPORTED)
+        endif()
+
+        {%- endfor %}
+
+        if(NOT TARGET {{ pkg_name }}::{{ pkg_name }})
+            add_library({{ pkg_name }}::{{ pkg_name }} INTERFACE IMPORTED)
+        endif()
+
+        # Load the debug and release library finders
+        get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+        file(GLOB CONFIG_FILES "${_DIR}/{{ pkg_name }}Target-*.cmake")
+
+        foreach(f ${CONFIG_FILES})
+            include(${f})
+        endforeach()
+
+        if({{ pkg_name }}_FIND_COMPONENTS)
+            foreach(_FIND_COMPONENT {{ '${'+pkg_name+'_FIND_COMPONENTS}' }})
+                list(FIND {{ pkg_name }}_COMPONENTS_{{ build_type }} "{{ pkg_name }}::${_FIND_COMPONENT}" _index)
+                if(${_index} EQUAL -1)
+                    conan_message(FATAL_ERROR "Conan: Component '${_FIND_COMPONENT}' NOT found in package '{{ pkg_name }}'")
+                else()
+                    conan_message(STATUS "Conan: Component '${_FIND_COMPONENT}' found in package '{{ pkg_name }}'")
+                endif()
+            endforeach()
+        endif()
+        """))
+
+    components_config_tpl = Template(textwrap.dedent("""\
+        ########## MACROS ###########################################################################
+        #############################################################################################
+        {{ conan_message }}
+        
+        # Requires CMake > 3.0
+        if(${CMAKE_VERSION} VERSION_LESS "3.0")
+            message(FATAL_ERROR "The 'cmake_find_package_multi' generator only works with CMake > 3.0")
+        endif()
+
+        include(${CMAKE_CURRENT_LIST_DIR}/{{ pkg_name }}Targets.cmake)
+
+        ########## FIND PACKAGE DEPENDENCY ##########################################################
+        #############################################################################################
+
+        include(CMakeFindDependencyMacro)
+
+        {%- for public_dep in pkg_public_deps %}
+
+        if(NOT {{ public_dep }}_FOUND)
+            if(${CMAKE_VERSION} VERSION_LESS "3.9.0")
+                find_package({{ public_dep }} REQUIRED NO_MODULE)
+            else()
+                find_dependency({{ public_dep }} REQUIRED NO_MODULE)
+            endif()
+        else()
+            message(STATUS "Dependency {{ public_dep }} already found")
+        endif()
+
+        {%- endfor %}
+
+        ########## TARGETS PROPERTIES ###############################################################
+        #############################################################################################
+
+        {%- for comp_name, comp in components %}
+        ########## COMPONENT {{ comp_name }} TARGET PROPERTIES ######################################
+
+        set_property(TARGET {{ pkg_name }}::{{ comp_name }} PROPERTY INTERFACE_LINK_LIBRARIES
+                         $<$<CONFIG:Release>:{{ '${'+pkg_name+'_'+comp_name+'_LINK_LIBS_RELEASE}' }} {{ '${'+pkg_name+'_'+comp_name+'_LINKER_FLAGS_LIST_RELEASE}' }}>
+                         $<$<CONFIG:RelWithDebInfo>:{{ '${'+pkg_name+'_'+comp_name+'_LINK_LIBS_RELWITHDEBINFO}' }} {{ '${'+pkg_name+'_'+comp_name+'_LINKER_FLAGS_LIST_RELWITHDEBINFO}' }}>
+                         $<$<CONFIG:MinSizeRel>:{{ '${'+pkg_name+'_'+comp_name+'_LINK_LIBS_MINSIZEREL}' }} {{ '${'+pkg_name+'_'+comp_name+'_LINKER_FLAGS_LIST_MINSIZEREL}' }}>
+                         $<$<CONFIG:Debug>:{{ '${'+pkg_name+'_'+comp_name+'_LINK_LIBS_DEBUG}' }} {{ '${'+pkg_name+'_'+comp_name+'_LINKER_FLAGS_LIST_DEBUG}' }}>)
+        set_property(TARGET {{ pkg_name }}::{{ comp_name }} PROPERTY INTERFACE_INCLUDE_DIRECTORIES
+                         $<$<CONFIG:Release>:{{ '${'+pkg_name+'_'+comp_name+'_INCLUDE_DIRS_RELEASE}' }}>
+                         $<$<CONFIG:RelWithDebInfo>:{{ '${'+pkg_name+'_'+comp_name+'_INCLUDE_DIRS_RELWITHDEBINFO}' }}>
+                         $<$<CONFIG:MinSizeRel>:{{ '${'+pkg_name+'_'+comp_name+'_INCLUDE_DIRS_MINSIZEREL}' }}>
+                         $<$<CONFIG:Debug>:{{ '${'+pkg_name+'_'+comp_name+'_INCLUDE_DIRS_DEBUG}' }}>)
+        set_property(TARGET {{ pkg_name }}::{{ comp_name }} PROPERTY INTERFACE_COMPILE_DEFINITIONS
+                         $<$<CONFIG:Release>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_DEFINITIONS_RELEASE}' }}>
+                         $<$<CONFIG:RelWithDebInfo>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_DEFINITIONS_RELWITHDEBINFO}' }}>
+                         $<$<CONFIG:MinSizeRel>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_DEFINITIONS_MINSIZEREL}' }}>
+                         $<$<CONFIG:Debug>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_DEFINITIONS_DEBUG}' }}>)
+        set_property(TARGET {{ pkg_name }}::{{ comp_name }} PROPERTY INTERFACE_COMPILE_OPTIONS
+                         $<$<CONFIG:Release>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_OPTIONS_LIST_RELEASE}' }}>
+                         $<$<CONFIG:RelWithDebInfo>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_OPTIONS_LIST_RELWITHDEBINFO}' }}>
+                         $<$<CONFIG:MinSizeRel>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_OPTIONS_LIST_MINSIZEREL}' }}>
+                         $<$<CONFIG:Debug>:{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_OPTIONS_LIST_DEBUG}' }}>)
+        set({{ pkg_name }}_{{ comp_name }}_TARGET_PROPERTIES TRUE)
+
+        {%- endfor %}
+
+        ########## GLOBAL TARGET PROPERTIES #########################################################
+
+        if(NOT {{ pkg_name }}_{{ pkg_name }}_TARGET_PROPERTIES)
+            set_property(TARGET {{ pkg_name }}::{{ pkg_name }} PROPERTY INTERFACE_LINK_LIBRARIES
+                             $<$<CONFIG:Release>:{{ '${'+pkg_name+'_COMPONENTS_RELEASE}' }}>
+                             $<$<CONFIG:RelWithDebInfo>:{{ '${'+pkg_name+'_COMPONENTS_RELWITHDEBINFO}' }}>
+                             $<$<CONFIG:MinSizeRel>:{{ '${'+pkg_name+'_COMPONENTS_MINSIZEREL}' }}>
+                             $<$<CONFIG:Debug>:{{ '${'+pkg_name+'_COMPONENTS_DEBUG}' }}>)
+        endif()
+        """))
+
     @property
     def filename(self):
         return None
@@ -98,26 +265,65 @@ def filename(self):
     @property
     def content(self):
         ret = {}
-        build_type = str(self.conanfile.settings.build_type)
-        build_type_suffix = "_{}".format(build_type.upper()) if build_type else ""
-        for _, cpp_info in self.deps_build_info.dependencies:
-            depname = cpp_info.get_name("cmake_find_package_multi")
-            public_deps_names = [self.deps_build_info[dep].get_name("cmake_find_package_multi")
-                                 for dep in cpp_info.public_deps]
-            ret["{}Config.cmake".format(depname)] = self._config(depname, cpp_info.version,
-                                                                 public_deps_names)
-            ret["{}ConfigVersion.cmake".format(depname)] = self.config_version_template.\
-                format(version=cpp_info.version)
-            ret["{}Targets.cmake".format(depname)] = self.targets_template.format(name=depname)
-
-            # If any config matches the build_type one, add it to the cpp_info
-            dep_cpp_info = extend(cpp_info, build_type.lower())
-            deps = DepsCppCmake(dep_cpp_info)
-            deps_names = ";".join(["{n}::{n}".format(n=n) for n in public_deps_names])
-            find_lib = target_template.format(name=depname, deps=deps,
-                                              build_type_suffix=build_type_suffix,
-                                              deps_names=deps_names)
-            ret["{}Target-{}.cmake".format(depname, build_type.lower())] = find_lib
+        build_type = str(self.conanfile.settings.build_type).upper()
+        build_type_suffix = "_{}".format(build_type) if build_type else ""
+        for pkg_name, cpp_info in self.deps_build_info.dependencies:
+            pkg_findname = self._get_name(cpp_info)
+            pkg_version = cpp_info.version
+            pkg_public_deps = [self._get_name(self.deps_build_info[public_dep]) for public_dep in
+                               cpp_info.public_deps]
+            ret["{}ConfigVersion.cmake".format(pkg_findname)] = self.config_version_template. \
+                format(version=pkg_version)
+            if not cpp_info.components:
+                public_deps_names = [self.deps_build_info[dep].get_name("cmake_find_package_multi")
+                                     for dep in cpp_info.public_deps]
+                ret["{}Config.cmake".format(pkg_findname)] = self._config(pkg_findname,
+                                                                          cpp_info.version,
+                                                                          public_deps_names)
+                ret["{}Targets.cmake".format(pkg_findname)] = self.targets_template.format(name=pkg_findname)
+
+                # If any config matches the build_type one, add it to the cpp_info
+                dep_cpp_info = extend(cpp_info, build_type.lower())
+                deps = DepsCppCmake(dep_cpp_info)
+                deps_names = ";".join(["{n}::{n}".format(n=n) for n in public_deps_names])
+                find_lib = target_template.format(name=pkg_findname, deps=deps,
+                                                  build_type_suffix=build_type_suffix,
+                                                  deps_names=deps_names)
+                ret["{}Target-{}.cmake".format(pkg_findname, build_type.lower())] = find_lib
+            else:
+                cpp_info = extend(cpp_info, build_type.lower())
+                pkg_info = DepsCppCmake(cpp_info)
+                pkg_public_deps_names = ";".join(["{n}::{n}".format(n=n) for n in pkg_public_deps])
+                components = self._get_components(pkg_name, pkg_findname, cpp_info)
+                # Note these are in reversed order, from more dependent to less dependent
+                pkg_components = " ".join(["{p}::{c}".format(p=pkg_findname, c=comp_findname) for
+                                           comp_findname, _ in reversed(components)])
+                global_target_variables = target_template.format(name=pkg_findname, deps=pkg_info,
+                                                                 build_type_suffix=build_type_suffix,
+                                                                 deps_names=pkg_public_deps_names)
+                variables = self.components_target_build_type_tpl.render(
+                    pkg_name=pkg_findname,
+                    global_target_variables=global_target_variables,
+                    pkg_components=pkg_components,
+                    build_type=build_type,
+                    components=components,
+                    conan_find_apple_frameworks=CMakeFindPackageCommonMacros.apple_frameworks_macro,
+                    conan_package_library_targets=CMakeFindPackageCommonMacros.conan_package_library_targets
+                )
+                ret["{}Target-{}.cmake".format(pkg_findname, build_type.lower())] = variables
+                targets = self.components_targets_tpl.render(
+                    pkg_name=pkg_findname,
+                    components=components,
+                    build_type=build_type
+                )
+                ret["{}Targets.cmake".format(pkg_findname)] = targets
+                target_config = self.components_config_tpl.render(
+                    pkg_name=pkg_findname,
+                    components=components,
+                    pkg_public_deps=pkg_public_deps,
+                    conan_message=CMakeFindPackageCommonMacros.conan_message
+                )
+                ret["{}Config.cmake".format(pkg_findname)] = target_config
         return ret
 
     def _config(self, name, version, public_deps_names):


Test patch:
diff --git a/conans/test/functional/generators/components/__init__.py b/conans/test/functional/generators/components/__init__.py
new file mode 100644
diff --git a/conans/test/functional/generators/components/cmake_find_package_multi_test.py b/conans/test/functional/generators/components/cmake_find_package_multi_test.py
new file mode 100644
--- /dev/null
+++ b/conans/test/functional/generators/components/cmake_find_package_multi_test.py
@@ -0,0 +1,700 @@
+import os
+import platform
+import textwrap
+import unittest
+
+from nose.plugins.attrib import attr
+
+from conans.model.ref import ConanFileReference
+from conans.test.utils.genconanfile import GenConanfile
+from conans.test.utils.tools import TestClient
+
+
+@attr('slow')
+class CMakeGeneratorsWithComponentsTest(unittest.TestCase):
+
+    @staticmethod
+    def _create_greetings(client, custom_names=False, components=True, test=False):
+        hello_h = textwrap.dedent("""
+            #pragma once
+            void hello(std::string noun);
+            """)
+
+        hello_cpp = textwrap.dedent("""
+            #include <iostream>
+            #include <string>
+
+            #include "hello.h"
+
+            void hello(std::string noun) {
+                #ifdef NDEBUG
+                std::cout << "Hello " << noun << " release!" << std::endl;
+                #else
+                std::cout << "Hello " << noun << " debug!" << std::endl;
+                #endif
+            }
+            """)
+
+        bye_h = textwrap.dedent("""
+            #pragma once
+            void bye(std::string noun);
+            """)
+
+        bye_cpp = textwrap.dedent("""
+            #include <iostream>
+            #include <string>
+
+            #include "bye.h"
+
+            void bye(std::string noun) {
+                #ifdef NDEBUG
+                std::cout << "Bye " << noun << " release!" << std::endl;
+                #else
+                std::cout << "Bye " << noun << " debug!" << std::endl;
+                #endif
+            }
+            """)
+
+        conanfile_greetings = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class GreetingsConan(ConanFile):
+                name = "greetings"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake"
+                exports_sources = "src/*"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                %s
+            """)
+        if components:
+            info = textwrap.dedent("""
+                        self.cpp_info.components["hello"].libs = ["hello"]
+                        self.cpp_info.components["bye"].libs = ["bye"]
+                        """)
+            if custom_names:
+                info += textwrap.dedent("""
+                        self.cpp_info.names["cmake_find_package_multi"] = "Greetings"
+                        self.cpp_info.components["hello"].names["cmake_find_package_multi"] = "Hello"
+                        self.cpp_info.components["bye"].names["cmake_find_package_multi"] = "Bye"
+                        """)
+        else:
+            info = textwrap.dedent("""
+                        self.cpp_info.libs = ["hello", "bye"]
+                        """)
+        wrapper = textwrap.TextWrapper(width=85, initial_indent="   ", subsequent_indent="        ")
+        conanfile_greetings = conanfile_greetings % wrapper.fill(info)
+
+        cmakelists_greetings = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(greetings CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            add_library(hello hello.cpp)
+            add_library(bye bye.cpp)
+            """)
+
+        test_package_greetings_conanfile = textwrap.dedent("""
+            import os
+            from conans import ConanFile, CMake
+
+            class GreetingsTestConan(ConanFile):
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake", "cmake_find_package_multi"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+
+                def test(self):
+                    os.chdir("bin")
+                    self.run(".%sexample" % os.sep)
+            """)
+        test_package_greetings_cpp = textwrap.dedent("""
+            #include <string>
+
+            #include "hello.h"
+            #include "bye.h"
+
+            int main() {
+                hello("Moon");
+                bye("Moon");
+            }
+            """)
+        test_package_greetings_cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(PackageTest CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(greetings)
+
+            add_executable(example example.cpp)
+            target_link_libraries(example greetings::greetings)
+            """)
+        client.save({"conanfile.py": conanfile_greetings,
+                     "src/CMakeLists.txt": cmakelists_greetings,
+                     "src/hello.h": hello_h,
+                     "src/hello.cpp": hello_cpp,
+                     "src/bye.h": bye_h,
+                     "src/bye.cpp": bye_cpp})
+        if test:
+            client.save({"fake_test_package/conanfile.py": test_package_greetings_conanfile,
+                         "fake_test_package/example.cpp": test_package_greetings_cpp,
+                         "fake_test_package/CMakeLists.txt": test_package_greetings_cmakelists})
+        client.run("create . -s build_type=Release")
+        client.run("create . -s build_type=Debug")
+
+    @staticmethod
+    def _create_world(client, conanfile=None, cmakelists=None, test_cmakelists=None):
+        _conanfile_world = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class WorldConan(ConanFile):
+                name = "world"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake_find_package_multi", "cmake"
+                exports_sources = "src/*"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                    self.cpp_info.components["helloworld"].requires = ["greetings::hello"]
+                    self.cpp_info.components["helloworld"].libs = ["helloworld"]
+                    self.cpp_info.components["worldall"].requires = ["helloworld",
+                                                                     "greetings::greetings"]
+                    self.cpp_info.components["worldall"].libs = ["worldall"]
+            """)
+        helloworld_h = textwrap.dedent("""
+            #pragma once
+
+            void helloWorld();
+            """)
+        helloworld_cpp = textwrap.dedent("""
+            #include <string>
+            #include "hello.h"
+
+            #include "helloworld.h"
+
+            void helloWorld() {
+                hello("World");
+            }
+            """)
+        worldall_h = textwrap.dedent("""
+            #pragma once
+
+            void worldAll();
+            """)
+        worldall_cpp = textwrap.dedent("""
+            #include <string>
+            #include "bye.h"
+            #include "helloworld.h"
+
+            #include "worldall.h"
+
+            void worldAll() {
+                helloWorld();
+                bye("World");
+            }
+            """)
+        _cmakelists_world = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(world CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(greetings COMPONENTS hello)
+
+            add_library(helloworld helloworld.cpp)
+            target_link_libraries(helloworld greetings::hello)
+
+            find_package(greetings COMPONENTS bye)
+
+            add_library(worldall worldall.cpp)
+            target_link_libraries(worldall helloworld greetings::greetings)
+            """)
+        test_conanfile = textwrap.dedent("""
+            import os
+            from conans import ConanFile, CMake
+
+            class WorldTestConan(ConanFile):
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake", "cmake_find_package_multi"
+                requires = "world/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+
+                def test(self):
+                    os.chdir("bin")
+                    self.run(".%sexample" % os.sep)
+                    self.run(".%sexample2" % os.sep)
+            """)
+        test_example_cpp = textwrap.dedent("""
+            #include "worldall.h"
+
+            int main() {
+                worldAll();
+            }
+            """)
+        _test_cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(PackageTest CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(world)
+
+            add_executable(example example.cpp)
+            target_link_libraries(example world::worldall)
+
+            add_executable(example2 example.cpp)
+            target_link_libraries(example2 world::world)
+            """)
+        client.save({"conanfile.py": conanfile or _conanfile_world,
+                     "src/CMakeLists.txt": cmakelists or _cmakelists_world,
+                     "src/helloworld.h": helloworld_h,
+                     "src/helloworld.cpp": helloworld_cpp,
+                     "src/worldall.h": worldall_h,
+                     "src/worldall.cpp": worldall_cpp,
+                     "fake_test_package/conanfile.py": test_conanfile,
+                     "fake_test_package/CMakeLists.txt": test_cmakelists or _test_cmakelists,
+                     "fake_test_package/example.cpp": test_example_cpp}, clean_first=True)
+        client.run("create . -s build_type=Release")
+        client.run("create . -s build_type=Debug")
+
+    def _install_build_run_test_package(self, client, build_type, run_example2=False):
+        client.run("install fake_test_package -s build_type=%s" % build_type)
+        client.run("build fake_test_package")
+        print("Dirs in current folder:", os.listdir(client.current_folder))
+        with client.chdir(os.path.join(client.current_folder, build_type)):
+            client.run_command(".%sexample" % os.sep)
+            if run_example2:
+                client.run_command(".%sexample2" % os.sep)
+
+    def basic_test(self):
+        client = TestClient()
+        self._create_greetings(client, test=True)
+        self._install_build_run_test_package(client, "Release")
+        self.assertIn("Hello Moon release!", client.out)
+        self.assertIn("Bye Moon release!", client.out)
+        self._install_build_run_test_package(client, "Debug")
+        self.assertIn("Hello Moon debug!", client.out)
+        self.assertIn("Bye Moon debug!", client.out)
+        self._create_world(client)
+        self._install_build_run_test_package(client, "Release", run_example2=True)
+        self.assertIn("Hello World release!", client.out)
+        self.assertIn("Bye World release!", client.out)
+        self._install_build_run_test_package(client, "Debug", run_example2=True)
+        self.assertIn("Hello World debug!", client.out)
+        self.assertIn("Bye World debug!", client.out)
+
+    def find_package_general_test(self):
+        client = TestClient()
+        self._create_greetings(client, custom_names=True)
+
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class WorldConan(ConanFile):
+                name = "world"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake", "cmake_find_package_multi"
+                exports_sources = "src/*"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                    self.cpp_info.names["cmake_find_package_multi"] = "World"
+                    self.cpp_info.components["helloworld"].names["cmake_find_package_multi"] = "Helloworld"
+                    self.cpp_info.components["helloworld"].requires = ["greetings::hello"]
+                    self.cpp_info.components["helloworld"].libs = ["Helloworld"]
+                    self.cpp_info.components["worldall"].names["cmake_find_package_multi"] = "Worldall"
+                    self.cpp_info.components["worldall"].requires = ["greetings::bye", "helloworld"]
+                    self.cpp_info.components["worldall"].libs = ["Worldall"]
+        """)
+        cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(world CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(Greetings)
+
+            add_library(Helloworld helloworld.cpp)
+            target_link_libraries(Helloworld Greetings::Hello)
+
+            add_library(Worldall worldall.cpp)
+            target_link_libraries(Worldall Helloworld Greetings::Bye)
+        """)
+        test_cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(PackageTest CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(World)
+
+            add_executable(example example.cpp)
+            target_link_libraries(example World::Worldall)
+
+            add_executable(example2 example.cpp)
+            target_link_libraries(example2 World::World)
+            """)
+        self._create_world(client, conanfile=conanfile, cmakelists=cmakelists,
+                           test_cmakelists=test_cmakelists)
+        self._install_build_run_test_package(client, "Release", run_example2=True)
+        self.assertIn("Hello World release!", client.out)
+        self.assertIn("Bye World release!", client.out)
+        self._install_build_run_test_package(client, "Debug", run_example2=True)
+        self.assertIn("Hello World debug!", client.out)
+        self.assertIn("Bye World debug!", client.out)
+
+    def find_package_components_test(self):
+        client = TestClient()
+        self._create_greetings(client)
+        conanfile2 = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class WorldConan(ConanFile):
+                name = "world"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake", "cmake_find_package_multi"
+                exports_sources = "src/*"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                    self.cpp_info.components["helloworld"].requires = ["greetings::hello"]
+                    self.cpp_info.components["helloworld"].libs = ["helloworld"]
+                    self.cpp_info.components["worldall"].requires = ["helloworld", "greetings::bye"]
+                    self.cpp_info.components["worldall"].libs = ["worldall"]
+        """)
+        cmakelists2 = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(world CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(greetings COMPONENTS hello)
+
+            add_library(helloworld helloworld.cpp)
+            target_link_libraries(helloworld greetings::hello)
+
+            find_package(greetings COMPONENTS bye)
+
+            add_library(worldall worldall.cpp)
+            target_link_libraries(worldall helloworld greetings::bye)
+        """)
+        self._create_world(client, conanfile=conanfile2, cmakelists=cmakelists2)
+        self._install_build_run_test_package(client, "Release", run_example2=True)
+        self.assertIn("Hello World release!", client.out)
+        self.assertIn("Bye World release!", client.out)
+        self._install_build_run_test_package(client, "Debug", run_example2=True)
+        self.assertIn("Hello World debug!", client.out)
+        self.assertIn("Bye World debug!", client.out)
+
+    def recipe_with_components_requiring_recipe_without_components_test(self):
+        client = TestClient()
+        self._create_greetings(client, components=False)
+
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class WorldConan(ConanFile):
+                name = "world"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake_find_package_multi", "cmake"
+                exports_sources = "src/*"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                    self.cpp_info.components["helloworld"].requires = ["greetings::greetings"]
+                    self.cpp_info.components["helloworld"].libs = ["helloworld"]
+                    self.cpp_info.components["worldall"].requires = ["helloworld",
+                                                                     "greetings::greetings"]
+                    self.cpp_info.components["worldall"].libs = ["worldall"]
+            """)
+        cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(world CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup(NO_OUTPUT_DIRS)
+            set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
+
+            find_package(greetings COMPONENTS hello)
+
+            add_library(helloworld helloworld.cpp)
+            target_link_libraries(helloworld greetings::greetings)
+
+            find_package(greetings COMPONENTS bye)
+
+            add_library(worldall worldall.cpp)
+            target_link_libraries(worldall helloworld greetings::greetings)
+            """)
+        self._create_world(client, conanfile=conanfile, cmakelists=cmakelists)
+        self._install_build_run_test_package(client, "Release", run_example2=True)
+        self.assertIn("Hello World release!", client.out)
+        self.assertIn("Bye World release!", client.out)
+        self._install_build_run_test_package(client, "Debug", run_example2=True)
+        self.assertIn("Hello World debug!", client.out)
+        self.assertIn("Bye World debug!", client.out)
+
+    def component_not_found_test(self):
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile
+
+            class GreetingsConan(ConanFile):
+                def package_info(self):
+                    self.cpp_info.components["hello"].libs = ["hello"]
+        """)
+        client = TestClient()
+        client.save({"conanfile.py": conanfile})
+        client.run("create . greetings/0.0.1@")
+
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile
+
+            class WorldConan(ConanFile):
+                requires = "greetings/0.0.1"
+
+                def package_info(self):
+                    self.cpp_info.components["helloworld"].requires = ["greetings::non-existent"]
+                    self.cpp_info.components["helloworld"].libs = ["helloworld"]
+        """)
+        client.save({"conanfile.py": conanfile})
+        client.run("create . world/0.0.1@")
+        client.run("install world/0.0.1@ -g cmake_find_package_multi", assert_error=True)
+        self.assertIn("ERROR: Component 'greetings::non-existent' not found in 'greetings' "
+                      "package requirement", client.out)
+
+    def component_not_found_cmake_test(self):
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile
+
+            class GreetingsConan(ConanFile):
+                def package_info(self):
+                    self.cpp_info.components["hello"].libs = ["hello"]
+        """)
+        client = TestClient()
+        client.save({"conanfile.py": conanfile})
+        client.run("create . greetings/0.0.1@")
+
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class ConsumerConan(ConanFile):
+                settings = "build_type"
+                generators = "cmake_find_package_multi"
+                requires = "greetings/0.0.1"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+        """)
+        cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(Consumer CXX)
+
+            find_package(greetings COMPONENTS hello)
+            find_package(greetings COMPONENTS non-existent)
+            """)
+        client.save({"conanfile.py": conanfile, "CMakeLists.txt": cmakelists})
+        client.run("install .")
+        client.run("build .", assert_error=True)
+        self.assertIn("Conan: Component 'hello' found in package 'greetings'", client.out)
+        self.assertIn("Conan: Component 'non-existent' NOT found in package 'greetings'", client.out)
+
+    def same_names_test(self):
+        client = TestClient()
+        conanfile_greetings = textwrap.dedent("""
+            from conans import ConanFile, CMake
+
+            class HelloConan(ConanFile):
+                name = "hello"
+                version = "0.0.1"
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake"
+                exports_sources = "src/*"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure(source_folder="src")
+                    cmake.build()
+
+                def package(self):
+                    self.copy("*.h", dst="include", src="src")
+                    self.copy("*.lib", dst="lib", keep_path=False)
+                    self.copy("*.a", dst="lib", keep_path=False)
+
+                def package_info(self):
+                    self.cpp_info.components["global"].name = "hello"
+                    self.cpp_info.components["global"].libs = ["hello"]
+            """)
+        hello_h = textwrap.dedent("""
+            #pragma once
+            void hello(std::string noun);
+            """)
+
+        hello_cpp = textwrap.dedent("""
+            #include <iostream>
+            #include <string>
+
+            #include "hello.h"
+
+            void hello(std::string noun) {
+                std::cout << "Hello " << noun << "!" << std::endl;
+            }
+            """)
+        cmakelists_greetings = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(greetings CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup()
+
+            add_library(hello hello.cpp)
+            """)
+        test_package_greetings_conanfile = textwrap.dedent("""
+            import os
+            from conans import ConanFile, CMake
+
+            class HelloTestConan(ConanFile):
+                settings = "os", "compiler", "build_type", "arch"
+                generators = "cmake", "cmake_find_package_multi"
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+
+                def test(self):
+                    os.chdir("bin")
+                    self.run(".%sexample" % os.sep)
+            """)
+        test_package_greetings_cpp = textwrap.dedent("""
+            #include <string>
+
+            #include "hello.h"
+
+            int main() {
+                hello("Moon");
+            }
+            """)
+        test_package_greetings_cmakelists = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.0)
+            project(PackageTest CXX)
+
+            include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+            conan_basic_setup()
+
+            find_package(hello)
+
+            add_executable(example example.cpp)
+            target_link_libraries(example hello::hello)
+            """)
+        client.save({"conanfile.py": conanfile_greetings,
+                     "src/CMakeLists.txt": cmakelists_greetings,
+                     "src/hello.h": hello_h,
+                     "src/hello.cpp": hello_cpp,
+                     "test_package/conanfile.py": test_package_greetings_conanfile,
+                     "test_package/example.cpp": test_package_greetings_cpp,
+                     "test_package/CMakeLists.txt": test_package_greetings_cmakelists})
+        client.run("create .")
+        self.assertIn("Hello Moon!", client.out)
+
+    def component_not_found_same_name_as_pkg_require_test(self):
+        zlib = GenConanfile("zlib", "0.1").with_setting("build_type")\
+            .with_generator("cmake_find_package_multi")
+        mypkg = GenConanfile("mypkg", "0.1").with_setting("build_type")\
+            .with_generator("cmake_find_package_multi")
+        final = GenConanfile("final", "0.1").with_setting("build_type")\
+            .with_generator("cmake_find_package_multi")\
+            .with_require(ConanFileReference("zlib", "0.1", None, None))\
+            .with_require(ConanFileReference("mypkg", "0.1", None, None))\
+            .with_package_info(cpp_info={"components": {"cmp": {"requires": ["mypkg::zlib",
+                                                                             "zlib::zlib"]}}},
+                               env_info={})
+        consumer = GenConanfile("consumer", "0.1").with_setting("build_type")\
+            .with_generator("cmake_find_package_multi")\
+            .with_requirement(ConanFileReference("final", "0.1", None, None))
+        client = TestClient()
+        client.save({"zlib.py": zlib, "mypkg.py": mypkg, "final.py": final, "consumer.py": consumer})
+        client.run("create zlib.py")
+        client.run("create mypkg.py")
+        client.run("create final.py")
+        client.run("install consumer.py", assert_error=True)
+        self.assertIn("Component 'mypkg::zlib' not found in 'mypkg' package requirement", client.out)
+