summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/terminal.py
blob: e14fc5dbc4fa5f923e4bc57aaf2f9c46f5b9ec5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import logging
import os
import oe.classutils
import oe.data
import shlex
import bb
from bb.process import Popen, ExecutionError


logger = logging.getLogger('BitBake.OE.Terminal')


class UnsupportedTerminal(StandardError):
    pass

class NoSupportedTerminals(StandardError):
    pass


class Registry(oe.classutils.ClassRegistry):
    command = None

    def __init__(cls, name, bases, attrs):
        super(Registry, cls).__init__(name.lower(), bases, attrs)

    @property
    def implemented(cls):
        return bool(cls.command)


class Terminal(Popen):
    __metaclass__ = Registry

    def __init__(self, command, title=None):
        self.format_command(command, title)
        logger.debug(1, "%s: running %s", self.name, self.command)

        try:
            Popen.__init__(self, self.command, shell=False)
        except OSError as exc:
            import errno
            if exc.errno == errno.ENOENT:
                raise UnsupportedTerminal(self.name)
            else:
                raise

    def format_command(self, command, title):
        fmt = {'title': title or 'Terminal', 'command': command}
        if isinstance(self.command, basestring):
            self.command = shlex.split(self.command.format(**fmt))
        else:
            self.command = [element.format(**fmt) for element in self.command]

class XTerminal(Terminal):
    def __init__(self, command, title=None):
        Terminal.__init__(self, command, title)
        if not os.environ.get('DISPLAY'):
            raise UnsupportedTerminal(self.name)

class Gnome(XTerminal):
    command = 'gnome-terminal --disable-factory -t "{title}" -x {command}'
    priority = 2

class Konsole(XTerminal):
    command = 'konsole -T "{title}" -e {command}'
    priority = 2

class XTerm(XTerminal):
    command = 'xterm -T "{title}" -e {command}'
    priority = 1

class Rxvt(XTerminal):
    command = 'rxvt -T "{title}" -e {command}'
    priority = 1

class Screen(Terminal):
    command = 'screen -D -m -t "{title}" {command}'


def prioritized():
    return Registry.prioritized()

def run(command, title, d):
    terminal = oe.data.typed_value('OE_TERMINAL', d).lower()
    if terminal == 'none':
        bb.fatal('Devshell usage disabled with OE_TERMINAL')
    elif terminal != 'auto':
        try:
            spawn(terminal, command, title)
            return
        except UnsupportedTerminal:
            bb.warn('Unsupported terminal "%s", defaulting to "auto"' %
                    terminal)
        except ExecutionError as exc:
            bb.fatal('Unable to spawn terminal %s: %s' % (terminal, exc))

    try:
        spawn_preferred(command, title)
    except NoSupportedTerminals:
        bb.fatal('No valid terminal found, unable to open devshell')
    except ExecutionError as exc:
        bb.fatal('Unable to spawn terminal %s: %s' % (terminal, exc))

def spawn_preferred(command, title=None):
    """Spawn the first supported terminal, by priority"""
    for terminal in prioritized():
        try:
            spawn(terminal.name, command, title)
            break
        except UnsupportedTerminal:
            continue
    else:
        raise NoSupportedTerminals()

def spawn(name, command, title=None):
    """Spawn the specified terminal, by name"""
    logger.debug(1, 'Attempting to spawn terminal "%s"', name)
    try:
        terminal = Registry.registry[name]
    except KeyError:
        raise UnsupportedTerminal(name)

    pipe = terminal(command, title)
    output = pipe.communicate()[0]
    if pipe.returncode != 0:
        raise ExecutionError(pipe.command, pipe.returncode, output)