1from abc import ABCMeta, abstractproperty
2
3import six
4
5from conans.errors import ConanException
6from conans.model.build_info import COMPONENT_SCOPE
7
8
9@six.add_metaclass(ABCMeta)
10class Generator(object):
11    name = None
12
13    def __init__(self, conanfile):
14        self.conanfile = conanfile
15        self.normalize = True
16        self._deps_build_info = conanfile.deps_cpp_info
17        self._deps_env_info = conanfile.deps_env_info
18        self._env_info = conanfile.env_info
19        self._deps_user_info = conanfile.deps_user_info
20        self._user_info_build = getattr(conanfile, 'user_info_build', None)
21
22    @classmethod
23    def _get_name(cls, obj):
24        return obj.get_name(cls.name)
25
26    @property
27    def deps_build_info(self):
28        return self._deps_build_info
29
30    @property
31    def deps_env_info(self):
32        return self._deps_env_info
33
34    @property
35    def deps_user_info(self):
36        return self._deps_user_info
37
38    @property
39    def env_info(self):
40        return self._env_info
41
42    @property
43    def settings(self):
44        return self.conanfile.settings
45
46    @abstractproperty
47    def content(self):
48        raise NotImplementedError()
49
50    @abstractproperty
51    def filename(self):
52        raise NotImplementedError()
53
54    def get_public_deps(self, cpp_info):
55        return cpp_info.public_deps
56
57
58class GeneratorComponentsMixin(object):
59
60    @classmethod
61    def sorted_components(self, cpp_info):
62        return cpp_info._get_sorted_components()
63
64    def _validate_components(self, cpp_info):
65        """ Check that all required components are provided by the dependencies """
66
67        def _check_component_in_requirements(require):
68            if COMPONENT_SCOPE in require:
69                req_name, req_comp_name = require.split(COMPONENT_SCOPE)
70                if req_name == req_comp_name:
71                    return
72                if req_comp_name not in self.deps_build_info[req_name].components:
73                    raise ConanException("Component '%s' not found in '%s' package requirement"
74                                         % (require, req_name))
75
76        for comp_name, comp in cpp_info.components.items():
77            for cmp_require in comp.requires:
78                _check_component_in_requirements(cmp_require)
79
80        for pkg_require in cpp_info.requires:
81            _check_component_in_requirements(pkg_require)
82
83    def _get_require_name(self, pkg_name, req):
84        pkg, cmp = req.split(COMPONENT_SCOPE) if COMPONENT_SCOPE in req else (pkg_name, req)
85        pkg_build_info = self.deps_build_info[pkg]
86        pkg_name = self._get_name(pkg_build_info)
87        # fallback namespace to pkg_name if not defined
88        pkg_namespace = pkg_name
89        if cmp in pkg_build_info.components:
90            cmp_name = self._get_name(pkg_build_info.components[cmp])
91        else:
92            cmp_name = pkg_name
93        return pkg_namespace, cmp_name
94
95    def _get_components(self, pkg_name, cpp_info):
96        ret = []
97        for comp_name, comp in self.sorted_components(cpp_info).items():
98            comp_genname = self._get_name(cpp_info.components[comp_name])
99            comp_requires_gennames = []
100            for require in comp.requires:
101                comp_requires_gennames.append(self._get_require_name(pkg_name, require))
102            ret.append((comp_genname, comp, comp_requires_gennames))
103        ret.reverse()
104        return ret
105
106    @classmethod
107    def get_public_deps(cls, cpp_info):
108        if cpp_info.requires:
109            deps = [it for it in cpp_info.requires if COMPONENT_SCOPE in it]
110            return [it.split(COMPONENT_SCOPE) for it in deps]
111        else:
112            return [(it, it) for it in cpp_info.public_deps]
113