class NewCppInfo(object): def __init__(self): self.components = DefaultOrderedDict(lambda: _NewComponent()) # Main package is a component with None key self.components[None] = _NewComponent() self._aggregated = None # A _NewComponent object with all the components aggregated def __getattr__(self, attr): return getattr(self.components[None], attr) def __setattr__(self, attr, value): if attr == "components": super(NewCppInfo, self).__setattr__(attr, value) else: setattr(self.components[None], attr, value) @property def has_components(self): return len(self.components) > 1 @property def component_names(self): return filter(None, self.components.keys()) def merge(self, other): """Merge 'other' into self. 'other' can be an old cpp_info object Used to merge Layout source + build cpp objects info (editables) :type other: NewCppInfo """ def merge_list(o, d): d.extend(e for e in o if e not in d) for varname in _ALL_NAMES: other_values = getattr(other, varname) if other_values is not None: current_values = self.components[None].get_init(varname, []) merge_list(other_values, current_values) if self.sysroot is None and other.sysroot: self.sysroot = other.sysroot if other.requires: current_values = self.components[None].get_init("requires", []) merge_list(other.requires, current_values) if other._generator_properties: current_values = self.components[None].get_init("_generator_properties", {}) current_values.update(other._generator_properties) # COMPONENTS for cname, c in other.components.items(): if cname is None: continue for varname in _ALL_NAMES: other_values = getattr(c, varname) if other_values is not None: current_values = self.components[cname].get_init(varname, []) merge_list(other_values, current_values) if c.requires: current_values = self.components[cname].get_init("requires", []) merge_list(c.requires, current_values) if c._generator_properties: current_values = self.components[cname].get_init("_generator_properties", {}) current_values.update(c._generator_properties) def set_relative_base_folder(self, folder): """Prepend the folder to all the directories""" for component in self.components.values(): for varname in _DIRS_VAR_NAMES: origin = getattr(component, varname) if origin is not None: origin[:] = [os.path.join(folder, el) for el in origin] if component._generator_properties is not None: updates = {} for prop_name, value in component._generator_properties.items(): if prop_name == "cmake_build_modules": if isinstance(value, list): updates[prop_name] = [os.path.join(folder, v) for v in value] else: updates[prop_name] = os.path.join(folder, value) component._generator_properties.update(updates) def get_sorted_components(self): """Order the components taking into account if they depend on another component in the same package (not scoped with ::). First less dependant return: {component_name: component} """ processed = [] # Names of the components ordered # FIXME: Cache the sort while (len(self.components) - 1) > len(processed): for name, c in self.components.items(): if name is None: continue req_processed = [n for n in c.required_component_names if n not in processed] if not req_processed and name not in processed: processed.append(name) return OrderedDict([(cname, self.components[cname]) for cname in processed]) def aggregated_components(self): """Aggregates all the components as global values, returning a new NewCppInfo""" if self._aggregated is None: if self.has_components: result = _NewComponent() for n in _ALL_NAMES: # Initialize all values, from None => [] setattr(result, n, []) # TODO: This is a bit dirty # Reversed to make more dependant first for name, component in reversed(self.get_sorted_components().items()): for n in _ALL_NAMES: if getattr(component, n): dest = result.get_init(n, []) dest.extend([i for i in getattr(component, n) if i not in dest]) # NOTE: The properties are not aggregated because they might refer only to the # component like "cmake_target_name" describing the target name FOR THE component # not the namespace. if component.requires: current_values = result.get_init("requires", []) current_values.extend(component.requires) # We copy the properties from the root object, even if we have components result._generator_properties = copy.copy(self._generator_properties) # FIXME: What to do about sysroot? else: result = copy.copy(self.components[None]) self._aggregated = NewCppInfo() self._aggregated.components[None] = result return self._aggregated @property def required_components(self): """Returns a list of tuples with (require, component_name) required by the package If the require is internal (to another component), the require will be None""" # FIXME: Cache the value ret = [] for key, comp in self.components.items(): ret.extend([r.split("::") for r in comp.requires if "::" in r and r not in ret]) ret.extend([(None, r) for r in comp.requires if "::" not in r and r not in ret]) return ret def clear_none(self): """A field with None meaning is 'not declared' but for consumers, that is irrelevant, an empty list is easier to handle and makes perfect sense.""" for c in self.components.values(): for varname in _ALL_NAMES: if getattr(c, varname) is None: setattr(c, varname, []) if c.requires is None: c.requires = [] if self.sysroot is None: self.sysroot = "" if self._generator_properties is None: self._generator_properties = {} def __str__(self): ret = [] for cname, c in self.components.items(): for n in _ALL_NAMES: ret.append("Component: '{}' " "Var: '{}' " "Value: '{}'".format(cname, n, getattr(c, n))) return "\n".join(ret)
class NewCppInfo(object): def __init__(self, ): super(NewCppInfo, self).__init__() self.components = DefaultOrderedDict(lambda: _NewComponent()) # Main package is a component with None key self.components[None] = _NewComponent() def __getattr__(self, attr): return getattr(self.components[None], attr) def __setattr__(self, attr, value): if attr in ["components"]: super(NewCppInfo, self).__setattr__(attr, value) else: setattr(self.components[None], attr, value) @property def has_components(self): return len(self.components) > 1 @property def component_names(self): return filter(None, self.components.keys()) def merge(self, other): """Merge 'other' into self. 'other' can be an old cpp_info object""" def merge_list(o, d): for e in o: if e not in d: d.append(e) for varname in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: if getattr(other, varname) is None: continue if getattr(self, varname) is None: setattr(self, varname, []) merge_list(getattr(other, varname), getattr(self, varname)) if self.sysroot is None and other.sysroot: self.sysroot = other.sysroot if other._generator_properties: if not self._generator_properties: self._generator_properties = {} self._generator_properties.update(other._generator_properties) if other.requires: if self.requires is None: self.requires = [] merge_list(other.requires, self.requires) # COMPONENTS for cname, c in other.components.items(): if cname is None: continue for varname in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: if not getattr(c, varname): continue if getattr(self.components[cname], varname) is None: setattr(self.components[cname], varname, []) merge_list(getattr(c, varname), getattr(self.components[cname], varname)) if c.requires: if self.components[cname].requires is None: self.components[cname].requires = [] merge_list(c.requires, self.components[cname].requires) if c._generator_properties: if self.components[cname]._generator_properties is None: self.components[cname]._generator_properties = {} self.components[cname]._generator_properties.update( c._generator_properties) def set_relative_base_folder(self, folder): """Prepend the folder to all the directories""" for cname, c in self.components.items(): for varname in _DIRS_VAR_NAMES: new_list = [] origin = getattr(self.components[cname], varname) if origin is not None: for el in origin: new_list.append(os.path.join(folder, el)) setattr(self.components[cname], varname, new_list) def get_sorted_components(self): """Order the components taking into account if they depend on another component in the same package (not scoped with ::). First less dependant return: {component_name: component} """ processed = [] # Names of the components ordered # FIXME: Cache the sort while (len(self.components) - 1) > len(processed): for name, c in self.components.items(): if name is None: continue req_processed = [ n for n in c.required_component_names if n not in processed ] if not req_processed and name not in processed: processed.append(name) return OrderedDict([(cname, self.components[cname]) for cname in processed]) def aggregate_components(self): """Aggregates all the components as global values""" if self.has_components: components = self.get_sorted_components() cnames = list(components.keys()) cnames.reverse() # More dependant first # Clean global values for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: setattr(self.components[None], n, []) for name in cnames: component = components[name] for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: if getattr(component, n): dest = getattr(self.components[None], n) if dest is None: setattr(self.components[None], n, []) dest += [ i for i in getattr(component, n) if i not in dest ] # NOTE: The properties are not aggregated because they might refer only to the # component like "cmake_target_name" describing the target name FOR THE component # not the namespace. if component.requires: if self.components[None].requires is None: self.components[None].requires = [] self.components[None].requires.extend(component.requires) # FIXME: What to do about sysroot? # Leave only the aggregated value main_value = self.components[None] self.components = DefaultOrderedDict(lambda: _NewComponent()) self.components[None] = main_value def copy(self): ret = NewCppInfo() ret._generator_properties = copy.copy(self._generator_properties) ret.components = DefaultOrderedDict(lambda: _NewComponent()) for comp_name in self.components: ret.components[comp_name] = copy.copy(self.components[comp_name]) return ret @property def required_components(self): """Returns a list of tuples with (require, component_name) required by the package If the require is internal (to another component), the require will be None""" # FIXME: Cache the value ret = [] for key, comp in self.components.items(): ret.extend([ r.split("::") for r in comp.requires if "::" in r and r not in ret ]) ret.extend([(None, r) for r in comp.requires if "::" not in r and r not in ret]) return ret def clear_none(self): """A field with None meaning is 'not declared' but for consumers, that is irrelevant, an empty list is easier to handle and makes perfect sense.""" for c in self.components.values(): for varname in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: if getattr(c, varname) is None: setattr(c, varname, []) if c.requires is None: c.requires = [] if self.sysroot is None: self.sysroot = "" if self._generator_properties is None: self._generator_properties = {} def __str__(self): ret = [] for cname, c in self.components.items(): for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: ret.append("Component: '{}' " "Var: '{}' " "Value: '{}'".format(cname, n, getattr(c, n))) return "\n".join(ret)