summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/terminal.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oe/terminal.py')
-rw-r--r--meta/lib/oe/terminal.py218
1 files changed, 218 insertions, 0 deletions
diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py
new file mode 100644
index 0000000..a33abd7
--- /dev/null
+++ b/meta/lib/oe/terminal.py
@@ -0,0 +1,218 @@
1import logging
2import oe.classutils
3import shlex
4from bb.process import Popen, ExecutionError
5
6logger = logging.getLogger('BitBake.OE.Terminal')
7
8
9class UnsupportedTerminal(Exception):
10 pass
11
12class NoSupportedTerminals(Exception):
13 pass
14
15
16class Registry(oe.classutils.ClassRegistry):
17 command = None
18
19 def __init__(cls, name, bases, attrs):
20 super(Registry, cls).__init__(name.lower(), bases, attrs)
21
22 @property
23 def implemented(cls):
24 return bool(cls.command)
25
26
27class Terminal(Popen):
28 __metaclass__ = Registry
29
30 def __init__(self, sh_cmd, title=None, env=None, d=None):
31 fmt_sh_cmd = self.format_command(sh_cmd, title)
32 try:
33 Popen.__init__(self, fmt_sh_cmd, env=env)
34 except OSError as exc:
35 import errno
36 if exc.errno == errno.ENOENT:
37 raise UnsupportedTerminal(self.name)
38 else:
39 raise
40
41 def format_command(self, sh_cmd, title):
42 fmt = {'title': title or 'Terminal', 'command': sh_cmd}
43 if isinstance(self.command, basestring):
44 return shlex.split(self.command.format(**fmt))
45 else:
46 return [element.format(**fmt) for element in self.command]
47
48class XTerminal(Terminal):
49 def __init__(self, sh_cmd, title=None, env=None, d=None):
50 Terminal.__init__(self, sh_cmd, title, env, d)
51 if not os.environ.get('DISPLAY'):
52 raise UnsupportedTerminal(self.name)
53
54class Gnome(XTerminal):
55 command = 'gnome-terminal -t "{title}" -x {command}'
56 priority = 2
57
58class Mate(XTerminal):
59 command = 'mate-terminal -t "{title}" -x {command}'
60 priority = 2
61
62class Xfce(XTerminal):
63 command = 'Terminal -T "{title}" -e "{command}"'
64 priority = 2
65
66 def __init__(self, command, title=None, env=None, d=None):
67 # Upstream binary name is Terminal but Debian/Ubuntu use
68 # xfce4-terminal to avoid possible(?) conflicts
69 distro = distro_name()
70 if distro == 'ubuntu' or distro == 'debian':
71 cmd = 'xfce4-terminal -T "{title}" -e "{command}"'
72 else:
73 cmd = command
74 XTerminal.__init__(self, cmd, title, env, d)
75
76class Konsole(XTerminal):
77 command = 'konsole -T "{title}" -e {command}'
78 priority = 2
79
80 def __init__(self, sh_cmd, title=None, env=None, d=None):
81 # Check version
82 vernum = check_konsole_version("konsole")
83 if vernum:
84 if vernum.split('.')[0] == "2":
85 logger.debug(1, 'Konsole from KDE 4.x will not work as devshell, skipping')
86 raise UnsupportedTerminal(self.name)
87 XTerminal.__init__(self, sh_cmd, title, env, d)
88
89class XTerm(XTerminal):
90 command = 'xterm -T "{title}" -e {command}'
91 priority = 1
92
93class Rxvt(XTerminal):
94 command = 'rxvt -T "{title}" -e {command}'
95 priority = 1
96
97class Screen(Terminal):
98 command = 'screen -D -m -t "{title}" -S devshell {command}'
99
100 def __init__(self, sh_cmd, title=None, env=None, d=None):
101 s_id = "devshell_%i" % os.getpid()
102 self.command = "screen -D -m -t \"{title}\" -S %s {command}" % s_id
103 Terminal.__init__(self, sh_cmd, title, env, d)
104 msg = 'Screen started. Please connect in another terminal with ' \
105 '"screen -r %s"' % s_id
106 if (d):
107 bb.event.fire(bb.event.LogExecTTY(msg, "screen -r %s" % s_id,
108 0.5, 10), d)
109 else:
110 logger.warn(msg)
111
112class TmuxRunning(Terminal):
113 """Open a new pane in the current running tmux window"""
114 name = 'tmux-running'
115 command = 'tmux split-window "{command}"'
116 priority = 2.75
117
118 def __init__(self, sh_cmd, title=None, env=None, d=None):
119 if not bb.utils.which(os.getenv('PATH'), 'tmux'):
120 raise UnsupportedTerminal('tmux is not installed')
121
122 if not os.getenv('TMUX'):
123 raise UnsupportedTerminal('tmux is not running')
124
125 Terminal.__init__(self, sh_cmd, title, env, d)
126
127class Tmux(Terminal):
128 """Start a new tmux session and window"""
129 command = 'tmux new -d -s devshell -n devshell "{command}"'
130 priority = 0.75
131
132 def __init__(self, sh_cmd, title=None, env=None, d=None):
133 if not bb.utils.which(os.getenv('PATH'), 'tmux'):
134 raise UnsupportedTerminal('tmux is not installed')
135
136 # TODO: consider using a 'devshell' session shared amongst all
137 # devshells, if it's already there, add a new window to it.
138 window_name = 'devshell-%i' % os.getpid()
139
140 self.command = 'tmux new -d -s {0} -n {0} "{{command}}"'.format(window_name)
141 Terminal.__init__(self, sh_cmd, title, env, d)
142
143 attach_cmd = 'tmux att -t {0}'.format(window_name)
144 msg = 'Tmux started. Please connect in another terminal with `tmux att -t {0}`'.format(window_name)
145 if d:
146 bb.event.fire(bb.event.LogExecTTY(msg, attach_cmd, 0.5, 10), d)
147 else:
148 logger.warn(msg)
149
150class Custom(Terminal):
151 command = 'false' # This is a placeholder
152 priority = 3
153
154 def __init__(self, sh_cmd, title=None, env=None, d=None):
155 self.command = d and d.getVar('OE_TERMINAL_CUSTOMCMD', True)
156 if self.command:
157 if not '{command}' in self.command:
158 self.command += ' {command}'
159 Terminal.__init__(self, sh_cmd, title, env, d)
160 logger.warn('Custom terminal was started.')
161 else:
162 logger.debug(1, 'No custom terminal (OE_TERMINAL_CUSTOMCMD) set')
163 raise UnsupportedTerminal('OE_TERMINAL_CUSTOMCMD not set')
164
165
166def prioritized():
167 return Registry.prioritized()
168
169def spawn_preferred(sh_cmd, title=None, env=None, d=None):
170 """Spawn the first supported terminal, by priority"""
171 for terminal in prioritized():
172 try:
173 spawn(terminal.name, sh_cmd, title, env, d)
174 break
175 except UnsupportedTerminal:
176 continue
177 else:
178 raise NoSupportedTerminals()
179
180def spawn(name, sh_cmd, title=None, env=None, d=None):
181 """Spawn the specified terminal, by name"""
182 logger.debug(1, 'Attempting to spawn terminal "%s"', name)
183 try:
184 terminal = Registry.registry[name]
185 except KeyError:
186 raise UnsupportedTerminal(name)
187
188 pipe = terminal(sh_cmd, title, env, d)
189 output = pipe.communicate()[0]
190 if pipe.returncode != 0:
191 raise ExecutionError(sh_cmd, pipe.returncode, output)
192
193def check_konsole_version(konsole):
194 import subprocess as sub
195 try:
196 p = sub.Popen(['sh', '-c', '%s --version' % konsole],stdout=sub.PIPE,stderr=sub.PIPE)
197 out, err = p.communicate()
198 ver_info = out.rstrip().split('\n')
199 except OSError as exc:
200 import errno
201 if exc.errno == errno.ENOENT:
202 return None
203 else:
204 raise
205 vernum = None
206 for ver in ver_info:
207 if ver.startswith('Konsole'):
208 vernum = ver.split(' ')[-1]
209 return vernum
210
211def distro_name():
212 try:
213 p = Popen(['lsb_release', '-i'])
214 out, err = p.communicate()
215 distro = out.split(':')[1].strip().lower()
216 except:
217 distro = "unknown"
218 return distro