Repository: conan-io/conan
Pull request #13039 (2023-02-03 00:24:42)

Add CONAN_DISABLE_STRICT_MODE to be able to build old packages (#13037)
Changelog: (Feature | Fix | Bugfix): Add a workaround to be able to build old packages which used CMakeDeps without switching to the new CMakeToolchain generator as well. See #13037

Docs: https://github.com/conan-io/docs/pull/XXXX

- [x] Refer to the issue that supports this Pull Request.
- [x] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [x] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one. 

<sup>**Note:** By default this PR will skip the slower tests and will use a limited set of python versions. Check [here](https://github.com/conan-io/conan/blob/develop/.github/PR_INCREASE_TESTING.md) how to increase the testing level by writing some tags in the current PR body text.</sup>


CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/conan-io/conan?pullRequest=13039) <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=13039) 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=13039) it.</sub>
stevn: Yeah, we are in the middle of migrating our packages to Conan 2.0. But only for new versions. We won't be touching the old versions.
stevn: See https://github.com/conan-io/docs/pull/2950

Patch:
diff --git a/conan/tools/microsoft/__init__.py b/conan/tools/microsoft/__init__.py
index 9c0bb920d..c060c64b8 100644
--- a/conan/tools/microsoft/__init__.py
+++ b/conan/tools/microsoft/__init__.py
@@ -6,4 +6,4 @@ 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
+    is_msvc_static_runtime, check_min_vs, msvs_toolset
diff --git a/conan/tools/microsoft/toolchain.py b/conan/tools/microsoft/toolchain.py
index b98f6be45..2be8a67da 100644
--- a/conan/tools/microsoft/toolchain.py
+++ b/conan/tools/microsoft/toolchain.py
@@ -7,7 +7,7 @@ from jinja2 import Template
 from conan.tools._check_build_profile import check_using_build_profile
 from conan.tools.build import build_jobs
 from conan.tools.intel.intel_cc import IntelCC
-from conan.tools.microsoft.visual import VCVars, msvc_version_to_toolset_version
+from conan.tools.microsoft.visual import VCVars, msvs_toolset
 from conans.errors import ConanException
 from conans.util.files import save, load
 
@@ -54,7 +54,7 @@ class MSBuildToolchain(object):
         self.configuration = conanfile.settings.build_type
         self.runtime_library = self._runtime_library(conanfile.settings)
         self.cppstd = conanfile.settings.get_safe("compiler.cppstd")
-        self.toolset = self._msvs_toolset(conanfile)
+        self.toolset = msvs_toolset(conanfile)
         self.properties = {}
         check_using_build_profile(self._conanfile)
 
@@ -82,37 +82,6 @@ class MSBuildToolchain(object):
         else:
             VCVars(self._conanfile).generate()
 
-    @staticmethod
-    def _msvs_toolset(conanfile):
-        settings = conanfile.settings
-        compiler = settings.get_safe("compiler")
-        compiler_version = settings.get_safe("compiler.version")
-        if compiler == "msvc":
-            subs_toolset = settings.get_safe("compiler.toolset")
-            if subs_toolset:
-                return subs_toolset
-            return msvc_version_to_toolset_version(compiler_version)
-        if compiler == "intel":
-            compiler_version = compiler_version if "." in compiler_version else \
-                "%s.0" % compiler_version
-            return "Intel C++ Compiler " + compiler_version
-        if compiler == "intel-cc":
-            return IntelCC(conanfile).ms_toolset
-        if compiler == "Visual Studio":
-            toolset = settings.get_safe("compiler.toolset")
-            if not toolset:
-                toolsets = {"17": "v143",
-                            "16": "v142",
-                            "15": "v141",
-                            "14": "v140",
-                            "12": "v120",
-                            "11": "v110",
-                            "10": "v100",
-                            "9": "v90",
-                            "8": "v80"}
-                toolset = toolsets.get(compiler_version)
-            return toolset or ""
-
     @staticmethod
     def _runtime_library(settings):
         compiler = settings.compiler
@@ -148,7 +117,7 @@ class MSBuildToolchain(object):
 
         cppstd = "stdcpp%s" % self.cppstd if self.cppstd else ""
         runtime_library = self.runtime_library
-        toolset = self.toolset
+        toolset = self.toolset or ""
         compile_options = self._conanfile.conf.get("tools.microsoft.msbuildtoolchain:compile_options",
                                                    default={}, check_type=dict)
         self.compile_options.update(compile_options)
diff --git a/conan/tools/microsoft/visual.py b/conan/tools/microsoft/visual.py
index 5050ee8b8..1c6925949 100644
--- a/conan/tools/microsoft/visual.py
+++ b/conan/tools/microsoft/visual.py
@@ -4,6 +4,7 @@ import textwrap
 from conans.client.tools import vs_installation_path
 from conans.client.tools.version import Version
 from conans.errors import ConanException, ConanInvalidConfiguration
+from conan.tools.intel.intel_cc import IntelCC
 
 CONAN_VCVARS_FILE = "conanvcvars.bat"
 
@@ -56,7 +57,7 @@ def msvc_version_to_toolset_version(version):
                 '191': 'v141',
                 '192': 'v142',
                 "193": 'v143'}
-    return toolsets[str(version)]
+    return toolsets.get(str(version))
 
 
 class VCVars:
@@ -275,3 +276,42 @@ def is_msvc_static_runtime(conanfile):
     :return: True, if msvc + runtime MT. Otherwise, False
     """
     return is_msvc(conanfile) and "MT" in msvc_runtime_flag(conanfile)
+
+
+def msvs_toolset(conanfile):
+    """ Returns the corresponding platform toolset based on the compiler of the given conanfile.
+        In case no toolset is configured in the profile, it will return a toolset based on the
+        compiler version, otherwise, it will return the toolset from the profile.
+        When there is no compiler version neither toolset configured, it will return None
+        It supports Visual Studio, msvc and Intel.
+    :param conanfile: Conanfile instance to access settings.compiler
+    :return: A toolset when compiler.version is valid or compiler.toolset is configured. Otherwise, None.
+    """
+    settings = conanfile.settings
+    compiler = settings.get_safe("compiler")
+    compiler_version = settings.get_safe("compiler.version")
+    if compiler == "msvc":
+        subs_toolset = settings.get_safe("compiler.toolset")
+        if subs_toolset:
+            return subs_toolset
+        return msvc_version_to_toolset_version(compiler_version)
+    if compiler == "intel" and compiler_version:
+        compiler_version = compiler_version if "." in compiler_version else \
+            f"{compiler_version}.0"
+        return "Intel C++ Compiler " + compiler_version
+    if compiler == "intel-cc":
+        return IntelCC(conanfile).ms_toolset
+    if compiler == "Visual Studio":
+        toolset = settings.get_safe("compiler.toolset")
+        if not toolset:
+            toolsets = {"17": "v143",
+                        "16": "v142",
+                        "15": "v141",
+                        "14": "v140",
+                        "12": "v120",
+                        "11": "v110",
+                        "10": "v100",
+                        "9": "v90",
+                        "8": "v80"}
+            toolset = toolsets.get(str(compiler_version))
+        return toolset
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
index 0006d8622..d9a668b37 100644
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -47,8 +47,14 @@ class CMake(object):
         :param generator_platform: Generator platform name or none to autodetect (-A cmake option)
         """
         if getattr(conanfile, "must_use_new_helpers", None):
-            raise ConanException("Using the wrong 'CMake' helper. To use CMakeDeps, CMakeToolchain "
-                                 "you should use 'from conan.tools.cmake import CMake'")
+            wrong_helper_msg = "Using the wrong 'CMake' helper. To use CMakeDeps, CMakeToolchain "\
+                               "you should use 'from conan.tools.cmake import CMake'. "\
+                               "Set environment variable CONAN_DISABLE_STRICT_MODE=1 to override "\
+                               "this check (should only be used to build old packages)."
+            if get_env("CONAN_DISABLE_STRICT_MODE", False):
+                conanfile.output.warning(wrong_helper_msg)
+            else:
+                raise ConanException(wrong_helper_msg)
         self._append_vcvars = append_vcvars
         self._conanfile = conanfile
         self._settings = conanfile.settings
diff --git a/conans/client/build/msbuild.py b/conans/client/build/msbuild.py
index 778b81a76..df8b7bee4 100644
--- a/conans/client/build/msbuild.py
+++ b/conans/client/build/msbuild.py
@@ -23,9 +23,14 @@ class MSBuild(object):
     def __init__(self, conanfile):
         if isinstance(conanfile, ConanFile):
             if getattr(conanfile, "must_use_new_helpers", None):
-                raise ConanException(
-                    "Using the wrong 'MSBuild' helper. To use MSBuildDeps, MSBuildToolchain "
-                    "you should use 'from conan.tools.microsoft import MSBuild'")
+                wrong_helper_msg = "Using the wrong 'MSBuild' helper. To use MSBuildDeps, MSBuildToolchain "\
+                                   "you should use 'from conan.tools.microsoft import MSBuild'. "\
+                                   "Set environment variable CONAN_DISABLE_STRICT_MODE=1 to override "\
+                                   "this check (should only be used to build old packages)."
+                if get_env("CONAN_DISABLE_STRICT_MODE", False):
+                    conanfile.output.warning(wrong_helper_msg)
+                else:
+                    raise ConanException(wrong_helper_msg)
             self._conanfile = conanfile
             self._settings = self._conanfile.settings
             self._output = self._conanfile.output


Test patch:
diff --git a/conans/test/unittests/tools/microsoft/test_msvs_toolset.py b/conans/test/unittests/tools/microsoft/test_msvs_toolset.py
new file mode 100644
index 000000000..de1ec93b0
--- /dev/null
+++ b/conans/test/unittests/tools/microsoft/test_msvs_toolset.py
@@ -0,0 +1,141 @@
+import pytest
+from conan.tools.microsoft import msvs_toolset
+from conan.errors import ConanException
+from conans.test.utils.mocks import ConanFileMock, MockSettings
+
+
+@pytest.mark.parametrize("compiler_version,expected_toolset", [
+    ("17", "v143"),
+    ("16", "v142"),
+    ("15", "v141"),
+    ("14", "v140"),
+    ("12", "v120"),
+    ("11", "v110"),
+    ("10", "v100"),
+    ("9", "v90"),
+    ("8", "v80")])
+def test_visual_studio_default(compiler_version, expected_toolset):
+    """ When running Visual Studio as compiler, and there is no toolset configured,
+        msvs_toolset must return a specific version based on the compiler version
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "Visual Studio", "compiler.version": compiler_version})
+    assert msvs_toolset(conanfile) == expected_toolset
+
+
+@pytest.mark.parametrize("compiler_version,toolset,expected_toolset", [
+    ("16", "v142_xp", "v142_xp"),
+    ("15", "v141_xp", "v141_xp"),
+    ("14", "v140_xp", "v140_xp"),
+    ("12", "v120_xp", "v120_xp"),
+    ("11", "v110_xp", "v110_xp")])
+def test_visual_studio_custom(compiler_version, toolset, expected_toolset):
+    """ When running Visual Studio as compiler, and there is a toolset configured,
+        msvs_toolset must return the specified toolset from profile
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "Visual Studio",
+                             "compiler.version": compiler_version,
+                             "compiler.toolset": toolset})
+    assert msvs_toolset(conanfile) == expected_toolset
+
+
+def test_invalid_compiler():
+    """ When the compiler version is unknown and there is no toolset configured,
+        msvs_toolset must return None
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "Visual Studio",
+                                       "compiler.version": "20220203"})
+    assert msvs_toolset(conanfile) is None
+
+
+@pytest.mark.parametrize("compiler_version,expected_toolset", [
+    ("193", "v143"),
+    ("192", "v142"),
+    ("191", "v141"),
+    ("190", "v140"),
+    ("180", "v120"),
+    ("170", "v110")])
+def test_msvc_default(compiler_version, expected_toolset):
+    """ When running msvc as compiler, and there is no toolset configured,
+        msvs_toolset must return a specific version based on the compiler version
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "msvc", "compiler.version": compiler_version})
+    assert msvs_toolset(conanfile) == expected_toolset
+
+
+@pytest.mark.parametrize("compiler_version,toolset,expected_toolset", [
+    ("193", "v143_xp", "v143_xp"),
+    ("192", "v142_xp", "v142_xp"),
+    ("191", "v141_xp", "v141_xp"),
+    ("190", "v140_xp", "v140_xp"),
+    ("180", "v120_xp", "v120_xp"),
+    ("170", "v110_xp", "v110_xp")])
+def test_msvc_custom(compiler_version, toolset, expected_toolset):
+    """ When running msvc as compiler, and there is a toolset configured,
+        msvs_toolset must return the specified toolset from profile
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "msvc",
+                             "compiler.version": compiler_version,
+                             "compiler.toolset": toolset})
+    assert msvs_toolset(conanfile) == expected_toolset
+
+
+@pytest.mark.parametrize("compiler_version,expected_toolset", [
+    ("19.1", "Intel C++ Compiler 19.1"),
+    ("19", "Intel C++ Compiler 19.0"),
+    ("18", "Intel C++ Compiler 18.0"),
+    ("17", "Intel C++ Compiler 17.0")])
+def test_intel_default(compiler_version, expected_toolset):
+    """ When running intel as compiler, and there is a compiler version configured,
+        msvs_toolset must return the compiler version as its toolset
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "intel", "compiler.version": compiler_version})
+    assert msvs_toolset(conanfile) == expected_toolset
+
+
+def test_intel_invalid():
+    """ When running intel as compiler, and there is no compiler version configured,
+        msvs_toolset must return None
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "intel"})
+    assert msvs_toolset(conanfile) is None
+
+
+def test_intel_cc_old_compiler():
+    """ When running intel-cc as compiler, and the compiler version configured is older than 2021
+        msvs_toolset must raise an ConanExpection
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "intel-cc", "compiler.version": "19.1"})
+    with pytest.raises(ConanException) as error:
+        msvs_toolset(conanfile)
+    assert "You have to use 'intel' compiler" in str(error.value)
+
+
+@pytest.mark.parametrize("compiler_version,compiler_mode,expected_toolset", [
+    ("2021.3", "classic", "Intel C++ Compiler 19.2"),
+    ("2021.2", "classic", "Intel C++ Compiler 19.2"),
+    ("2021.1", "classic", "Intel C++ Compiler 19.2"),
+    ("2021.3", "icx", "Intel C++ Compiler 2021"),
+    ("2021.2", "icx", "Intel C++ Compiler 2021"),
+    ("2021.1", "icx", "Intel C++ Compiler 2021"),
+    ("2021.3", "dpcpp", "Intel(R) oneAPI DPC++ Compiler"),
+    ("2021.2", "dpcpp", "Intel(R) oneAPI DPC++ Compiler"),
+    ("2021.1", "dpcpp", "Intel(R) oneAPI DPC++ Compiler"),
+    ("2021.3", None, "Intel(R) oneAPI DPC++ Compiler")])
+def test_intel_cc_default(compiler_version, compiler_mode, expected_toolset):
+    """ When running intel-cc as compiler, and there is a proper compiler version configured,
+        and the compiler.mode is configured,
+        msvs_toolset must return a compiler toolset based on compiler.mode
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"compiler": "intel-cc",
+                                       "compiler.version": compiler_version,
+                                       "compiler.mode": compiler_mode})
+    assert msvs_toolset(conanfile) == expected_toolset