diff --git a/conan/cps/cps.py b/conan/cps/cps.py index 6e04b7b8bbb..f4dbe944f8c 100644 --- a/conan/cps/cps.py +++ b/conan/cps/cps.py @@ -40,6 +40,7 @@ def __init__(self, component_type=None): self.requires = [] self.location = None self.link_location = None + self.link_languages = None self.link_libraries = None # system libraries def serialize(self): @@ -56,6 +57,8 @@ def serialize(self): component["link_location"] = self.link_location if self.link_libraries: component["link_libraries"] = self.link_libraries + if self.link_languages: + component["link_languages"] = self.link_languages return component @staticmethod @@ -68,6 +71,7 @@ def deserialize(data): comp.location = data.get("location") comp.link_location = data.get("link_location") comp.link_libraries = data.get("link_libraries") + comp.link_languages = data.get("link_languages") return comp @staticmethod @@ -90,12 +94,15 @@ def from_cpp_info(cpp_info, conanfile, libname=None): cps_comp.location = cpp_info.location cps_comp.link_location = cpp_info.link_location cps_comp.link_libraries = cpp_info.system_libs + langs = {"C": "c", "C++": "cpp"} + cps_comp.link_languages = [langs[lang] for lang in cpp_info.languages or []] required = cpp_info.requires cps_comp.requires = [f":{c}" if "::" not in c else c.replace("::", ":") for c in required] return cps_comp def update(self, conf, conf_def): # TODO: conf not used at the moent + self.link_languages = self.link_languages or conf_def.get("link_languages") self.location = self.location or conf_def.get("location") self.link_location = self.link_location or conf_def.get("link_location") self.link_libraries = self.link_libraries or conf_def.get("link_libraries") @@ -223,6 +230,29 @@ def strip_prefix(dirs): basefile = basefile[3:] cpp_info.libs = [basefile] # FIXME: Missing requires + elif comp.type is CPSComponentType.DYLIB: + if comp.link_location: + link_location = comp.link_location + link_location = link_location.replace("@prefix@/", "") + cpp_info.libdirs = [os.path.dirname(link_location)] + filename = os.path.basename(link_location) + basefile, ext = os.path.splitext(filename) + if basefile.startswith("lib") and ext != ".lib": + basefile = basefile[3:] + cpp_info.libs = [basefile] + location = comp.location + location = location.replace("@prefix@/", "") + cpp_info.bindirs = [os.path.dirname(location)] + else: # TODO: same as archive, refactor + location = comp.location + location = location.replace("@prefix@/", "") + cpp_info.libdirs = [os.path.dirname(location)] + filename = os.path.basename(location) + basefile, ext = os.path.splitext(filename) + if basefile.startswith("lib") and ext != ".lib": + basefile = basefile[3:] + cpp_info.libs = [basefile] + # FIXME: Missing requires cpp_info.system_libs = comp.link_libraries else: for comp_name, comp in self.components.items(): diff --git a/conan/tools/cmake/cmakedeps2/cmakedeps.py b/conan/tools/cmake/cmakedeps2/cmakedeps.py index e2ab0b6e657..34893a35ddf 100644 --- a/conan/tools/cmake/cmakedeps2/cmakedeps.py +++ b/conan/tools/cmake/cmakedeps2/cmakedeps.py @@ -259,7 +259,8 @@ def generate(self): pkg_name = self._cmakedeps.get_cmake_filename(dep) # https://cmake.org/cmake/help/v3.22/guide/using-dependencies/index.html if cmake_find_mode == FIND_MODE_NONE: - cps = glob.glob(f"**/{pkg_name}.cps", root_dir=dep.package_folder, recursive=True) + cps = glob.glob(os.path.join(dep.package_folder, f"**/{pkg_name}.cps"), + recursive=True) if cps: loc = os.path.dirname(os.path.join(dep.package_folder, cps[0])) loc = loc.replace("\\", "/") diff --git a/test/functional/toolchains/cmake/test_cps.py b/test/functional/toolchains/cmake/test_cps.py index ad660a40eca..222747e3df9 100644 --- a/test/functional/toolchains/cmake/test_cps.py +++ b/test/functional/toolchains/cmake/test_cps.py @@ -8,7 +8,8 @@ @pytest.mark.tool("cmake", "4.2") -def test_cps(): +@pytest.mark.parametrize("shared", [False, True]) +def test_cps(shared): c = TestClient() c.run("new cmake_lib") conanfile = textwrap.dedent("""\ @@ -67,7 +68,9 @@ def package_info(self): # First, try with the standard mypkg-config.cmake consumption c.save({"conanfile.py": conanfile, "CMakeLists.txt": cmake}) - c.run("create") + + shared_arg = "-o &:shared=True" if shared else "" + c.run(f"create {shared_arg}") assert "mypkg/0.1: Hello World Release!" in c.out # Lets consume directly with CPS @@ -119,5 +122,5 @@ def test(self): shutil.rmtree(os.path.join(c.current_folder, "test_package", "build")) c.save({"test_package/conanfile.py": test_conanfile, "test_package/CMakeLists.txt": test_cmake}) - c.run("create --build=missing -c tools.cmake.cmakedeps:new=will_break_next") + c.run(f"create {shared_arg} --build=never -c tools.cmake.cmakedeps:new=will_break_next") assert "mypkg/0.1: Hello World Release!" in c.out diff --git a/test/integration/cps/test_cps.py b/test/integration/cps/test_cps.py index 407f91a13ba..6dfbcee5338 100644 --- a/test/integration/cps/test_cps.py +++ b/test/integration/cps/test_cps.py @@ -122,38 +122,106 @@ def package_info(self): assert 'set(zlib_LIBS_RELEASE zlib)' in cmake -def test_cps_merge(): - folder = temp_folder() - cps_base = textwrap.dedent(""" +def test_cps_shared_in_pkg(): + c = TestClient() + cps = textwrap.dedent("""\ { - "components" : { - "mypkg" : { + "components" : + { + "mypkg" : + { "includes" : [ "@prefix@/include" ], - "type" : "archive" + "type" : "dylib" } }, - "cps_version" : "0.12.0", - "name" : "mypkg", - "version" : "1.0" + "cps_path" : "@prefix@/cps", + "cps_version" : "0.13.0", + "name" : "mypkg" } """) - cps_conf = textwrap.dedent(""" + cps_release = textwrap.dedent("""\ { - "components" : { - "mypkg" : { - "link_languages" : [ "cpp" ], - "location" : "@prefix@/lib/mypkg.lib" + "components" : + { + "mypkg" : + { + "link_location" : "@prefix@/lib/mypkg.lib", + "location" : "@prefix@/bin/mypkg.dll" } }, "configuration" : "Release", "name" : "mypkg" } """) + cps = "".join(cps.splitlines()) + cps_release = "".join(cps_release.splitlines()) + conanfile = textwrap.dedent(f""" + import os, json + from conan.tools.files import save + from conan import ConanFile + class Pkg(ConanFile): + name = "mypkg" + version = "1.0" + + def package(self): + cps = '{cps}' + cps_path = os.path.join(self.package_folder, "mypkg.cps") + save(self, cps_path, cps) + cps = '{cps_release}' + cps_path = os.path.join(self.package_folder, "mypkg@release.cps") + save(self, cps_path, cps) + + def package_info(self): + from conan.cps import CPS + self.cpp_info = CPS.load("mypkg.cps").to_conan() + """) + c.save({"pkg/conanfile.py": conanfile}) + c.run("create pkg") + + settings = "-s os=Windows -s compiler=msvc -s compiler.version=191 -s arch=x86_64" + + c.run(f"install --requires=mypkg/1.0 {settings} -g CMakeDeps") + cmake = c.load("mypkg-release-x86_64-data.cmake") + assert 'set(mypkg_INCLUDE_DIRS_RELEASE "${mypkg_PACKAGE_FOLDER_RELEASE}/include")' in cmake + assert 'set(mypkg_LIB_DIRS_RELEASE "${mypkg_PACKAGE_FOLDER_RELEASE}/lib")' + assert 'set(mypkg_LIBS_RELEASE mypkg)' in cmake + + +def test_cps_merge(): + folder = temp_folder() + + cps_base = textwrap.dedent("""{ + "components": { + "mypkg":{ + "includes": ["@prefix@/include"], + "type": "archive" + } + }, + "cps_path": "@prefix@/cps", + "cps_version": "0.13.0", + "name": "mypkg" + } + """) + cps_conf = textwrap.dedent("""{ + "components" : { + "mypkg" : { + "link_languages" : [ "cpp" ], + "location" : "@prefix@/lib/mypkg.lib" + } + }, + "configuration" : "Release", + "name" : "mypkg" + } + """) save_files(folder, {"mypkg.cps": cps_base, "mypkg@release.cps": cps_conf}) cps = CPS.load(os.path.join(folder, "mypkg.cps")) json_cps = cps.serialize() - print(json.dumps(json_cps, indent=2)) + mypkg = json_cps["components"]["mypkg"] + assert mypkg["includes"] == ["@prefix@/include"] + assert mypkg["location"] == "@prefix@/lib/mypkg.lib" + assert mypkg["type"] == "archive" + assert mypkg["link_languages"] == ["cpp"] def test_extended_cpp_info():