<commit_before>"""Generic Node and Tree Objects"""
import uuid
import collections

from compage import formatter, exception

__all__ = ['Node', 'Tree']

class Node(object):
    """The node object, holds parent information"""
    def __init__(self, name, parent=None):
        super(Node, self).__init__()
        self._name = name

        if parent is not None and not isinstance(parent, Node):
            msg = "parent '{0}' should be of type {1} or None".format(
                parent, Node)
            raise exception.NodeCreationError(msg)

        self._parent = parent
        self._nid = uuid.uuid4().hex[:8]

    @property
    def name(self):
        return self._name

    @property
    def parent(self):
        return self._parent

    @property
    def nid(self):
        return self._nid

    @property
    def short_info(self):
        return "{0}({1}|{2})".format(
            self.__class__.__name__,
            self.name,
            self.nid[:5],
        )

    def __eq__(self, other):
        if other is None:
            return False
        return self.nid == other.nid

    def __neq__(self, other):
        return not self == other

    def __repr__(self):
        return "{0}({1}|{2}|parent='{3}|{4}')".format(
            self.__class__.__name__,
            self.name,
            self.nid,
            self.parent.name if self.parent else None,
            self.parent.nid if self.parent else None,
        )

class Tree(object):
    """Provides queries on the given node objects"""
    def __init__(self, nodes):
        super(Tree, self).__init__()
        self.nodes = nodes
        self._num_nodes = len(self.nodes)
        self._root_nodes = self._get_root_nodes()

    @classmethod
    def from_dict(cls, d):
        nodes = cls._create_nodes_from_dict(d)
        return cls(nodes)

    @property
    def root_nodes(self):
        return self._root_nodes

    def get_leaf_nodes(self):
        """Get all leaf nodes i.e, nodes with no children"""
        visited = []
        for node in self.nodes:
            if not [child for child in self.get_children(node)]:
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def walk(self, tree_node):
        """Depth first iterator for the given node"""
        visited = []
        yield tree_node
        for child in self.get_children(tree_node):
            for node in self.walk(child):
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def get_children(self, tree_node):
        """Iterator for immediate children of the given node"""
        visited = []
        for node in self.nodes:
            if node.parent == tree_node:
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def get_lineage(self, tree_node):
        """
        Iterator for all parents of the given node
        starting from immediate parent
        """
        if tree_node.parent is None:
            return

        visited = []
        yield tree_node.parent
        for parent in self.get_lineage(tree_node.parent):
            if self._is_visited(visited, parent):
                continue
            yield parent
            self._add_to_visited(visited, parent)

    def get_hierarchy(self, tree_node):
        """Return heirarchy of the node with the top ancestor to the node"""
        lineage = [l for l in self.get_lineage(tree_node) if l]
        return [node for node in reversed(lineage)] + [tree_node]

    def to_dict(self, repr_as=None):
        """
        Returns the tree as a dictionary

        Args:
            `repr_as` (str, optional):
                For displaying information, this can be set to attribute name
                of the node like - name, nid, short_info, parent etc.
                Default is `Node` objects.

        Returns:
            A tree structured dictionary of the `Node` objects.

        """
        def repr_func(node):
            if repr_as is not None:
                return getattr(node, repr_as)
            else:
                return node
        n_tree = self._nested_tree()
        for node in self.nodes:
            heirarchy = map(repr_func, [n for n in self.get_hierarchy(node)])
            self._add_to_nested_tree(n_tree, heirarchy)
        return self._nested_dict(n_tree)

    def render(self):
        to_render = str(self)
        replacers = (
            ('\": {}', ''), ('\": {', ''), ('"', '\___  '), ('},', ''),
            ('}', ''), (',', ''), ('{', ''),
        )
        for (replace_part, replace_by) in replacers:
            to_render = to_render.replace(replace_part, replace_by)

        return to_render

    def _get_root_nodes(self):
        """Get all root nodes i.e, nodes with `None` as parent"""
        return [node for node in self.nodes if node.parent is None]

    def _is_visited(self, visited, node):
        if node is not None:
            return node.nid in visited
        return False

    def _add_to_visited(self, visited, node):
        if node is not None:
            visited.append(node.nid)

    # From https://gist.github.com/hrldcpr/2012250
    def _nested_tree(self):
        return collections.defaultdict(self._nested_tree)

    def _add_to_nested_tree(self, nested_tree, path):
        for node in path:
            nested_tree = nested_tree[node]

    def _nested_dict(self, nested_tree):
        return {k: self._nested_dict(nested_tree[k]) for k in nested_tree}

    @classmethod
    def _create_nodes_from_dict(cls, d):
        uid_tree, name_map = cls._make_uid_tree(d)
        node_map = {}
        cls._create_nodes_recursively(uid_tree, name_map, node_map)
        return node_map.values()

    @classmethod
    def _make_uid_tree(cls, d):
        uid_tree = {}
        name_map = {}
        cls._recurse_uid_tree(d, uid_tree, name_map)
        return uid_tree, name_map

    @classmethod
    def _recurse_uid_tree(cls, d, uid_tree, name_map):
        for k, v in d.items():
            new_k = uuid.uuid4().hex[:8]
            name_map[new_k] = k
            uid_tree[new_k] = {}
            cls._recurse_uid_tree(v, uid_tree[new_k], name_map)

    @classmethod
    def _create_nodes_recursively(
            cls, uid_dict, name_map, node_map, parent_uid=None):
        for uid in uid_dict:
            if parent_uid is None:
                node_map.setdefault(uid, Node(name=name_map[uid]))
            for child_uid in uid_dict[uid]:
                node_map.setdefault(
                    child_uid,
                    Node(name=name_map[child_uid], parent=node_map[uid]),
                )
            cls._create_nodes_recursively(
                uid_dict[uid], name_map, node_map, parent_uid=uid)

    def __eq__(self, other):
        return self.to_dict(repr_as='nid') == other.to_dict(repr_as='nid')

    def __ne__(self, other):
        return not self == other

    def __repr__(self):
        return formatter.FormattedDict(self.to_dict(repr_as='name')).__repr__()
<commit_msg>Added render() to nodeutil<commit_after>"""Generic Node and Tree Objects"""
import uuid
import collections

from compage import formatter, exception

__all__ = ['Node', 'Tree']

class Node(object):
    """The node object, holds parent information"""
    def __init__(self, name, parent=None):
        super(Node, self).__init__()
        self._name = name

        if parent is not None and not isinstance(parent, Node):
            msg = "parent '{0}' should be of type {1} or None".format(
                parent, Node)
            raise exception.NodeCreationError(msg)

        self._parent = parent
        self._nid = uuid.uuid4().hex[:8]

    @property
    def name(self):
        return self._name

    @property
    def parent(self):
        return self._parent

    @property
    def nid(self):
        return self._nid

    @property
    def short_info(self):
        return "{0}({1}|{2})".format(
            self.__class__.__name__,
            self.name,
            self.nid[:5],
        )

    def __eq__(self, other):
        if other is None:
            return False
        return self.nid == other.nid

    def __neq__(self, other):
        return not self == other

    def __repr__(self):
        return "{0}({1}|{2}|parent='{3}|{4}')".format(
            self.__class__.__name__,
            self.name,
            self.nid,
            self.parent.name if self.parent else None,
            self.parent.nid if self.parent else None,
        )

class Tree(object):
    """Provides queries on the given node objects"""
    def __init__(self, nodes):
        super(Tree, self).__init__()
        self.nodes = nodes
        self._num_nodes = len(self.nodes)
        self._root_nodes = self._get_root_nodes()

        # characters for rendering tree
        self._pipe_char = '|'
        self._node_end_char = '_'
        self._indentation_char = ' '
        self._indent = 4
        self._indentation = self._indent * self._indentation_char
        self._node_char = '{0}{1} '.format(
            self._pipe_char, self._node_end_char * max([1, self._indent - 1]))

    @classmethod
    def from_dict(cls, d):
        nodes = cls._create_nodes_from_dict(d)
        return cls(nodes)

    @property
    def root_nodes(self):
        return self._root_nodes

    def get_leaf_nodes(self):
        """Get all leaf nodes i.e, nodes with no children"""
        visited = []
        for node in self.nodes:
            if not [child for child in self.get_children(node)]:
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def walk(self, tree_node):
        """Depth first iterator for the given node"""
        visited = []
        yield tree_node
        for child in self.get_children(tree_node):
            for node in self.walk(child):
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def walk_with_level(self, tree_node, level=0):
        """Depth first iterator for the given node"""
        visited = []
        yield tree_node, level
        for child in sorted(
                self.get_children(tree_node), key=lambda n: n.name):
            for node, this_level in self.walk_with_level(child, level + 1):
                if self._is_visited(visited, node):
                    continue
                yield node, this_level
                self._add_to_visited(visited, node)

    def get_children(self, tree_node):
        """Iterator for immediate children of the given node"""
        visited = []
        for node in self.nodes:
            if node.parent == tree_node:
                if self._is_visited(visited, node):
                    continue
                yield node
                self._add_to_visited(visited, node)

    def get_lineage(self, tree_node):
        """
        Iterator for all parents of the given node
        starting from immediate parent
        """
        if tree_node.parent is None:
            return

        visited = []
        yield tree_node.parent
        for parent in self.get_lineage(tree_node.parent):
            if self._is_visited(visited, parent):
                continue
            yield parent
            self._add_to_visited(visited, parent)

    def get_hierarchy(self, tree_node):
        """Return heirarchy of the node with the top ancestor to the node"""
        lineage = [l for l in self.get_lineage(tree_node) if l]
        return [node for node in reversed(lineage)] + [tree_node]

    def to_dict(self, repr_as=None):
        """
        Returns the tree as a dictionary

        Args:
            `repr_as` (str, optional):
                For displaying information, this can be set to attribute name
                of the node like - name, nid, short_info, parent etc.
                Default is `Node` objects.

        Returns:
            A tree structured dictionary of the `Node` objects.

        """
        def repr_func(node):
            if repr_as is not None:
                return getattr(node, repr_as)
            else:
                return node
        n_tree = self._nested_tree()
        for node in self.nodes:
            heirarchy = map(repr_func, [n for n in self.get_hierarchy(node)])
            self._add_to_nested_tree(n_tree, heirarchy)
        return self._nested_dict(n_tree)

    def render(self):
        """Returns the tree structure as a string"""
        out = []
        levels = []
        for root_node in sorted(self.root_nodes, key=lambda n: n.name):
            for node, level in self.walk_with_level(root_node):
                for index, (lv, out_string) in enumerate(
                        zip(reversed(levels), reversed(out))):
                    if lv == level - 1:
                        break
                    if lv > level:
                        new_out_string = self._add_level_to_string(
                            level,
                            out_string,
                        )
                        out[len(out) - 1 - index] = new_out_string
                levels.append(level)
                out.append('{0}{1}{2}'.format(
                    self._indentation * level,
                    self._node_char,
                    node.name,
                    ),
                )
        return '\n'.join(self._add_spacing(out))

    def _add_spacing(self, lines):
        out = []
        for index, string in enumerate(lines):
            out.append(string)
            if index + 1 < len(lines):
                spacer = []
                for char in lines[index + 1]:
                    if char != self._pipe_char:
                        spacer.append(self._indentation_char)
                    else:
                        spacer.append(self._pipe_char)
                spacer_string = ''.join(spacer).rstrip(self._indentation_char)
                out.append(spacer_string)
        return out

    def _add_level_to_string(self, level, string):
        string_levels = string.split(self._indentation)
        string_levels[level] = self._pipe_char
        return (self._indentation).join(string_levels)

    def _get_root_nodes(self):
        """Get all root nodes i.e, nodes with `None` as parent"""
        return [node for node in self.nodes if node.parent is None]

    def _is_visited(self, visited, node):
        if node is not None:
            return node.nid in visited
        return False

    def _add_to_visited(self, visited, node):
        if node is not None:
            visited.append(node.nid)

    # From https://gist.github.com/hrldcpr/2012250
    def _nested_tree(self):
        return collections.defaultdict(self._nested_tree)

    def _add_to_nested_tree(self, nested_tree, path):
        for node in path:
            nested_tree = nested_tree[node]

    def _nested_dict(self, nested_tree):
        return {k: self._nested_dict(nested_tree[k]) for k in nested_tree}

    @classmethod
    def _create_nodes_from_dict(cls, d):
        uid_tree, name_map = cls._make_uid_tree(d)
        node_map = {}
        cls._create_nodes_recursively(uid_tree, name_map, node_map)
        return node_map.values()

    @classmethod
    def _make_uid_tree(cls, d):
        uid_tree = {}
        name_map = {}
        cls._recurse_uid_tree(d, uid_tree, name_map)
        return uid_tree, name_map

    @classmethod
    def _recurse_uid_tree(cls, d, uid_tree, name_map):
        for k, v in d.items():
            new_k = uuid.uuid4().hex[:8]
            name_map[new_k] = k
            uid_tree[new_k] = {}
            cls._recurse_uid_tree(v, uid_tree[new_k], name_map)

    @classmethod
    def _create_nodes_recursively(
            cls, uid_dict, name_map, node_map, parent_uid=None):
        for uid in uid_dict:
            if parent_uid is None:
                node_map.setdefault(uid, Node(name=name_map[uid]))
            for child_uid in uid_dict[uid]:
                node_map.setdefault(
                    child_uid,
                    Node(name=name_map[child_uid], parent=node_map[uid]),
                )
            cls._create_nodes_recursively(
                uid_dict[uid], name_map, node_map, parent_uid=uid)

    def __eq__(self, other):
        return self.to_dict(repr_as='nid') == other.to_dict(repr_as='nid')

    def __ne__(self, other):
        return not self == other

    def __repr__(self):
        return formatter.FormattedDict(self.to_dict(repr_as='name')).__repr__()
<|endoftext|>


<commit_before>from __future__ import absolute_import

import os
from subprocess import PIPE, Popen
from collections import namedtuple
from itertools import product

import cpuinfo

from devito.base import *  # noqa
from devito.builtins import *  # noqa
from devito.data.allocators import *  # noqa
from devito.dimension import *  # noqa
from devito.equation import *  # noqa
from devito.grid import *  # noqa
from devito.finite_differences import *  # noqa
from devito.function import Buffer, NODE, CELL  # noqa
from devito.logger import error, warning, info, set_log_level  # noqa
from devito.parameters import *  # noqa
from devito.tools import *  # noqa

from devito.compiler import compiler_registry
from devito.backends import backends_registry, init_backend

from ._version import get_versions  # noqa
__version__ = get_versions()['version']
del get_versions

# Setup compiler and backend
configuration.add('compiler', 'custom', list(compiler_registry),
                  callback=lambda i: compiler_registry[i]())
configuration.add('backend', 'core', list(backends_registry), callback=init_backend)

# Should Devito run a first-touch Operator upon allocating data?
configuration.add('first-touch', 0, [0, 1], lambda i: bool(i), False)

# Should Devito ignore any unknown runtime arguments supplied to Operator.apply(),
# or rather raise an exception (the default behaviour)?
configuration.add('ignore-unknowns', 0, [0, 1], lambda i: bool(i), False)

# Execution mode setup
def _reinit_compiler(val):  # noqa
    # Force re-build the compiler
    configuration['compiler'].__init__(suffix=configuration['compiler'].suffix,
                                       mpi=configuration['mpi'])
    return bool(val)
configuration.add('openmp', 0, [0, 1], callback=_reinit_compiler)  # noqa
configuration.add('mpi', 0, [0, 1], callback=_reinit_compiler)

# Autotuning setup
AT_LEVELs = ['off', 'basic', 'aggressive']
AT_MODEs = ['preemptive', 'runtime']
at_default_mode = {'core': 'preemptive', 'yask': 'runtime', 'ops': 'runtime'}
at_setup = namedtuple('at_setup', 'level mode')
at_accepted = AT_LEVELs + [list(i) for i in product(AT_LEVELs, AT_MODEs)]
def _at_callback(val):  # noqa
    if isinstance(val, str):
        level, mode = val, at_default_mode[configuration['backend']]
    else:
        level, mode = val
    if level == 'off':
        level = False
    return at_setup(level, mode)
configuration.add('autotuning', 'off', at_accepted, callback=_at_callback,  # noqa
                  impacts_jit=False)

# Should Devito emit the JIT compilation commands?
configuration.add('debug-compiler', 0, [0, 1], lambda i: bool(i), False)

# Set the Instruction Set Architecture (ISA)
ISAs = ['cpp', 'avx', 'avx2', 'avx512']
configuration.add('isa', 'cpp', ISAs)

# Set the CPU architecture (only codename)
PLATFORMs = ['intel64', 'snb', 'ivb', 'hsw', 'bdw', 'skx', 'knl']
configuration.add('platform', 'intel64', PLATFORMs)

# (Undocumented) escape hatch for cross-compilation
configuration.add('cross-compile', None)

def infer_cpu():
    """
    Detect the highest Instruction Set Architecture and the platform
    codename using cpu flags and/or leveraging other tools. Return default
    values if the detection procedure was unsuccesful.
    """
    cpu_info = cpuinfo.get_cpu_info()
    # ISA
    isa = configuration._defaults['isa']
    for i in reversed(configuration._accepted['isa']):
        if any(j.startswith(i) for j in cpu_info['flags']):
            # Using `startswith`, rather than `==`, as a flag such as 'avx512'
            # appears as 'avx512f, avx512cd, ...'
            isa = i
            break
    # Platform
    try:
        # First, try leveraging `gcc`
        p1 = Popen(['gcc', '-march=native', '-Q', '--help=target'], stdout=PIPE)
        p2 = Popen(['grep', 'march'], stdin=p1.stdout, stdout=PIPE)
        p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
        output, _ = p2.communicate()
        platform = output.decode("utf-8").split()[1]
        # Full list of possible /platform/ values at this point at:
        # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
        platform = {'sandybridge': 'snb', 'ivybridge': 'ivb', 'haswell': 'hsw',
                    'broadwell': 'bdw', 'skylake': 'skx', 'knl': 'knl'}[platform]
    except:
        # Then, try infer from the brand name, otherwise fallback to default
        try:
            platform = cpu_info['brand'].split()[4]
            platform = {'v2': 'ivb', 'v3': 'hsw', 'v4': 'bdw', 'v5': 'skx'}[platform]
        except:
            platform = None
    # Is it a known platform?
    if platform not in configuration._accepted['platform']:
        platform = configuration._defaults['platform']
    return isa, platform

# In develop-mode:
# - Some optimizations may not be applied to the generated code.
# - The compiler performs more type and value checking
def _switch_cpu(develop_mode):
    if bool(develop_mode) is False:
        isa, platform = infer_cpu()
        configuration['isa'] = os.environ.get('DEVITO_ISA', isa)
        configuration['platform'] = os.environ.get('DEVITO_PLATFORM', platform)
    else:
        configuration['isa'] = 'cpp'
        configuration['platform'] = 'intel64'
configuration.add('develop-mode', True, [False, True], _switch_cpu)  # noqa

# Initialize the configuration, either from the environment or
# defaults. This will also trigger the backend initialization
init_configuration()

# Expose a mechanism to clean up the symbol caches (SymPy's, Devito's)
clear_cache = CacheManager().clear  # noqa

# Helper functions to switch on/off optimisation levels
def mode_develop():
    """Run all future :class:`Operator`s in develop mode. This is the default
    configuration for Devito."""
    configuration['develop-mode'] = True

def mode_performance():
    """Run all future :class:`Operator`s in performance mode. The performance
    mode will also try to allocate any future :class:`TensorFunction` with
    a suitable NUMA strategy."""
    configuration['develop-mode'] = False
    configuration['autotuning'] = ['aggressive',
                                   at_default_mode[configuration['backend']]]

def mode_benchmark():
    """Like ``mode_performance``, but also switch YASK's autotuner mode to
    ``preemptive``."""
    mode_performance()
    configuration['autotuning'] = ['aggressive', 'preemptive']
<commit_msg>autotuning: Add destructive mode<commit_after>from __future__ import absolute_import

import os
from subprocess import PIPE, Popen
from collections import namedtuple
from itertools import product

import cpuinfo

from devito.base import *  # noqa
from devito.builtins import *  # noqa
from devito.data.allocators import *  # noqa
from devito.dimension import *  # noqa
from devito.equation import *  # noqa
from devito.grid import *  # noqa
from devito.finite_differences import *  # noqa
from devito.function import Buffer, NODE, CELL  # noqa
from devito.logger import error, warning, info, set_log_level  # noqa
from devito.parameters import *  # noqa
from devito.tools import *  # noqa

from devito.compiler import compiler_registry
from devito.backends import backends_registry, init_backend

from ._version import get_versions  # noqa
__version__ = get_versions()['version']
del get_versions

# Setup compiler and backend
configuration.add('compiler', 'custom', list(compiler_registry),
                  callback=lambda i: compiler_registry[i]())
configuration.add('backend', 'core', list(backends_registry), callback=init_backend)

# Should Devito run a first-touch Operator upon allocating data?
configuration.add('first-touch', 0, [0, 1], lambda i: bool(i), False)

# Should Devito ignore any unknown runtime arguments supplied to Operator.apply(),
# or rather raise an exception (the default behaviour)?
configuration.add('ignore-unknowns', 0, [0, 1], lambda i: bool(i), False)

# Execution mode setup
def _reinit_compiler(val):  # noqa
    # Force re-build the compiler
    configuration['compiler'].__init__(suffix=configuration['compiler'].suffix,
                                       mpi=configuration['mpi'])
    return bool(val)
configuration.add('openmp', 0, [0, 1], callback=_reinit_compiler)  # noqa
configuration.add('mpi', 0, [0, 1], callback=_reinit_compiler)

# Autotuning setup
AT_LEVELs = ['off', 'basic', 'aggressive']
AT_MODEs = ['preemptive', 'destructive', 'runtime']
at_default_mode = {'core': 'preemptive', 'yask': 'runtime', 'ops': 'runtime'}
at_setup = namedtuple('at_setup', 'level mode')
at_accepted = AT_LEVELs + [list(i) for i in product(AT_LEVELs, AT_MODEs)]
def _at_callback(val):  # noqa
    if isinstance(val, str):
        level, mode = val, at_default_mode[configuration['backend']]
    else:
        level, mode = val
    if level == 'off':
        level = False
    return at_setup(level, mode)
configuration.add('autotuning', 'off', at_accepted, callback=_at_callback,  # noqa
                  impacts_jit=False)

# Should Devito emit the JIT compilation commands?
configuration.add('debug-compiler', 0, [0, 1], lambda i: bool(i), False)

# Set the Instruction Set Architecture (ISA)
ISAs = ['cpp', 'avx', 'avx2', 'avx512']
configuration.add('isa', 'cpp', ISAs)

# Set the CPU architecture (only codename)
PLATFORMs = ['intel64', 'snb', 'ivb', 'hsw', 'bdw', 'skx', 'knl']
configuration.add('platform', 'intel64', PLATFORMs)

# (Undocumented) escape hatch for cross-compilation
configuration.add('cross-compile', None)

def infer_cpu():
    """
    Detect the highest Instruction Set Architecture and the platform
    codename using cpu flags and/or leveraging other tools. Return default
    values if the detection procedure was unsuccesful.
    """
    cpu_info = cpuinfo.get_cpu_info()
    # ISA
    isa = configuration._defaults['isa']
    for i in reversed(configuration._accepted['isa']):
        if any(j.startswith(i) for j in cpu_info['flags']):
            # Using `startswith`, rather than `==`, as a flag such as 'avx512'
            # appears as 'avx512f, avx512cd, ...'
            isa = i
            break
    # Platform
    try:
        # First, try leveraging `gcc`
        p1 = Popen(['gcc', '-march=native', '-Q', '--help=target'], stdout=PIPE)
        p2 = Popen(['grep', 'march'], stdin=p1.stdout, stdout=PIPE)
        p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
        output, _ = p2.communicate()
        platform = output.decode("utf-8").split()[1]
        # Full list of possible /platform/ values at this point at:
        # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
        platform = {'sandybridge': 'snb', 'ivybridge': 'ivb', 'haswell': 'hsw',
                    'broadwell': 'bdw', 'skylake': 'skx', 'knl': 'knl'}[platform]
    except:
        # Then, try infer from the brand name, otherwise fallback to default
        try:
            platform = cpu_info['brand'].split()[4]
            platform = {'v2': 'ivb', 'v3': 'hsw', 'v4': 'bdw', 'v5': 'skx'}[platform]
        except:
            platform = None
    # Is it a known platform?
    if platform not in configuration._accepted['platform']:
        platform = configuration._defaults['platform']
    return isa, platform

# In develop-mode:
# - Some optimizations may not be applied to the generated code.
# - The compiler performs more type and value checking
def _switch_cpu(develop_mode):
    if bool(develop_mode) is False:
        isa, platform = infer_cpu()
        configuration['isa'] = os.environ.get('DEVITO_ISA', isa)
        configuration['platform'] = os.environ.get('DEVITO_PLATFORM', platform)
    else:
        configuration['isa'] = 'cpp'
        configuration['platform'] = 'intel64'
configuration.add('develop-mode', True, [False, True], _switch_cpu)  # noqa

# Initialize the configuration, either from the environment or
# defaults. This will also trigger the backend initialization
init_configuration()

# Expose a mechanism to clean up the symbol caches (SymPy's, Devito's)
clear_cache = CacheManager().clear  # noqa

# Helper functions to switch on/off optimisation levels
def mode_develop():
    """Run all future :class:`Operator`s in develop mode. This is the default
    configuration for Devito."""
    configuration['develop-mode'] = True

def mode_performance():
    """Run all future :class:`Operator`s in performance mode. The performance
    mode will also try to allocate any future :class:`TensorFunction` with
    a suitable NUMA strategy."""
    configuration['develop-mode'] = False
    configuration['autotuning'] = ['aggressive',
                                   at_default_mode[configuration['backend']]]

def mode_benchmark():
    """Like ``mode_performance``, but also switch YASK's autotuner mode to
    ``preemptive``."""
    mode_performance()
    configuration['autotuning'] = ['aggressive', 'preemptive']
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import re
from utils import detect_string, gen_hashcode

IOWAIT_THRESHOLD = 35.0
TOTAL_THRESHOLD = 40.0

def detect_keyword(exp, content, mode=0):
    match = re.findall(exp, content)
    if mode == 1:
        return match if match else []
    return match[0] if match else None

def java_binder(cpu_usage, mainstack, process):
    for exp in [r'\s+at\s+android\.os\.BinderProxy\.transact\(Native Method\)\n\s+(at\s+.+)\n', r'\s+at\s+android\.os\.BinderProxy\.transact\(Binder\.java:\d+\)\n\s+(at\s+.+)\n']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, detail, None
    return None, None, None

def system_busy(cpu_usage, mainstack, process):
    total = detect_keyword(r'(\d+(?:\.\d+)?)\%\s+TOTAL', cpu_usage)
    iowait = detect_keyword(r'(\d+(?:\.\d+)?)\%\s+iowait', cpu_usage)
    if iowait and float(iowait) >= IOWAIT_THRESHOLD:
        return "iowait", "CPU usage: iowait >= 35%", None
    if total and float(total) >= TOTAL_THRESHOLD:
        topprocess = detect_keyword(r'\s+\d+(?:\.\d+)?\%\s+(?:\d+)?/([\w-]+(?:\.[\w-]+)*)', cpu_usage)
        return topprocess, ("CPU usage: TOTAL >= 40%, " + topprocess + " occupied the most."), None
    return None, None, None

def native_binder(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+((?:/.+)+(?:\s+\(.+\))?)', mainstack, 1)
    binder = False
    for detail in details:
        if not binder and 'libbinder.so' in detail:
            binder = True
        if binder and 'libbinder.so' not in detail:
            for key in ['libmedia\.so', 'libtvmanager\.so', 'libaudiomanager\.so', 'libgui\.so', 'libsystem_server\.so', 'libchannelmanager\.so']:
                if detect_keyword((r'%s\s+\(.+\)' %(key)), detail):
                    return key.replace("\\", ""), detail, None
    return None, None, None

def native_lock(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+((?:/.+)+(?:\s+\(.+\))?)', mainstack, 1)
    lock = False
    for detail in details:
        if not lock and 'libc.so (__futex_syscall' in detail:
            lock = True
        if lock and 'libc.so' not in detail:
            return process, detail, None
    return None, None, None

def java_lock(cpu_usage, mainstack, process):
    for exp in [r'\s+-\s+(waiting\s+on\s+<.+>\s+(?:\(a\s+\w+(?:[.$]\w+)+\))?)', r'\s+-\s+(waiting\s+to\s+lock\s+<.+>\s+(?:\(a\s+\w+(?:[.$]\w+)+\))?)']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, re.sub(r'<\w+>', 'addr.', detail), None
    return None, None, None

def mthread_busy(cpu_usage, mainstack, process):
    for exp in [r'\s+(at\s+java\.io\..+)', r'\s+(at\s+android\.database\.sqlite\..+)']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, detail, None
    return None, None, None

def auto_recover(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+(at\s+\w+(?:[.$]\w+)+(?:\(.+\))?)', mainstack, 1)
    if details and "android.os.MessageQueue.nativePollOnce" in details[0]:
        return None, None, "RECOVER"
    return None, None, None

def debug(cpu_usage, mainstack, process):
    detail = detect_keyword(r'.*(SuspendSelfForDebugger).*', mainstack)
    if detail:
        return None, None, "DEBUG"
    return None, None, None

def detect_basic_info(logcat):
    process, cpu_usage, mainstack = None, None, None
    for minor_part in logcat.split('\n\n'):
        if not process:
            process = detect_string(minor_part, r'^Process:\s+(\w+(?:[.$_]\w+)+)')
        if not cpu_usage and detect_string(minor_part, r'(CPU\s+usage\s+from\s+-?\w+\s+to\s+-?\w+)'):
            cpu_usage = minor_part
        if not mainstack and detect_string(minor_part, r'^(\"main\")\s+'):
            mainstack = minor_part
    return process, cpu_usage, mainstack

METHODS = [java_binder, native_lock, java_lock, system_busy, native_binder, mthread_busy, debug, auto_recover]

def app_anr(logcat, headers):
    process, cpu_usage, mainstack = detect_basic_info(logcat)
    if process and cpu_usage and mainstack:
        for method in METHODS:
            proc, detail, tag = method(cpu_usage, mainstack, process)
            print proc, detail, tag
            if tag in ["RECOVER", "DEBUG"]:
                break
            if proc and detail:
                md5 = gen_hashcode({'issue_owner': proc, 'detail': detail})
                return md5, {'issue_owner': proc, 'detail': detail}, None
    return None, None, None
<commit_msg>rm log<commit_after># -*- coding: utf-8 -*-

import re
from utils import detect_string, gen_hashcode

IOWAIT_THRESHOLD = 35.0
TOTAL_THRESHOLD = 40.0

def detect_keyword(exp, content, mode=0):
    match = re.findall(exp, content)
    if mode == 1:
        return match if match else []
    return match[0] if match else None

def java_binder(cpu_usage, mainstack, process):
    for exp in [r'\s+at\s+android\.os\.BinderProxy\.transact\(Native Method\)\n\s+(at\s+.+)\n', r'\s+at\s+android\.os\.BinderProxy\.transact\(Binder\.java:\d+\)\n\s+(at\s+.+)\n']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, detail, None
    return None, None, None

def system_busy(cpu_usage, mainstack, process):
    total = detect_keyword(r'(\d+(?:\.\d+)?)\%\s+TOTAL', cpu_usage)
    iowait = detect_keyword(r'(\d+(?:\.\d+)?)\%\s+iowait', cpu_usage)
    if iowait and float(iowait) >= IOWAIT_THRESHOLD:
        return "iowait", "CPU usage: iowait >= 35%", None
    if total and float(total) >= TOTAL_THRESHOLD:
        topprocess = detect_keyword(r'\s+\d+(?:\.\d+)?\%\s+(?:\d+)?/([\w-]+(?:\.[\w-]+)*)', cpu_usage)
        return topprocess, ("CPU usage: TOTAL >= 40%, " + topprocess + " occupied the most."), None
    return None, None, None

def native_binder(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+((?:/.+)+(?:\s+\(.+\))?)', mainstack, 1)
    binder = False
    for detail in details:
        if not binder and 'libbinder.so' in detail:
            binder = True
        if binder and 'libbinder.so' not in detail:
            for key in ['libmedia\.so', 'libtvmanager\.so', 'libaudiomanager\.so', 'libgui\.so', 'libsystem_server\.so', 'libchannelmanager\.so']:
                if detect_keyword((r'%s\s+\(.+\)' %(key)), detail):
                    return key.replace("\\", ""), detail, None
    return None, None, None

def native_lock(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+((?:/.+)+(?:\s+\(.+\))?)', mainstack, 1)
    lock = False
    for detail in details:
        if not lock and 'libc.so (__futex_syscall' in detail:
            lock = True
        if lock and 'libc.so' not in detail:
            return process, detail, None
    return None, None, None

def java_lock(cpu_usage, mainstack, process):
    for exp in [r'\s+-\s+(waiting\s+on\s+<.+>\s+(?:\(a\s+\w+(?:[.$]\w+)+\))?)', r'\s+-\s+(waiting\s+to\s+lock\s+<.+>\s+(?:\(a\s+\w+(?:[.$]\w+)+\))?)']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, re.sub(r'<\w+>', 'addr.', detail), None
    return None, None, None

def mthread_busy(cpu_usage, mainstack, process):
    for exp in [r'\s+(at\s+java\.io\..+)', r'\s+(at\s+android\.database\.sqlite\..+)']:
        detail = detect_keyword(exp, mainstack)
        if detail:
            return process, detail, None
    return None, None, None

def auto_recover(cpu_usage, mainstack, process):
    details = detect_keyword(r'\s+(at\s+\w+(?:[.$]\w+)+(?:\(.+\))?)', mainstack, 1)
    if details and "android.os.MessageQueue.nativePollOnce" in details[0]:
        return None, None, "RECOVER"
    return None, None, None

def debug(cpu_usage, mainstack, process):
    detail = detect_keyword(r'.*(SuspendSelfForDebugger).*', mainstack)
    if detail:
        return None, None, "DEBUG"
    return None, None, None

def detect_basic_info(logcat):
    process, cpu_usage, mainstack = None, None, None
    for minor_part in logcat.split('\n\n'):
        if not process:
            process = detect_string(minor_part, r'^Process:\s+(\w+(?:[.$_]\w+)+)')
        if not cpu_usage and detect_string(minor_part, r'(CPU\s+usage\s+from\s+-?\w+\s+to\s+-?\w+)'):
            cpu_usage = minor_part
        if not mainstack and detect_string(minor_part, r'^(\"main\")\s+'):
            mainstack = minor_part
    return process, cpu_usage, mainstack

METHODS = [java_binder, native_lock, java_lock, system_busy, native_binder, mthread_busy, debug, auto_recover]

def app_anr(logcat, headers):
    process, cpu_usage, mainstack = detect_basic_info(logcat)
    if process and cpu_usage and mainstack:
        for method in METHODS:
            proc, detail, tag = method(cpu_usage, mainstack, process)
            if tag in ["RECOVER", "DEBUG"]:
                break
            if proc and detail:
                md5 = gen_hashcode({'issue_owner': proc, 'detail': detail})
                return md5, {'issue_owner': proc, 'detail': detail}, None
    return None, None, None
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012  Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from importlib.abc import Finder
from importlib.abc import SourceLoader
import imp
import os
import sys
import xmlparser

class ModuleFinder(Finder):
    def __init__(self, context, prompt):
        self.context = context
        self.prompt = prompt

    def find_module(self, fullname, path=None):
        #print ("looking for", fullname, "in", path)
        # Search only for new nemubot modules (packages init)
        if path is None:
            for mpath in self.context.modules_path:
                #print ("looking for", fullname, "in", mpath)
                if os.path.isfile(mpath + fullname + ".xml"):
                    return ModuleLoader(self.context, self.prompt, fullname,
                                        mpath, mpath + fullname + ".xml")
                elif (os.path.isfile(mpath + fullname + ".py") or
                      os.path.isfile(mpath + fullname + "/__init__.py")):
                    return ModuleLoader(self.context, self.prompt,
                                        fullname, mpath, None)
        #print ("not found")
        return None

class ModuleLoader(SourceLoader):
    def __init__(self, context, prompt, fullname, path, config_path):
        self.context = context
        self.prompt = prompt
        self.name = fullname
        self.config_path = config_path

        if config_path is not None:
            self.config = xmlparser.parse_file(config_path)
            if self.config.hasAttribute("name"):
                self.name = self.config["name"]

        if os.path.isfile(path + fullname + ".py"):
            self.source_path = path + self.name + ".py"
            self.package = False
        elif os.path.isfile(path + fullname + "/__init__.py"):
            self.source_path = path + self.name + "/__init__.py"
            self.package = True
        else:
            raise ImportError

    def get_filename(self, fullname):
        """Return the path to the source file as found by the finder."""
        return self.source_path

    def get_data(self, path):
        """Return the data from path as raw bytes."""
        with open(path, 'rb') as file:
            return file.read()

    def path_mtime(self, path):
        st = os.stat(path)
        return int(st.st_mtime)

    def set_data(self, path, data):
        """Write bytes data to a file."""
        parent, filename = os.path.split(path)
        path_parts = []
        # Figure out what directories are missing.
        while parent and not os.path.isdir(parent):
            parent, part = os.path.split(parent)
            path_parts.append(part)
        # Create needed directories.
        for part in reversed(path_parts):
            parent = os.path.join(parent, part)
            try:
                os.mkdir(parent)
            except FileExistsError:
                # Probably another Python process already created the dir.
                continue
            except PermissionError:
                # If can't get proper access, then just forget about writing
                # the data.
                return
        try:
            with open(path, 'wb') as file:
                file.write(data)
        except (PermissionError, FileExistsError):
            pass

    def get_code(self, fullname):
        return SourceLoader.get_code(self, fullname)

    def get_source(self, fullname):
        return SourceLoader.get_source(self, fullname)

    def is_package(self, fullname):
        return self.package

    def load_module(self, fullname):
        module = self._load_module(fullname, sourceless=True)

        # Remove the module from sys list
        del sys.modules[fullname]

        # If the module was already loaded, then reload it
        if hasattr(module, '__LOADED__'):
            reload(module)

        # Check that is a valid nemubot module
        if not hasattr(module, "nemubotversion"):
            raise ImportError("Module `%s' is not a nemubot module."%self.name)
        # Check module version
        if module.nemubotversion != self.context.version:
            raise ImportError("Module `%s' is not compatible with this "
                              "version." % self.name)

        # Set module common functions and datas
        module.__LOADED__ = True

        # Set module common functions and datas
        module.DEBUG = False
        module.name = fullname
        module.print = lambda msg: print("[%s] %s"%(module.name, msg))
        module.print_debug = lambda msg: mod_print_dbg(module, msg)

        if not hasattr(module, "NODATA"):
            module.DATAS = xmlparser.parse_file(self.context.datas_path
                                                + module.name + ".xml")
            module.save = lambda: mod_save(module, self.context.datas_path)
        else:
            module.DATAS = None
            module.save = lambda: False
        module.CONF = self.config
        module.has_access = lambda msg: mod_has_access(module,
                                                       module.CONF, msg)

        # Load dependancies
        if module.CONF is not None and module.CONF.hasNode("dependson"):
            module.MODS = dict()
            for depend in module.CONF.getNodes("dependson"):
                for md in MODS:
                    if md.name == depend["name"]:
                        mod.MODS[md.name] = md
                        break
                if depend["name"] not in module.MODS:
                    print ("\033[1;31mERROR:\033[0m in module `%s', module "
                           "`%s' require by this module but is not loaded."
                           % (module.name, depend["name"]))
                    return

        # Add the module to the global modules list
        if self.context.add_module(module):

            # Launch the module
            if hasattr(module, "load"):
                module.load()

            # Register hooks
            register_hooks(module, self.context, self.prompt)

            print ("  Module `%s' successfully loaded." % module.name)
        else:
            raise ImportError("An error occurs while importing `%s'."
                              % module.name)
        return module

def add_cap_hook(prompt, module, cmd):
    if hasattr(module, cmd["call"]):
        prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"]))
    else:
        print ("Warning: In module `%s', no function `%s' defined for `%s' "
               "command hook." % (module.name, cmd["call"], cmd["name"]))

def register_hooks(module, context, prompt):
    """Register all available hooks"""
    if module.CONF is not None:
        # Register command hooks
        if module.CONF.hasNode("command"):
            for cmd in module.CONF.getNodes("command"):
                if cmd.hasAttribute("name") and cmd.hasAttribute("call"):
                    add_cap_hook(prompt, module, cmd)

        # Register message hooks
        if module.CONF.hasNode("message"):
            for msg in module.CONF.getNodes("message"):
                context.hooks.register_hook(module, msg)

##########################
#                        #
#    Module functions    #
#                        #
##########################

def mod_print_dbg(mod, msg):
    if mod.DEBUG:
        print("{%s} %s"%(mod.name, msg))

def mod_save(mod, datas_path):
    mod.DATAS.save(datas_path + "/" + mod.name + ".xml")
    mod.print ("Saving!")

def mod_has_access(mod, config, msg):
    if config is not None and config.hasNode("channel"):
        for chan in config.getNodes("channel"):
            if (chan["server"] is None or chan["server"] == msg.srv.id) and (
                chan["channel"] is None or chan["channel"] == msg.channel):
                return True
        return False
    else:
        return True
<commit_msg>Pass context as argument to load for modules<commit_after># -*- coding: utf-8 -*-

# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012  Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from importlib.abc import Finder
from importlib.abc import SourceLoader
import imp
import os
import sys
import xmlparser

class ModuleFinder(Finder):
    def __init__(self, context, prompt):
        self.context = context
        self.prompt = prompt

    def find_module(self, fullname, path=None):
        #print ("looking for", fullname, "in", path)
        # Search only for new nemubot modules (packages init)
        if path is None:
            for mpath in self.context.modules_path:
                #print ("looking for", fullname, "in", mpath)
                if os.path.isfile(mpath + fullname + ".xml"):
                    return ModuleLoader(self.context, self.prompt, fullname,
                                        mpath, mpath + fullname + ".xml")
                elif (os.path.isfile(mpath + fullname + ".py") or
                      os.path.isfile(mpath + fullname + "/__init__.py")):
                    return ModuleLoader(self.context, self.prompt,
                                        fullname, mpath, None)
        #print ("not found")
        return None

class ModuleLoader(SourceLoader):
    def __init__(self, context, prompt, fullname, path, config_path):
        self.context = context
        self.prompt = prompt
        self.name = fullname
        self.config_path = config_path

        if config_path is not None:
            self.config = xmlparser.parse_file(config_path)
            if self.config.hasAttribute("name"):
                self.name = self.config["name"]

        if os.path.isfile(path + fullname + ".py"):
            self.source_path = path + self.name + ".py"
            self.package = False
        elif os.path.isfile(path + fullname + "/__init__.py"):
            self.source_path = path + self.name + "/__init__.py"
            self.package = True
        else:
            raise ImportError

    def get_filename(self, fullname):
        """Return the path to the source file as found by the finder."""
        return self.source_path

    def get_data(self, path):
        """Return the data from path as raw bytes."""
        with open(path, 'rb') as file:
            return file.read()

    def path_mtime(self, path):
        st = os.stat(path)
        return int(st.st_mtime)

    def set_data(self, path, data):
        """Write bytes data to a file."""
        parent, filename = os.path.split(path)
        path_parts = []
        # Figure out what directories are missing.
        while parent and not os.path.isdir(parent):
            parent, part = os.path.split(parent)
            path_parts.append(part)
        # Create needed directories.
        for part in reversed(path_parts):
            parent = os.path.join(parent, part)
            try:
                os.mkdir(parent)
            except FileExistsError:
                # Probably another Python process already created the dir.
                continue
            except PermissionError:
                # If can't get proper access, then just forget about writing
                # the data.
                return
        try:
            with open(path, 'wb') as file:
                file.write(data)
        except (PermissionError, FileExistsError):
            pass

    def get_code(self, fullname):
        return SourceLoader.get_code(self, fullname)

    def get_source(self, fullname):
        return SourceLoader.get_source(self, fullname)

    def is_package(self, fullname):
        return self.package

    def load_module(self, fullname):
        module = self._load_module(fullname, sourceless=True)

        # Remove the module from sys list
        del sys.modules[fullname]

        # If the module was already loaded, then reload it
        if hasattr(module, '__LOADED__'):
            reload(module)

        # Check that is a valid nemubot module
        if not hasattr(module, "nemubotversion"):
            raise ImportError("Module `%s' is not a nemubot module."%self.name)
        # Check module version
        if module.nemubotversion != self.context.version:
            raise ImportError("Module `%s' is not compatible with this "
                              "version." % self.name)

        # Set module common functions and datas
        module.__LOADED__ = True

        # Set module common functions and datas
        module.DEBUG = False
        module.name = fullname
        module.print = lambda msg: print("[%s] %s"%(module.name, msg))
        module.print_debug = lambda msg: mod_print_dbg(module, msg)

        if not hasattr(module, "NODATA"):
            module.DATAS = xmlparser.parse_file(self.context.datas_path
                                                + module.name + ".xml")
            module.save = lambda: mod_save(module, self.context.datas_path)
        else:
            module.DATAS = None
            module.save = lambda: False
        module.CONF = self.config
        module.has_access = lambda msg: mod_has_access(module,
                                                       module.CONF, msg)

        # Load dependancies
        if module.CONF is not None and module.CONF.hasNode("dependson"):
            module.MODS = dict()
            for depend in module.CONF.getNodes("dependson"):
                for md in MODS:
                    if md.name == depend["name"]:
                        mod.MODS[md.name] = md
                        break
                if depend["name"] not in module.MODS:
                    print ("\033[1;31mERROR:\033[0m in module `%s', module "
                           "`%s' require by this module but is not loaded."
                           % (module.name, depend["name"]))
                    return

        # Add the module to the global modules list
        if self.context.add_module(module):

            # Launch the module
            if hasattr(module, "load"):
                module.load(self.context)

            # Register hooks
            register_hooks(module, self.context, self.prompt)

            print ("  Module `%s' successfully loaded." % module.name)
        else:
            raise ImportError("An error occurs while importing `%s'."
                              % module.name)
        return module

def add_cap_hook(prompt, module, cmd):
    if hasattr(module, cmd["call"]):
        prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"]))
    else:
        print ("Warning: In module `%s', no function `%s' defined for `%s' "
               "command hook." % (module.name, cmd["call"], cmd["name"]))

def register_hooks(module, context, prompt):
    """Register all available hooks"""
    if module.CONF is not None:
        # Register command hooks
        if module.CONF.hasNode("command"):
            for cmd in module.CONF.getNodes("command"):
                if cmd.hasAttribute("name") and cmd.hasAttribute("call"):
                    add_cap_hook(prompt, module, cmd)

        # Register message hooks
        if module.CONF.hasNode("message"):
            for msg in module.CONF.getNodes("message"):
                context.hooks.register_hook(module, msg)

##########################
#                        #
#    Module functions    #
#                        #
##########################

def mod_print_dbg(mod, msg):
    if mod.DEBUG:
        print("{%s} %s"%(mod.name, msg))

def mod_save(mod, datas_path):
    mod.DATAS.save(datas_path + "/" + mod.name + ".xml")
    mod.print ("Saving!")

def mod_has_access(mod, config, msg):
    if config is not None and config.hasNode("channel"):
        for chan in config.getNodes("channel"):
            if (chan["server"] is None or chan["server"] == msg.srv.id) and (
                chan["channel"] is None or chan["channel"] == msg.channel):
                return True
        return False
    else:
        return True
<|endoftext|>


<commit_before>#!/usr/bin/env python

import os
import sys
import stat
import errno
import logging
import StringIO

try:
    import _find_fuse_parts
except ImportError:
    pass
import fuse

from linode import Api

fuse.fuse_python_api = (0, 2)

write_cache = {}

class LinodeFSStat(fuse.Stat):

    def __init__(self):
        self.st_mode = 0
        self.st_ino = 0
        self.st_dev = 0
        self.st_nlink = 0
        self.st_uid = 0
        self.st_gid = 0
        self.st_size = 0
        self.st_atime = 0
        self.st_mtime = 0
        self.st_ctime = 0

class LinodeFS(fuse.Fuse):
    _api = None
    _objects_to_create = []
    _linodes = []

    def __init__(self, *args, **kwargs):
        fuse.Fuse.__init__(self, *args, **kwargs)

        logging.basicConfig(filename='linodefs.log', level=logging.DEBUG)
        logging.debug("Starting LinodeFS")

    def make_connection(self):
        if hasattr(self, 'api_url'):
            API.endpoint = self.api_url

        self._api = Api(self.api_key)

    def get_cached_linodes(self):
        if not self._linodes:
            self._linodes = self.api_handle.linode.list()

        return self._linodes

    def get_linode_by_name(self, name):
        linodes = self.get_cached_linodes()
        return next(linode for linode in linodes if linode['LABEL']==name)

    @property
    def api_handle(self):
        if not self._api:
            self.make_connection

        return self._api

    def _read_linode_names(self):
        linodes = self.get_cached_linodes()
        logging.debug("%s" % linodes)

        return [linode['LABEL'] for linode in linodes]

    def _get_object(self, path_tokens):
        """Return an object instance from path_tokens (i.e. result
        of path.split('/') or None if object doesn't exist"""
        linode_name, object_name = path_tokens[1], path_tokens[2]
        try:
            linode = self.get_linode_by_name(linode_name)
            return linode[object_name]
        except (ContainerDoesNotExistError, ObjectDoesNotExistError):
            return None

    def getattr(self, path):
        logging.debug("getattr(path='%s')" % path)

        st = LinodeFSStats()

        if path == '/':
            st.st_mode = stat.S_IFDIR | 0755
            st.st_nlink = 2
            return st
        elif path in self._objects_to_create:
            logging.debug("getattr(path='%s'): file is scheduled for creation" % (path))
            st.st_mode = stat.S_IFREG | 0644
            st.st_nlink = 1
            st.st_size = 0
            return st

        path_tokens = path.split('/')

        if 2 == len(path_tokens):
            linode_names = self._read_linode_names()

            if path_tokens[1] in linode_names:
                st.st_mode = stat.S_IFDIR | 0755
                st.st_nlink = 2
                return st
            else:
                return -errno.ENOENT
        elif 3 == len(path_tokens):
            obj = self._get_object(path_tokens)

            if obj:
                st.st_mode = stat.S_IFREG | 0444
                st.st_nlink = 1
                st.st_size = obj.size
            else:
                # getattr() might be called for a new file which doesn't
                # exist yet, so we need to make it writable in such case
                #st.st_mode = stat.S_IFREG | 0644
                #st.st_nlink = 1
                #st.st_size = 0
                return -errno.ENOENT
            return st

        return -errno.ENOENT

    def readdir(self, path, offset):
        logging.debug("readdir(path='%s', offset='%s')" % (path, offset))

        if "/" == path:
            try:
                linode_names = self._read_linode_names()

                logging.debug("linode names = %s" % linode_names)
                dirs = [".", ".."] + linode_names

                logging.debug("dirs = %s" % dirs)

                for r in  dirs:
                    logging.debug("yielding %s" % r)
                    yield fuse.Direntry(r)
                #return dirs
            except Exception:
                logging.exception("exception in readdir()")
        else:
            path_tokens = path.split("/")

            if 2 != len(path_tokens):
                # we should only have 1 level depth
                logging.warning("Path '%s' is deeper than it should" % path)
                return

            try:
                linode_name = path_tokens[1]
                linode = self.get_linode_by_name(linode_name)
                dirs = [".", "..","info"] +  [str('disk'+obj['DISKID']) for disk in
                        self.api_handle.linode.disk.list({linodeid:linode['LINODEID']})]

                logging.debug("dirs = %s" % dirs)

                for r in dirs:
                    yield fuse.Direntry(r)
            except Exception:
                logging.exception("exception while trying to list container objects")

    def mkdir(self, path, mode):
        logging.debug("mkdir(path='%s', mode='%s')" % (path, mode))

        path_tokens = path.split('/')
        if 2 != len(path_tokens):
            logging.warning("attempting to create a non-container dir %s" % path)
            return -errno.EOPNOTSUPP

        linode_name = path_tokens[1]

        self.api_handle.linode.create(container_name)

        return 0

    def rmdir(self, path):
        logging.debug("rmdir(path='%s')" % (path,))

        path_tokens = path.split('/')

        if 1 == len(path_tokens):
            return -errno.EPERM
        elif 2 == len(path_tokens):
            container_name = path_tokens[1]

            try:
                container = self.api_handle.get_container(container_name)
            except ContainerDoesNotExistError:
                return -errno.ENOENT

            if 0 != len(container.list_objects()):
                return -errno.ENOTEMPTY

            container.delete()

            return 0
        elif 3 <= len(path_tokens):
            return -errno.EOPNOTSUPP

    def mknod(self, path, mode, dev):
        logging.debug("mknod(path='%s', mode='%s', dev='%s')" % (path, mode, dev))

        try:
            path_tokens = path.split('/')
            if 3 != len(path_tokens):
                return -errno.EPERM

            container_name = path_tokens[1]
            object_name = path_tokens[2]

            self.api_handle.upload_object_via_stream(StringIO.StringIO('\n'),
                    self.api_handle.get_container(container_name),
                    object_name,
                    extra={"content_type": "application/octet-stream"})
            return 0
        except Exception:
            logging.exception("exception in mknod()")

    def open(self, path, flags):
        logging.debug("open(path='%s', flags='%s')" % (path, flags))
        return 0
        path_tokens = path.split('/')

        if 3 != len(path_tokens):
            logging.warning("path_tokens != 3")
            return -errno.EOPNOTSUPP

        try:
#            obj = self._get_object(path_tokens)
#            # we allow opening existing files in read-only mode
#            if obj:
#                accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
#                if (flags & accmode) != os.O_RDONLY:
#                    return -errno.EACCES
            return 0
        except Exception:
            logging.exception("exception in open()")

    def read(self, path, size, offset):
        logging.debug("read(path='%s', size=%s, offset=%s)" % (path, size, offset))

        path_tokens = path.split('/')
        if 3 != len(path_tokens):
            return -errno.EOPNOTSUPP

        container_name, object_name = path_tokens[1], path_tokens[2]
        try:
            container = self.api_handle.get_container(container_name)
            obj = container.get_object(object_name)
        except (ContainerDoesNotExistError, ObjectDoesNotExistError):
            return -errno.ENOENT

        try:
            content = ''.join([line for line in obj.as_stream()])
        except:
            logging.exception("error reading file content")
            return

        slen = len(content)
        if offset < slen:
            if offset + size > slen:
                size = slen - offset
            response = content[offset:offset+size]
        else:
            response = ''
        return response

    def write(self, path, buff, offset):
        logging.debug("write(path='%s', buff=<skip>, offset='%s')" % (path, offset))

        try:
            if path not in write_cache:
                write_cache[path] = [buff,]
            else:
                write_cache.append(buff)

            return len(buff)
        except Exception:
            logging.exception("exception in write()")

    def unlink(self, path):
        logging.debug("unlink(path='%s')" % (path,))

        try:
            path_tokens = path.split('/')
            if 3 != len(path_tokens):
                return

            obj = self._get_object(path_tokens)
            if not obj:
                return -errno.ENOENT

            obj.delete()
            return 0
        except Exception:
            logging.exception("error while processing unlink()")

    def release(self, path, flags):
        logging.debug("release(path='%s', flags='%s')" % (path, flags))

        # XXX: what's the nature of this?
        if "-" == path:
            return 0

        try:
            path_tokens = path.split("/")
            container_name, object_name = path_tokens[1], path_tokens[2]

            if len(write_cache[path]) > 0:
                self.unlink(path)
                self.api_handle.upload_object_via_stream(StringIO.StringIO(''.join(write_cache[path])),
                        self.api_handle.get_container(container_name),
                        object_name,
                        extra={"content_type": "application/octet-stream"})
                del write_cache[path]
            return 0
        except KeyError:
            logging.warning("no cached entry for path: %s" % path)
            return 0
        except Exception:
            logging.exception("exception in release()")

    def truncate(self, path, size):
        return 0

    def utime(self, path, times):
        return 0

    def fsync(self, path, isfsyncfile):
        return 0

def main():
    usage="""
LinodeFS

""" + fuse.Fuse.fusage
    server = LinodeFS(version="%prog " + fuse.__version__,
                     usage=usage,
                     dash_s_do='setsingle')

    server.parser.add_option(mountopt='api_key', metavar='API_KEY',
            help=("API Key"))
    server.parser.add_option(mountopt='api_url', metavar='API_URL',
            help=("API URL"))
    server.parse(values=server, errex=1)

    if not (hasattr(server, 'api_key')):
        print >>sys.stderr, "Please specify an API Key."
        sys.exit(1)

    try:
        server.make_connection()
    except Exception, err:
        print >>sys.stderr, "Cannot connect to Linode API: %s" % str(err)
        sys.exit(1)

    server.main()

if __name__ == '__main__':
    main()
<commit_msg>we have no container.get_object, use _get_object<commit_after>#!/usr/bin/env python

import os
import sys
import stat
import errno
import logging
import StringIO

try:
    import _find_fuse_parts
except ImportError:
    pass
import fuse

from linode import Api

fuse.fuse_python_api = (0, 2)

write_cache = {}

class LinodeFSStat(fuse.Stat):

    def __init__(self):
        self.st_mode = 0
        self.st_ino = 0
        self.st_dev = 0
        self.st_nlink = 0
        self.st_uid = 0
        self.st_gid = 0
        self.st_size = 0
        self.st_atime = 0
        self.st_mtime = 0
        self.st_ctime = 0

class LinodeFS(fuse.Fuse):
    _api = None
    _objects_to_create = []
    _linodes = []

    def __init__(self, *args, **kwargs):
        fuse.Fuse.__init__(self, *args, **kwargs)

        logging.basicConfig(filename='linodefs.log', level=logging.DEBUG)
        logging.debug("Starting LinodeFS")

    def make_connection(self):
        if hasattr(self, 'api_url'):
            API.endpoint = self.api_url

        self._api = Api(self.api_key)

    def get_cached_linodes(self):
        if not self._linodes:
            self._linodes = self.api_handle.linode.list()

        return self._linodes

    def get_linode_by_name(self, name):
        linodes = self.get_cached_linodes()
        return next(linode for linode in linodes if linode['LABEL']==name)

    @property
    def api_handle(self):
        if not self._api:
            self.make_connection

        return self._api

    def _read_linode_names(self):
        linodes = self.get_cached_linodes()
        logging.debug("%s" % linodes)

        return [linode['LABEL'] for linode in linodes]

    def _get_object(self, path_tokens):
        """Return an object instance from path_tokens (i.e. result
        of path.split('/') or None if object doesn't exist"""
        linode_name, object_name = path_tokens[1], path_tokens[2]
        try:
            linode = self.get_linode_by_name(linode_name)
            return linode[object_name]
        except (ContainerDoesNotExistError, ObjectDoesNotExistError):
            return None

    def getattr(self, path):
        logging.debug("getattr(path='%s')" % path)

        st = LinodeFSStats()

        if path == '/':
            st.st_mode = stat.S_IFDIR | 0755
            st.st_nlink = 2
            return st
        elif path in self._objects_to_create:
            logging.debug("getattr(path='%s'): file is scheduled for creation" % (path))
            st.st_mode = stat.S_IFREG | 0644
            st.st_nlink = 1
            st.st_size = 0
            return st

        path_tokens = path.split('/')

        if 2 == len(path_tokens):
            linode_names = self._read_linode_names()

            if path_tokens[1] in linode_names:
                st.st_mode = stat.S_IFDIR | 0755
                st.st_nlink = 2
                return st
            else:
                return -errno.ENOENT
        elif 3 == len(path_tokens):
            obj = self._get_object(path_tokens)

            if obj:
                st.st_mode = stat.S_IFREG | 0444
                st.st_nlink = 1
                st.st_size = obj.size
            else:
                # getattr() might be called for a new file which doesn't
                # exist yet, so we need to make it writable in such case
                #st.st_mode = stat.S_IFREG | 0644
                #st.st_nlink = 1
                #st.st_size = 0
                return -errno.ENOENT
            return st

        return -errno.ENOENT

    def readdir(self, path, offset):
        logging.debug("readdir(path='%s', offset='%s')" % (path, offset))

        if "/" == path:
            try:
                linode_names = self._read_linode_names()

                logging.debug("linode names = %s" % linode_names)
                dirs = [".", ".."] + linode_names

                logging.debug("dirs = %s" % dirs)

                for r in  dirs:
                    logging.debug("yielding %s" % r)
                    yield fuse.Direntry(r)
                #return dirs
            except Exception:
                logging.exception("exception in readdir()")
        else:
            path_tokens = path.split("/")

            if 2 != len(path_tokens):
                # we should only have 1 level depth
                logging.warning("Path '%s' is deeper than it should" % path)
                return

            try:
                linode_name = path_tokens[1]
                linode = self.get_linode_by_name(linode_name)
                dirs = [".", "..","info"] +  [str('disk'+obj['DISKID']) for disk in
                        self.api_handle.linode.disk.list({linodeid:linode['LINODEID']})]

                logging.debug("dirs = %s" % dirs)

                for r in dirs:
                    yield fuse.Direntry(r)
            except Exception:
                logging.exception("exception while trying to list container objects")

    def mkdir(self, path, mode):
        logging.debug("mkdir(path='%s', mode='%s')" % (path, mode))

        path_tokens = path.split('/')
        if 2 != len(path_tokens):
            logging.warning("attempting to create a non-container dir %s" % path)
            return -errno.EOPNOTSUPP

        linode_name = path_tokens[1]

        self.api_handle.linode.create(container_name)

        return 0

    def rmdir(self, path):
        logging.debug("rmdir(path='%s')" % (path,))

        path_tokens = path.split('/')

        if 1 == len(path_tokens):
            return -errno.EPERM
        elif 2 == len(path_tokens):
            container_name = path_tokens[1]

            try:
                container = self.api_handle.get_container(container_name)
            except ContainerDoesNotExistError:
                return -errno.ENOENT

            if 0 != len(container.list_objects()):
                return -errno.ENOTEMPTY

            container.delete()

            return 0
        elif 3 <= len(path_tokens):
            return -errno.EOPNOTSUPP

    def mknod(self, path, mode, dev):
        logging.debug("mknod(path='%s', mode='%s', dev='%s')" % (path, mode, dev))

        try:
            path_tokens = path.split('/')
            if 3 != len(path_tokens):
                return -errno.EPERM

            container_name = path_tokens[1]
            object_name = path_tokens[2]

            self.api_handle.upload_object_via_stream(StringIO.StringIO('\n'),
                    self.api_handle.get_container(container_name),
                    object_name,
                    extra={"content_type": "application/octet-stream"})
            return 0
        except Exception:
            logging.exception("exception in mknod()")

    def open(self, path, flags):
        logging.debug("open(path='%s', flags='%s')" % (path, flags))
        return 0
        path_tokens = path.split('/')

        if 3 != len(path_tokens):
            logging.warning("path_tokens != 3")
            return -errno.EOPNOTSUPP

        try:
#            obj = self._get_object(path_tokens)
#            # we allow opening existing files in read-only mode
#            if obj:
#                accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
#                if (flags & accmode) != os.O_RDONLY:
#                    return -errno.EACCES
            return 0
        except Exception:
            logging.exception("exception in open()")

    def read(self, path, size, offset):
        logging.debug("read(path='%s', size=%s, offset=%s)" % (path, size, offset))

        path_tokens = path.split('/')
        if 3 != len(path_tokens):
            return -errno.EOPNOTSUPP

        try:
            obj = self._get_object(path_tokens)
        except (ContainerDoesNotExistError, ObjectDoesNotExistError):
            return -errno.ENOENT

        try:
            content = ''.join([line for line in obj.as_stream()])
        except:
            logging.exception("error reading file content")
            return

        slen = len(content)
        if offset < slen:
            if offset + size > slen:
                size = slen - offset
            response = content[offset:offset+size]
        else:
            response = ''
        return response

    def write(self, path, buff, offset):
        logging.debug("write(path='%s', buff=<skip>, offset='%s')" % (path, offset))

        try:
            if path not in write_cache:
                write_cache[path] = [buff,]
            else:
                write_cache.append(buff)

            return len(buff)
        except Exception:
            logging.exception("exception in write()")

    def unlink(self, path):
        logging.debug("unlink(path='%s')" % (path,))

        try:
            path_tokens = path.split('/')
            if 3 != len(path_tokens):
                return

            obj = self._get_object(path_tokens)
            if not obj:
                return -errno.ENOENT

            obj.delete()
            return 0
        except Exception:
            logging.exception("error while processing unlink()")

    def release(self, path, flags):
        logging.debug("release(path='%s', flags='%s')" % (path, flags))

        # XXX: what's the nature of this?
        if "-" == path:
            return 0

        try:
            path_tokens = path.split("/")
            container_name, object_name = path_tokens[1], path_tokens[2]

            if len(write_cache[path]) > 0:
                self.unlink(path)
                self.api_handle.upload_object_via_stream(StringIO.StringIO(''.join(write_cache[path])),
                        self.api_handle.get_container(container_name),
                        object_name,
                        extra={"content_type": "application/octet-stream"})
                del write_cache[path]
            return 0
        except KeyError:
            logging.warning("no cached entry for path: %s" % path)
            return 0
        except Exception:
            logging.exception("exception in release()")

    def truncate(self, path, size):
        return 0

    def utime(self, path, times):
        return 0

    def fsync(self, path, isfsyncfile):
        return 0

def main():
    usage="""
LinodeFS

""" + fuse.Fuse.fusage
    server = LinodeFS(version="%prog " + fuse.__version__,
                     usage=usage,
                     dash_s_do='setsingle')

    server.parser.add_option(mountopt='api_key', metavar='API_KEY',
            help=("API Key"))
    server.parser.add_option(mountopt='api_url', metavar='API_URL',
            help=("API URL"))
    server.parse(values=server, errex=1)

    if not (hasattr(server, 'api_key')):
        print >>sys.stderr, "Please specify an API Key."
        sys.exit(1)

    try:
        server.make_connection()
    except Exception, err:
        print >>sys.stderr, "Cannot connect to Linode API: %s" % str(err)
        sys.exit(1)

    server.main()

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before># Copyright 2020, 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This program configures and builds Emacs.

It’s an internal implementation detail of the Bazel rules for Emacs Lisp; don’t
use it outside the rules or depend on its behavior."""

import argparse
import os
import pathlib
import shlex
import shutil
import subprocess
import tempfile

def main() -> None:
    """Configures and builds Emacs."""
    parser = argparse.ArgumentParser(allow_abbrev=False)
    parser.add_argument('--source', type=pathlib.Path, required=True)
    parser.add_argument('--install', type=pathlib.Path, required=True)
    parser.add_argument('--cc', type=pathlib.Path, required=True)
    parser.add_argument('--cflags', required=True)
    parser.add_argument('--ldflags', required=True)
    parser.add_argument('--module-header', type=pathlib.Path)
    args = parser.parse_args()
    windows = os.name == 'nt'
    if windows:
        bash = _find_bash(args.cc)
    with tempfile.TemporaryDirectory() as temp:
        temp = pathlib.Path(temp)
        build = temp / 'build'
        install = args.install.resolve()
        shutil.copytree(args.source, build)

        def run(*command: str) -> None:
            env = None
            if windows:
                # Building Emacs requires MinGW, see nt/INSTALL.W64.  Therefore
                # we invoke commands through the MinGW shell, see
                # https://www.msys2.org/wiki/Launchers/#the-idea.
                env = dict(os.environ, MSYSTEM='MINGW64', CHERE_INVOKING='1')
                command = [bash, '-l', '-c', shlex.join(command)]
            try:
                subprocess.run(command, check=True, cwd=build, env=env,
                               stdin=subprocess.DEVNULL,
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                               encoding='utf-8', errors='backslashescape')
            except subprocess.CalledProcessError as ex:
                print('command', ' '.join(map(shlex.quote, command)),
                      'failed, output follows:')
                print(ex.stdout)
                print()
                try:
                    config_log = build / 'config.log'
                    content = config_log.read_text('utf-8', 'backslashescape')
                    print('config.log follows:')
                    print(content)
                except FileNotFoundError:
                    print('config.log not found')
                raise

        run('./configure', '--prefix=' + str(install),
            '--without-all', '--without-ns', '--with-x-toolkit=no',
            # Enable threads explicitly to work around
            # https://debbugs.gnu.org/cgi/bugreport.cgi?bug=30106 in older
            # Emacs versions.
            '--with-modules', '--with-threads',
            '--disable-build-details',
            'CC=' + str(args.cc.resolve()),
            'CFLAGS=' + args.cflags,
            'LDFLAGS=' + args.ldflags)
        run('make', 'install')
    # Delete source files that have a corresponding compiled file, as these
    # files don’t work well with Coverage (see
    # e.g. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=40766).
    for compiled in install.glob('share/emacs/*/lisp/**/*.elc'):
        compiled.with_suffix('.el').unlink()
    # Sanity check to verify that the resulting binary works.
    subprocess.run([install / 'bin' / 'emacs', '--quick', '--batch'],
                   check=True, stdin=subprocess.DEVNULL)
    if args.module_header:
        # Copy emacs-module.h to the desired location.
        shutil.copy(install / 'include' / 'emacs-module.h', args.module_header)

def _find_bash(c_compiler: pathlib.Path) -> pathlib.Path:
    if c_compiler.parts[-2:] != ('mingw64', 'bin'):
        raise ValueError(f'unsupported C compiler location {c_compiler}')
    msys = c_compiler.parents[2]
    bash = msys / 'bin' / 'bash.exe'
    if not bash.is_file():
        raise FileNotFoundError(f'no Bash program found in {msys}')
    return bash

if __name__ == '__main__':
    main()
<commit_msg>Convert installation directory to POSIX format for “configure”.<commit_after># Copyright 2020, 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This program configures and builds Emacs.

It’s an internal implementation detail of the Bazel rules for Emacs Lisp; don’t
use it outside the rules or depend on its behavior."""

import argparse
import os
import pathlib
import shlex
import shutil
import subprocess
import tempfile

def main() -> None:
    """Configures and builds Emacs."""
    parser = argparse.ArgumentParser(allow_abbrev=False)
    parser.add_argument('--source', type=pathlib.Path, required=True)
    parser.add_argument('--install', type=pathlib.Path, required=True)
    parser.add_argument('--cc', type=pathlib.Path, required=True)
    parser.add_argument('--cflags', required=True)
    parser.add_argument('--ldflags', required=True)
    parser.add_argument('--module-header', type=pathlib.Path)
    args = parser.parse_args()
    windows = os.name == 'nt'
    if windows:
        bash = _find_bash(args.cc)
    with tempfile.TemporaryDirectory() as temp:
        temp = pathlib.Path(temp)
        build = temp / 'build'
        install = args.install.resolve()
        shutil.copytree(args.source, build)

        def run(*command: str) -> None:
            env = None
            if windows:
                # Building Emacs requires MinGW, see nt/INSTALL.W64.  Therefore
                # we invoke commands through the MinGW shell, see
                # https://www.msys2.org/wiki/Launchers/#the-idea.
                env = dict(os.environ, MSYSTEM='MINGW64', CHERE_INVOKING='1')
                command = [bash, '-l', '-c', shlex.join(command)]
            try:
                subprocess.run(command, check=True, cwd=build, env=env,
                               stdin=subprocess.DEVNULL,
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                               encoding='utf-8', errors='backslashescape')
            except subprocess.CalledProcessError as ex:
                print('command', ' '.join(map(shlex.quote, command)),
                      'failed, output follows:')
                print(ex.stdout)
                print()
                try:
                    config_log = build / 'config.log'
                    content = config_log.read_text('utf-8', 'backslashescape')
                    print('config.log follows:')
                    print(content)
                except FileNotFoundError:
                    print('config.log not found')
                raise

        run('./configure', '--prefix=' + str(install.as_posix()),
            '--without-all', '--without-ns', '--with-x-toolkit=no',
            # Enable threads explicitly to work around
            # https://debbugs.gnu.org/cgi/bugreport.cgi?bug=30106 in older
            # Emacs versions.
            '--with-modules', '--with-threads',
            '--disable-build-details',
            'CC=' + str(args.cc.resolve()),
            'CFLAGS=' + args.cflags,
            'LDFLAGS=' + args.ldflags)
        run('make', 'install')
    # Delete source files that have a corresponding compiled file, as these
    # files don’t work well with Coverage (see
    # e.g. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=40766).
    for compiled in install.glob('share/emacs/*/lisp/**/*.elc'):
        compiled.with_suffix('.el').unlink()
    # Sanity check to verify that the resulting binary works.
    subprocess.run([install / 'bin' / 'emacs', '--quick', '--batch'],
                   check=True, stdin=subprocess.DEVNULL)
    if args.module_header:
        # Copy emacs-module.h to the desired location.
        shutil.copy(install / 'include' / 'emacs-module.h', args.module_header)

def _find_bash(c_compiler: pathlib.Path) -> pathlib.Path:
    if c_compiler.parts[-2:] != ('mingw64', 'bin'):
        raise ValueError(f'unsupported C compiler location {c_compiler}')
    msys = c_compiler.parents[2]
    bash = msys / 'bin' / 'bash.exe'
    if not bash.is_file():
        raise FileNotFoundError(f'no Bash program found in {msys}')
    return bash

if __name__ == '__main__':
    main()
<|endoftext|>