Repository: conan-io/conan

Issue:
Support components on cmake_find_package generator
Changelog: Feature: Support ``cpp_info.components`` on ``cmake_find_package`` generator.
Docs: missing

This implementation preserves `CONAN_LIB::XXXXX` per library targets

- [x] Refer to the issue that supports this Pull Request: closes #6718
- [x] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [x] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one. 

<sup>**Note:** By default this PR will skip the slower tests and will use a limited set of python versions. Check [here](https://github.com/conan-io/conan/blob/develop/.github/PR_INCREASE_TESTING.md) how to increase the testing level by writing some tags in the current PR body text.</sup>



Discussion:

Resolving patch:
diff --git a/conans/client/generators/cmake_find_package.py b/conans/client/generators/cmake_find_package.py
--- a/conans/client/generators/cmake_find_package.py
+++ b/conans/client/generators/cmake_find_package.py
@@ -1,3 +1,4 @@
+from jinja2 import Template
 import textwrap
 
 from conans.client.generators.cmake import DepsCppCmake
@@ -5,10 +6,14 @@
                                                                 CMakeFindPackageCommonMacros,
                                                                 find_transitive_dependencies)
 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 CMakeFindPackageGenerator(Generator):
+    name = "cmake_find_package"
+
     find_template = textwrap.dedent("""
         {macros_and_functions}
 
@@ -43,52 +48,262 @@ class CMakeFindPackageGenerator(Generator):
         endif()
         """)
 
+    find_components_tpl = Template(textwrap.dedent("""\
+        ########## MACROS ###########################################################################
+        #############################################################################################
+        {{ conan_message }}
+        {{ conan_find_apple_frameworks }}
+        {{ conan_package_library_targets }}
+
+        ########### FOUND PACKAGE ###################################################################
+        #############################################################################################
+
+        include(FindPackageHandleStandardArgs)
+
+        conan_message(STATUS "Conan: Using autogenerated Find{{ pkg_name }}.cmake")
+        set({{ pkg_name }}_FOUND 1)
+        set({{ pkg_name }}_VERSION "{{ pkg_version }}")
+        set({{ pkg_name }}_COMPONENTS {{ pkg_components }})
+
+        find_package_handle_standard_args({{ pkg_name }} REQUIRED_VARS
+                                          {{ pkg_name }}_VERSION VERSION_VAR {{ pkg_name }}_VERSION)
+        mark_as_advanced({{ pkg_name }}_FOUND {{ pkg_name }}_VERSION)
+
+        if({{ pkg_name }}_FIND_COMPONENTS)
+            foreach(_FIND_COMPONENT {{ '${'+pkg_name+'_FIND_COMPONENTS}' }})
+                list(FIND {{ pkg_name }}_COMPONENTS "{{ 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()
+
+        ########### VARIABLES #######################################################################
+        #############################################################################################
+
+        {{ global_target_variables }}
+
+        {%- for comp_name, comp in components %}
+
+        ########### COMPONENT {{ comp_name }} VARIABLES #############################################
+
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDE_DIRS {{ comp.include_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDE_DIR {{ comp.include_path }})
+        set({{ pkg_name }}_{{ comp_name }}_INCLUDES {{ comp.include_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_LIB_DIRS {{ comp.lib_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_RES_DIRS {{ comp.res_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_DEFINITIONS {{ comp.defines }})
+        set({{ pkg_name }}_{{ comp_name }}_COMPILE_DEFINITIONS {{ comp.compile_definitions }})
+        set({{ pkg_name }}_{{ comp_name }}_COMPILE_OPTIONS_LIST "{{ comp.cxxflags_list }}" "{{ comp.cflags_list }}")
+        set({{ pkg_name }}_{{ comp_name }}_LIBS {{ comp.libs }})
+        set({{ pkg_name }}_{{ comp_name }}_SYSTEM_LIBS {{ comp.system_libs }})
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORK_DIRS {{ comp.framework_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS {{ comp.frameworks }})
+        set({{ pkg_name }}_{{ comp_name }}_BUILD_MODULES_PATHS {{ comp.build_modules_paths }})
+        set({{ pkg_name }}_{{ comp_name }}_DEPENDENCIES {{ comp.public_deps }})
+        set({{ pkg_name }}_{{ comp_name }}_LINKER_FLAGS_LIST
+                $<$<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 }}>
+        )
+
+        {%- endfor %}
+
+
+        ########## FIND PACKAGE DEPENDENCY ##########################################################
+        #############################################################################################
+
+        include(CMakeFindDependencyMacro)
+
+        {%- for public_dep in pkg_public_deps %}
+
+        if(NOT {{ public_dep }}_FOUND)
+            find_dependency({{ public_dep }} REQUIRED)
+        else()
+            conan_message(STATUS "Conan: Dependency {{ public_dep }} already found")
+        endif()
+
+        {%- endfor %}
+
+
+        ########## FIND LIBRARIES & FRAMEWORKS / DYNAMIC VARS #######################################
+        #############################################################################################
+
+        {%- for comp_name, comp in components %}
+
+        ########## COMPONENT {{ comp_name }} FIND LIBRARIES & FRAMEWORKS / DYNAMIC VARS #############
+
+        set({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS_FOUND "")
+        conan_find_apple_frameworks({{ pkg_name }}_{{ comp_name }}_FRAMEWORKS_FOUND "{{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORKS}' }}" "{{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORK_DIRS}' }}")
+
+        set({{ pkg_name }}_{{ comp_name }}_LIB_TARGETS "")
+        set({{ pkg_name }}_{{ comp_name }}_NOT_USED "")
+        set({{ pkg_name }}_{{ comp_name }}_LIBS_FRAMEWORKS_DEPS {{ '${'+pkg_name+'_'+comp_name+'_FRAMEWORKS_FOUND}' }} {{ '${'+pkg_name+'_'+comp_name+'_SYSTEM_LIBS}' }} {{ '${'+pkg_name+'_'+comp_name+'_DEPENDENCIES}' }})
+        conan_package_library_targets("{{ '${'+pkg_name+'_'+comp_name+'_LIBS}' }}"
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LIB_DIRS}' }}"
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LIBS_FRAMEWORKS_DEPS}' }}"
+                                      {{ pkg_name }}_{{ comp_name }}_NOT_USED
+                                      {{ pkg_name }}_{{ comp_name }}_LIB_TARGETS
+                                      ""
+                                      "{{ pkg_name }}_{{ comp_name }}")
+
+        set({{ pkg_name }}_{{ comp_name }}_LINK_LIBS {{ '${'+pkg_name+'_'+comp_name+'_LIB_TARGETS}' }} {{ '${'+pkg_name+'_'+comp_name+'_DEPENDENCIES}' }})
+
+        set(CMAKE_MODULE_PATH {{ comp.build_paths }} ${CMAKE_MODULE_PATH})
+        set(CMAKE_PREFIX_PATH {{ comp.build_paths }} ${CMAKE_PREFIX_PATH})
+
+        foreach(_BUILD_MODULE_PATH {{ '${'+pkg_name+'_'+comp_name+'_BUILD_MODULES_PATHS}' }})
+            include(${_BUILD_MODULE_PATH})
+        endforeach()
+
+        {%- endfor %}
+
+
+        ########## TARGETS ##########################################################################
+        #############################################################################################
+
+        {%- for comp_name, comp in components %}
+
+        ########## COMPONENT {{ comp_name }} TARGET #################################################
+
+        if(NOT ${CMAKE_VERSION} VERSION_LESS "3.0")
+            # Target approach
+            if(NOT TARGET {{ pkg_name }}::{{ comp_name }})
+                add_library({{ pkg_name }}::{{ comp_name }} INTERFACE IMPORTED)
+                set_target_properties({{ pkg_name }}::{{ comp_name }} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_INCLUDE_DIRS}' }}")
+                set_target_properties({{ pkg_name }}::{{ comp_name }} PROPERTIES INTERFACE_LINK_DIRECTORIES
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LIB_DIRS}' }}")
+                set_target_properties({{ pkg_name }}::{{ comp_name }} PROPERTIES INTERFACE_LINK_LIBRARIES
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_LINK_LIBS}' }};{{ '${'+pkg_name+'_'+comp_name+'_LINKER_FLAGS_LIST}' }}")
+                set_target_properties({{ pkg_name }}::{{ comp_name }} PROPERTIES INTERFACE_COMPILE_DEFINITIONS
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_DEFINITIONS}' }}")
+                set_target_properties({{ pkg_name }}::{{ comp_name }} PROPERTIES INTERFACE_COMPILE_OPTIONS
+                                      "{{ '${'+pkg_name+'_'+comp_name+'_COMPILE_OPTIONS_LIST}' }}")
+            endif()
+        endif()
+
+        {%- endfor %}
+
+        ########## GLOBAL TARGET ####################################################################
+
+        if(NOT ${CMAKE_VERSION} VERSION_LESS "3.0")
+            if(NOT TARGET {{ pkg_name }}::{{ pkg_name }})
+                add_library({{ pkg_name }}::{{ pkg_name }} INTERFACE IMPORTED)
+                set_target_properties({{ pkg_name }}::{{ pkg_name }} PROPERTIES INTERFACE_LINK_LIBRARIES
+                                      "{{ '${'+pkg_name+'_COMPONENTS}' }}")
+            endif()
+        endif()
+
+    """))
+
     @property
     def filename(self):
         return None
 
+    @classmethod
+    def _get_name(cls, obj):
+        get_name = getattr(obj, "get_name")
+        return get_name(cls.name)
+
     @property
     def content(self):
         ret = {}
-        for _, cpp_info in self.deps_build_info.dependencies:
-            depname = cpp_info.get_name("cmake_find_package")
-            ret["Find%s.cmake" % depname] = self._find_for_dep(depname, cpp_info)
+        for pkg_name, cpp_info in self.deps_build_info.dependencies:
+            pkg_findname = self._get_name(cpp_info)
+            ret["Find%s.cmake" % pkg_findname] = self._find_for_dep(pkg_name, pkg_findname, cpp_info)
         return ret
 
-    def _find_for_dep(self, name, cpp_info):
-        # The common macros
-        macros_and_functions = "\n".join([
-            CMakeFindPackageCommonMacros.conan_message,
-            CMakeFindPackageCommonMacros.apple_frameworks_macro,
-            CMakeFindPackageCommonMacros.conan_package_library_targets,
-            ])
-
-        # compose the cpp_info with its "debug" or "release" specific config
-        dep_cpp_info = cpp_info
-        build_type = self.conanfile.settings.get_safe("build_type")
-        if build_type:
-            dep_cpp_info = extend(dep_cpp_info, build_type.lower())
-
-        # The find_libraries_block, all variables for the package, and creation of targets
-        public_deps_names = [self.deps_build_info[dep].get_name("cmake_find_package") for dep in
-                             dep_cpp_info.public_deps]
-        deps_names = ";".join(["{n}::{n}".format(n=n) for n in public_deps_names])
-
-        deps = DepsCppCmake(dep_cpp_info)
-        find_libraries_block = target_template.format(name=name, deps=deps, build_type_suffix="",
-                                                      deps_names=deps_names)
-
-        # The find_transitive_dependencies block
-        find_dependencies_block = ""
-        if dep_cpp_info.public_deps:
-            # Here we are generating FindXXX, so find_modules=True
-            f = find_transitive_dependencies(public_deps_names, find_modules=True)
-            # proper indentation
-            find_dependencies_block = ''.join("        " + line if line.strip() else line
-                                              for line in f.splitlines(True))
-
-        tmp = self.find_template.format(name=name, version=dep_cpp_info.version,
-                                        find_libraries_block=find_libraries_block,
-                                        find_dependencies_block=find_dependencies_block,
-                                        macros_and_functions=macros_and_functions)
-        return tmp
+    def _get_components(self, pkg_name, pkg_findname, cpp_info):
+        find_package_components = []
+        for comp_name, comp in self.sorted_components(cpp_info).items():
+            comp_findname = self._get_name(cpp_info.components[comp_name])
+            deps_cpp_cmake = DepsCppCmake(comp)
+            deps_cpp_cmake.public_deps = self._get_component_requires(pkg_name, pkg_findname, comp)
+            find_package_components.append((comp_findname, deps_cpp_cmake))
+        find_package_components.reverse()  # From the less dependent to most one
+        return find_package_components
+
+    def _get_component_requires(self, pkg_name, pkg_findname, comp):
+        comp_requires_findnames = []
+        for require in comp.requires:
+            if COMPONENT_SCOPE in require:
+                comp_require_pkg_name, comp_require_comp_name = require.split(COMPONENT_SCOPE)
+                comp_require_pkg = self.deps_build_info[comp_require_pkg_name]
+                comp_require_pkg_findname = self._get_name(comp_require_pkg)
+                if comp_require_comp_name == comp_require_pkg_name:
+                    comp_require_comp_findname = comp_require_pkg_findname
+                elif comp_require_comp_name in self.deps_build_info[comp_require_pkg_name].components:
+                    comp_require_comp = comp_require_pkg.components[comp_require_comp_name]
+                    comp_require_comp_findname = self._get_name(comp_require_comp)
+                else:
+                    raise ConanException("Component '%s' not found in '%s' package requirement"
+                                         % (require, comp_require_pkg_name))
+            else:
+                comp_require_pkg_findname = pkg_findname
+                comp_require_comp = self.deps_build_info[pkg_name].components[require]
+                comp_require_comp_findname = self._get_name(comp_require_comp)
+            f = "{}::{}".format(comp_require_pkg_findname, comp_require_comp_findname)
+            comp_requires_findnames.append(f)
+        return " ".join(comp_requires_findnames)
+
+    def _find_for_dep(self, pkg_name, pkg_findname, cpp_info):
+        # return the content of the FindXXX.cmake file for the package "pkg_name"
+        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]
+        pkg_public_deps_names = ";".join(["{n}::{n}".format(n=n) for n in pkg_public_deps])
+        if cpp_info.components:
+            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)])
+            pkg_info = DepsCppCmake(cpp_info)
+            global_target_variables = target_template.format(name=pkg_findname, deps=pkg_info,
+                                                             build_type_suffix="",
+                                                             deps_names=pkg_public_deps_names)
+            return self.find_components_tpl.render(
+                pkg_name=pkg_findname,
+                pkg_version=pkg_version,
+                pkg_components=pkg_components,
+                global_target_variables=global_target_variables,
+                pkg_public_deps=pkg_public_deps,
+                components=components,
+                conan_message=CMakeFindPackageCommonMacros.conan_message,
+                conan_find_apple_frameworks=CMakeFindPackageCommonMacros.apple_frameworks_macro,
+                conan_package_library_targets=CMakeFindPackageCommonMacros.conan_package_library_targets)
+        else:
+            # The common macros
+            macros_and_functions = "\n".join([
+                CMakeFindPackageCommonMacros.conan_message,
+                CMakeFindPackageCommonMacros.apple_frameworks_macro,
+                CMakeFindPackageCommonMacros.conan_package_library_targets,
+                ])
+
+            # compose the cpp_info with its "debug" or "release" specific config
+            dep_cpp_info = cpp_info
+            build_type = self.conanfile.settings.get_safe("build_type")
+            if build_type:
+                dep_cpp_info = extend(dep_cpp_info, build_type.lower())
+
+            # The find_libraries_block, all variables for the package, and creation of targets
+            deps = DepsCppCmake(dep_cpp_info)
+            find_libraries_block = target_template.format(name=pkg_findname, deps=deps,
+                                                          build_type_suffix="",
+                                                          deps_names=pkg_public_deps_names)
+
+            # The find_transitive_dependencies block
+            find_dependencies_block = ""
+            if dep_cpp_info.public_deps:
+                # Here we are generating FindXXX, so find_modules=True
+                f = find_transitive_dependencies(pkg_public_deps, find_modules=True)
+                # proper indentation
+                find_dependencies_block = ''.join("        " + line if line.strip() else line
+                                                  for line in f.splitlines(True))
+
+            return self.find_template.format(name=pkg_findname, version=pkg_version,
+                                             find_libraries_block=find_libraries_block,
+                                             find_dependencies_block=find_dependencies_block,
+                                             macros_and_functions=macros_and_functions)
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -25,6 +25,7 @@ def __init__(self, factory):
     def __getitem__(self, key):
         if key not in self.keys():
             super(DefaultOrderedDict, self).__setitem__(key, self.factory())
+            super(DefaultOrderedDict, self).__getitem__(key).name = key
         return super(DefaultOrderedDict, self).__getitem__(key)
 
     def __copy__(self):
diff --git a/conans/model/conan_generator.py b/conans/model/conan_generator.py
--- a/conans/model/conan_generator.py
+++ b/conans/model/conan_generator.py
@@ -41,3 +41,6 @@ def content(self):
     @abstractproperty
     def filename(self):
         raise NotImplementedError()
+
+    def sorted_components(self, cpp_info):
+        return cpp_info._get_sorted_components()


Test patch:
diff --git a/conans/test/functional/generators/cmake_components_test.py b/conans/test/functional/generators/cmake_components_test.py
new file mode 100644
--- /dev/null
+++ b/conans/test/functional/generators/cmake_components_test.py
@@ -0,0 +1,643 @@
+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) {
+                std::cout << "Hello " << noun << "!" << std::endl;
+            }
+            """)
+
+        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) {
+                std::cout << "Bye " << noun << "!" << std::endl;
+            }
+            """)
+
+        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"] = "Greetings"
+                        self.cpp_info.components["hello"].names["cmake_find_package"] = "Hello"
+                        self.cpp_info.components["bye"].names["cmake_find_package"] = "Bye"
+                        """)
+        else:
+            info = textwrap.dedent("""
+                        self.cpp_info.libs = ["hello", "bye"]
+                        """)
+        wrapper = textwrap.TextWrapper(width=81, 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()
+
+            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"
+
+                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()
+
+            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({"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 .")
+
+    @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", "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()
+
+            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"
+
+                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()
+
+            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,
+                     "test_package/conanfile.py": test_conanfile,
+                     "test_package/CMakeLists.txt": test_cmakelists or _test_cmakelists,
+                     "test_package/example.cpp": test_example_cpp})
+        client.run("create .")
+
+    def basic_test(self):
+        client = TestClient()
+        self._create_greetings(client, test=True)
+        self.assertIn("Hello Moon!", client.out)
+        self.assertIn("Bye Moon!", client.out)
+        self._create_world(client)
+        self.assertIn("Hello World!", client.out)
+        self.assertIn("Bye World!", 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"
+                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"] = "World"
+                    self.cpp_info.components["helloworld"].names["cmake_find_package"] = "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"] = "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()
+
+            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()
+
+            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.assertIn("Hello World!", client.out)
+        self.assertIn("Bye World!", 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"
+                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()
+
+            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.assertIn("Hello World!", client.out)
+        self.assertIn("Bye World!", 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", "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()
+
+            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.assertIn("Hello World!", client.out)
+        self.assertIn("Bye World!", 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", 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):
+                generators = "cmake_find_package"
+                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"
+
+                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_generator("cmake_find_package")
+        mypkg = GenConanfile("mypkg", "0.1").with_generator("cmake_find_package")
+        final = GenConanfile("final", "0.1").with_generator("cmake_find_package")\
+            .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_generator("cmake_find_package")\
+            .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)