Repository: conan-io/conan
Pull request #12559 (2022-11-17 15:30:20)

Feature/cmakedeps refactor
Changelog: Omit
Docs: Omit

A small refactor: moving some variable declaration from ``xxx-target.cmake`` to ``xxx-data.cmake``
Also, removing a unused legacy CONAN_... variable



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..190cde0a4 100644
--- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py
@@ -49,16 +49,6 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate):
 
         ########### VARIABLES #######################################################################
         #############################################################################################
-
-        set({{ pkg_name }}_COMPILE_OPTIONS{{ config_suffix }}
-            "$<$<COMPILE_LANGUAGE:CXX>{{ ':${' }}{{ pkg_name }}_COMPILE_OPTIONS_CXX{{ config_suffix }}}>"
-            "$<$<COMPILE_LANGUAGE:C>{{ ':${' }}{{ pkg_name }}_COMPILE_OPTIONS_C{{ config_suffix }}}>")
-
-        set({{ pkg_name }}_LINKER_FLAGS{{ config_suffix }}
-            "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>{{ ':${' }}{{ pkg_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>"
-            "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,MODULE_LIBRARY>{{ ':${' }}{{ pkg_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>"
-            "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,EXECUTABLE>{{ ':${' }}{{ pkg_name }}_EXE_LINK_FLAGS{{ config_suffix }}}>")
-
         set({{ pkg_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "") # Will be filled later
         conan_find_apple_frameworks({{ pkg_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "{{ '${' }}{{ pkg_name }}_FRAMEWORKS{{ config_suffix }}}" "{{ '${' }}{{ pkg_name }}_FRAMEWORK_DIRS{{ config_suffix }}}")
 
@@ -67,7 +57,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 +136,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'}}
@@ -188,10 +178,7 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate):
                 set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_COMPILE_DEFINITIONS
                              $<$<CONFIG:{{ configuration }}>:{{tvalue(pkg_name, comp_variable_name, 'COMPILE_DEFINITIONS', config_suffix)}}> APPEND)
                 set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_COMPILE_OPTIONS
-                             $<$<CONFIG:{{ configuration }}>:
-                             {{tvalue(pkg_name, comp_variable_name, 'COMPILE_OPTIONS_C', config_suffix)}}
-                             {{tvalue(pkg_name, comp_variable_name, 'COMPILE_OPTIONS_CXX', config_suffix)}}> APPEND)
-                set({{ pkg_name }}_{{ comp_variable_name }}_TARGET_PROPERTIES TRUE)
+                             $<$<CONFIG:{{ configuration }}>:{{tvalue(pkg_name, comp_variable_name, 'COMPILE_OPTIONS', config_suffix)}}> APPEND)
 
                 {%- if set_interface_link_directories %}
                 # This is only used for '#pragma comment(lib, "foo")' (automatic link)
diff --git a/conan/tools/cmake/cmakedeps/templates/target_data.py b/conan/tools/cmake/cmakedeps/templates/target_data.py
index cb4b733b0..f22858592 100644
--- a/conan/tools/cmake/cmakedeps/templates/target_data.py
+++ b/conan/tools/cmake/cmakedeps/templates/target_data.py
@@ -102,6 +102,15 @@ class ConfigDataTemplate(CMakeDepsFileTemplate):
               set({{ pkg_name }}_FRAMEWORKS{{ config_suffix }} {{ global_cpp.frameworks }})
               set({{ pkg_name }}_BUILD_DIRS{{ config_suffix }} {{ global_cpp.build_paths }})
 
+              # COMPOUND VARIABLES
+              set({{ pkg_name }}_COMPILE_OPTIONS{{ config_suffix }}
+                  "$<$<COMPILE_LANGUAGE:CXX>{{ ':${' }}{{ pkg_name }}_COMPILE_OPTIONS_CXX{{ config_suffix }}}>"
+                  "$<$<COMPILE_LANGUAGE:C>{{ ':${' }}{{ pkg_name }}_COMPILE_OPTIONS_C{{ config_suffix }}}>")
+              set({{ pkg_name }}_LINKER_FLAGS{{ config_suffix }}
+                  "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>{{ ':${' }}{{ pkg_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>"
+                  "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,MODULE_LIBRARY>{{ ':${' }}{{ pkg_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>"
+                  "$<$<STREQUAL{{ ':$' }}<TARGET_PROPERTY:TYPE>,EXECUTABLE>{{ ':${' }}{{ pkg_name }}_EXE_LINK_FLAGS{{ config_suffix }}}>")
+
 
               set({{ pkg_name }}_COMPONENTS{{ config_suffix }} {{ components_names }})
               {%- for comp_variable_name, comp_target_name, cpp in components_cpp %}
@@ -123,12 +132,15 @@ class ConfigDataTemplate(CMakeDepsFileTemplate):
               set({{ pkg_name }}_{{ comp_variable_name }}_DEPENDENCIES{{ config_suffix }} {{ cpp.public_deps }})
               set({{ pkg_name }}_{{ comp_variable_name }}_SHARED_LINK_FLAGS{{ config_suffix }} {{ cpp.sharedlinkflags_list }})
               set({{ pkg_name }}_{{ comp_variable_name }}_EXE_LINK_FLAGS{{ config_suffix }} {{ cpp.exelinkflags_list }})
+              # COMPOUND VARIABLES
               set({{ pkg_name }}_{{ comp_variable_name }}_LINKER_FLAGS{{ config_suffix }}
                       $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>{{ ':${' }}{{ pkg_name }}_{{ comp_variable_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>
                       $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,MODULE_LIBRARY>{{ ':${' }}{{ pkg_name }}_{{ comp_variable_name }}_SHARED_LINK_FLAGS{{ config_suffix }}}>
                       $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>{{ ':${' }}{{ pkg_name }}_{{ comp_variable_name }}_EXE_LINK_FLAGS{{ config_suffix }}}>
               )
-
+              set({{ pkg_name }}_{{ comp_variable_name }}_COMPILE_OPTIONS{{ config_suffix }}
+                  "$<$<COMPILE_LANGUAGE:CXX>{{ ':${' }}{{ pkg_name }}_{{ comp_variable_name }}_COMPILE_OPTIONS_CXX{{ config_suffix }}}>"
+                  "$<$<COMPILE_LANGUAGE:C>{{ ':${' }}{{ pkg_name }}_{{ comp_variable_name }}_COMPILE_OPTIONS_C{{ config_suffix }}}>")
 
               {%- endfor %}
           """)
diff --git a/conan/tools/cmake/presets.py b/conan/tools/cmake/presets.py
index c58a2337e..6d4a9a735 100644
--- a/conan/tools/cmake/presets.py
+++ b/conan/tools/cmake/presets.py
@@ -142,7 +142,8 @@ def _contents(conanfile, toolchain_file, cache_variables, generator):
     return ret
 
 
-def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables):
+def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables,
+                        user_presets_path=None):
     cache_variables = cache_variables or {}
     if platform.system() == "Windows" and generator == "MinGW Makefiles":
         if "CMAKE_SH" not in cache_variables:
@@ -186,18 +187,27 @@ def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables):
 
     data = json.dumps(data, indent=4)
     save(preset_path, data)
-    save_cmake_user_presets(conanfile, preset_path)
+    save_cmake_user_presets(conanfile, preset_path, user_presets_path)
 
 
-def save_cmake_user_presets(conanfile, preset_path):
+def save_cmake_user_presets(conanfile, preset_path, user_presets_path=None):
+    if user_presets_path is False:
+        return
+
     # Try to save the CMakeUserPresets.json if layout declared and CMakeLists.txt found
     if conanfile.source_folder and conanfile.source_folder != conanfile.generators_folder:
-        if os.path.exists(os.path.join(conanfile.source_folder, "CMakeLists.txt")):
+        if user_presets_path:
+            output_dir = os.path.join(conanfile.source_folder, user_presets_path) \
+                if not os.path.isabs(user_presets_path) else user_presets_path
+        else:
+            output_dir = conanfile.source_folder
+
+        if user_presets_path or os.path.exists(os.path.join(output_dir, "CMakeLists.txt")):
             """
                 Contents for the CMakeUserPresets.json
                 It uses schema version 4 unless it is forced to 2
             """
-            user_presets_path = os.path.join(conanfile.source_folder, "CMakeUserPresets.json")
+            user_presets_path = os.path.join(output_dir, "CMakeUserPresets.json")
             if not os.path.exists(user_presets_path):
                 data = {"version": _schema_version(conanfile, default=4),
                         "vendor": {"conan": dict()}}
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 698e050d2..f22d6f760 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -11,7 +11,7 @@ from conan.tools.build import build_jobs
 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 conan.tools.microsoft.visual import msvc_version_to_toolset_version
 from conans.client.subsystems import deduce_subsystem, WINDOWS
 from conans.errors import ConanException
 from conans.util.files import load
@@ -667,6 +667,30 @@ class TryCompileBlock(Block):
         """)
 
 
+class CompilersBlock(Block):
+    template = textwrap.dedent(r"""
+        {% for lang, compiler_path in compilers.items() %}
+        set(CMAKE_{{ lang }}_COMPILER "{{ compiler_path|replace('\\', '/') }}")
+        {% endfor %}
+    """)
+
+    def context(self):
+        # Reading configuration from "tools.build:compiler_executables" -> {"C": "/usr/bin/gcc"}
+        compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={},
+                                                     check_type=dict)
+        # Map the possible languages
+        compilers = {}
+        # Allowed <LANG> variables (and <LANG>_LAUNCHER)
+        compilers_mapping = {"c": "C", "cuda": "CUDA", "cpp": "CXX", "objc": "OBJC",
+                             "objcxx": "OBJCXX", "rc": "RC", 'fortran': "Fortran", 'asm': "ASM",
+                             "hip": "HIP", "ispc": "ISPC"}
+        for comp, lang in compilers_mapping.items():
+            # To set CMAKE_<LANG>_COMPILER
+            if comp in compilers_by_conf:
+                compilers[lang] = compilers_by_conf[comp]
+        return {"compilers": compilers}
+
+
 class GenericSystemBlock(Block):
     template = textwrap.dedent("""
         {% if cmake_sysroot %}
@@ -690,21 +714,6 @@ class GenericSystemBlock(Block):
         {% if toolset %}
         set(CMAKE_GENERATOR_TOOLSET "{{ toolset }}" CACHE STRING "" FORCE)
         {% endif %}
-        {% if compiler %}
-        if(NOT DEFINED ENV{CC})
-        set(CMAKE_C_COMPILER {{ compiler }})
-        endif()
-        {% endif %}
-        {% if compiler_cpp %}
-        if(NOT DEFINED ENV{CXX})
-        set(CMAKE_CXX_COMPILER {{ compiler_cpp }})
-        endif()
-        {% endif %}
-        {% if compiler_rc %}
-        if(NOT DEFINED ENV{RC})
-        set(CMAKE_RC_COMPILER {{ compiler_rc }})
-        endif()
-        {% endif %}
         """)
 
     def _get_toolset(self, generator):
@@ -766,23 +775,6 @@ class GenericSystemBlock(Block):
                     "armv8": "ARM64"}.get(arch)
         return None
 
-    def _get_compiler(self, generator):
-        compiler = self._conanfile.settings.get_safe("compiler")
-        os_ = self._conanfile.settings.get_safe("os")
-
-        compiler_c = compiler_cpp = compiler_rc = None
-
-        # TODO: Check if really necessary now that conanvcvars is used
-        if "Ninja" in str(generator) and is_msvc(self._conanfile):
-            compiler_c = compiler_cpp = "cl"
-        elif os_ == "Windows" and compiler == "clang" and "Visual" not in generator:
-            # definition of compiler only if not using the VS Clang toolset
-            compiler_rc = "clang"
-            compiler_c = "clang"
-            compiler_cpp = "clang++"
-
-        return compiler_c, compiler_cpp, compiler_rc
-
     def _get_generic_system_name(self):
         os_host = self._conanfile.settings.get_safe("os")
         os_build = self._conanfile.settings_build.get_safe("os")
@@ -843,19 +835,13 @@ class GenericSystemBlock(Block):
         generator = self._toolchain.generator
         generator_platform = self._get_generator_platform(generator)
         toolset = self._get_toolset(generator)
-
-        compiler, compiler_cpp, compiler_rc = self._get_compiler(generator)
-
         system_name, system_version, system_processor = self._get_cross_build()
 
         # This is handled by the tools.apple:sdk_path and CMAKE_OSX_SYSROOT in Apple
         cmake_sysroot = self._conanfile.conf.get("tools.build:sysroot")
         cmake_sysroot = cmake_sysroot.replace("\\", "/") if cmake_sysroot is not None else None
 
-        return {"compiler": compiler,
-                "compiler_rc": compiler_rc,
-                "compiler_cpp": compiler_cpp,
-                "toolset": toolset,
+        return {"toolset": toolset,
                 "generator_platform": generator_platform,
                 "cmake_system_name": system_name,
                 "cmake_system_version": system_version,
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 599a9338c..cd54a9a87 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -12,7 +12,7 @@ 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, PkgConfigBlock, \
-    SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock
+    SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock, CompilersBlock
 from conan.tools.intel import IntelCC
 from conan.tools.microsoft import VCVars
 from conan.tools.microsoft.visual import vs_ide_version
@@ -127,6 +127,7 @@ class CMakeToolchain(object):
         self.blocks = ToolchainBlocks(self._conanfile, self,
                                       [("user_toolchain", UserToolchain),
                                        ("generic_system", GenericSystemBlock),
+                                       ("compilers", CompilersBlock),
                                        ("android_system", AndroidSystemBlock),
                                        ("apple_system", AppleSystemBlock),
                                        ("fpic", FPicBlock),
@@ -145,6 +146,7 @@ class CMakeToolchain(object):
                                        ("output_dirs", OutputDirsBlock)])
 
         check_using_build_profile(self._conanfile)
+        self.user_presets_path = None
 
     def _context(self):
         """ Returns dict, the context for the template
@@ -194,7 +196,8 @@ class CMakeToolchain(object):
             else:
                 cache_variables[name] = value
 
-        write_cmake_presets(self._conanfile, toolchain, self.generator, cache_variables)
+        write_cmake_presets(self._conanfile, toolchain, self.generator, cache_variables,
+                            self.user_presets_path)
 
     def _get_generator(self, recipe_generator):
         # Returns the name of the generator to be used by CMake
diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py
index 7b0f735df..df84c6c2d 100644
--- a/conan/tools/gnu/autotoolstoolchain.py
+++ b/conan/tools/gnu/autotoolstoolchain.py
@@ -123,9 +123,12 @@ class AutotoolsToolchain:
 
     def environment(self):
         env = Environment()
-        if is_msvc(self._conanfile):
-            env.define("CXX", "cl")
-            env.define("CC", "cl")
+        compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={}, check_type=dict)
+        if compilers_by_conf:
+            compilers_mapping = {"c": "CC", "cpp": "CXX", "cuda": "NVCC", "fortran": "FC"}
+            for comp, env_var in compilers_mapping.items():
+                if comp in compilers_by_conf:
+                    env.define(env_var, compilers_by_conf[comp])
         env.append("CPPFLAGS", ["-D{}".format(d) for d in self.defines])
         env.append("CXXFLAGS", self.cxxflags)
         env.append("CFLAGS", self.cflags)
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index b75fec4bd..e5ca38954 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -163,10 +163,14 @@ class MesonToolchain(object):
                 default_comp = "gcc"
                 default_comp_cpp = "g++"
 
+        # Read configuration for compilers
+        compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={},
+                                                     check_type=dict)
         # Read the VirtualBuildEnv to update the variables
         build_env = VirtualBuildEnv(self._conanfile).vars()
-        self.c = build_env.get("CC") or default_comp
-        self.cpp = build_env.get("CXX") or default_comp_cpp
+        self.c = compilers_by_conf.get("c") or build_env.get("CC") or default_comp
+        self.cpp = compilers_by_conf.get("cpp") or build_env.get("CXX") or default_comp_cpp
+        # FIXME: Should we use the new tools.build:compiler_executables and avoid buildenv?
         self.c_ld = build_env.get("CC_LD") or build_env.get("LD")
         self.cpp_ld = build_env.get("CXX_LD") or build_env.get("LD")
         self.ar = build_env.get("AR")
@@ -190,7 +194,7 @@ class MesonToolchain(object):
         self.objcpp_args = []
         self.objcpp_link_args = []
 
-        self._resolve_apple_flags_and_variables(build_env)
+        self._resolve_apple_flags_and_variables(build_env, compilers_by_conf)
         self._resolve_android_cross_compilation()
 
     def _get_default_dirs(self):
@@ -232,7 +236,7 @@ class MesonToolchain(object):
             ret["libdir"] = libdir
         return ret
 
-    def _resolve_apple_flags_and_variables(self, build_env):
+    def _resolve_apple_flags_and_variables(self, build_env, compilers_by_conf):
         if not self._is_apple_system:
             return
         # SDK path is mandatory for cross-building
@@ -251,8 +255,8 @@ class MesonToolchain(object):
         self.apple_isysroot_flag = ["-isysroot", sdk_path] if sdk_path else []
         self.apple_min_version_flag = [apple_min_version_flag(self._conanfile)]
         # Objective C/C++ ones
-        self.objc = "clang"
-        self.objcpp = "clang++"
+        self.objc = compilers_by_conf.get("objc", "clang")
+        self.objcpp = compilers_by_conf.get("objcxx", "clang++")
         self.objc_args = self._get_env_list(build_env.get('OBJCFLAGS', []))
         self.objc_link_args = self._get_env_list(build_env.get('LDFLAGS', []))
         self.objcpp_args = self._get_env_list(build_env.get('OBJCXXFLAGS', []))
diff --git a/conan/tools/microsoft/__init__.py b/conan/tools/microsoft/__init__.py
index c956ae068..79598b28e 100644
--- a/conan/tools/microsoft/__init__.py
+++ b/conan/tools/microsoft/__init__.py
@@ -3,5 +3,7 @@ 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.nmakedeps import NMakeDeps
 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/nmakedeps.py b/conan/tools/microsoft/nmakedeps.py
new file mode 100644
index 000000000..f8af5696e
--- /dev/null
+++ b/conan/tools/microsoft/nmakedeps.py
@@ -0,0 +1,61 @@
+import os
+
+from conan.tools.env import Environment
+from conans.model.new_build_info import NewCppInfo
+
+
+class NMakeDeps(object):
+
+    def __init__(self, conanfile):
+        """
+        :param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
+        """
+        self._conanfile = conanfile
+        self._environment = None
+
+    # TODO: This is similar from AutotoolsDeps: Refactor and make common
+    def _get_cpp_info(self):
+        ret = NewCppInfo()
+        deps = self._conanfile.dependencies.host.topological_sort
+        deps = [dep for dep in reversed(deps.values())]
+        for dep in deps:
+            dep_cppinfo = dep.cpp_info.aggregated_components()
+            # In case we have components, aggregate them, we do not support isolated
+            # "targets" with autotools
+            ret.merge(dep_cppinfo)
+        return ret
+
+    @property
+    def environment(self):
+        # TODO: Seems we want to make this uniform, equal to other generators
+        if self._environment is None:
+            cpp_info = self._get_cpp_info()
+
+            lib_paths = ";".join(cpp_info.libdirs or [])
+
+            def format_lib(lib):
+                ext = os.path.splitext(lib)[1]
+                return lib if ext in (".so", ".lib", ".a", ".dylib", ".bc") else '%s.lib' % lib
+
+            ret = []
+            ret.extend(cpp_info.exelinkflags or [])
+            ret.extend(cpp_info.sharedlinkflags or [])
+            ret.extend([format_lib(lib) for lib in cpp_info.libs or []])
+            link_args = " ".join(ret)
+
+            cl_flags = [f'-I"{p}"' for p in cpp_info.includedirs or []]
+            cl_flags.extend(cpp_info.cflags or [])
+            cl_flags.extend(cpp_info.cxxflags or [])
+
+            env = Environment()
+            env.append("CL", " ".join(cl_flags))
+            env.append_path("LIB", lib_paths)
+            env.append("_LINK_", link_args)
+            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("conannmakedeps")
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/conan_api.py b/conans/client/conan_api.py
index 7e4a23ac6..1c46eabb6 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -1343,6 +1343,9 @@ class ConanAPIV1(object):
         old_lock = GraphLockFile.load(old_lockfile, revisions_enabled)
         new_lockfile = _make_abs_path(new_lockfile, cwd)
         new_lock = GraphLockFile.load(new_lockfile, revisions_enabled)
+        if old_lock.profile_host is None or new_lock.profile_host is None:
+            raise ConanException("Lockfiles with --base do not contain profile information, "
+                                 "cannot be used. Create a full lockfile")
         if old_lock.profile_host.dumps() != new_lock.profile_host.dumps():
             raise ConanException("Profiles of lockfiles are different\n%s:\n%s\n%s:\n%s"
                                  % (old_lockfile, old_lock.profile_host.dumps(),
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..dad8cdc36 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", "NMakeDeps"]
 
     def add(self, name, generator_class, custom=False):
         if name not in self._generators or custom:
@@ -150,6 +150,12 @@ 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
+        elif generator_name == "NMakeDeps":
+            from conan.tools.microsoft import NMakeDeps
+            return NMakeDeps
         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)
diff --git a/conans/model/conf.py b/conans/model/conf.py
index b7044625b..e19f04017 100644
--- a/conans/model/conf.py
+++ b/conans/model/conf.py
@@ -52,6 +52,7 @@ BUILT_IN_CONFS = {
     "tools.build:defines": "List of extra definition flags used by different toolchains like CMakeToolchain and AutotoolsToolchain",
     "tools.build:sharedlinkflags": "List of extra flags used by CMakeToolchain for CMAKE_SHARED_LINKER_FLAGS_INIT variable",
     "tools.build:exelinkflags": "List of extra flags used by CMakeToolchain for CMAKE_EXE_LINKER_FLAGS_INIT variable",
+    "tools.build:compiler_executables": "Defines a Python dict-like with the compilers path to be used. Allowed keys {'c', 'cpp', 'cuda', 'objc', 'objcxx', 'rc', 'fortran', 'asm', 'hip', 'ispc'}",
     "tools.microsoft.bash:subsystem": "Set subsystem to use for Windows. Possible values: 'msys2', 'msys', 'cygwin', 'wsl' and 'sfu'",
     "tools.microsoft.bash:path": "Path to the shell executable. Default: 'bash'",
     "tools.apple:sdk_path": "Path for the sdk location. This value will be passed as SDKROOT or -isysroot depending on the generator used",


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_win_clang.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
index 368645491..a6c30e1b7 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
@@ -27,6 +27,8 @@ def client():
         build_type=Release
         compiler=clang
         compiler.version=12
+        [conf]
+        tools.build:compiler_executables={"cpp": "clang++", "c": "clang", "rc": "clang"}
         """)
     conanfile = textwrap.dedent("""
         import os
diff --git a/conans/test/functional/toolchains/gnu/autotools/test_win_bash.py b/conans/test/functional/toolchains/gnu/autotools/test_win_bash.py
index 1fbb389a8..28d0972a2 100644
--- a/conans/test/functional/toolchains/gnu/autotools/test_win_bash.py
+++ b/conans/test/functional/toolchains/gnu/autotools/test_win_bash.py
@@ -20,6 +20,7 @@ def test_autotools_bash_complete():
     save(client.cache.new_config_path, textwrap.dedent("""
             tools.microsoft.bash:subsystem=msys2
             tools.microsoft.bash:path={}
+            tools.build:compiler_executables={{"c": "cl", "cpp": "cl"}}
             """.format(bash_path)))
 
     main = gen_function_cpp(name="main")
@@ -31,7 +32,6 @@ def test_autotools_bash_complete():
     conanfile = textwrap.dedent("""
         from conans import ConanFile
         from conan.tools.gnu import Autotools
-        from conan.tools.env import Environment
 
         class TestConan(ConanFile):
             settings = "os", "compiler", "arch", "build_type"
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..e1a2409bf
--- /dev/null
+++ b/conans/test/functional/toolchains/test_nmake_toolchain.py
@@ -0,0 +1,57 @@
+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)
+    client.run("new dep/1.0 -m=cmake_lib")
+    client.run(f'create . -tf=None {settings} '
+               f'-c tools.cmake.cmaketoolchain:generator="Visual Studio 15"')
+
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            requires = "dep/1.0"
+            generators = "NMakeToolchain", "NMakeDeps"
+
+            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", includes=["dep"], calls=["dep"])},
+                clean_first=True)
+    client.run("install . {}".format(settings))
+    client.run("build .")
+    client.run_command("simple.exe")
+    assert "dep/1.0" in client.out
+    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/graph_lock/lock_recipe_test.py b/conans/test/integration/graph_lock/lock_recipe_test.py
index 989e5fefa..f3b6e96be 100644
--- a/conans/test/integration/graph_lock/lock_recipe_test.py
+++ b/conans/test/integration/graph_lock/lock_recipe_test.py
@@ -20,6 +20,17 @@ class LockRecipeTest(unittest.TestCase):
         self.assertIn("Lockfiles with --base do not contain profile information, "
                       "cannot be used. Create a full lockfile", client.out)
 
+    def test_error_pass_base_update(self):
+        client = TestClient()
+        client.save({"conanfile.py": GenConanfile()})
+        client.run("create . pkg/0.1@")
+        client.save({"conanfile.py": GenConanfile().with_require("pkg/0.1")})
+        client.run("lock create conanfile.py --base --lockfile-out=conan.lock")
+        client.run("lock create conanfile.py --base --lockfile-out=conan2.lock")
+        client.run("lock update conan.lock conan2.lock", assert_error=True)
+        self.assertIn("Lockfiles with --base do not contain profile information, "
+                      "cannot be used. Create a full lockfile", client.out)
+
     def test_lock_recipe(self):
         client = TestClient()
         client.save({"conanfile.py": GenConanfile().with_setting("os")})
diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
index 1b1a9b76c..e219bcc5a 100644
--- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -8,6 +8,7 @@ from mock import mock
 
 from conan.tools.cmake.presets import load_cmake_presets
 from conans.test.assets.genconanfile import GenConanfile
+from conans.test.utils.test_files import temp_folder
 from conans.test.utils.tools import TestClient
 from conans.util.files import rmdir
 
@@ -784,3 +785,63 @@ def test_pkg_config_block():
     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
+
+
+@pytest.mark.parametrize("path", ["subproject", False])
+def test_user_presets_custom_location(path):
+    client = TestClient()
+    conanfile = textwrap.dedent("""
+                import os
+                from conan import ConanFile
+                from conan.tools.cmake import cmake_layout, CMakeToolchain
+
+                class Conan(ConanFile):
+                    settings = "os", "arch", "compiler", "build_type"
+
+                    def generate(self):
+                        t = CMakeToolchain(self)
+                        t.user_presets_path = {}
+                        t.generate()
+
+                    def layout(self):
+                        cmake_layout(self)
+                """.format('"{}"'.format(path) if isinstance(path, str) else path))
+    client.save({"CMakeLists.txt": "",
+                 "subproject/CMakeLists.txt": "",
+                 "subproject2/foo.txt": "",
+                 "conanfile.py": conanfile})
+
+    # We want to generate it to build the subproject
+    client.run("install . ")
+
+    if path is not False:
+        assert not os.path.exists(os.path.join(client.current_folder, "CMakeUserPresets.json"))
+        assert os.path.exists(os.path.join(client.current_folder, "subproject", "CMakeUserPresets.json"))
+    else:
+        assert not os.path.exists(os.path.join(client.current_folder, "CMakeUserPresets.json"))
+        assert not os.path.exists(os.path.join(client.current_folder, "False", "CMakeUserPresets.json"))
+
+
+def test_set_cmake_lang_compilers_and_launchers():
+    profile = textwrap.dedent(r"""
+    [settings]
+    os=Windows
+    arch=x86_64
+    compiler=clang
+    compiler.version=15
+    compiler.libcxx=libstdc++11
+    [conf]
+    tools.build:compiler_executables={"c": "/my/local/gcc", "cpp": "g++", "rc": "C:\\local\\rc.exe"}
+    """)
+    client = TestClient(path_with_spaces=False)
+    conanfile = GenConanfile().with_settings("os", "arch", "compiler")\
+        .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(CMAKE_C_COMPILER clang)' not in toolchain
+    assert 'set(CMAKE_CXX_COMPILER clang++)' not in toolchain
+    assert 'set(CMAKE_C_COMPILER "/my/local/gcc")' in toolchain
+    assert 'set(CMAKE_CXX_COMPILER "g++")' in toolchain
+    assert 'set(CMAKE_RC_COMPILER "C:/local/rc.exe")' in toolchain
diff --git a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
index 2d30b9eb1..2b9670452 100644
--- a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
+++ b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py
@@ -29,6 +29,7 @@ def conanfile():
     c.settings.compiler.libcxx = "libstdc++"
     c.settings.os = "Windows"
     c.conf = Conf()
+    c.conf.define("tools.cmake.cmaketoolchain:system_name", "potato")
     c.folders.set_base_generators(".")
     c._conan_node = Mock()
     c._conan_node.dependencies = []
@@ -38,29 +39,29 @@ def conanfile():
 def test_cmake_toolchain(conanfile):
     toolchain = CMakeToolchain(conanfile)
     content = toolchain.content
-    assert 'set(CMAKE_C_COMPILER clang)' in content
+    assert 'set(CMAKE_SYSTEM_NAME potato)' in content
 
 
 def test_remove(conanfile):
     toolchain = CMakeToolchain(conanfile)
     toolchain.blocks.remove("generic_system")
     content = toolchain.content
-    assert 'CMAKE_C_COMPILER' not in content
+    assert 'CMAKE_SYSTEM_NAME' not in content
 
 
 def test_template_remove(conanfile):
     toolchain = CMakeToolchain(conanfile)
     toolchain.blocks["generic_system"].template = ""
     content = toolchain.content
-    assert 'CMAKE_C_COMPILER' not in content
+    assert 'CMAKE_SYSTEM_NAME' not in content
 
 
 def test_template_change(conanfile):
     toolchain = CMakeToolchain(conanfile)
     tmp = toolchain.blocks["generic_system"].template
-    toolchain.blocks["generic_system"].template = tmp.replace("CMAKE_C_COMPILER", "OTHER_THING")
+    toolchain.blocks["generic_system"].template = tmp.replace("CMAKE_SYSTEM_NAME", "OTHER_THING")
     content = toolchain.content
-    assert 'set(OTHER_THING clang)' in content
+    assert 'set(OTHER_THING potato)' in content
 
 
 def test_context_change(conanfile):
@@ -69,25 +70,26 @@ def test_context_change(conanfile):
 
     def context(self):
         assert self
-        return {"compiler": None}
+        return {"cmake_system_name": None}
+
     tmp.context = types.MethodType(context, tmp)
     content = toolchain.content
-    assert 'CMAKE_C_COMPILER' not in content
+    assert 'CMAKE_SYSTEM_NAME' not in content
 
 
 def test_context_update(conanfile):
     toolchain = CMakeToolchain(conanfile)
-    compiler = toolchain.blocks["generic_system"].values["compiler"]
-    toolchain.blocks["generic_system"].values["compiler"] = "Super" + compiler
+    cmake_system_name = toolchain.blocks["generic_system"].values["cmake_system_name"]
+    toolchain.blocks["generic_system"].values["cmake_system_name"] = "Super" + cmake_system_name
     content = toolchain.content
-    assert 'set(CMAKE_C_COMPILER Superclang)' in content
+    assert 'set(CMAKE_SYSTEM_NAME Superpotato)' in content
 
 
 def test_context_replace(conanfile):
     toolchain = CMakeToolchain(conanfile)
-    toolchain.blocks["generic_system"].values = {"compiler": "SuperClang"}
+    toolchain.blocks["generic_system"].values = {"cmake_system_name": "SuperPotato"}
     content = toolchain.content
-    assert 'set(CMAKE_C_COMPILER SuperClang)' in content
+    assert 'set(CMAKE_SYSTEM_NAME SuperPotato)' in content
 
 
 def test_replace_block(conanfile):
@@ -102,7 +104,7 @@ def test_replace_block(conanfile):
     toolchain.blocks["generic_system"] = MyBlock
     content = toolchain.content
     assert 'HelloWorld' in content
-    assert 'CMAKE_C_COMPILER' not in content
+    assert 'set(CMAKE_SYSTEM_NAME potato)' not in content
 
 
 def test_add_new_block(conanfile):
@@ -117,7 +119,7 @@ def test_add_new_block(conanfile):
     toolchain.blocks["mynewblock"] = MyBlock
     content = toolchain.content
     assert 'Hello World!!!' in content
-    assert 'CMAKE_C_COMPILER' in content
+    assert 'set(CMAKE_SYSTEM_NAME potato)' in content
 
 
 def test_user_toolchain(conanfile):