diff options
Diffstat (limited to 'meta/lib/oe/terminal.py')
-rw-r--r-- | meta/lib/oe/terminal.py | 218 |
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 0000000000..a33abd733d --- /dev/null +++ b/meta/lib/oe/terminal.py | |||
@@ -0,0 +1,218 @@ | |||
1 | import logging | ||
2 | import oe.classutils | ||
3 | import shlex | ||
4 | from bb.process import Popen, ExecutionError | ||
5 | |||
6 | logger = logging.getLogger('BitBake.OE.Terminal') | ||
7 | |||
8 | |||
9 | class UnsupportedTerminal(Exception): | ||
10 | pass | ||
11 | |||
12 | class NoSupportedTerminals(Exception): | ||
13 | pass | ||
14 | |||
15 | |||
16 | class 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 | |||
27 | class 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 | |||
48 | class 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 | |||
54 | class Gnome(XTerminal): | ||
55 | command = 'gnome-terminal -t "{title}" -x {command}' | ||
56 | priority = 2 | ||
57 | |||
58 | class Mate(XTerminal): | ||
59 | command = 'mate-terminal -t "{title}" -x {command}' | ||
60 | priority = 2 | ||
61 | |||
62 | class 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 | |||
76 | class 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 | |||
89 | class XTerm(XTerminal): | ||
90 | command = 'xterm -T "{title}" -e {command}' | ||
91 | priority = 1 | ||
92 | |||
93 | class Rxvt(XTerminal): | ||
94 | command = 'rxvt -T "{title}" -e {command}' | ||
95 | priority = 1 | ||
96 | |||
97 | class 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 | |||
112 | class 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 | |||
127 | class 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 | |||
150 | class 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 | |||
166 | def prioritized(): | ||
167 | return Registry.prioritized() | ||
168 | |||
169 | def 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 | |||
180 | def 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 | |||
193 | def 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 | |||
211 | def 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 | ||