3636 ("dep/[>=1.0 <2]" , "dep/2.1" , "dep/2.1@system" , "dep/1.1" ),
3737 # PATTERN - PATTERN REPLACE
3838 ("dep/[>=1.3 <2]" , "dep/*" , "dep/[>=1.0 <1.9]" , "dep/1.1" ),
39+ # DIRECT REPLACE OF PINNED VERSIONS
40+ ("dep/1.3" , "dep/1.3" , "dep/1.5" , "dep/1.5" ),
3941])
4042@pytest .mark .parametrize ("tool_require" , [False , True ])
4143class TestReplaceRequires :
@@ -160,14 +162,16 @@ def test_replace_requires_test_requires():
160162 assert "gtest/0.1: gtest/0.2" in c .out # The replacement happens
161163
162164
163- def test_replace_requires_consumer_references ():
165+ # We test even replacing by itself, not great, but shouldn't crash
166+ @pytest .mark .parametrize ("name, version" , [("zlib" , "0.1" ), ("zlib" , "0.2" ), ("zlib-ng" , "0.1" )])
167+ def test_replace_requires_consumer_references (name , version ):
164168 c = TestClient ()
165169 # IMPORTANT: The replacement package must be target-compatible
166- zlib_ng = textwrap .dedent ("""
170+ dep = textwrap .dedent (f """
167171 from conan import ConanFile
168172 class ZlibNG(ConanFile):
169- name = "zlib-ng "
170- version = "0.1 "
173+ name = "{ name } "
174+ version = "{ version } "
171175 def package_info(self):
172176 self.cpp_info.set_property("cmake_file_name", "ZLIB")
173177 self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
@@ -189,24 +193,78 @@ def package_info(self):
189193 self.output.info(f"DEP ZLIB package_info: {self.dependencies['zlib'].ref.name}!")
190194 self.cpp_info.requires = ["zlib::zlib"]
191195 """ )
192- c .save ({"zlibng /conanfile.py" : zlib_ng ,
196+ c .save ({"dep /conanfile.py" : dep ,
193197 "app/conanfile.py" : conanfile ,
194- "profile" : "[replace_requires]\n zlib/0.1: zlib-ng/0.1 " })
195- c .run ("create zlibng " )
198+ "profile" : f "[replace_requires]\n zlib/0.1: { name } / { version } " })
199+ c .run ("create dep " )
196200 c .run ("build app -pr=profile" )
197- assert "zlib/0.1: zlib-ng/0.1 " in c .out
198- assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib-ng !" in c .out
199- assert "conanfile.py (app/0.1): DEP ZLIB build: zlib-ng !" in c .out
201+ assert f "zlib/0.1: { name } / { version } " in c .out
202+ assert f "conanfile.py (app/0.1): DEP ZLIB generate: { name } !" in c .out
203+ assert f "conanfile.py (app/0.1): DEP ZLIB build: { name } !" in c .out
200204 # Check generated CMake code. If the targets are NOT compatible, then the replacement
201205 # Cannot happen
202206 assert "find_package(ZLIB)" in c .out
203207 assert "target_link_libraries(... ZLIB::ZLIB)" in c .out
204208 cmake = c .load ("app/ZLIBTargets.cmake" )
205209 assert "add_library(ZLIB::ZLIB INTERFACE IMPORTED)" in cmake
206210 c .run ("create app -pr=profile" )
207- assert "zlib/0.1: zlib-ng/0.1" in c .out
208- assert "app/0.1: DEP ZLIB generate: zlib-ng!" in c .out
209- assert "app/0.1: DEP ZLIB build: zlib-ng!" in c .out
211+ assert f"zlib/0.1: { name } /{ version } " in c .out
212+ assert f"app/0.1: DEP ZLIB generate: { name } !" in c .out
213+ assert f"app/0.1: DEP ZLIB build: { name } !" in c .out
214+
215+
216+ def test_replace_requires_consumer_references_error_multiple ():
217+ # https://github.com/conan-io/conan/issues/17407
218+ c = TestClient ()
219+ # IMPORTANT: The replacement package must be target-compatible
220+ zlib = textwrap .dedent ("""
221+ from conan import ConanFile
222+ class ZlibNG(ConanFile):
223+ name = "zlib"
224+ version = "0.2"
225+ def package_info(self):
226+ self.cpp_info.set_property("cmake_file_name", "ZLIB")
227+ self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
228+ """ )
229+ conanfile = textwrap .dedent ("""
230+ from conan import ConanFile
231+ class App(ConanFile):
232+ name = "app"
233+ version = "0.1"
234+ settings = "build_type"
235+ requires = "zlib/0.1", "bzip2/0.1"
236+ generators = "CMakeDeps"
237+
238+ def generate(self):
239+ self.output.info(f"DEP ZLIB generate: {self.dependencies['zlib'].ref.name}!")
240+ self.output.info(f"DEP BZIP2 generate: {self.dependencies['bzip2'].ref.name}!")
241+ def build(self):
242+ self.output.info(f"DEP ZLIB build: {self.dependencies['zlib'].ref.name}!")
243+ self.output.info(f"DEP BZIP2 build: {self.dependencies['bzip2'].ref.name}!")
244+ def package_info(self):
245+ self.output.info(f"DEP ZLIB package_info: {self.dependencies['zlib'].ref.name}!")
246+ self.cpp_info.requires = ["zlib::zlib", "bzip2::bzip2"]
247+ """ )
248+ c .save ({"zlib/conanfile.py" : zlib ,
249+ "app/conanfile.py" : conanfile ,
250+ "profile" : "[replace_requires]\n zlib/0.1: zlib/0.2\n bzip2/0.1: zlib/0.2" })
251+ c .run ("create zlib" )
252+ c .run ("build app -pr=profile" )
253+ assert "zlib/0.1: zlib/0.2" in c .out
254+ assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib!" in c .out
255+ assert "conanfile.py (app/0.1): DEP ZLIB build: zlib!" in c .out
256+ assert "conanfile.py (app/0.1): DEP BZIP2 generate: zlib!" in c .out
257+ assert "conanfile.py (app/0.1): DEP BZIP2 build: zlib!" in c .out
258+ # Check generated CMake code. If the targets are NOT compatible, then the replacement
259+ # Cannot happen
260+ assert "find_package(ZLIB)" in c .out
261+ assert "target_link_libraries(... ZLIB::ZLIB ZLIB::ZLIB)" in c .out
262+ cmake = c .load ("app/ZLIBTargets.cmake" )
263+ assert "add_library(ZLIB::ZLIB INTERFACE IMPORTED)" in cmake
264+ c .run ("create app -pr=profile" )
265+ assert "zlib/0.1: zlib/0.2" in c .out
266+ assert "app/0.1: DEP ZLIB generate: zlib!" in c .out
267+ assert "app/0.1: DEP ZLIB build: zlib!" in c .out
210268
211269
212270def test_replace_requires_consumer_components_options ():
0 commit comments