Skip to content

Commit

Permalink
improvements deduce locations incubating cmakedeps
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded committed Jan 17, 2025
1 parent ecb0fab commit f6e8afb
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 25 deletions.
72 changes: 51 additions & 21 deletions conan/internal/model/cpp_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"):
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
4 changes: 0 additions & 4 deletions conan/tools/cmake/cmakedeps2/target_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down

0 comments on commit f6e8afb

Please sign in to comment.