Repository: conan-io/conan
Pull request #12049 (2022-09-07 08:04:16)

CMakeDeps managing CMAKE_MAP_IMPORTED_CONFIG_XXX
Changelog: Bugfix: The `CMakeDeps` generator was not working properly when the consumer maps configurations of the dependencies using `CMAKE_MAP_IMPORTED_CONFIG_XXX`.
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/12041


Patch:
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index cdd07fb27..63cb6a1a1 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -99,7 +99,7 @@ class CMake(object):
         with chdir(self, build_folder):
             self._conanfile.run(command)
 
-    def _build(self, build_type=None, target=None, cli_args=None, build_tool_args=None):
+    def _build(self, build_type=None, target=None, cli_args=None, build_tool_args=None, env=""):
         bf = self._conanfile.build_folder
         is_multi = is_multi_configuration(self._generator)
         if build_type and not is_multi:
@@ -127,7 +127,7 @@ class CMake(object):
         arg_list = " ".join(filter(None, arg_list))
         command = "%s --build %s" % (self._cmake_program, arg_list)
         self._conanfile.output.info("CMake command: %s" % command)
-        self._conanfile.run(command)
+        self._conanfile.run(command, env=env)
 
     def build(self, build_type=None, target=None, cli_args=None, build_tool_args=None):
         self._build(build_type, target, cli_args, build_tool_args)
@@ -149,7 +149,7 @@ class CMake(object):
         self._conanfile.output.info("CMake command: %s" % command)
         self._conanfile.run(command)
 
-    def test(self, build_type=None, target=None, cli_args=None, build_tool_args=None):
+    def test(self, build_type=None, target=None, cli_args=None, build_tool_args=None, env=""):
         if self._conanfile.conf.get("tools.build:skip_test", check_type=bool):
             return
         if not target:
@@ -157,5 +157,7 @@ class CMake(object):
             is_ninja = "Ninja" in self._generator
             target = "RUN_TESTS" if is_multi and not is_ninja else "test"
 
+        # The default for ``test()`` is both the buildenv and the runenv
+        env = ["conanbuild", "conanrun"] if env == "" else env
         self._build(build_type=build_type, target=target, cli_args=cli_args,
-                    build_tool_args=build_tool_args)
+                    build_tool_args=build_tool_args, env=env)
diff --git a/conan/tools/cmake/cmakedeps/templates/macros.py b/conan/tools/cmake/cmakedeps/templates/macros.py
index 17d96f88d..6ae09ea75 100644
--- a/conan/tools/cmake/cmakedeps/templates/macros.py
+++ b/conan/tools/cmake/cmakedeps/templates/macros.py
@@ -59,7 +59,7 @@ class MacrosTemplate(CMakeDepsFileTemplate):
                        add_library(${_LIB_NAME} UNKNOWN IMPORTED)
                    endif()
                    # Link library file
-                   set_target_properties(${_LIB_NAME} PROPERTIES IMPORTED_LOCATION ${CONAN_FOUND_LIBRARY})
+                   set_target_properties(${_LIB_NAME} PROPERTIES IMPORTED_LOCATION${config_suffix} ${CONAN_FOUND_LIBRARY})
                    list(APPEND _out_libraries_target ${_LIB_NAME})
                    message(VERBOSE "Conan: Found: ${CONAN_FOUND_LIBRARY}")
                else()
diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py
index a56422356..7c29c05ef 100644
--- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py
@@ -67,7 +67,7 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate):
 
         ######## Create an interface target to contain all the dependencies (frameworks, system and conan deps)
         if(NOT TARGET {{ pkg_name+'_DEPS_TARGET'}})
-            add_library({{ pkg_name+'_DEPS_TARGET'}} INTERFACE)
+            add_library({{ pkg_name+'_DEPS_TARGET'}} INTERFACE IMPORTED)
         endif()
 
         set_property(TARGET {{ pkg_name + '_DEPS_TARGET'}}
@@ -146,7 +146,7 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate):
 
                 ######## Create an interface target to contain all the dependencies (frameworks, system and conan deps)
                 if(NOT TARGET {{ pkg_name + '_' + comp_variable_name + '_DEPS_TARGET'}})
-                    add_library({{ pkg_name + '_' + comp_variable_name + '_DEPS_TARGET'}} INTERFACE)
+                    add_library({{ pkg_name + '_' + comp_variable_name + '_DEPS_TARGET'}} INTERFACE IMPORTED)
                 endif()
 
                 set_property(TARGET {{ pkg_name + '_' + comp_variable_name + '_DEPS_TARGET'}}
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index eda75b0d6..698e050d2 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -12,6 +12,7 @@ from conan.tools.build.cross_building import cross_building
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
 from conan.tools.intel import IntelCC
 from conan.tools.microsoft.visual import is_msvc, msvc_version_to_toolset_version
+from conans.client.subsystems import deduce_subsystem, WINDOWS
 from conans.errors import ConanException
 from conans.util.files import load
 
@@ -153,7 +154,7 @@ class FPicBlock(Block):
     template = textwrap.dedent("""
         {% if fpic %}
         message(STATUS "Conan toolchain: Setting CMAKE_POSITION_INDEPENDENT_CODE={{ fpic }} (options.fPIC)")
-        set(CMAKE_POSITION_INDEPENDENT_CODE {{ fpic }} CACHE BOOL "Position independent code") 
+        set(CMAKE_POSITION_INDEPENDENT_CODE {{ fpic }} CACHE BOOL "Position independent code")
         {% endif %}
         """)
 
@@ -559,6 +560,34 @@ class FindFiles(Block):
         }
 
 
+class PkgConfigBlock(Block):
+    template = textwrap.dedent("""
+        {% if pkg_config %}
+        set(PKG_CONFIG_EXECUTABLE {{ pkg_config }} CACHE FILEPATH "pkg-config executable")
+        {% endif %}
+        {% if pkg_config_path %}
+        if (DEFINED ENV{PKG_CONFIG_PATH})
+        set(ENV{PKG_CONFIG_PATH} "{{ pkg_config_path }}$ENV{PKG_CONFIG_PATH}")
+        else()
+        set(ENV{PKG_CONFIG_PATH} "{{ pkg_config_path }}")
+        endif()
+        {% endif %}
+        """)
+
+    def context(self):
+        pkg_config = self._conanfile.conf.get("tools.gnu:pkg_config", check_type=str)
+        if pkg_config:
+            pkg_config = pkg_config.replace("\\", "/")
+        pkg_config_path = self._conanfile.generators_folder
+        if pkg_config_path:
+            # hardcoding scope as "build"
+            subsystem = deduce_subsystem(self._conanfile, "build")
+            pathsep = ":" if subsystem != WINDOWS else ";"
+            pkg_config_path = pkg_config_path.replace("\\", "/") + pathsep
+        return {"pkg_config": pkg_config,
+                "pkg_config_path": pkg_config_path}
+
+
 class UserToolchain(Block):
     template = textwrap.dedent("""
         {% for user_toolchain in paths %}
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 310648c41..599a9338c 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -11,8 +11,8 @@ from conan.tools.cmake.presets import write_cmake_presets
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
 from conan.tools.cmake.toolchain.blocks import ToolchainBlocks, UserToolchain, GenericSystemBlock, \
     AndroidSystemBlock, AppleSystemBlock, FPicBlock, ArchitectureBlock, GLibCXXBlock, VSRuntimeBlock, \
-    CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, SkipRPath, \
-    SharedLibBock, OutputDirsBlock, ExtraFlagsBlock
+    CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, PkgConfigBlock, \
+    SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock
 from conan.tools.intel import IntelCC
 from conan.tools.microsoft import VCVars
 from conan.tools.microsoft.visual import vs_ide_version
@@ -139,6 +139,7 @@ class CMakeToolchain(object):
                                        ("cmake_flags_init", CMakeFlagsInitBlock),
                                        ("try_compile", TryCompileBlock),
                                        ("find_paths", FindFiles),
+                                       ("pkg_config", PkgConfigBlock),
                                        ("rpath", SkipRPath),
                                        ("shared", SharedLibBock),
                                        ("output_dirs", OutputDirsBlock)])
diff --git a/conan/tools/microsoft/__init__.py b/conan/tools/microsoft/__init__.py
index c956ae068..e0c0c339a 100644
--- a/conan/tools/microsoft/__init__.py
+++ b/conan/tools/microsoft/__init__.py
@@ -3,5 +3,6 @@ from conan.tools.microsoft.msbuild import MSBuild
 from conan.tools.microsoft.msbuilddeps import MSBuildDeps
 from conan.tools.microsoft.subsystems import unix_path
 from conan.tools.microsoft.toolchain import MSBuildToolchain
+from conan.tools.microsoft.nmaketoolchain import NMakeToolchain
 from conan.tools.microsoft.visual import msvc_runtime_flag, VCVars, is_msvc, \
     is_msvc_static_runtime, check_min_vs
diff --git a/conan/tools/microsoft/nmaketoolchain.py b/conan/tools/microsoft/nmaketoolchain.py
new file mode 100644
index 000000000..ef4795385
--- /dev/null
+++ b/conan/tools/microsoft/nmaketoolchain.py
@@ -0,0 +1,56 @@
+from conan.tools._compilers import cppstd_flag, build_type_flags
+from conan.tools.env import Environment
+
+
+class NMakeToolchain(object):
+    """
+    https://learn.microsoft.com/en-us/cpp/build/reference/running-nmake?view=msvc-170#toolsini-and-nmake
+    We have also explored the usage of Tools.ini:
+    https://learn.microsoft.com/en-us/cpp/build/reference/running-nmake?view=msvc-170
+    but not possible, because it cannot include other files, it will also potentially collide with
+    a user Tool.ini, without easy resolution. At least the environment is additive.
+    """
+    def __init__(self, conanfile):
+        """
+        :param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
+        """
+        self._conanfile = conanfile
+        self._environment = None
+
+    @property
+    def cl_flags(self):
+        cppflags = []
+        build_type = self._conanfile.settings.get_safe("build_type")
+        if build_type in ['Release', 'RelWithDebInfo', 'MinSizeRel']:
+            cppflags.append("/DNDEBUG")
+
+        bt = build_type_flags(self._conanfile.settings)
+        if bt:
+            cppflags.extend(bt)
+
+        cppstd = cppstd_flag(self._conanfile.settings)
+        if cppstd:
+            cppflags.append(cppstd)
+        from conan.tools.microsoft import msvc_runtime_flag
+        flag = msvc_runtime_flag(self._conanfile)
+        if flag:
+            cppflags.append("-{}".format(flag))
+        return " ".join(cppflags).replace("-", "/")
+
+    @property
+    def environment(self):
+        # TODO: Seems we want to make this uniform, equal to other generators
+        if self._environment is None:
+            env = Environment()
+            # The whole injection of toolchain happens in CL env-var, the others LIBS, _LINK_
+            env.append("CL", self.cl_flags)
+            self._environment = env
+        return self._environment
+
+    def vars(self, scope="build"):
+        return self.environment.vars(self._conanfile, scope=scope)
+
+    def generate(self, scope="build"):
+        self.vars(scope).save_script("conannmaketoolchain")
+        from conan.tools.microsoft import VCVars
+        VCVars(self._conanfile).generate()
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index c7f6c8bd6..ad204d43b 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -94,7 +94,7 @@ _t_default_settings_yml = Template(textwrap.dedent("""
                       "9", "9.1", "9.2", "9.3", "9.4",
                       "10", "10.1", "10.2", "10.3",
                       "11", "11.1", "11.2", "11.3",
-                      "12"]
+                      "12", "12.1", "12.2"]
             libcxx: [libstdc++, libstdc++11]
             threads: [None, posix, win32]  # Windows MinGW
             exception: [None, dwarf2, sjlj, seh]  # Windows MinGW
diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py
index 86d19ae10..97838c290 100644
--- a/conans/client/generators/__init__.py
+++ b/conans/client/generators/__init__.py
@@ -73,7 +73,7 @@ class GeneratorManager(object):
                                 "VirtualRunEnv", "VirtualBuildEnv", "AutotoolsDeps",
                                 "AutotoolsToolchain", "BazelDeps", "BazelToolchain", "PkgConfigDeps",
                                 "VCVars", "IntelCC", "XcodeDeps", "PremakeDeps", "XcodeToolchain",
-                                "MesonDeps"]
+                                "MesonDeps", "NMakeToolchain"]
 
     def add(self, name, generator_class, custom=False):
         if name not in self._generators or custom:
@@ -150,6 +150,9 @@ class GeneratorManager(object):
         elif generator_name == "XcodeToolchain":
             from conan.tools.apple import XcodeToolchain
             return XcodeToolchain
+        elif generator_name == "NMakeToolchain":
+            from conan.tools.microsoft import NMakeToolchain
+            return NMakeToolchain
         else:
             raise ConanException("Internal Conan error: Generator '{}' "
                                  "not commplete".format(generator_name))
diff --git a/conans/client/migrations_settings.py b/conans/client/migrations_settings.py
index 31bd00bf6..599a389fd 100644
--- a/conans/client/migrations_settings.py
+++ b/conans/client/migrations_settings.py
@@ -4030,4 +4030,156 @@ cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
 
 settings_1_54_0 = settings_1_53_0
 
-settings_1_55_0 = settings_1_54_0
+settings_1_55_0 = """
+# Only for cross building, 'os_build/arch_build' is the system that runs Conan
+os_build: [Windows, WindowsStore, Linux, Macos, FreeBSD, SunOS, AIX, VxWorks]
+arch_build: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7]
+
+# Only for building cross compilation tools, 'os_target/arch_target' is the system for
+# which the tools generate code
+os_target: [Windows, Linux, Macos, Android, iOS, watchOS, tvOS, FreeBSD, SunOS, AIX, Arduino, Neutrino]
+arch_target: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7, xtensalx6, xtensalx106, xtensalx7]
+
+# Rest of the settings are "host" settings:
+# - For native building/cross building: Where the library/program will run.
+# - For building cross compilation tools: Where the cross compiler will run.
+os:
+    Windows:
+        subsystem: [None, cygwin, msys, msys2, wsl]
+    WindowsStore:
+        version: ["8.1", "10.0"]
+    WindowsCE:
+        platform: ANY
+        version: ["5.0", "6.0", "7.0", "8.0"]
+    Linux:
+    iOS:
+        version: &ios_version
+                 ["7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3",
+                  "11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4",
+                  "13.0", "13.1", "13.2", "13.3", "13.4", "13.5", "13.6", "13.7",
+                  "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7", "14.8",
+                  "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6", "16.0", "16.1"]
+        sdk: [None, "iphoneos", "iphonesimulator"]
+        sdk_version: [None, "11.3", "11.4", "12.0", "12.1", "12.2", "12.4",
+                      "13.0", "13.1", "13.2", "13.4", "13.5", "13.6", "13.7",
+                      "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "15.0", "15.2", "15.4", "15.5", "16.0", "16.1"]
+    watchOS:
+        version: ["4.0", "4.1", "4.2", "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1", "6.2",
+                  "7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "8.0", "8.1", "8.3", "8.4", "8.5", "8.6", "8.7", "9.0", "9.1"]
+        sdk: [None, "watchos", "watchsimulator"]
+        sdk_version: [None, "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1", "6.2",
+                      "7.0", "7.1", "7.2", "7.4", "8.0", "8.0.1", "8.3", "8.5", "9.0", "9.1"]
+    tvOS:
+        version: ["11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4",
+                  "13.0", "13.2", "13.3", "13.4", "14.0", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7",
+                  "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6", "16.0", "16.1"]
+        sdk: [None, "appletvos", "appletvsimulator"]
+        sdk_version: [None, "11.3", "11.4", "12.0", "12.1", "12.2", "12.4",
+                      "13.0", "13.1", "13.2", "13.4", "14.0", "14.2", "14.3", "14.5", "15.0", "15.2", "15.4", "16.0", "16.1"]
+    Macos:
+        version: [None, "10.6", "10.7", "10.8", "10.9", "10.10", "10.11", "10.12", "10.13", "10.14", "10.15", "11.0", "12.0", "13.0"]
+        sdk: [None, "macosx"]
+        sdk_version: [None, "10.13", "10.14", "10.15", "11.0", "11.1", "11.3", "12.0", "12.1", "12.3", "13.0"]
+        subsystem:
+            None:
+            catalyst:
+                ios_version: *ios_version
+    Android:
+        api_level: ANY
+    FreeBSD:
+    SunOS:
+    AIX:
+    Arduino:
+        board: ANY
+    Emscripten:
+    Neutrino:
+        version: ["6.4", "6.5", "6.6", "7.0", "7.1"]
+    baremetal:
+    VxWorks:
+        version: ["7"]
+arch: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv4, armv4i, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7, xtensalx6, xtensalx106, xtensalx7]
+compiler:
+    sun-cc:
+        version: ["5.10", "5.11", "5.12", "5.13", "5.14", "5.15"]
+        threads: [None, posix]
+        libcxx: [libCstd, libstdcxx, libstlport, libstdc++]
+    gcc: &gcc
+        version: ["4.1", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9",
+                  "5", "5.1", "5.2", "5.3", "5.4", "5.5",
+                  "6", "6.1", "6.2", "6.3", "6.4", "6.5",
+                  "7", "7.1", "7.2", "7.3", "7.4", "7.5",
+                  "8", "8.1", "8.2", "8.3", "8.4",
+                  "9", "9.1", "9.2", "9.3", "9.4",
+                  "10", "10.1", "10.2", "10.3",
+                  "11", "11.1", "11.2", "11.3",
+                  "12", "12.1", "12.2"]
+        libcxx: [libstdc++, libstdc++11]
+        threads: [None, posix, win32]  # Windows MinGW
+        exception: [None, dwarf2, sjlj, seh]  # Windows MinGW
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+    Visual Studio: &visual_studio
+        runtime: [MD, MT, MTd, MDd]
+        version: ["8", "9", "10", "11", "12", "14", "15", "16", "17"]
+        toolset: [None, v90, v100, v110, v110_xp, v120, v120_xp,
+                  v140, v140_xp, v140_clang_c2, LLVM-vs2012, LLVM-vs2012_xp,
+                  LLVM-vs2013, LLVM-vs2013_xp, LLVM-vs2014, LLVM-vs2014_xp,
+                  LLVM-vs2017, LLVM-vs2017_xp, v141, v141_xp, v141_clang_c2, v142,
+                  llvm, ClangCL, v143]
+        cppstd: [None, 14, 17, 20, 23]
+    msvc:
+        version: [170, 180, 190, 191, 192, 193]
+        update: [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+        runtime: [static, dynamic]
+        runtime_type: [Debug, Release]
+        cppstd: [98, 14, 17, 20, 23]
+        toolset: [None, v110_xp, v120_xp, v140_xp, v141_xp]
+    clang:
+        version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0",
+                  "5.0", "6.0", "7.0", "7.1",
+                  "8", "9", "10", "11", "12", "13", "14", "15", "16"]
+        libcxx: [None, libstdc++, libstdc++11, libc++, c++_shared, c++_static]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+        runtime: [None, MD, MT, MTd, MDd, static, dynamic]
+        runtime_type: [None, Debug, Release]
+        runtime_version: [None, v140, v141, v142, v143]
+    apple-clang: &apple_clang
+        version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0", "11.0", "12.0", "13", "13.0", "13.1", "14", "14.0"]
+        libcxx: [libstdc++, libc++]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+    intel:
+        version: ["11", "12", "13", "14", "15", "16", "17", "18", "19", "19.1"]
+        update: [None, ANY]
+        base:
+            gcc:
+                <<: *gcc
+                threads: [None]
+                exception: [None]
+            Visual Studio:
+                <<: *visual_studio
+            apple-clang:
+                <<: *apple_clang
+    intel-cc:
+        version: ["2021.1", "2021.2", "2021.3"]
+        update: [None, ANY]
+        mode: ["icx", "classic", "dpcpp"]
+        libcxx: [None, libstdc++, libstdc++11, libc++]
+        cppstd: [None, 98, gnu98, 03, gnu03, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+        runtime: [None, static, dynamic]
+        runtime_type: [None, Debug, Release]
+    qcc:
+        version: ["4.4", "5.4", "8.3"]
+        libcxx: [cxx, gpp, cpp, cpp-ne, accp, acpp-ne, ecpp, ecpp-ne]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17]
+    mcst-lcc:
+        version: ["1.19", "1.20", "1.21", "1.22", "1.23", "1.24", "1.25"]
+        base:
+            gcc:
+                <<: *gcc
+                threads: [None]
+                exceptions: [None]
+
+build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel]
+
+
+cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]  # Deprecated, use compiler.cppstd
+"""
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
index b3737b516..bacba824f 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -438,7 +438,8 @@ class ConanFile(object):
                     return tools.run_in_windows_bash(self, bashcmd=cmd, cwd=cwd, subsystem=subsystem,
                                                      msys_mingw=msys_mingw, with_login=with_login)
             envfiles_folder = self.generators_folder or os.getcwd()
-            _env = [_env] if _env and isinstance(_env, str) else []
+            _env = [_env] if _env and isinstance(_env, str) else (_env or [])
+            assert isinstance(_env, list)
             wrapped_cmd = command_env_wrapper(self, cmd, _env, envfiles_folder=envfiles_folder,
                                               scope=scope)
             return self._conan_runner(wrapped_cmd, output, os.path.abspath(RUN_LOG_NAME), cwd)


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
index f44aea035..45b5d1d97 100644
--- a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
+++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
@@ -574,3 +574,86 @@ def test_error_missing_build_type():
     run_app = r".\Release\app.exe" if platform.system() == "Windows" else "./Release/app"
     client.run_command(run_app)
     assert "Hello World Release!" in client.out
+
+
+@pytest.mark.tool_cmake
+def test_map_imported_config():
+    # https://github.com/conan-io/conan/issues/12041
+
+    client = TestClient()
+    client.run("new hello/1.0 -m=cmake_lib")
+    client.run("create . -tf=None -s build_type=Release")
+
+    # It is necessary a 2-level test to make the fixes evident
+    talk_cpp = gen_function_cpp(name="talk", includes=["hello"], calls=["hello"])
+    talk_h = gen_function_h(name="talk")
+    conanfile = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.cmake import CMake
+        from conan.tools.files import copy
+        class HelloConan(ConanFile):
+            name = 'talk'
+            version = '1.0'
+            exports_sources = "*"
+            generators = "CMakeDeps", "CMakeToolchain"
+            requires = ("hello/1.0", )
+            settings = "os", "compiler", "arch", "build_type"
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+                cmake.build()
+
+            def package(self):
+                cmake = CMake(self)
+                cmake.install()
+
+            def package_info(self):
+                self.cpp_info.libs.append("talk")
+        """)
+
+    client.save({"conanfile.py": conanfile,
+                 "CMakeLists.txt": gen_cmakelists(libname="talk",
+                                                  libsources=["talk.cpp"], find_package=["hello"],
+                                                  install=True, public_header="talk.h"),
+                 "talk.cpp": talk_cpp,
+                 "talk.h": talk_h}, clean_first=True)
+    client.run("create . -tf=None -s build_type=Release")
+
+    conanfile = textwrap.dedent("""
+        [requires]
+        talk/1.0
+        [generators]
+        CMakeDeps
+        CMakeToolchain
+        """)
+
+    cmakelists = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(app)
+        set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release)
+        find_package(talk REQUIRED)
+        add_executable(app main.cpp)
+        target_link_libraries(app talk::talk)
+        """)
+
+    client.save({
+        "conanfile.txt": conanfile,
+        "main.cpp": gen_function_cpp(name="main", includes=["talk"], calls=["talk"]),
+        "CMakeLists.txt": cmakelists
+    }, clean_first=True)
+
+    client.run("install . -s build_type=Release")
+    if platform.system() != "Windows":
+        client.run_command("cmake . -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake "
+                           "-DCMAKE_BUILD_TYPE=DEBUG")
+        client.run_command("cmake --build .")
+        client.run_command("./app")
+    else:
+        client.run_command("cmake . -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake")
+        client.run_command("cmake --build . --config Debug")
+        client.run_command("Debug\\app.exe")
+    assert "hello/1.0: Hello World Release!" in client.out
+    assert "talk: Release!" in client.out
+    assert "main: Debug!" in client.out
diff --git a/conans/test/functional/toolchains/cmake/test_cmake.py b/conans/test/functional/toolchains/cmake/test_cmake.py
index 5451a7fbb..1d29c3c6d 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake.py
@@ -552,9 +552,7 @@ class CMakeInstallTest(unittest.TestCase):
         cmakelist = textwrap.dedent("""
             cmake_minimum_required(VERSION 2.8)
             project(App C)
-            if(CONAN_TOOLCHAIN_INCLUDED AND CMAKE_VERSION VERSION_LESS "3.15")
-                include("${CMAKE_BINARY_DIR}/conan_project_include.cmake")
-            endif()
+
             if(NOT CMAKE_TOOLCHAIN_FILE)
                 message(FATAL ">> Not using toolchain")
             endif()
@@ -585,6 +583,56 @@ class CMakeInstallTest(unittest.TestCase):
         self.assertTrue(os.path.exists(os.path.join(package_folder, "include", "header.h")))
 
 
+@pytest.mark.tool_cmake
+class CMakeTestTest(unittest.TestCase):
+    """
+    test the cmake.test() helper
+    """
+    def test_test(self):
+
+        conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.cmake import CMake
+            class App(ConanFile):
+                settings = "os", "arch", "compiler", "build_type"
+                generators = "CMakeDeps", "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv"
+                exports_sources = "CMakeLists.txt", "example.cpp"
+
+                def build_requirements(self):
+                    self.test_requires("test/0.1")
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+                    cmake.test()
+            """)
+
+        cmakelist = textwrap.dedent("""
+            cmake_minimum_required(VERSION 3.15)
+            project(App CXX)
+            find_package(test CONFIG REQUIRED)
+            add_executable(example example.cpp)
+            target_link_libraries(example test::test)
+
+            enable_testing()
+            add_test(NAME example
+                      COMMAND example)
+            """)
+        c = TestClient()
+        c.run("new test/0.1 -m=cmake_lib")
+        c.run("create .  -tf=None -o test*:shared=True")
+
+        c.save({"conanfile.py": conanfile,
+                "CMakeLists.txt": cmakelist,
+                "example.cpp": gen_function_cpp(name="main", includes=["test"], calls=["test"])},
+               clean_first=True)
+
+        # The create flow must work
+        c.run("create . pkg/0.1@ -pr:b=default -o test*:shared=True")
+        assert "1/1 Test #1: example ..........................   Passed" in c.out
+
+
 @pytest.mark.tool_cmake
 class CMakeOverrideCacheTest(unittest.TestCase):
 
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index 397aac643..49f73cf5a 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -1099,7 +1099,7 @@ def test_cmake_toolchain_vars_when_option_declared():
 
     t.run("new mylib/1.0 --template cmake_lib")
     t.save({"CMakeLists.txt": cmakelists})
-    
+
     # The generated toolchain should set `BUILD_SHARED_LIBS` to `OFF`,
     # and `CMAKE_POSITION_INDEPENDENT_CODE` to `ON` and the calls to
     # `option()` in the CMakeLists.txt should respect the existing values.
@@ -1112,7 +1112,7 @@ def test_cmake_toolchain_vars_when_option_declared():
     assert "mylib target type: STATIC_LIBRARY" in t.out
     assert f"mylib position independent code: {fpic_cmake_value}" in t.out
 
-    # When building manually, ensure the value passed by the toolchain overrides the ones in 
+    # When building manually, ensure the value passed by the toolchain overrides the ones in
     # the CMakeLists
     fpic_option = "-o mylib:fPIC=False" if platform.system() != "Windows" else ""
     t.run(f"install . -o mylib:shared=False {fpic_option}")
@@ -1198,10 +1198,10 @@ def test_find_program_for_tool_requires():
 
             def layout(self):
                 cmake_layout(self)
-            
+
             def requirements(self):
                 self.requires("foobar/1.0")
-            
+
             def build_requirements(self):
                 self.tool_requires("foobar/1.0")
     """)
@@ -1227,7 +1227,63 @@ def test_find_program_for_tool_requires():
 
     with client.chdir("build"):
         client.run_command("cmake .. -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release")
-        # Verify binary executable is found from build context package, 
+        # Verify binary executable is found from build context package,
         # and library comes from host context package
         assert f"package/{build_context_package_id}/bin/foobin" in client.out
         assert f"package/{host_context_package_id}/include" in client.out
+
+
+@pytest.mark.tool_pkg_config
+def test_cmaketoolchain_and_pkg_config_path():
+    """
+    Lightweight test which is loading a dependency as a *.pc file through
+    CMake thanks to pkg_check_modules macro.
+    It's working because PKG_CONFIG_PATH env variable is being loaded automatically
+    by the CMakeToolchain generator.
+    """
+    client = TestClient()
+    dep = textwrap.dedent("""
+    from conan import ConanFile
+    class DepConan(ConanFile):
+        name = "dep"
+        version = "1.0"
+        def package_info(self):
+            self.cpp_info.libs = ["dep"]
+    """)
+    pkg = textwrap.dedent("""
+    from conan import ConanFile
+    from conan.tools.cmake import CMake, cmake_layout
+    class HelloConan(ConanFile):
+        name = "pkg"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+        generators = "PkgConfigDeps", "CMakeToolchain"
+        exports_sources = "CMakeLists.txt"
+
+        def requirements(self):
+            self.requires("dep/1.0")
+
+        def layout(self):
+            cmake_layout(self)
+
+        def build(self):
+            cmake = CMake(self)
+            cmake.configure()
+            cmake.build()
+    """)
+    cmakelists = textwrap.dedent("""
+    cmake_minimum_required(VERSION 3.15)
+    project(pkg CXX)
+
+    find_package(PkgConfig REQUIRED)
+    # We should have PKG_CONFIG_PATH created in the current environment
+    pkg_check_modules(DEP REQUIRED IMPORTED_TARGET dep)
+    """)
+    client.save({
+        "dep/conanfile.py": dep,
+        "pkg/conanfile.py": pkg,
+        "pkg/CMakeLists.txt": cmakelists
+    })
+    client.run("create dep/conanfile.py")
+    client.run("create pkg/conanfile.py")
+    assert "Found dep, version 1.0" in client.out
diff --git a/conans/test/functional/toolchains/test_nmake_toolchain.py b/conans/test/functional/toolchains/test_nmake_toolchain.py
new file mode 100644
index 000000000..532ad34b3
--- /dev/null
+++ b/conans/test/functional/toolchains/test_nmake_toolchain.py
@@ -0,0 +1,51 @@
+import platform
+import textwrap
+
+import pytest
+
+from conans.test.assets.sources import gen_function_cpp
+from conans.test.functional.utils import check_exe_run
+from conans.test.utils.tools import TestClient
+
+
+@pytest.mark.parametrize("compiler, version, runtime, cppstd, build_type",
+                         [("msvc", "190", "dynamic", "14", "Release"),
+                          ("msvc", "191", "static", "17", "Debug")])
+@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
+def test_toolchain_nmake(compiler, version, runtime, cppstd, build_type):
+    client = TestClient(path_with_spaces=False)
+    settings = {"compiler": compiler,
+                "compiler.version": version,
+                "compiler.cppstd": cppstd,
+                "compiler.runtime": runtime,
+                "build_type": build_type,
+                "arch": "x86_64"}
+
+    # Build the profile according to the settings provided
+    settings = " ".join('-s %s="%s"' % (k, v) for k, v in settings.items() if v)
+
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "NMakeToolchain"
+
+            def build(self):
+                self.run(f"nmake /f makefile")
+            """)
+    makefile = textwrap.dedent("""\
+        all: simple.exe
+
+        .cpp.obj:
+          cl $(cppflags) $*.cpp
+
+        simple.exe: simple.obj
+          cl $(cppflags) simple.obj
+        """)
+    client.save({"conanfile.py": conanfile,
+                 "makefile": makefile,
+                 "simple.cpp": gen_function_cpp(name="main")})
+    client.run("install . {}".format(settings))
+    client.run("build .")
+    client.run_command("simple.exe")
+    check_exe_run(client.out, "main", "msvc", version, build_type, "x86_64", cppstd)
diff --git a/conans/test/functional/utils.py b/conans/test/functional/utils.py
index e318c6c6d..611fb8bab 100644
--- a/conans/test/functional/utils.py
+++ b/conans/test/functional/utils.py
@@ -83,7 +83,7 @@ def check_exe_run(output, names, compiler, version, build_type, arch, cppstd, de
         if extra_msg:  # For ``conan new`` templates
             assert "{} {} {}".format(name, extra_msg, build_type) in output
         else:
-            assert "{}: {}".format(name, extra_msg, build_type) in output
+            assert "{}: {}".format(name, build_type) in output
         if compiler == "msvc":
             if arch == "x86":
                 assert "{} _M_IX86 defined".format(name) in output
diff --git a/conans/test/integration/configuration/conf/test_conf_profile.py b/conans/test/integration/configuration/conf/test_conf_profile.py
index fd23859c0..c2c208665 100644
--- a/conans/test/integration/configuration/conf/test_conf_profile.py
+++ b/conans/test/integration/configuration/conf/test_conf_profile.py
@@ -17,7 +17,7 @@ def client():
             settings = "os", "arch", "compiler", "build_type"
             generators = "CMakeToolchain"
 
-            def run(self, cmd):  # INTERCEPTOR of running
+            def run(self, cmd, env=None):  # INTERCEPTOR of running
                 self.output.info("RECIPE-RUN: {}".format(cmd))
 
             def build(self):
diff --git a/conans/test/integration/package_id/test_validate.py b/conans/test/integration/package_id/test_validate.py
index e77a007ce..eeb1268d3 100644
--- a/conans/test/integration/package_id/test_validate.py
+++ b/conans/test/integration/package_id/test_validate.py
@@ -353,3 +353,55 @@ class TestValidate(unittest.TestCase):
         assert "conanfile.py: ERROR: Invalid ID: never ever" in c.out
         assert "Trying to install dependencies, but this configuration will fail to build a package"\
                in c.out
+
+
+class TestValidateCppstd:
+    """ aims to be a very close to real use case of cppstd management and validation in recipes
+    """
+    def test_build_17_consume_14(self):
+        client = TestClient()
+        conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.errors import ConanInvalidConfiguration
+            class Pkg(ConanFile):
+                name = "pkg"
+                version = "0.1"
+                settings = "compiler"
+
+                def validate_build(self):
+                    # Explicit logic instead of using check_min_cppstd that hides details
+                    if int(str(self.settings.compiler.cppstd)) < 17:
+                        raise ConanInvalidConfiguration("I need at least cppstd=17 to build")
+
+                def validate(self):
+                    if int(str(self.settings.compiler.cppstd)) < 14:
+                        raise ConanInvalidConfiguration("I need at least cppstd=14 to be used")
+
+                def compatibility(conanfile):
+                    return [{"settings": [("compiler.cppstd", v)]} for v in ("11", "14", "17", "20")]
+            """)
+
+        client.save({"conanfile.py": conanfile})
+
+        settings = "-s compiler=gcc -s compiler.version=9 -s compiler.libcxx=libstdc++"
+        client.run(f"create . {settings} -s compiler.cppstd=17")
+        assert "pkg/0.1:7558a15eea8c5762fa0602874a38dde9d36229c1 - Build" in client.out
+
+        # create with cppstd=14 fails, not enough
+        client.run(f"create . {settings} -s compiler.cppstd=14", assert_error=True)
+        assert "pkg/0.1:e480cd97ef8f9dfdc31a7c0710c26d9b8d78375a - Invalid" in client.out
+        assert "pkg/0.1: Cannot build for this configuration: I need at least cppstd=17 to build" \
+               in client.out
+
+        # Install with cppstd=14 can fallback to the previous one
+        client.run(f"install pkg/0.1@ {settings} -s compiler.cppstd=14")
+        assert "Using compatible package '7558a15eea8c5762fa0602874a38dde9d36229c1'" in client.out
+        assert "pkg/0.1:7558a15eea8c5762fa0602874a38dde9d36229c1 - Cache" in client.out
+
+        # FIXME 2.0: install with not enough cppstd should fail, but it doesnt
+        # It is important to know that this only happens with "compatible_packages", otherwise
+        # it will also fail (ConanCenter)
+        client.run(f"install pkg/0.1@ {settings} -s compiler.cppstd=11")
+        # not even trying to fallback to compatibles
+        assert "Using compatible package '7558a15eea8c5762fa0602874a38dde9d36229c1'" in client.out
+        assert "pkg/0.1:7558a15eea8c5762fa0602874a38dde9d36229c1 - Cache" in client.out
diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
index d0bef4d84..1b1a9b76c 100644
--- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -758,3 +758,29 @@ def test_presets_ninja_msvc(arch, arch_toolset):
     presets = json.loads(client.load("build/14/generators/CMakePresets.json"))
     assert "architecture" in presets["configurePresets"][0]
     assert "toolset" not in presets["configurePresets"][0]
+
+
+def test_pkg_config_block():
+    os_ = platform.system()
+    os_ = "Macos" if os_ == "Darwin" else os_
+    profile = textwrap.dedent("""
+        [settings]
+        os=%s
+        arch=x86_64
+
+        [conf]
+        tools.gnu:pkg_config=/usr/local/bin/pkg-config
+        """ % os_)
+
+    client = TestClient(path_with_spaces=False)
+    conanfile = GenConanfile().with_settings("os", "arch")\
+        .with_generator("CMakeToolchain")
+    client.save({"conanfile.py": conanfile,
+                "profile": profile})
+    client.run("install . -pr:b profile -pr:h profile")
+    toolchain = client.load("conan_toolchain.cmake")
+    assert 'set(PKG_CONFIG_EXECUTABLE /usr/local/bin/pkg-config CACHE FILEPATH ' in toolchain
+    pathsep = ":" if os_ != "Windows" else ";"
+    pkg_config_path_set = 'set(ENV{PKG_CONFIG_PATH} "%s$ENV{PKG_CONFIG_PATH}")' % \
+                          (client.current_folder.replace("\\", "/") + pathsep)
+    assert pkg_config_path_set in toolchain