Repository: conan-io/conan

Issue:
Conan fails to detect the compiler although `CC` and `CXX` are set
### Environment details

* Operating System+version: ubuntu-22.04
* Compiler+version: 13.1.0
* Conan version: 2.0.9
* Python version: 3.11.4


### Steps to reproduce

I am creating a Conda package of my software, as part of a Github C/I workflow. My software depends on other software (e.g.: Boost). For dependencies for which there isn't a Conda package available I use Conan to install them.

Here is the idea, scattered over the various files (`.github/workflows/linux-conda.yml`, `environment/conda/build.sh`):
- Create a Conda environment
- Install the boa Conda package builder and execute: `conda mambabuild environment/conda --override-channels --channel conda-forge`

Then `build.sh` is executed which includes:

```bash
conan profile detect                                                                                             
conan install . \                                                           
    --profile=default \                                                                                          
    --settings:host compiler.cppstd=17 \                                                                         
    --settings:build compiler.cppstd=17 \                                                                        
    --settings:build build_type=Release \                                                                        
    --build=missing \                                                                                            
    --output-folder=build
```

This results in an error (see log below). Conan is not able to correctly detect the compiler. The `CC` and `CXX` variables are set, so I would expect that those are used to detect the compiler.

I tried passing in the compiler explicitly, when calling `conan install`, using `--setting compiler=$CXX`, but that failed with `ERROR: 'settings.compiler' value not defined`. Maybe because the whole compiler section is missing from the profile?

I think this is a bug, given that the compiler is installed and pointed to by the standard variables. If not, then my question is how to get Conan to detect the compiler correctly in this situation.

I use the same conan commands to install Conan packages outside of the Conda build environment (on Linux, macOS, Windows), and those work fine. It seems that the Conda build environment is confusing Conan. Maybe because of the atypical setting of `CC` and `CXX`?

### Logs

```
2023-08-24T16:10:09.7983374Z CC and CXX: /usr/share/miniconda/envs/test/conda-bld/lue_1692893106258/_build_env/bin/x86_64-conda-linux-gnu-cc, /usr/share/miniconda/envs/test/conda-bld/lue_1692893106258/_build_env/bin/x86_64-conda-linux-gnu-c++ 
2023-08-24T16:10:09.7984827Z ERROR: Not able to automatically detect '/usr/share/miniconda/envs/test/conda-bld/lue_1692893106258/_build_env/bin/x86_64-conda-linux-gnu-cc' version
2023-08-24T16:10:09.7985898Z No compiler was detected (one may not be needed)
2023-08-24T16:10:09.7986712Z WARN: This profile is a guess of your environment, please check it.
2023-08-24T16:10:09.7987819Z WARN: The output of this command is not guaranteed to be stable and can change in future Conan versions.
2023-08-24T16:10:09.7988301Z WARN: Use your own profile files for stability.
2023-08-24T16:10:09.7988673Z Saving detected profile to /home/runner/.conan2/profiles/default
2023-08-24T16:10:09.8382941Z + conan install . --profile=default --settings:host compiler.cppstd=17 --settings:build compiler.cppstd=17 --settings:build build_type=Release --build=missing --output-folder=build
2023-08-24T16:10:10.1836158Z ERROR: 'settings.compiler' value not defined
```

Somewhat later on in the log:

```
2023-08-24T16:10:10.3898835Z Detected profile:
2023-08-24T16:10:10.3899230Z [settings]
2023-08-24T16:10:10.3899457Z arch=x86_64
2023-08-24T16:10:10.3899668Z build_type=Release
2023-08-24T16:10:10.3900101Z os=Linux
```

Discussion:
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/conan-io/conan?pullRequest=15664) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/conan-io/conan?pullRequest=15664) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/conan-io/conan?pullRequest=15664) it.</sub>

Patch:
diff --git a/conan/api/subapi/install.py b/conan/api/subapi/install.py
index 49dc57eb9..79394d3ab 100644
--- a/conan/api/subapi/install.py
+++ b/conan/api/subapi/install.py
@@ -1,6 +1,7 @@
 from conan.internal.conan_app import ConanApp
 from conan.internal.deploy import do_deploys
 from conans.client.generators import write_generators
+from conans.client.graph.install_graph import InstallGraph
 from conans.client.installer import BinaryInstaller
 from conans.errors import ConanInvalidConfiguration
 
@@ -18,8 +19,11 @@ class InstallAPI:
         app = ConanApp(self.conan_api)
         installer = BinaryInstaller(app, self.conan_api.config.global_conf,
                                     self.conan_api.local.editable_packages)
-        installer.install_system_requires(deps_graph)  # TODO: Optimize InstallGraph computation
-        installer.install(deps_graph, remotes)
+        install_graph = InstallGraph(deps_graph)
+        install_graph.raise_errors()
+        install_order = install_graph.install_order()
+        installer.install_system_requires(deps_graph, install_order=install_order)
+        installer.install(deps_graph, remotes, install_order=install_order)
 
     def install_system_requires(self, graph, only_info=False):
         """ Install binaries for dependency graph
diff --git a/conan/internal/api/detect_api.py b/conan/internal/api/detect_api.py
index cc0fa7625..4600e769b 100644
--- a/conan/internal/api/detect_api.py
+++ b/conan/internal/api/detect_api.py
@@ -276,7 +276,7 @@ def detect_default_compiler():
         command = cc or cxx
         if "clang" in command.lower():
             return _clang_compiler(command)
-        if "gcc" in command or "g++" in command or "c++" in command:
+        if "gnu-cc" in command or "gcc" in command or "g++" in command or "c++" in command:
             gcc, gcc_version, compiler_exe = _gcc_compiler(command)
             if platform.system() == "Darwin" and gcc is None:
                 output.error("%s detected as a frontend using apple-clang. "
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 455cc5743..0a1fcabeb 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -12,6 +12,7 @@ from conan.tools.build import build_jobs
 from conan.tools.build.flags import architecture_flag, libcxx_flags
 from conan.tools.build.cross_building import cross_building
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
+from conan.tools.cmake.utils import is_multi_configuration
 from conan.tools.intel import IntelCC
 from conan.tools.microsoft.visual import msvc_version_to_toolset_version
 from conans.client.subsystems import deduce_subsystem, WINDOWS
@@ -544,25 +545,62 @@ class UserToolchain(Block):
 class ExtraFlagsBlock(Block):
     """This block is adding flags directly from user [conf] section"""
 
-    template = textwrap.dedent("""
-        # Extra c, cxx, linkflags and defines
+    _template = textwrap.dedent("""
+        # Conan conf flags start: {{config}}
         {% if cxxflags %}
-        string(APPEND CONAN_CXX_FLAGS "{% for cxxflag in cxxflags %} {{ cxxflag }}{% endfor %}")
+        string(APPEND CONAN_CXX_FLAGS{{suffix}} "{% for cxxflag in cxxflags %} {{ cxxflag }}{% endfor %}")
         {% endif %}
         {% if cflags %}
-        string(APPEND CONAN_C_FLAGS "{% for cflag in cflags %} {{ cflag }}{% endfor %}")
+        string(APPEND CONAN_C_FLAGS{{suffix}} "{% for cflag in cflags %} {{ cflag }}{% endfor %}")
         {% endif %}
         {% if sharedlinkflags %}
-        string(APPEND CONAN_SHARED_LINKER_FLAGS "{% for sharedlinkflag in sharedlinkflags %} {{ sharedlinkflag }}{% endfor %}")
+        string(APPEND CONAN_SHARED_LINKER_FLAGS{{suffix}} "{% for sharedlinkflag in sharedlinkflags %} {{ sharedlinkflag }}{% endfor %}")
         {% endif %}
         {% if exelinkflags %}
-        string(APPEND CONAN_EXE_LINKER_FLAGS "{% for exelinkflag in exelinkflags %} {{ exelinkflag }}{% endfor %}")
+        string(APPEND CONAN_EXE_LINKER_FLAGS{{suffix}} "{% for exelinkflag in exelinkflags %} {{ exelinkflag }}{% endfor %}")
         {% endif %}
         {% if defines %}
+        {% if config %}
+        add_compile_definitions($<$<CONFIG:{{config}}>:{% for define in defines %}" {{ define }}"{% endfor %}>)
+        {% else %}
         add_compile_definitions({% for define in defines %} "{{ define }}"{% endfor %})
         {% endif %}
+        {% endif %}
+        # Conan conf flags end
     """)
 
+    @property
+    def template(self):
+        if not is_multi_configuration(self._toolchain.generator):
+            return self._template
+
+        sections = {}
+        if os.path.exists(CONAN_TOOLCHAIN_FILENAME):
+            existing_toolchain = load(CONAN_TOOLCHAIN_FILENAME)
+            lines = existing_toolchain.splitlines()
+            current_section = None
+            for line in lines:
+                if line.startswith("# Conan conf flags start: "):
+                    section_name = line.split(":", 1)[1].strip()
+                    current_section = [line]
+                    sections[section_name] = current_section
+                elif line == "# Conan conf flags end":
+                    current_section.append(line)
+                    current_section = None
+                elif current_section is not None:
+                    current_section.append(line)
+            sections.pop("", None)  # Just in case it had a single config before
+
+        config = self._conanfile.settings.get_safe("build_type")
+        for k, v in sections.items():
+            if k != config:
+                v.insert(0, "{% raw %}")
+                v.append("{% endraw %}")
+        sections[config] = [self._template]
+        sections = ["\n".join(lines) for lines in sections.values()]
+        sections = "\n".join(sections)
+        return sections
+
     def context(self):
         # Now, it's time to get all the flags defined by the user
         cxxflags = self._toolchain.extra_cxxflags + self._conanfile.conf.get("tools.build:cxxflags", default=[], check_type=list)
@@ -579,7 +617,14 @@ class ExtraFlagsBlock(Block):
             self._conanfile.output.warning("tools.build:cxxflags or cflags are defined, but Android NDK toolchain may be overriding "
                                            "the values. Consider setting tools.android:cmake_legacy_toolchain to False.")
 
+        config = ""
+        suffix = ""
+        if is_multi_configuration(self._toolchain.generator):
+            config = self._conanfile.settings.get_safe("build_type")
+            suffix = f"_{config.upper()}" if config else ""
         return {
+            "config": config,
+            "suffix": suffix,
             "cxxflags": cxxflags,
             "cflags": cflags,
             "sharedlinkflags": sharedlinkflags,
@@ -590,6 +635,22 @@ class ExtraFlagsBlock(Block):
 
 class CMakeFlagsInitBlock(Block):
     template = textwrap.dedent("""
+        foreach(config ${CMAKE_CONFIGURATION_TYPES})
+            string(TOUPPER ${config} config)
+            if(DEFINED CONAN_CXX_FLAGS_${config})
+              string(APPEND CMAKE_CXX_FLAGS_${config}_INIT " ${CONAN_CXX_FLAGS_${config}}")
+            endif()
+            if(DEFINED CONAN_C_FLAGS_${config})
+              string(APPEND CMAKE_C_FLAGS_${config}_INIT " ${CONAN_C_FLAGS_${config}}")
+            endif()
+            if(DEFINED CONAN_SHARED_LINKER_FLAGS_${config})
+              string(APPEND CMAKE_SHARED_LINKER_FLAGS_${config}_INIT " ${CONAN_SHARED_LINKER_FLAGS_${config}}")
+            endif()
+            if(DEFINED CONAN_EXE_LINKER_FLAGS_${config})
+              string(APPEND CMAKE_EXE_LINKER_FLAGS_${config}_INIT " ${CONAN_EXE_LINKER_FLAGS_${config}}")
+            endif()
+        endforeach()
+
         if(DEFINED CONAN_CXX_FLAGS)
           string(APPEND CMAKE_CXX_FLAGS_INIT " ${CONAN_CXX_FLAGS}")
         endif()
@@ -602,6 +663,7 @@ class CMakeFlagsInitBlock(Block):
         if(DEFINED CONAN_EXE_LINKER_FLAGS)
           string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${CONAN_EXE_LINKER_FLAGS}")
         endif()
+
         """)
 
 
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 4c644e395..c0b9021eb 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -222,8 +222,16 @@ class DepsGraphBuilder(object):
     @staticmethod
     def _resolved_system(node, require, profile_build, profile_host, resolve_prereleases):
         profile = profile_build if node.context == CONTEXT_BUILD else profile_host
-        dep_type = "platform_tool_requires" if require.build else "platform_requires"
-        system_reqs = getattr(profile, dep_type)
+        if node.context == CONTEXT_BUILD:
+            # If we are in the build context, the platform_tool_requires ALSO applies to the
+            # regular requires. It is not possible in the build context to have tool-requires
+            # and regular requires to the same package from Conan and from Platform
+            system_reqs = profile.platform_tool_requires
+            if not require.build:
+                system_reqs = system_reqs + profile.platform_requires
+        else:
+            system_reqs = profile.platform_tool_requires if require.build \
+                else profile.platform_requires
         if system_reqs:
             version_range = require.version_range
             for d in system_reqs:
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index c044f4fb1..107e20dc0 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -4,7 +4,7 @@ import textwrap
 
 from conan.api.output import ConanOutput
 from conans.client.graph.graph import RECIPE_CONSUMER, RECIPE_VIRTUAL, BINARY_SKIP, \
-    BINARY_MISSING, BINARY_INVALID, Overrides, BINARY_BUILD, BINARY_EDITABLE_BUILD
+    BINARY_MISSING, BINARY_INVALID, Overrides, BINARY_BUILD, BINARY_EDITABLE_BUILD, BINARY_PLATFORM
 from conans.errors import ConanInvalidConfiguration, ConanException
 from conans.model.package_ref import PkgReference
 from conans.model.recipe_ref import RecipeReference
@@ -370,7 +370,8 @@ class InstallGraph:
 
     def _initialize_deps_graph(self, deps_graph):
         for node in deps_graph.ordered_iterate():
-            if node.recipe in (RECIPE_CONSUMER, RECIPE_VIRTUAL) or node.binary == BINARY_SKIP:
+            if node.recipe in (RECIPE_CONSUMER, RECIPE_VIRTUAL) \
+                    or node.binary in (BINARY_SKIP, BINARY_PLATFORM):
                 continue
 
             key = node.ref if self._order == "recipe" else node.pref
diff --git a/conans/client/installer.py b/conans/client/installer.py
index 551cf8226..7c05e2724 100644
--- a/conans/client/installer.py
+++ b/conans/client/installer.py
@@ -7,7 +7,7 @@ from conans.client.conanfile.build import run_build_method
 from conans.client.conanfile.package import run_package_method
 from conans.client.generators import write_generators
 from conans.client.graph.graph import BINARY_BUILD, BINARY_CACHE, BINARY_DOWNLOAD, BINARY_EDITABLE, \
-    BINARY_PLATFORM, BINARY_UPDATE, BINARY_EDITABLE_BUILD, BINARY_SKIP
+    BINARY_UPDATE, BINARY_EDITABLE_BUILD, BINARY_SKIP
 from conans.client.graph.install_graph import InstallGraph
 from conans.client.source import retrieve_exports_sources, config_source
 from conans.errors import (ConanException, conanfile_exception_formatter, conanfile_remove_attr)
@@ -199,9 +199,10 @@ class BinaryInstaller:
         config_source(export_source_folder, conanfile, self._hook_manager)
 
     @staticmethod
-    def install_system_requires(graph, only_info=False):
-        install_graph = InstallGraph(graph)
-        install_order = install_graph.install_order()
+    def install_system_requires(graph, only_info=False, install_order=None):
+        if install_order is None:
+            install_graph = InstallGraph(graph)
+            install_order = install_graph.install_order()
 
         for level in install_order:
             for install_reference in level:
@@ -236,16 +237,15 @@ class BinaryInstaller:
                 for package in install_reference.packages.values():
                     self._install_source(package.nodes[0], remotes)
 
-    def install(self, deps_graph, remotes):
+    def install(self, deps_graph, remotes, install_order=None):
         assert not deps_graph.error, "This graph cannot be installed: {}".format(deps_graph)
+        if install_order is None:
+            install_graph = InstallGraph(deps_graph)
+            install_graph.raise_errors()
+            install_order = install_graph.install_order()
 
         ConanOutput().title("Installing packages")
 
-        # order by levels and separate the root node (ref=None) from the rest
-        install_graph = InstallGraph(deps_graph)
-        install_graph.raise_errors()
-        install_order = install_graph.install_order()
-
         package_count = sum([sum(len(install_reference.packages.values())
                                  for level in install_order
                                  for install_reference in level)])
@@ -295,9 +295,6 @@ class BinaryInstaller:
         self._remote_manager.get_package(node.pref, node.binary_remote)
 
     def _handle_package(self, package, install_reference, handled_count, total_count):
-        if package.binary == BINARY_PLATFORM:
-            return
-
         if package.binary in (BINARY_EDITABLE, BINARY_EDITABLE_BUILD):
             self._handle_node_editable(package)
             return
diff --git a/conans/model/requires.py b/conans/model/requires.py
index e744217a4..50b7739f8 100644
--- a/conans/model/requires.py
+++ b/conans/model/requires.py
@@ -299,10 +299,12 @@ class Requirement:
             # Unknown, default. This happens all the time while check_downstream as shared is unknown
             # FIXME
             downstream_require = require.copy_requirement()
-            if pkg_type in (PackageType.SHARED, PackageType.STATIC, PackageType.APP):
-                downstream_require.headers = False
-            if pkg_type in (PackageType.SHARED, PackageType.APP):
+            # This is faster than pkg_type in (pkg.shared, pkg.static, ....)
+            if pkg_type is PackageType.SHARED or pkg_type is PackageType.APP:
                 downstream_require.libs = False
+                downstream_require.headers = False
+            elif pkg_type is PackageType.STATIC:
+                downstream_require.headers = False
 
         assert require.visible, "at this point require should be visible"
 
@@ -365,7 +367,7 @@ class Requirement:
         non_embed_mode = getattr(dep_conanfile, "package_id_non_embed_mode", non_embed_mode)
         unknown_mode = getattr(dep_conanfile, "package_id_unknown_mode", unknown_mode)
         if self.headers or self.libs:  # only if linked
-            if pkg_type in (PackageType.SHARED, PackageType.APP):
+            if pkg_type is PackageType.SHARED or pkg_type is PackageType.APP:
                 if dep_pkg_type is PackageType.SHARED:
                     self.package_id_mode = non_embed_mode
                 else:
diff --git a/conans/model/settings.py b/conans/model/settings.py
index 7270f8745..c7b3837aa 100644
--- a/conans/model/settings.py
+++ b/conans/model/settings.py
@@ -46,7 +46,7 @@ class SettingsItem(object):
         result = SettingsItem({}, name=self._name)
         result._value = self._value
         if not isinstance(self._definition, dict):
-            result._definition = self._definition[:]
+            result._definition = self._definition  # Not necessary to copy this, not mutable
         else:
             result._definition = {k: v.copy() for k, v in self._definition.items()}
         return result
@@ -235,14 +235,12 @@ class Settings(object):
         """ deepcopy, recursive
         """
         result = Settings({}, name=self._name, parent_value=self._parent_value)
-        for k, v in self._data.items():
-            result._data[k] = v.copy()
+        result._data = {k: v.copy() for k, v in self._data.items()}
         return result
 
     def copy_conaninfo_settings(self):
         result = Settings({}, name=self._name, parent_value=self._parent_value)
-        for k, v in self._data.items():
-            result._data[k] = v.copy_conaninfo_settings()
+        result._data = {k: v.copy_conaninfo_settings() for k, v in self._data.items()}
         return result
 
     @staticmethod


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index 7a3d7054f..e92fea6ac 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -1686,3 +1686,87 @@ class TestEnvironmentInPresets:
 
         c.run_command(f"ctest --preset conan-release")
         assert "tests passed" in c.out
+
+
+@pytest.mark.tool("cmake")
+@pytest.mark.skipif(platform.system() != "Windows", reason="neeed multi-config")
+def test_cmake_toolchain_cxxflags_multi_config():
+    c = TestClient()
+    profile_release = textwrap.dedent(r"""
+        include(default)
+        [conf]
+        tools.build:defines=["answer=42"]
+        tools.build:cxxflags=["/Zc:__cplusplus"]
+        """)
+    profile_debug = textwrap.dedent(r"""
+        include(default)
+        [settings]
+        build_type=Debug
+        [conf]
+        tools.build:defines=["answer=123"]
+        tools.build:cxxflags=["/W4"]
+        """)
+
+    conanfile = textwrap.dedent(r'''
+        from conan import ConanFile
+        from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
+
+        class Test(ConanFile):
+            exports_sources = "CMakeLists.txt", "src/*"
+            settings = "os", "compiler", "arch", "build_type"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                cmake_layout(self)
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+                cmake.build()
+        ''')
+
+    main = textwrap.dedent(r"""
+        #include <iostream>
+        #include <stdio.h>
+
+        #define STR(x)   #x
+        #define SHOW_DEFINE(x) printf("%s=%s\n", #x, STR(x))
+
+        int main() {
+            SHOW_DEFINE(answer);
+            char a = 123L;  // to trigger warnings
+
+            #if __cplusplus
+            std::cout << "CPLUSPLUS: __cplusplus" << __cplusplus<< "\n";
+            #endif
+        }
+        """)
+
+    cmakelists = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(Test CXX)
+        add_executable(example src/main.cpp)
+        """)
+
+    c.save({"conanfile.py": conanfile,
+            "profile_release": profile_release,
+            "profile_debug": profile_debug,
+            "src/main.cpp": main,
+            "CMakeLists.txt": cmakelists}, clean_first=True)
+    c.run("install . -pr=./profile_release")
+    c.run("install . -pr=./profile_debug")
+
+    with c.chdir("build"):
+        c.run_command("cmake .. -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake")
+        c.run_command("cmake --build . --config Release")
+        assert "warning C4189" not in c.out
+        c.run_command("cmake --build . --config Debug")
+        assert "warning C4189" in c.out
+
+    c.run_command(r"build\Release\example.exe")
+    assert 'answer=42' in c.out
+    assert "CPLUSPLUS: __cplusplus20" in c.out
+
+    c.run_command(r"build\Debug\example.exe")
+    assert 'answer=123' in c.out
+    assert "CPLUSPLUS: __cplusplus19" in c.out
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 b09dccd59..c2fcd265e 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
@@ -155,7 +155,7 @@ class TestVSClangCL:
                    '-c tools.cmake.cmaketoolchain:generator="{}"'.format(generator))
         assert 'cmake -G "{}"'.format(generator) in client.out
         assert "MSVC-like command-line" in client.out
-        assert "main __clang_major__14" in client.out
+        assert "main __clang_major__16" in client.out
         # Check this! Clang compiler in Windows is reporting MSC_VER and MSVC_LANG!
         assert "main _MSC_VER193" in client.out
         assert "main _MSVC_LANG2017" in client.out
diff --git a/conans/test/integration/conanfile/conan_data_test.py b/conans/test/integration/conanfile/conan_data_test.py
index dbbdc6a0a..89f10d659 100644
--- a/conans/test/integration/conanfile/conan_data_test.py
+++ b/conans/test/integration/conanfile/conan_data_test.py
@@ -9,7 +9,7 @@ import yaml
 from conans.model.recipe_ref import RecipeReference
 from conans.test.utils.file_server import TestFileServer
 from conans.test.utils.test_files import tgz_with_contents
-from conans.test.utils.tools import TestClient
+from conans.test.utils.tools import TestClient, GenConanfile
 from conans.util.files import md5sum, sha1sum, sha256sum, load
 
 
@@ -323,3 +323,55 @@ def test_conandata_trim():
     assert "1.1" not in data2
     assert data1 == data2
     assert "pkg/1.0: Exported: pkg/1.0#70612e15e4fc9af1123fe11731ac214f" in c.out
+
+
+def test_trim_conandata_as_hook():
+    c = TestClient()
+    c.save_home({"extensions/hooks/hook_trim.py": textwrap.dedent("""
+    from conan.tools.files import trim_conandata
+
+    def post_export(conanfile):
+        trim_conandata(conanfile)
+    """)})
+
+    conandata_yml = textwrap.dedent("""\
+            sources:
+              "1.0":
+                url: "url1"
+                sha256: "sha1"
+            patches:
+              "1.0":
+                - patch_file: "patches/some_patch"
+                  base_path: "source_subfolder"
+            something: else
+              """)
+    c.save({"conanfile.py": GenConanfile("pkg"),
+            "conandata.yml": conandata_yml})
+    c.run("export . --version=1.0")
+    layout = c.exported_layout()
+    data1 = load(os.path.join(layout.export(), "conandata.yml"))
+    assert "pkg/1.0: Exported: pkg/1.0#03af39add1c7c9d68dcdb10b6968a14d" in c.out
+    conandata_yml2 = textwrap.dedent("""\
+            sources:
+             "1.0":
+               url: "url1"
+               sha256: "sha1"
+             "1.1":
+               url: "url2"
+               sha256: "sha2"
+            patches:
+             "1.1":
+               - patch_file: "patches/some_patch2"
+                 base_path: "source_subfolder"
+             "1.0":
+               - patch_file: "patches/some_patch"
+                 base_path: "source_subfolder"
+            something: else
+            """)
+    c.save({"conandata.yml": conandata_yml2})
+    c.run("export . --version=1.0")
+    layout = c.exported_layout()
+    data2 = load(os.path.join(layout.export(), "conandata.yml"))
+    assert "1.1" not in data2
+    assert data1 == data2
+    assert "pkg/1.0: Exported: pkg/1.0#03af39add1c7c9d68dcdb10b6968a14d" in c.out
diff --git a/conans/test/integration/graph/test_platform_requires.py b/conans/test/integration/graph/test_platform_requires.py
index 486b6d0a5..2b4c0fcab 100644
--- a/conans/test/integration/graph/test_platform_requires.py
+++ b/conans/test/integration/graph/test_platform_requires.py
@@ -58,6 +58,19 @@ class TestPlatformRequires:
         client.run("create . -pr=profile", assert_error=True)
         assert "ERROR: Package 'dep/1.0' not resolved: No remote defined" in client.out
 
+    def test_platform_requires_build_context(self):
+        """
+        platform_requires must work in the build context too
+        """
+        client = TestClient(light=True)
+        client.save({"tool/conanfile.py": GenConanfile("tool", "1.0").with_requires("dep/1.0"),
+                     "pkg/conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/1.0"),
+                     "profile": "[settings]\nos=Linux\n[platform_requires]\ndep/1.0"})
+        client.run("create tool -pr:b=profile --build-require")
+        assert "dep/1.0 - Platform" in client.out
+        client.run("create pkg -pr:b=profile")
+        assert "dep/1.0 - Platform" in client.out
+
     def test_graph_info_platform_requires_range(self):
         """
         graph info doesn't crash
@@ -136,6 +149,15 @@ class TestPlatformRequires:
         assert "ERROR: Package 'dep/1.1' not resolved" in client.out
 
 
+class TestPlatformTestRequires:
+    def test_platform_requires(self):
+        client = TestClient()
+        client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_test_requires("dep/1.0"),
+                     "profile": "[platform_requires]\ndep/1.0"})
+        client.run("create . -pr=profile")
+        assert "dep/1.0 - Platform" in client.out
+
+
 class TestPlatformRequiresLock:
 
     def test_platform_requires_range(self):
diff --git a/conans/test/integration/graph/test_system_tools.py b/conans/test/integration/graph/test_system_tools.py
index f5d101d15..8b64f9e0f 100644
--- a/conans/test/integration/graph/test_system_tools.py
+++ b/conans/test/integration/graph/test_system_tools.py
@@ -136,6 +136,26 @@ class TestToolRequires:
         client.run("install . -pr=profile", assert_error=True)
         assert "ERROR: Package 'tool/1.1' not resolved" in client.out
 
+    def test_require_build_context(self):
+        """ https://github.com/conan-io/conan/issues/15659
+        app -> libcurl --(tool-require)--> libtool -> automake -> autoconf
+                                             \\--(br)-> automake -> autoconf
+        """
+        c = TestClient()
+        c.save({"autoconf/conanfile.py": GenConanfile("autoconf", "0.1"),
+                "automake/conanfile.py": GenConanfile("automake", "0.1").with_requires("autoconf/0.1"),
+                "libtool/conanfile.py": GenConanfile("libtool", "0.1").with_requires("automake/0.1")
+                                                                      .with_tool_requires("automake/0.1"),
+                "libcurl/conanfile.py": GenConanfile("libcurl", "0.1").with_tool_requires("libtool/0.1"),
+                "app/conanfile.py": GenConanfile().with_requires("libcurl/0.1"),
+                "profile_build": "[settings]\nos=Linux\n[platform_tool_requires]\nautomake/0.1"})
+        c.run("create autoconf")
+        c.run("create automake")
+        c.run("export libtool")
+        c.run("export libcurl")
+        c.run("install app -pr:b=profile_build --build=missing")
+        assert "Install finished successfully" in c.out
+
 
 class TestToolRequiresLock:
 
diff --git a/conans/test/performance/__init__.py b/conans/test/performance/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/performance/test_large_graph.py b/conans/test/performance/test_large_graph.py
new file mode 100644
index 000000000..a497c27ac
--- /dev/null
+++ b/conans/test/performance/test_large_graph.py
@@ -0,0 +1,43 @@
+import cProfile
+import json
+import pstats
+import time
+from pstats import SortKey
+
+from conans.test.assets.genconanfile import GenConanfile
+from conans.test.utils.tools import TestClient
+
+
+def test_large_graph():
+    c = TestClient(cache_folder="T:/mycache")
+    num_test = 40
+    num_pkgs = 40
+
+    """for i in range(num_test):
+        conanfile = GenConanfile(f"test{i}", "0.1")
+        if i > 0:
+            conanfile.with_requires(f"test{i-1}/0.1")
+        c.save({"conanfile.py": conanfile})
+        c.run("create .")
+
+    for i in range(num_pkgs):
+        conanfile = GenConanfile(f"pkg{i}", "0.1").with_test_requires(f"test{num_test-1}/0.1")
+        if i > 0:
+            conanfile.with_requires(f"pkg{i-1}/0.1")
+        c.save({"conanfile.py": conanfile})
+        c.run("create .")
+
+    """
+    t = time.time()
+    pr = cProfile.Profile()
+    pr.enable()
+    c.run(f"install --requires=pkg{num_pkgs - 1}/0.1")
+    pr.disable()
+    print(time.time()-t)
+
+    sortby = SortKey.CUMULATIVE
+    ps = pstats.Stats(pr).sort_stats(sortby)
+    ps.print_stats()
+
+    #graph = json.loads(c.stdout)
+    #assert len(graph["graph"]["nodes"]) == 1 + num_pkgs + num_test * num_pkgs