diff --git a/conan/internal/model/cpp_info.py b/conan/internal/model/cpp_info.py index d05f0cd4d8d..0bf9026d749 100644 --- a/conan/internal/model/cpp_info.py +++ b/conan/internal/model/cpp_info.py @@ -285,7 +285,7 @@ def type(self): @type.setter def type(self, value): - self._type = value + self._type = PackageType(value) if value is not None else None @property def location(self): @@ -538,6 +538,7 @@ def _find_matching(dirs, pattern): static_location = None shared_location = None dll_location = None + deduced_type = None # libname is exactly the pattern, e.g., ["mylib.a"] instead of ["mylib"] _, ext = os.path.splitext(libname) if ext in (".lib", ".a", ".dll", ".so", ".dylib"): @@ -562,52 +563,78 @@ def _find_matching(dirs, pattern): if shared_location: out.warning(f"Lib {libname} has both static {static_location} and " f"shared {shared_location} in the same package") - if pkg_type is PackageType.STATIC: + if self._type is PackageType.STATIC or pkg_type is PackageType.STATIC: self._location = static_location - self._type = PackageType.STATIC + deduced_type = PackageType.STATIC else: self._location = shared_location - self._type = PackageType.SHARED + deduced_type = PackageType.SHARED elif dll_location: self._location = dll_location self._link_location = static_location - self._type = PackageType.SHARED + deduced_type = PackageType.SHARED else: self._location = static_location - self._type = PackageType.STATIC + deduced_type = PackageType.STATIC elif shared_location: self._location = shared_location - self._type = PackageType.SHARED + deduced_type = PackageType.SHARED elif dll_location: # Only .dll but no link library self._location = dll_location - self._type = PackageType.SHARED + deduced_type = PackageType.SHARED if not self._location: raise ConanException(f"{conanfile}: Cannot obtain 'location' for library '{libname}' " f"in {libdirs}. You can specify 'cpp_info.location' directly " f"or report in github.com/conan-io/conan/issues if you think it " f"should have been deduced correctly.") + if self._type is not None and self._type != deduced_type: + ConanException(f"{conanfile}: Incorrect deduced type '{deduced_type}' for library" + f" '{libname}' that declared .type='{self._type}'") + self._type = deduced_type if self._type != pkg_type: out.warning(f"Lib {libname} deduced as '{self._type}, but 'package_type={pkg_type}'") def deduce_locations(self, conanfile, component_name=""): + name = f'{conanfile} cpp_info.components["{component_name}"]' if component_name \ + else f'{conanfile} cpp_info' + # executable if self._exe: # exe is a new field, it should have the correct location + if self._type is None: + self._type = PackageType.APP + if self._type is not PackageType.APP: + raise ConanException(f"{name} incorrect .type {self._type} for .exe {self._exe}") + if self.libs: + raise ConanException(f"{name} has both .exe and .libs") + if not self.location: + raise ConanException(f"{name} has .exe and no .location") return - if self._location or self._link_location: - if self._type is None or self._type is PackageType.HEADER: - raise ConanException("Incorrect cpp_info defining location without type or header") + if self._type is PackageType.APP: + # old school Conan application packages withoud defining an exe, not an error return - if self._type not in [None, PackageType.SHARED, PackageType.STATIC, PackageType.APP]: + + # libraries + if len(self.libs) > 1: # it could be 0, as the libs itself is not necessary + raise ConanException(f"{name} has more than 1 library in .libs: {self.libs}, " + "cannot deduce locations") + # fully defined by user in conanfile, nothing to do. + if self._location or self._link_location: + if self._type not in [PackageType.SHARED, PackageType.STATIC]: + raise ConanException(f"{name} location defined without defined library type") return - num_libs = len(self.libs) - if num_libs == 0: + + # possible header only, which allows also an empty header-only only for common flags + if len(self.libs) == 0: + if self._type is None: + self._type = PackageType.HEADER return - elif num_libs > 1: - raise ConanException( - f"More than 1 library defined in cpp_info.libs, cannot deduce CPS ({num_libs} libraries found)") - else: - # If no location is defined, it's time to guess the location - self._auto_deduce_locations(conanfile, component_name=component_name) + + # automatic location deduction from a single .lib=["lib"] + if self._type not in [None, PackageType.SHARED, PackageType.STATIC]: + raise ConanException(f"{name} has a library but .type {self._type} is not static/shared") + + # If no location is defined, it's time to guess the location + self._auto_deduce_locations(conanfile, component_name=component_name) class CppInfo: @@ -779,6 +806,9 @@ def required_components(self): return ret def deduce_full_cpp_info(self, conanfile): + if conanfile.cpp_info.has_components and (conanfile.cpp_info.exe or conanfile.cpp_info.libs): + raise ConanException(f"{conanfile}: 'cpp_info' contains components and .exe or .libs") + result = CppInfo() # clone it if self.libs and len(self.libs) > 1: # expand in multiple components @@ -796,7 +826,7 @@ def deduce_full_cpp_info(self, conanfile): common = self._package.clone() common.libs = [] - common.type = str(PackageType.HEADER) # the type of components is a string! + common.type = PackageType.HEADER # the type of components is a string! common.requires = list(result.components.keys()) + (self.requires or []) result.components["_common"] = common else: diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py index f6ef07c956a..0cb24590de9 100644 --- a/conan/tools/cmake/cmakedeps2/target_configuration.py +++ b/conan/tools/cmake/cmakedeps2/target_configuration.py @@ -217,8 +217,6 @@ def _get_exes(self, cpp_info, pkg_name, pkg_folder, pkg_folder_var): exes = {} if cpp_info.has_components: - assert not cpp_info.exe, "Package has components and exe" - assert not cpp_info.libs, "Package has components and libs" for name, comp in cpp_info.components.items(): if comp.exe or comp.type is PackageType.APP: target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile, @@ -228,8 +226,6 @@ def _get_exes(self, cpp_info, pkg_name, pkg_folder, pkg_folder_var): exes[target] = exe_location else: if cpp_info.exe: - assert not cpp_info.libs, "Package has exe and libs" - assert cpp_info.location, "Package has exe and no location" target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile) target = target_name or f"{pkg_name}::{pkg_name}" exe_location = self._path(cpp_info.location, pkg_folder, pkg_folder_var)