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)
Beispiel #2
0
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)