Repository: conan-io/conan

Issue:
[bug] using 'replace_requires' leads MSBuildDeps generator to include non-existent property sheet
### Describe the bug

I'm using component 'wt' with recipe from conan-center-index.
'wt' requires 'sqlite3'.
I'm building a component named 'sqlite3see' which is a drop-in replacement for 'sqlite3'.
For 'sqlite3see' I'm using the recipe from conan-center-index and changed only source code URL.

My profile for building 'wt' contains 'replace_requires'
`[replace_requires]`
`sqlite3/*: sqlite3see/[>3]`

This part works fine.

Now I'm consuming 'wt' in my own component using MSBuild.
I use same profile with 'replace_requires'.
I use MSBuildDeps generator to get property sheets which I import into my project.

The bug is:
Generator created a property sheet named `conan_sqlite3see.props`.
Generator created a property sheet named `conan_wt.props`.
The property sheet for 'wt' wants to import a property sheet of `conan_sqlite3.props`.
But 'sqlite3' was redirected to 'sqlite3see'.
So there is no property sheet `conan_sqlite3.props`

This but was introduced in 2.12.

This bug is related to #17557

Revisions of Conan prior to 2.12 generated different property sheets.
Property sheets for 'wt' did not include the property sheets of requirements.


### How to reproduce it

Maybe could be reproduced with other 'replace_requires' as well (e.g. 'zlib'->'zlig-ng').


Discussion:
Hi @chr-thien 

Thanks for your report.

I am trying to reproduce, and did some extra checks in the test suite in https://github.com/conan-io/conan/pull/17967.

So far it seems to be working fine, the ``conan_openssl_release_x64.props`` properties file is including the replacement``conan_zlib-ng.props``, not the original ``conan_zlib.props``.

It would be great to have a minimal reproducible case, ideally self-contained, it can be done with quick ``conan new cmake_lib`` templates, maybe?


I tried to create minimum working example with  zlib-ng and libpng. Everything works as expected.

Then I had a look at the recipe of 'wt' from conan-center-index and found this:

>    def package_info(self): 
> ...
>       # wtdbosqlite3
>       if self.options.get_safe("with_sqlite"):
>            self.cpp_info.components["wtdbosqlite3"].set_property("cmake_target_name", "Wt::DboSqlite3")
>            self.cpp_info.components["wtdbosqlite3"].libs = ["wtdbosqlite3{}".format(suffix)]
>            self.cpp_info.components["wtdbosqlite3"].requires = ["wtdbo", "sqlite3::sqlite3"]
 
Here it sets a requirement to 'sqlite3::sqlite3' directly. This could be the problem. It seems that 'replace_requires' is not taken into account here.
Thanks for the details.
I have extended the check for the tests that check components, and indeed, the case for components was not covered, so I added the fix in https://github.com/conan-io/conan/pull/17967. I'll try to add it to next Conan 2.15 release
If you know how to do it, it would be useful if you could test your project running from source from my branch in https://github.com/conan-io/conan/pull/17967, that would be good feedback too.



Patch:
diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py
index 58b0d87ddd4..6a95733b42a 100644
--- a/conan/tools/microsoft/msbuilddeps.py
+++ b/conan/tools/microsoft/msbuilddeps.py
@@ -336,9 +336,14 @@ def _package_props_files(self, require, dep, build=False):
                 public_deps = []  # To store the xml dependencies/file names
                 for required_pkg, required_comp in comp_info.parsed_requires():
                     if required_pkg is not None:  # Points to a component of a different package
-                        if required_pkg in pkg_deps:  # The transitive dep might have been skipped
-                            public_deps.append(required_pkg if required_pkg == required_comp
-                                               else "{}_{}".format(required_pkg, required_comp))
+                        try:
+                            required = pkg_deps[required_pkg]
+                        except KeyError:  # The transitive dep might have been skipped
+                            required = None
+                        if required:  # The transitive dep might have been skipped
+                            required_name = required.ref.name
+                            public_deps.append(required_name if required_pkg == required_comp
+                                               else "{}_{}".format(required_name, required_comp))
                     else:  # Points to a component of same package
                         public_deps.append("{}_{}".format(dep_name, required_comp))
                 public_deps = [self._get_valid_xml_format(d) for d in public_deps]


Test patch:
diff --git a/test/integration/graph/test_replace_requires.py b/test/integration/graph/test_replace_requires.py
index 4b832ebac4a..ef6e86bb4ba 100644
--- a/test/integration/graph/test_replace_requires.py
+++ b/test/integration/graph/test_replace_requires.py
@@ -418,14 +418,15 @@ def package_info(self):
             class App(ConanFile):
                 name = "app"
                 version = "0.1"
-                settings = "build_type"
+                settings = "build_type", "arch"
                 requires = "openssl/0.1", {zlib}
                 package_type = "application"
-                generators = "CMakeDeps", "PkgConfigDeps"
+                generators = "CMakeDeps", "PkgConfigDeps", "MSBuildDeps"
             """)
         profile = textwrap.dedent("""
             [settings]
             build_type = Release
+            arch=x86_64
 
             [replace_requires]
             zlib/0.1: zlib-ng/0.1
@@ -454,6 +455,13 @@ class App(ConanFile):
         assert "set_property(TARGET openssl::openssl APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \
                '             "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake
 
+        # checking MSBuildDeps
+        zlib_ng_props = c.load("app/conan_zlib-ng.props")
+        assert 'Project="conan_zlib-ng_release_x64.props"' in zlib_ng_props
+        props = c.load("app/conan_openssl_release_x64.props")
+        assert "<Import Condition=\"'$(conan_zlib-ng_props_imported)' != 'True'\"" \
+               " Project=\"conan_zlib-ng.props\"/>" in props
+
     @pytest.mark.parametrize("diamond", [True, False])
     def test_openssl_components(self, diamond):
         c = TestClient()
@@ -490,14 +498,15 @@ def package_info(self):
             class App(ConanFile):
                 name = "app"
                 version = "0.1"
-                settings = "build_type"
+                settings = "build_type", "arch"
                 requires = "openssl/0.1", {zlib}
                 package_type = "application"
-                generators = "CMakeDeps", "PkgConfigDeps"
+                generators = "CMakeDeps", "PkgConfigDeps", "MSBuildDeps"
             """)
         profile = textwrap.dedent("""
             [settings]
             build_type = Release
+            arch=x86_64
 
             [replace_requires]
             zlib/0.1: zlib-ng/0.1
@@ -526,6 +535,14 @@ class App(ConanFile):
         assert "set_property(TARGET openssl::crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \
                '             "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake
 
+        # checking MSBuildDeps
+        zlib_ng_props = c.load("app/conan_zlib-ng.props")
+        assert 'Project="conan_zlib-ng_release_x64.props"' in zlib_ng_props
+
+        props = c.load("app/conan_openssl_crypto_release_x64.props")
+        assert "<Import Condition=\"'$(conan_zlib-ng_props_imported)' != 'True'\"" \
+               " Project=\"conan_zlib-ng.props\"/>" in props
+
     @pytest.mark.parametrize("diamond", [True, False])
     @pytest.mark.parametrize("explicit_requires", [True, False])
     def test_zlib_components(self, diamond, explicit_requires):
@@ -565,14 +582,15 @@ def package_info(self):
             class App(ConanFile):
                 name = "app"
                 version = "0.1"
-                settings = "build_type"
+                settings = "build_type", "arch"
                 requires = "openssl/0.1", {zlib}
                 package_type = "application"
-                generators = "CMakeDeps", "PkgConfigDeps"
+                generators = "CMakeDeps", "PkgConfigDeps", "MSBuildDeps"
             """)
         profile = textwrap.dedent("""
             [settings]
             build_type = Release
+            arch = x86_64
 
             [replace_requires]
             zlib/0.1: zlib-ng/0.1
@@ -604,6 +622,15 @@ class App(ConanFile):
         assert "set_property(TARGET openssl::openssl APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \
                '             "$<$<CONFIG:RELEASE>:zlib-ng::zlib-ng>")' in cmake
 
+        # checking MSBuildDeps
+        zlib_ng_props = c.load("app/conan_zlib-ng.props")
+        assert "<Import Condition=\"'$(conan_zlib-ng_myzlib_props_imported)' != 'True'\" " \
+               "Project=\"conan_zlib-ng_myzlib.props\"/" in zlib_ng_props
+
+        props = c.load("app/conan_openssl_release_x64.props")
+        assert "<Import Condition=\"'$(conan_zlib-ng_props_imported)' != 'True'\"" \
+               " Project=\"conan_zlib-ng.props\"/>" in props
+
     @pytest.mark.parametrize("diamond", [True, False])
     @pytest.mark.parametrize("package_requires", [False, True])
     def test_both_components(self, diamond, package_requires):
@@ -645,14 +672,15 @@ def package_info(self):
             class App(ConanFile):
                 name = "app"
                 version = "0.1"
-                settings = "build_type"
+                settings = "build_type", "arch"
                 requires = "openssl/0.1", {zlib}
                 package_type = "application"
-                generators = "CMakeDeps", "PkgConfigDeps"
+                generators = "CMakeDeps", "PkgConfigDeps", "MSBuildDeps"
             """)
         profile = textwrap.dedent("""
             [settings]
             build_type = Release
+            arch = x86_64
 
             [replace_requires]
             zlib/0.1: zlib-ng/0.1
@@ -687,3 +715,16 @@ class App(ConanFile):
         else:
             assert "set_property(TARGET openssl::crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \
                    '             "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake
+
+        # checking MSBuildDeps
+        zlib_ng_props = c.load("app/conan_zlib-ng.props")
+        assert "<Import Condition=\"'$(conan_zlib-ng_myzlib_props_imported)' != 'True'\" " \
+               "Project=\"conan_zlib-ng_myzlib.props\"/" in zlib_ng_props
+
+        props = c.load("app/conan_openssl_crypto_release_x64.props")
+        if package_requires:
+            assert "<Import Condition=\"'$(conan_zlib-ng_props_imported)' != 'True'\"" \
+                   " Project=\"conan_zlib-ng.props\"/>" in props
+        else:
+            assert "<Import Condition=\"'$(conan_zlib-ng_myzlib_props_imported)' != 'True'\"" \
+                   " Project=\"conan_zlib-ng_myzlib.props\"/>" in props