1import os
2from collections import OrderedDict
3from copy import copy
4
5from conans.errors import ConanException
6from conans.util.conan_v2_mode import conan_v2_error
7
8DEFAULT_INCLUDE = "include"
9DEFAULT_LIB = "lib"
10DEFAULT_BIN = "bin"
11DEFAULT_RES = "res"
12DEFAULT_SHARE = "share"
13DEFAULT_BUILD = ""
14DEFAULT_FRAMEWORK = "Frameworks"
15
16COMPONENT_SCOPE = "::"
17
18
19class DefaultOrderedDict(OrderedDict):
20
21    def __init__(self, factory):
22        self.factory = factory
23        super(DefaultOrderedDict, self).__init__()
24
25    def __getitem__(self, key):
26        if key not in self.keys():
27            super(DefaultOrderedDict, self).__setitem__(key, self.factory())
28            super(DefaultOrderedDict, self).__getitem__(key).name = key
29        return super(DefaultOrderedDict, self).__getitem__(key)
30
31    def __copy__(self):
32        the_copy = DefaultOrderedDict(self.factory)
33        for key, value in super(DefaultOrderedDict, self).items():
34            the_copy[key] = value
35        return the_copy
36
37
38class BuildModulesDict(dict):
39    """
40    A dictionary with append and extend for cmake build modules to keep it backwards compatible
41    with the list interface
42    """
43
44    def __getitem__(self, key):
45        if key not in self.keys():
46            super(BuildModulesDict, self).__setitem__(key, list())
47        return super(BuildModulesDict, self).__getitem__(key)
48
49    def _append(self, item):
50        if item.endswith(".cmake"):
51            self["cmake"].append(item)
52            self["cmake_multi"].append(item)
53            self["cmake_find_package"].append(item)
54            self["cmake_find_package_multi"].append(item)
55
56    def append(self, item):
57        conan_v2_error("Use 'self.cpp_info.build_modules[\"<generator>\"].append(\"{item}\")' "
58                       'instead'.format(item=item))
59        self._append(item)
60
61    def extend(self, items):
62        conan_v2_error("Use 'self.cpp_info.build_modules[\"<generator>\"].extend({items})' "
63                       "instead".format(items=items))
64        for item in items:
65            self._append(item)
66
67    @classmethod
68    def from_list(cls, build_modules):
69        the_dict = BuildModulesDict()
70        the_dict.extend(build_modules)
71        return the_dict
72
73
74def dict_to_abs_paths(the_dict, rootpath):
75    new_dict = {}
76    for generator, values in the_dict.items():
77        new_dict[generator] = [os.path.join(rootpath, p) if not os.path.isabs(p) else p
78                               for p in values]
79    return new_dict
80
81
82def merge_lists(seq1, seq2):
83    return seq1 + [s for s in seq2 if s not in seq1]
84
85
86def merge_dicts(d1, d2):
87    def merge_lists(seq1, seq2):
88        return [s for s in seq1 if s not in seq2] + seq2
89
90    result = d1.copy()
91    for k, v in d2.items():
92        if k not in d1.keys():
93            result[k] = v
94        else:
95            result[k] = merge_lists(d1[k], d2[k])
96    return result
97
98
99class _CppInfo(object):
100    """ Object that stores all the necessary information to build in C/C++.
101    It is intended to be system independent, translation to
102    specific systems will be produced from this info
103    """
104
105    def __init__(self):
106        self._name = None
107        self._generator_properties = {}
108        self.names = {}
109        self.system_libs = []  # Ordered list of system libraries
110        self.includedirs = []  # Ordered list of include paths
111        self.srcdirs = []  # Ordered list of source paths
112        self.libdirs = []  # Directories to find libraries
113        self.resdirs = []  # Directories to find resources, data, etc
114        self.bindirs = []  # Directories to find executables and shared libs
115        self.builddirs = []
116        self.frameworks = []  # Macos .framework
117        self.frameworkdirs = []
118        self.rootpaths = []
119        self.libs = []  # The libs to link against
120        self.defines = []  # preprocessor definitions
121        self.cflags = []  # pure C flags
122        self.cxxflags = []  # C++ compilation flags
123        self.sharedlinkflags = []  # linker flags
124        self.exelinkflags = []  # linker flags
125        self.objects = []  # objects to link
126        self.build_modules = BuildModulesDict()  # FIXME: This should be just a plain dict
127        self.filenames = {}  # name of filename to create for various generators
128        self.rootpath = ""
129        self.sysroot = ""
130        self.requires = []
131        self._build_modules_paths = None
132        self._build_modules = None
133        self._include_paths = None
134        self._lib_paths = None
135        self._bin_paths = None
136        self._build_paths = None
137        self._res_paths = None
138        self._src_paths = None
139        self._framework_paths = None
140        self.version = None  # Version of the conan package
141        self.description = None  # Description of the conan package
142        # When package is editable, filter_empty=False, so empty dirs are maintained
143        self.filter_empty = True
144
145    def _filter_paths(self, paths):
146        abs_paths = [os.path.join(self.rootpath, p)
147                     if not os.path.isabs(p) else p for p in paths]
148        if self.filter_empty:
149            return [p for p in abs_paths if os.path.isdir(p)]
150        else:
151            return abs_paths
152
153    @property
154    def build_modules_paths(self):
155        if self._build_modules_paths is None:
156            if isinstance(self.build_modules, list):  # FIXME: This should be just a plain dict
157                conan_v2_error("Use 'self.cpp_info.build_modules[\"<generator>\"] = "
158                               "{the_list}' instead".format(the_list=self.build_modules))
159                self.build_modules = BuildModulesDict.from_list(self.build_modules)
160                # Invalidate necessary, get_build_modules used raise_incorrect_components_definition
161                self._build_modules = None
162            tmp = dict_to_abs_paths(BuildModulesDict(self.get_build_modules()), self.rootpath)
163            self._build_modules_paths = tmp
164        return self._build_modules_paths
165
166    @property
167    def include_paths(self):
168        if self._include_paths is None:
169            self._include_paths = self._filter_paths(self.includedirs)
170        return self._include_paths
171
172    @property
173    def lib_paths(self):
174        if self._lib_paths is None:
175            self._lib_paths = self._filter_paths(self.libdirs)
176        return self._lib_paths
177
178    @property
179    def src_paths(self):
180        if self._src_paths is None:
181            self._src_paths = self._filter_paths(self.srcdirs)
182        return self._src_paths
183
184    @property
185    def bin_paths(self):
186        if self._bin_paths is None:
187            self._bin_paths = self._filter_paths(self.bindirs)
188        return self._bin_paths
189
190    @property
191    def build_paths(self):
192        if self._build_paths is None:
193            self._build_paths = self._filter_paths(self.builddirs)
194        return self._build_paths
195
196    @property
197    def res_paths(self):
198        if self._res_paths is None:
199            self._res_paths = self._filter_paths(self.resdirs)
200        return self._res_paths
201
202    @property
203    def framework_paths(self):
204        if self._framework_paths is None:
205            self._framework_paths = self._filter_paths(self.frameworkdirs)
206        return self._framework_paths
207
208    @property
209    def name(self):
210        conan_v2_error("Use 'get_name(generator)' instead")
211        return self._name
212
213    @name.setter
214    def name(self, value):
215        self._name = value
216
217    # TODO: Deprecate for 2.0. Only cmake and pkg_config generators should access this.
218    #  Use get_property for 2.0
219    def get_name(self, generator, default_name=True):
220        property_name = None
221        if "pkg_config" in generator:
222            property_name = "pkg_config_name"
223        return self.get_property(property_name) \
224               or self.names.get(generator, self._name if default_name else None)
225
226    # TODO: Deprecate for 2.0. Only cmake generators should access this. Use get_property for 2.0
227    def get_filename(self, generator, default_name=True):
228        # Default to the legacy "names"
229        return self.filenames.get(generator) or self.names.get(generator, self._name if default_name else None)
230
231    # TODO: Deprecate for 2.0. Use get_property for 2.0
232    def get_build_modules(self):
233        if self._build_modules is None:  # Not cached yet
234            self._build_modules = self.build_modules
235        return self._build_modules
236
237    def set_property(self, property_name, value):
238        self._generator_properties[property_name] = value
239
240    def get_property(self, property_name):
241        try:
242            return self._generator_properties[property_name]
243        except KeyError:
244            pass
245
246    # Compatibility for 'cppflags' (old style property to allow decoration)
247    def get_cppflags(self):
248        conan_v2_error("'cpp_info.cppflags' is deprecated, use 'cxxflags' instead")
249        return self.cxxflags
250
251    def set_cppflags(self, value):
252        conan_v2_error("'cpp_info.cppflags' is deprecated, use 'cxxflags' instead")
253        self.cxxflags = value
254
255    cppflags = property(get_cppflags, set_cppflags)
256
257
258class Component(_CppInfo):
259
260    def __init__(self, rootpath, version, default_values):
261        super(Component, self).__init__()
262        self.rootpath = rootpath
263        if default_values.includedir is not None:
264            self.includedirs.append(default_values.includedir)
265        if default_values.libdir is not None:
266            self.libdirs.append(default_values.libdir)
267        if default_values.bindir is not None:
268            self.bindirs.append(default_values.bindir)
269        if default_values.resdir is not None:
270            self.resdirs.append(default_values.resdir)
271        if default_values.builddir is not None:
272            self.builddirs.append(default_values.builddir)
273        if default_values.frameworkdir is not None:
274            self.frameworkdirs.append(default_values.frameworkdir)
275        self.requires = []
276        self.version = version
277
278
279class CppInfoDefaultValues(object):
280
281    def __init__(self, includedir=None, libdir=None, bindir=None,
282                 resdir=None, builddir=None, frameworkdir=None):
283        self.includedir = includedir
284        self.libdir = libdir
285        self.bindir = bindir
286        self.resdir = resdir
287        self.builddir = builddir
288        self.frameworkdir = frameworkdir
289
290
291class CppInfo(_CppInfo):
292    """ Build Information declared to be used by the CONSUMERS of a
293    conans. That means that consumers must use this flags and configs i order
294    to build properly.
295    Defined in user CONANFILE, directories are relative at user definition time
296    """
297
298    def __init__(self, ref_name, root_folder, default_values=None):
299        super(CppInfo, self).__init__()
300        self._ref_name = ref_name
301        self._name = ref_name
302        self.rootpath = root_folder  # the full path of the package in which the conans is found
303        self._default_values = default_values or CppInfoDefaultValues(DEFAULT_INCLUDE, DEFAULT_LIB,
304                                                                      DEFAULT_BIN, DEFAULT_RES,
305                                                                      DEFAULT_BUILD,
306                                                                      DEFAULT_FRAMEWORK)
307        if self._default_values.includedir is not None:
308            self.includedirs.append(self._default_values.includedir)
309        if self._default_values.libdir is not None:
310            self.libdirs.append(self._default_values.libdir)
311        if self._default_values.bindir is not None:
312            self.bindirs.append(self._default_values.bindir)
313        if self._default_values.resdir is not None:
314            self.resdirs.append(self._default_values.resdir)
315        if self._default_values.builddir is not None:
316            self.builddirs.append(self._default_values.builddir)
317        if self._default_values.frameworkdir is not None:
318            self.frameworkdirs.append(self._default_values.frameworkdir)
319        self.components = DefaultOrderedDict(lambda: Component(self.rootpath,
320                                                               self.version, self._default_values))
321        # public_deps is needed to accumulate list of deps for cmake targets
322        self.public_deps = []
323        self._configs = {}
324
325    def __str__(self):
326        return self._ref_name
327
328    def get_name(self, generator, default_name=True):
329        name = super(CppInfo, self).get_name(generator, default_name=default_name)
330
331        # Legacy logic for pkg_config generator
332        from conans.client.generators.pkg_config import PkgConfigGenerator
333        if generator == PkgConfigGenerator.name:
334            fallback = self._name.lower() if self._name != self._ref_name else self._ref_name
335            if PkgConfigGenerator.name not in self.names and self._name != self._name.lower():
336                conan_v2_error("Generated file and name for {gen} generator will change in"
337                               " Conan v2 to '{name}'. Use 'self.cpp_info.names[\"{gen}\"]"
338                               " = \"{fallback}\"' in your recipe to continue using current name."
339                               .format(gen=PkgConfigGenerator.name, name=name, fallback=fallback))
340            name = self.names.get(generator, fallback)
341        return name
342
343    @property
344    def configs(self):
345        return self._configs
346
347    def __getattr__(self, config):
348        def _get_cpp_info():
349            result = _CppInfo()
350            result.filter_empty = self.filter_empty
351            result.rootpath = self.rootpath
352            result.sysroot = self.sysroot
353            result.includedirs.append(self._default_values.includedir)
354            result.libdirs.append(self._default_values.libdir)
355            result.bindirs.append(self._default_values.bindir)
356            result.resdirs.append(self._default_values.resdir)
357            result.builddirs.append(self._default_values.builddir)
358            result.frameworkdirs.append(self._default_values.frameworkdir)
359            return result
360
361        return self._configs.setdefault(config, _get_cpp_info())
362
363    def _raise_incorrect_components_definition(self, package_name, package_requires):
364        if not self.components and not self.requires:
365            return
366
367        # Raise if mixing components
368        if self.components and \
369            (self.includedirs != ([self._default_values.includedir]
370                if self._default_values.includedir is not None else []) or
371             self.libdirs != ([self._default_values.libdir]
372                if self._default_values.libdir is not None else []) or
373             self.bindirs != ([self._default_values.bindir]
374                if self._default_values.bindir is not None else []) or
375             self.resdirs != ([self._default_values.resdir]
376                if self._default_values.resdir is not None else []) or
377             self.builddirs != ([self._default_values.builddir]
378                if self._default_values.builddir is not None else []) or
379             self.frameworkdirs != ([self._default_values.frameworkdir]
380                if self._default_values.frameworkdir is not None  else []) or
381             self.libs or
382             self.system_libs or
383             self.frameworks or
384             self.defines or
385             self.cflags or
386             self.cxxflags or
387             self.sharedlinkflags or
388             self.exelinkflags or
389             self.objects or
390             self.get_build_modules() or
391             self.requires):
392            raise ConanException("self.cpp_info.components cannot be used with self.cpp_info "
393                                 "global values at the same time")
394        if self._configs:
395            raise ConanException("self.cpp_info.components cannot be used with self.cpp_info configs"
396                                 " (release/debug/...) at the same time")
397
398        pkg_requires = [require.ref.name for require in package_requires.values()]
399
400        def _check_components_requires_instersection(comp_requires):
401            reqs = [it.split(COMPONENT_SCOPE)[0] for it in comp_requires if COMPONENT_SCOPE in it]
402            # Raise on components requires without package requires
403            for pkg_require in pkg_requires:
404                if package_requires[pkg_require].private or package_requires[pkg_require].override:
405                    # Not standard requires, skip
406                    continue
407                if pkg_require not in reqs:
408                    raise ConanException("Package require '%s' not used in components requires"
409                                         % pkg_require)
410            # Raise on components requires requiring inexistent package requires
411            for comp_require in reqs:
412                reason = None
413                if comp_require not in pkg_requires:
414                    reason = "not defined as a recipe requirement"
415                elif package_requires[comp_require].private and package_requires[
416                    comp_require].override:
417                    reason = "it was defined as an overridden private recipe requirement"
418                elif package_requires[comp_require].private:
419                    reason = "it was defined as a private recipe requirement"
420                elif package_requires[comp_require].override:
421                    reason = "it was defined as an overridden recipe requirement"
422
423                if reason is not None:
424                    raise ConanException("Package require '%s' declared in components requires "
425                                         "but %s" % (comp_require, reason))
426
427        if self.components:
428            # Raise on component name
429            for comp_name, comp in self.components.items():
430                if comp_name == package_name:
431                    raise ConanException(
432                        "Component name cannot be the same as the package name: '%s'"
433                        % comp_name)
434
435            # check that requires are used in components and check that components exists in requires
436            requires_from_components = set()
437            for comp_name, comp in self.components.items():
438                requires_from_components.update(comp.requires)
439
440            _check_components_requires_instersection(requires_from_components)
441        else:
442            _check_components_requires_instersection(self.requires)
443
444
445class _BaseDepsCppInfo(_CppInfo):
446    def __init__(self):
447        super(_BaseDepsCppInfo, self).__init__()
448
449    def update(self, dep_cpp_info):
450        def merge_lists(seq1, seq2):
451            return [s for s in seq1 if s not in seq2] + seq2
452
453        self.system_libs = merge_lists(self.system_libs, dep_cpp_info.system_libs)
454        self.includedirs = merge_lists(self.includedirs, dep_cpp_info.include_paths)
455        self.srcdirs = merge_lists(self.srcdirs, dep_cpp_info.src_paths)
456        self.libdirs = merge_lists(self.libdirs, dep_cpp_info.lib_paths)
457        self.bindirs = merge_lists(self.bindirs, dep_cpp_info.bin_paths)
458        self.resdirs = merge_lists(self.resdirs, dep_cpp_info.res_paths)
459        self.builddirs = merge_lists(self.builddirs, dep_cpp_info.build_paths)
460        self.frameworkdirs = merge_lists(self.frameworkdirs, dep_cpp_info.framework_paths)
461        self.libs = merge_lists(self.libs, dep_cpp_info.libs)
462        self.frameworks = merge_lists(self.frameworks, dep_cpp_info.frameworks)
463        self.build_modules = merge_dicts(self.build_modules, dep_cpp_info.build_modules_paths)
464        self.requires = merge_lists(self.requires, dep_cpp_info.requires)
465        self.rootpaths.append(dep_cpp_info.rootpath)
466
467        # Note these are in reverse order
468        self.defines = merge_lists(dep_cpp_info.defines, self.defines)
469        self.cxxflags = merge_lists(dep_cpp_info.cxxflags, self.cxxflags)
470        self.cflags = merge_lists(dep_cpp_info.cflags, self.cflags)
471        self.sharedlinkflags = merge_lists(dep_cpp_info.sharedlinkflags, self.sharedlinkflags)
472        self.exelinkflags = merge_lists(dep_cpp_info.exelinkflags, self.exelinkflags)
473        self.objects = merge_lists(dep_cpp_info.objects, self.objects)
474        if not self.sysroot:
475            self.sysroot = dep_cpp_info.sysroot
476
477    @property
478    def build_modules_paths(self):
479        return self.build_modules
480
481    @property
482    def include_paths(self):
483        return self.includedirs
484
485    @property
486    def lib_paths(self):
487        return self.libdirs
488
489    @property
490    def src_paths(self):
491        return self.srcdirs
492
493    @property
494    def bin_paths(self):
495        return self.bindirs
496
497    @property
498    def build_paths(self):
499        return self.builddirs
500
501    @property
502    def res_paths(self):
503        return self.resdirs
504
505    @property
506    def framework_paths(self):
507        return self.frameworkdirs
508
509
510class DepCppInfo(object):
511
512    def __init__(self, cpp_info):
513        self._cpp_info = cpp_info
514        self._libs = None
515        self._system_libs = None
516        self._frameworks = None
517        self._defines = None
518        self._cxxflags = None
519        self._cflags = None
520        self._sharedlinkflags = None
521        self._exelinkflags = None
522        self._objects = None
523        self._requires = None
524
525        self._include_paths = None
526        self._lib_paths = None
527        self._bin_paths = None
528        self._build_paths = None
529        self._res_paths = None
530        self._src_paths = None
531        self._framework_paths = None
532        self._build_modules_paths = None
533        self._sorted_components = None
534        self._check_component_requires()
535
536    def __str__(self):
537        return str(self._cpp_info)
538
539    def __getattr__(self, item):
540        try:
541            attr = self._cpp_info.__getattribute__(item)
542        except AttributeError:  # item is not defined, get config (CppInfo)
543            attr = self._cpp_info.__getattr__(item)
544        return attr
545
546    def _aggregated_dict_values(self, item):
547        values = getattr(self, "_%s" % item)
548        if values is not None:
549            return values
550        if self._cpp_info.components:
551            values = {}
552            for component in self._get_sorted_components().values():
553                values = merge_dicts(values, getattr(component, item))
554        else:
555            values = getattr(self._cpp_info, item)
556        setattr(self, "_%s" % item, values)
557        return values
558
559    def _aggregated_list_values(self, item):
560        values = getattr(self, "_%s" % item)
561        if values is not None:
562            return values
563        if self._cpp_info.components:
564            values = []
565            for component in self._get_sorted_components().values():
566                values = merge_lists(values, getattr(component, item))
567        else:
568            values = getattr(self._cpp_info, item)
569        setattr(self, "_%s" % item, values)
570        return values
571
572    @staticmethod
573    def _filter_component_requires(requires):
574        return [r for r in requires if COMPONENT_SCOPE not in r]
575
576    def _check_component_requires(self):
577        for comp_name, comp in self._cpp_info.components.items():
578            missing_deps = [require for require in self._filter_component_requires(comp.requires)
579                            if require not in self._cpp_info.components]
580            if missing_deps:
581                raise ConanException("Component '%s' required components not found in this package: "
582                                     "%s" % (comp_name, ", ".join("'%s'" % d for d in missing_deps)))
583            bad_requires = [r for r in comp.requires if r.startswith(COMPONENT_SCOPE)]
584            if bad_requires:
585                msg = "Leading character '%s' not allowed in %s requires: %s. Omit it to require " \
586                      "components inside the same package." \
587                      % (COMPONENT_SCOPE, comp_name, bad_requires)
588                raise ConanException(msg)
589
590    def _get_sorted_components(self):
591        """
592        Sort Components from most dependent one first to the less dependent one last
593        :return: List of sorted components
594        """
595        if not self._sorted_components:
596            if any([[require for require in self._filter_component_requires(comp.requires)]
597                    for comp in self._cpp_info.components.values()]):
598                ordered = OrderedDict()
599                components = copy(self._cpp_info.components)
600                while len(ordered) != len(self._cpp_info.components):
601                    # Search next element to be processed
602                    for comp_name, comp in components.items():
603                        # Check if component is not required and can be added to ordered
604                        if comp_name not in [require for dep in components.values() for require in
605                                             self._filter_component_requires(dep.requires)]:
606                            ordered[comp_name] = comp
607                            del components[comp_name]
608                            break
609                    else:
610                        raise ConanException("There is a dependency loop in "
611                                             "'self.cpp_info.components' requires")
612                self._sorted_components = ordered
613            else:  # If components do not have requirements, keep them in the same order
614                self._sorted_components = self._cpp_info.components
615        return self._sorted_components
616
617    @property
618    def build_modules_paths(self):
619        return self._aggregated_dict_values("build_modules_paths")
620
621    @property
622    def include_paths(self):
623        return self._aggregated_list_values("include_paths")
624
625    @property
626    def lib_paths(self):
627        return self._aggregated_list_values("lib_paths")
628
629    @property
630    def src_paths(self):
631        return self._aggregated_list_values("src_paths")
632
633    @property
634    def bin_paths(self):
635        return self._aggregated_list_values("bin_paths")
636
637    @property
638    def build_paths(self):
639        return self._aggregated_list_values("build_paths")
640
641    @property
642    def res_paths(self):
643        return self._aggregated_list_values("res_paths")
644
645    @property
646    def framework_paths(self):
647        return self._aggregated_list_values("framework_paths")
648
649    @property
650    def libs(self):
651        return self._aggregated_list_values("libs")
652
653    @property
654    def system_libs(self):
655        return self._aggregated_list_values("system_libs")
656
657    @property
658    def frameworks(self):
659        return self._aggregated_list_values("frameworks")
660
661    @property
662    def defines(self):
663        return self._aggregated_list_values("defines")
664
665    @property
666    def cxxflags(self):
667        return self._aggregated_list_values("cxxflags")
668
669    @property
670    def cflags(self):
671        return self._aggregated_list_values("cflags")
672
673    @property
674    def sharedlinkflags(self):
675        return self._aggregated_list_values("sharedlinkflags")
676
677    @property
678    def exelinkflags(self):
679        return self._aggregated_list_values("exelinkflags")
680
681    @property
682    def objects(self):
683        return self._aggregated_list_values("objects")
684
685    @property
686    def requires(self):
687        return self._aggregated_list_values("requires")
688
689
690class DepsCppInfo(_BaseDepsCppInfo):
691    """ Build Information necessary to build a given conans. It contains the
692    flags, directories and options if its dependencies. The conans CONANFILE
693    should use these flags to pass them to the underlaying build system (Cmake, make),
694    so deps info is managed
695    """
696
697    def __init__(self):
698        super(DepsCppInfo, self).__init__()
699        self._dependencies = OrderedDict()
700        self._configs = {}
701
702    def __getattr__(self, config):
703        return self._configs.setdefault(config, _BaseDepsCppInfo())
704
705    @property
706    def configs(self):
707        return self._configs
708
709    @property
710    def dependencies(self):
711        return self._dependencies.items()
712
713    @property
714    def deps(self):
715        return self._dependencies.keys()
716
717    def __getitem__(self, item):
718        return self._dependencies[item]
719
720    def add(self, pkg_name, cpp_info):
721        assert pkg_name == str(cpp_info), "'{}' != '{}'".format(pkg_name, cpp_info)
722        assert isinstance(cpp_info, (CppInfo, DepCppInfo))
723        self._dependencies[pkg_name] = cpp_info
724        super(DepsCppInfo, self).update(cpp_info)
725        for config, cpp_info in cpp_info.configs.items():
726            self._configs.setdefault(config, _BaseDepsCppInfo()).update(cpp_info)
727