Repository: conan-io/conan

Issue:
Switch build_type with python api
On our build server we usually do something like

```
pip install --upgrade conan
```

```python
api, _, _ = ConanAPIV1.factory()
api.create(..., settings=...+["build_type=Release"])
api.upload...
api.create(..., settings=...+["build_type=Debug"])
api.upload...
```

That worked well until Dec 19, 2018 (conan 1.10.2).

Since the following conan update (must be 1.11.0), the second ```api.create()``` always failed. It prints
```
Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Debug
compiler=Visual Studio
compiler.runtime=MD
compiler.version=15
...
```

 ```compiler.runtime=MD``` is wrong, should be ```MDd```. It should be automatically set by conan based on the ```build_type```.

It only happens when the debug build is invoked after the release build.

Workaround: Recreate the api before switching ```build_type```.

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.




Discussion:

Resolving patch:
diff --git a/conans/client/client_cache.py b/conans/client/client_cache.py
--- a/conans/client/client_cache.py
+++ b/conans/client/client_cache.py
@@ -45,10 +45,8 @@ class ClientCache(SimplePaths):
     def __init__(self, base_folder, store_folder, output):
         self.conan_folder = join(base_folder, ".conan")
         self._conan_config = None
-        self._settings = None
         self._output = output
         self._store_folder = store_folder or self.conan_config.storage_path or self.conan_folder
-        self._default_profile = None
         self._no_lock = None
         self.client_cert_path = normpath(join(self.conan_folder, CLIENT_CERT))
         self.client_cert_key_path = normpath(join(self.conan_folder, CLIENT_KEY))
@@ -172,50 +170,46 @@ def hooks_path(self):
 
     @property
     def default_profile(self):
-        if self._default_profile is None:
-            if not os.path.exists(self.default_profile_path):
-                self._output.writeln("Auto detecting your dev setup to initialize the "
-                                     "default profile (%s)" % self.default_profile_path,
-                                     Color.BRIGHT_YELLOW)
-
-                default_settings = detect_defaults_settings(self._output)
-                self._output.writeln("Default settings", Color.BRIGHT_YELLOW)
-                self._output.writeln("\n".join(["\t%s=%s" % (k, v) for (k, v) in default_settings]),
-                                     Color.BRIGHT_YELLOW)
-                self._output.writeln("*** You can change them in %s ***" % self.default_profile_path,
-                                     Color.BRIGHT_MAGENTA)
-                self._output.writeln("*** Or override with -s compiler='other' -s ...s***\n\n",
-                                     Color.BRIGHT_MAGENTA)
-
-                self._default_profile = Profile()
-                tmp = OrderedDict(default_settings)
-                self._default_profile.update_settings(tmp)
-                save(self.default_profile_path, self._default_profile.dumps())
-            else:
-                self._default_profile, _ = read_profile(self.default_profile_path, get_cwd(),
-                                                        self.profiles_path)
-
-            # Mix profile settings with environment
-            mixed_settings = _mix_settings_with_env(self._default_profile.settings)
-            self._default_profile.settings = mixed_settings
+        if not os.path.exists(self.default_profile_path):
+            self._output.writeln("Auto detecting your dev setup to initialize the "
+                                 "default profile (%s)" % self.default_profile_path,
+                                 Color.BRIGHT_YELLOW)
+
+            default_settings = detect_defaults_settings(self._output)
+            self._output.writeln("Default settings", Color.BRIGHT_YELLOW)
+            self._output.writeln("\n".join(["\t%s=%s" % (k, v) for (k, v) in default_settings]),
+                                 Color.BRIGHT_YELLOW)
+            self._output.writeln("*** You can change them in %s ***" % self.default_profile_path,
+                                 Color.BRIGHT_MAGENTA)
+            self._output.writeln("*** Or override with -s compiler='other' -s ...s***\n\n",
+                                 Color.BRIGHT_MAGENTA)
+
+            default_profile = Profile()
+            tmp = OrderedDict(default_settings)
+            default_profile.update_settings(tmp)
+            save(self.default_profile_path, default_profile.dumps())
+        else:
+            default_profile, _ = read_profile(self.default_profile_path, get_cwd(),
+                                              self.profiles_path)
 
-        return self._default_profile
+        # Mix profile settings with environment
+        mixed_settings = _mix_settings_with_env(default_profile.settings)
+        default_profile.settings = mixed_settings
+        return default_profile
 
     @property
     def settings(self):
         """Returns {setting: [value, ...]} defining all the possible
            settings without values"""
-        if not self._settings:
-            # TODO: Read default environment settings
-            if not os.path.exists(self.settings_path):
-                save(self.settings_path, normalize(default_settings_yml))
-                settings = Settings.loads(default_settings_yml)
-            else:
-                content = load(self.settings_path)
-                settings = Settings.loads(content)
 
-            self._settings = settings
-        return self._settings
+        if not os.path.exists(self.settings_path):
+            save(self.settings_path, normalize(default_settings_yml))
+            settings = Settings.loads(default_settings_yml)
+        else:
+            content = load(self.settings_path)
+            settings = Settings.loads(content)
+
+        return settings
 
     @property
     def hooks(self):
@@ -297,8 +291,6 @@ def remove_package_locks(self, reference):
 
     def invalidate(self):
         self._conan_config = None
-        self._settings = None
-        self._default_profile = None
         self._no_lock = None
 
     # Metadata


Test patch:
diff --git a/conans/test/functional/conan_api/two_conan_creates_test.py b/conans/test/functional/conan_api/two_conan_creates_test.py
new file mode 100644
--- /dev/null
+++ b/conans/test/functional/conan_api/two_conan_creates_test.py
@@ -0,0 +1,40 @@
+import os
+import unittest
+import platform
+
+from conans.client.conan_api import ConanAPIV1
+from conans.client.tools.env import environment_append
+from conans.client.tools.files import chdir
+from conans.model.ref import PackageReference
+from conans.test.utils.test_files import temp_folder
+from conans.util.files import load
+
+
+class ConanCreateTest(unittest.TestCase):
+
+    @unittest.skipUnless(platform.system() == "Windows", "only Windows test")
+    def test_preprocessor_called_second_api_call(self):
+        """"When calling twice to conan create with the Conan python API, the default
+        settings shouldn't be cached. To test that the default profile is not cached,
+        this test is verifying that the setting preprocessor is adjusting the runtime
+        to MDd when build_type=Debug after a different call to conan create that could
+        cache the runtime to MD (issue reported at: #4246) """
+        tmp = temp_folder()
+        with environment_append({"CONAN_USER_HOME": tmp}):
+            with chdir(tmp):
+                api, cache, _ = ConanAPIV1.factory()
+                api.new(name="lib/1.0@conan/stable", bare=True)
+
+                def get_conaninfo(info):
+                    package_id = info["installed"][0]["packages"][0]["id"]
+                    folder = cache.package(PackageReference.loads("lib/1.0@conan/stable:%s"
+                                                                  % package_id))
+                    return load(os.path.join(folder, "conaninfo.txt"))
+
+                settings = ["compiler=Visual Studio", "compiler.version=15", "build_type=Release"]
+                info = api.create(".", user="conan", channel="stable", settings=settings)
+                self.assertIn("compiler.runtime=MD", get_conaninfo(info))
+
+                settings = ["compiler=Visual Studio", "compiler.version=15", "build_type=Debug"]
+                info = api.create(".", user="conan", channel="stable", settings=settings)
+                self.assertIn("compiler.runtime=MDd", get_conaninfo(info))