1import os 2 3from conans.client.file_copier import FileCopier 4from conans.errors import ConanException 5 6 7class _PatternEntry(object): 8 9 def __init__(self): 10 self.include = [] 11 self.lib = [] 12 self.bin = [] 13 self.src = [] 14 self.build = [] 15 self.res = [] 16 self.framework = [] 17 18 19class _Patterns(object): 20 21 def __init__(self): 22 self.source = _PatternEntry() 23 self.build = _PatternEntry() 24 25 26class AutoPackager(object): 27 28 def __init__(self, conanfile): 29 self._conanfile = conanfile 30 self.patterns = _Patterns() 31 32 self.patterns.source.include = ["*.h", "*.hpp", "*.hxx"] 33 self.patterns.source.lib = [] 34 self.patterns.source.bin = [] 35 36 self.patterns.build.include = ["*.h", "*.hpp", "*.hxx"] 37 self.patterns.build.lib = ["*.so", "*.so.*", "*.a", "*.lib", "*.dylib"] 38 self.patterns.build.bin = ["*.exe", "*.dll"] 39 40 def run(self): 41 cf = self._conanfile 42 # Check that the components declared in source/build are in package 43 cnames = set(cf.cpp.source.component_names) 44 cnames = cnames.union(set(cf.cpp.build.component_names)) 45 if cnames.difference(set(cf.cpp.package.component_names)): 46 # TODO: Raise? Warning? Ignore? 47 raise ConanException("There are components declared in cpp.source.components" 48 " or in cpp.build.components that are not declared in" 49 " cpp.package.components") 50 if cnames: 51 for cname in cnames: 52 if cname in cf.cpp.source.components: 53 self._package_cppinfo("source", cf.cpp.source.components[cname], 54 cf.cpp.package.components[cname]) 55 if cname in cf.cpp.build.components: 56 self._package_cppinfo("build", cf.cpp.build.components[cname], 57 cf.cpp.package.components[cname]) 58 else: # No components declared 59 self._package_cppinfo("source", cf.cpp.source, cf.cpp.package) 60 self._package_cppinfo("build", cf.cpp.build, cf.cpp.package) 61 62 63 def _package_cppinfo(self, origin_name, origin_cppinfo, dest_cppinfo): 64 """ 65 @param origin_name: one from ["source", "build"] 66 @param origin_cppinfo: cpp_info object of an origin (can be a component cppinfo too) 67 @param dest_cppinfo: cpp_info object of the package or a component from package 68 """ 69 70 patterns_var = getattr(self.patterns, origin_name) 71 base_folder = getattr(self._conanfile, "{}_folder".format(origin_name)) 72 73 for var in ["include", "lib", "bin", "framework", "src", "build", "res"]: 74 dirs_var_name = "{}dirs".format(var) 75 origin_paths = getattr(origin_cppinfo, dirs_var_name) 76 if not origin_paths: 77 continue 78 patterns = getattr(patterns_var, var) 79 destinations = getattr(dest_cppinfo, dirs_var_name) 80 81 if not destinations: # For example: Not declared "includedirs" in package.cpp_info 82 continue 83 84 if len(destinations) > 1: 85 # Check if there is only one possible destination at package, otherwise the 86 # copy would need to be done manually 87 err_msg = "The package has more than 1 cpp_info.{}, cannot package automatically" 88 raise ConanException(err_msg.format(dirs_var_name)) 89 90 for d in origin_paths: 91 copier = FileCopier([os.path.join(base_folder, d)], 92 self._conanfile.folders.base_package) 93 for pattern in patterns: 94 copier(pattern, dst=destinations[0]) 95 96