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