Repository: conan-io/conan

Issue:
[bug] compiler.base.runtime is not set based on build_type when using Visual Studio as the base compiler
<!--
  Please don't forget to update the issue title.
  Include all applicable information to help us reproduce your problem.

  To help us debug your issue please explain:
-->
Currently, `compiler.runtime` is implicitly set based on the `build_type` being used.  When using `compiler=Intel` and Visual Studio for a base compiler, we expect `compiler.base.runtime` to be set based on the build_type as well.  This is not the case, though, and it needs to be set explicitly on the command line or conditionally set directly in the recipe (which I do not think is recommended).  There are easy workarounds to this, but it makes using the Intel compiler settings not as straight forward.

### Environment Details (include every applicable attribute)
  * Operating System+version: Windows 10
  * Conan version: 1.21.0
  * Python version: 3.7.3

### Steps to reproduce (Include if Applicable)
Start from a new bare recipe
`conan new test/1.2.3 -b`

Create a package from this bare recipe with `compiler=Intel`, `compiler.base="Visual Studio"`, `build_type`, and compiler version settings
`conan create . -s compiler=intel -s compiler.version=19 -s compiler.base="Visual Studio" -s compiler.base.version=15 -s build_type=Release`

The following error is received
`ERROR: test/1.2.3: 'settings.compiler.base.runtime' value not defined`

Meanwhile running with `Visual_Studio` as the compiler implicitly sets the runtime and succeeds
`conan create . -s compiler="Visual Studio" -s compiler.version=15 -s build_type=Release`

> [settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=Visual Studio
compiler.runtime=MD
compiler.version=15


Discussion:
Yes, this might make sense. The implementation would be to complete the ``_fill_runtime()`` function of ``settings_preprocessor`` to apply also to the ``base`` compiler.

Just to be clear, this is not a bug, but a feature request, labeled as such. The fact that there were automation to deduce the ``runtime`` for the main ``Visual Studio`` compiler, doesn't automatically imply that not having it for the Intel compiler base sub-compiler was a bug. It makes sense to add this feature, but not a bug.

Thanks for suggesting it!

Lets give it a try.

Resolving patch:
diff --git a/conans/client/settings_preprocessor.py b/conans/client/settings_preprocessor.py
--- a/conans/client/settings_preprocessor.py
+++ b/conans/client/settings_preprocessor.py
@@ -50,12 +50,20 @@ def check_flag_available(values_range, value, setting_id):
 
 def _fill_runtime(settings):
     try:
+        runtime = "MDd" if settings.get_safe("build_type") == "Debug" else "MD"
         if settings.compiler == "Visual Studio":
-            if settings.get_safe("compiler.runtime") is None:
-                settings.compiler.runtime = "MDd" if settings.get_safe("build_type") == "Debug" \
-                                                  else "MD"
-                logger.info("Setting 'compiler.runtime' not declared, automatically "
-                            "adjusted to '%s'" % settings.compiler.runtime)
+            runtime_key = "compiler.runtime"
+            if settings.get_safe(runtime_key) is None:
+                settings.compiler.runtime = runtime
+        elif settings.compiler == "intel" and settings.get_safe("compiler.base") == "Visual Studio":
+            runtime_key = "compiler.base.runtime"
+            if settings.get_safe(runtime_key) is None:
+                settings.compiler.base.runtime = runtime
+        else:
+            return
+
+        logger.info("Setting '{}' not declared, automatically adjusted to '{}'".format(
+                runtime_key, runtime))
     except Exception:  # If the settings structure doesn't match these general
         # asumptions, like unexistant runtime
         pass


Test patch:
diff --git a/conans/test/unittests/client/settings_preprocessor_test.py b/conans/test/unittests/client/settings_preprocessor_test.py
new file mode 100644
--- /dev/null
+++ b/conans/test/unittests/client/settings_preprocessor_test.py
@@ -0,0 +1,68 @@
+# coding=utf-8
+
+import unittest
+
+from mock import mock
+
+from conans import Settings
+from conans.client.conf import default_settings_yml
+from conans.client.settings_preprocessor import preprocess
+from conans.test.utils.conanfile import MockSettings
+
+
+class SettingsCompilerIntelVisualPreprocessorTest(unittest.TestCase):
+
+    def setUp(self):
+        self.settings = Settings.loads(default_settings_yml)
+        self.settings.compiler = "intel"
+        self.settings.compiler.base = "Visual Studio"
+
+    def release_build_type_runtime_test(self):
+        self.settings.build_type = "Release"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.base.runtime, "MD")
+
+    def debug_build_type_runtime_test(self):
+        self.settings.build_type = "Debug"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.base.runtime, "MDd")
+
+    def different_base_compiler_test(self):
+        self.settings.compiler.base = "gcc"
+        self.settings.build_type = "Debug"
+        preprocess(self.settings)
+        self.assertIsNone(self.settings.compiler.base.get_safe("runtime"))
+
+    def custom_base_runtime_set_test(self):
+        self.settings.build_type = "Debug"
+        self.settings.compiler.base.runtime = "MT"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.base.runtime, "MT")
+
+class SettingsCompilerVisualPreprocessorTest(unittest.TestCase):
+
+    def setUp(self):
+        self.settings = Settings.loads(default_settings_yml)
+        self.settings.compiler = "Visual Studio"
+
+    def release_build_type_runtime_test(self):
+        self.settings.build_type = "Release"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.runtime, "MD")
+
+    def debug_build_type_runtime_test(self):
+        self.settings.build_type = "Debug"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.runtime, "MDd")
+
+    def different_base_compiler_test(self):
+        self.settings.compiler = "gcc"
+        self.settings.build_type = "Debug"
+        preprocess(self.settings)
+        self.assertIsNone(self.settings.compiler.get_safe("runtime"))
+
+    def custom_base_runtime_set_test(self):
+        self.settings.build_type = "Debug"
+        self.settings.compiler.runtime = "MT"
+        preprocess(self.settings)
+        self.assertEqual(self.settings.compiler.runtime, "MT")