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