Repository: conan-io/conan
Pull request: Ensure msvc cppstd compatibility fallback does not ignore 194 binaries
Changelog: Bugfix: Ensure msvc cppstd compatibility fallback does not ignore 194 binaries.
Docs: Omit

This is one of the alternatives, another one might be to test each factor in isolation too?

Closes https://github.com/conan-io/conan/issues/16564

Problem statement:
[question] How to disable compatilibity?
### What is your question?

Hi,

I'm trying to switch from C++17 to C++20. I'm using
```
core.package_id:default_embed_mode=full_mode
core.package_id:default_non_embed_mode=full_mode
core.package_id:default_unknown_mode=full_mode
```
I've noticed that conan assumes that binaries with C++17 can be used instead of building binaries for C++20. How can I forbid this through profile or global.conf? I would like each cppstd to have separate binaries.

### Have you read the CONTRIBUTING guide?

- [X] I've read the CONTRIBUTING guide

Hints from issue discussion:
Hi @Nekto89 thanks a lot for your question.

The main idea behind the compatibility plugin is to allow users to modify it as needed.

While individual recipes can disable the default compatibility checks by using the `extensions_properties` attribute ([docs here](https://docs.conan.io/2/reference/conanfile/attributes.html#extension-properties)), your best bet would be to modify your `compatibility.py` plugin as explained [here](https://docs.conan.io/2/reference/extensions/binary_compatibility.html#reference-extensions-binary-compatibility), maybe adding a line to the default `cppstd_compat.py` file like:

```py
def cppstd_compat(conanfile):
    if self.conf.get("user.compatibility:skip", default=False):
        return
    ...
```

This will allow you to define the `user.compatibility:skip` conf in your profiles, and even scope it to only those packages you deem necessary (or all of them) by using patterns

In your profile:
```
[conf]
# Skip compat for every package
*:user.compatibility:skip=True
```

Note that this file also implements useful fallbacks for msvc versions as well as the new `cstd` compatibility, in case you need to keep those around, feel free to rearrange the file to suit your needs!

Let me know if this helps :)
Thanks, having to change compatibility.py will slightly increase complexity of configuring conan. Also this default behavior doesn't look very safe. So there is no built-in way to completely disable compatibility except for not forgetting to replace body of method with "pass"?
> Thanks, having to change compatibility.py will slightly increase complexity of configuring conan.

I think this should not be an increase in configuration complexity. Your conan configuration should be managed via the `conan config install` mechanisim, which will install your modififications to this file alongside the rest of the config
Also default compatibility works in a strange way. With conan 2.4.1
```
[settings]
arch=x86_64
os=Windows
compiler=msvc
compiler.version=194
compiler.update=0
compiler.runtime=dynamic
compiler.cppstd=20
build_type=Debug
```

it looks for 193 and ignores 194:
```
fmt/10.1.1@user/channel: Checking 4 compatible configurations
fmt/10.1.1@user/channel: Compatible configurations not found in cache, checking servers
fmt/10.1.1@user/channel: '3138c33a093356caafe595a6afb075fe030b39d8': compiler.cppstd=14, compiler.version=193
fmt/10.1.1@user/channel: '04cb32c4ac1dc1292377e35c31747068e8dccca4': compiler.cppstd=17, compiler.version=193
fmt/10.1.1@user/channel: 'c836d3e0f897d3dae1d548efef0948473e70730b': compiler.version=193
fmt/10.1.1@user/channel: 'ff3ca53c106736062900673f741cba64c9b4a86e': compiler.cppstd=23, compiler.version=193
```

On server I have 
![image](https://github.com/conan-io/conan/assets/4276548/086f7a47-f012-4010-966f-18449468aa5f)
but it didn't find it
Hi @Nekto89 thanks a lot for the report! Indeed, this seems like an oversight of the msvc 194->193 compatibility fallback. I've now submited https://github.com/conan-io/conan/pull/16573 to try to address it :)

Commit message: More general test, each combination of factors will now be tested

Patch to review:
diff --git a/conans/client/graph/compatibility.py b/conans/client/graph/compatibility.py
index a23669c8870..81052537247 100644
--- a/conans/client/graph/compatibility.py
+++ b/conans/client/graph/compatibility.py
@@ -68,7 +68,7 @@ def cppstd_compat(conanfile):
                 comb = comb.copy()
                 comb.update(f)
                 new_combinations.append(comb)
-        combinations = new_combinations
+        combinations.extend(new_combinations)
 
     ret = []
     for comb in combinations:
diff --git a/test/integration/package_id/compatible_test.py b/test/integration/package_id/compatible_test.py
index dca4e3c063a..a931701fc54 100644
--- a/test/integration/package_id/compatible_test.py
+++ b/test/integration/package_id/compatible_test.py
@@ -400,3 +400,21 @@ def package_id(self):
 
         c.run("install --requires=pdfium/2020.9 -pr=myprofile -s build_type=Debug")
         assert "missing. Using compatible package" in c.out
+
+    def test_compatibility_msvc_and_cppstd(self):
+        """msvc 194 would not find compatible packages built with same version but different cppstd
+        due to an issue in the msvc fallback compatibility rule."""
+        tc = TestClient()
+        profile = textwrap.dedent("""
+                   [settings]
+                   compiler=msvc
+                   compiler.version=194
+                   compiler.runtime=dynamic
+                   """)
+        tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_setting("compiler"),
+                 "conanfile.py": GenConanfile("app", "1.0").with_require("dep/1.0").with_setting("compiler"),
+                 "profile": profile})
+
+        tc.run("create dep -pr=profile -s compiler.cppstd=20")
+        tc.run("create . -pr=profile -s compiler.cppstd=17")
+        tc.assert_listed_binary({"dep/1.0": ("b6d26a6bc439b25b434113982791edf9cab4d004", "Cache")})


Code review comments:
diff_hunk: @@ -68,7 +68,7 @@ def cppstd_compat(conanfile):
                 comb = comb.copy()
                 comb.update(f)
                 new_combinations.append(comb)
path: conans/client/graph/compatibility.py
text: @user1:
This is not failing by luck but subtly buggy, it should be:
```
new_comb= comb.copy()
new_comb.update(f)
new_combinations.append(new_comb)
```