diff options
Diffstat (limited to 'yocto-poky/meta/lib/oe/terminal.py')
-rw-r--r-- | yocto-poky/meta/lib/oe/terminal.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/yocto-poky/meta/lib/oe/terminal.py b/yocto-poky/meta/lib/oe/terminal.py new file mode 100644 index 000000000..52a891388 --- /dev/null +++ b/yocto-poky/meta/lib/oe/terminal.py @@ -0,0 +1,263 @@ +import logging +import oe.classutils +import shlex +from bb.process import Popen, ExecutionError +from distutils.version import LooseVersion + +logger = logging.getLogger('BitBake.OE.Terminal') + + +class UnsupportedTerminal(Exception): + pass + +class NoSupportedTerminals(Exception): + 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, sh_cmd, title=None, env=None, d=None): + fmt_sh_cmd = self.format_command(sh_cmd, title) + try: + Popen.__init__(self, fmt_sh_cmd, env=env) + except OSError as exc: + import errno + if exc.errno == errno.ENOENT: + raise UnsupportedTerminal(self.name) + else: + raise + + def format_command(self, sh_cmd, title): + fmt = {'title': title or 'Terminal', 'command': sh_cmd} + if isinstance(self.command, basestring): + return shlex.split(self.command.format(**fmt)) + else: + return [element.format(**fmt) for element in self.command] + +class XTerminal(Terminal): + def __init__(self, sh_cmd, title=None, env=None, d=None): + Terminal.__init__(self, sh_cmd, title, env, d) + if not os.environ.get('DISPLAY'): + raise UnsupportedTerminal(self.name) + +class Gnome(XTerminal): + command = 'gnome-terminal -t "{title}" --disable-factory -x {command}' + priority = 2 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + # Recent versions of gnome-terminal does not support non-UTF8 charset: + # https://bugzilla.gnome.org/show_bug.cgi?id=732127; as a workaround, + # clearing the LC_ALL environment variable so it uses the locale. + # Once fixed on the gnome-terminal project, this should be removed. + if os.getenv('LC_ALL'): os.putenv('LC_ALL','') + + # Check version + vernum = check_terminal_version("gnome-terminal") + if vernum and LooseVersion(vernum) >= '3.10': + logger.debug(1, 'Gnome-Terminal 3.10 or later does not support --disable-factory') + self.command = 'gnome-terminal -t "{title}" -x {command}' + XTerminal.__init__(self, sh_cmd, title, env, d) + +class Mate(XTerminal): + command = 'mate-terminal -t "{title}" -x {command}' + priority = 2 + +class Xfce(XTerminal): + command = 'xfce4-terminal -T "{title}" -e "{command}"' + priority = 2 + +class Terminology(XTerminal): + command = 'terminology -T="{title}" -e {command}' + priority = 2 + +class Konsole(XTerminal): + command = 'konsole --nofork -p tabtitle="{title}" -e {command}' + priority = 2 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + # Check version + vernum = check_terminal_version("konsole") + if vernum and LooseVersion(vernum) < '2.0.0': + # Konsole from KDE 3.x + self.command = 'konsole -T "{title}" -e {command}' + XTerminal.__init__(self, sh_cmd, title, env, d) + +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}" -S devshell {command}' + + def __init__(self, sh_cmd, title=None, env=None, d=None): + s_id = "devshell_%i" % os.getpid() + self.command = "screen -D -m -t \"{title}\" -S %s {command}" % s_id + Terminal.__init__(self, sh_cmd, title, env, d) + msg = 'Screen started. Please connect in another terminal with ' \ + '"screen -r %s"' % s_id + if (d): + bb.event.fire(bb.event.LogExecTTY(msg, "screen -r %s" % s_id, + 0.5, 10), d) + else: + logger.warn(msg) + +class TmuxRunning(Terminal): + """Open a new pane in the current running tmux window""" + name = 'tmux-running' + command = 'tmux split-window "{command}"' + priority = 2.75 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + if not bb.utils.which(os.getenv('PATH'), 'tmux'): + raise UnsupportedTerminal('tmux is not installed') + + if not os.getenv('TMUX'): + raise UnsupportedTerminal('tmux is not running') + + if not check_tmux_pane_size('tmux'): + raise UnsupportedTerminal('tmux pane too small') + + Terminal.__init__(self, sh_cmd, title, env, d) + +class TmuxNewWindow(Terminal): + """Open a new window in the current running tmux session""" + name = 'tmux-new-window' + command = 'tmux new-window -n "{title}" "{command}"' + priority = 2.70 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + if not bb.utils.which(os.getenv('PATH'), 'tmux'): + raise UnsupportedTerminal('tmux is not installed') + + if not os.getenv('TMUX'): + raise UnsupportedTerminal('tmux is not running') + + Terminal.__init__(self, sh_cmd, title, env, d) + +class Tmux(Terminal): + """Start a new tmux session and window""" + command = 'tmux new -d -s devshell -n devshell "{command}"' + priority = 0.75 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + if not bb.utils.which(os.getenv('PATH'), 'tmux'): + raise UnsupportedTerminal('tmux is not installed') + + # TODO: consider using a 'devshell' session shared amongst all + # devshells, if it's already there, add a new window to it. + window_name = 'devshell-%i' % os.getpid() + + self.command = 'tmux new -d -s {0} -n {0} "{{command}}"'.format(window_name) + Terminal.__init__(self, sh_cmd, title, env, d) + + attach_cmd = 'tmux att -t {0}'.format(window_name) + msg = 'Tmux started. Please connect in another terminal with `tmux att -t {0}`'.format(window_name) + if d: + bb.event.fire(bb.event.LogExecTTY(msg, attach_cmd, 0.5, 10), d) + else: + logger.warn(msg) + +class Custom(Terminal): + command = 'false' # This is a placeholder + priority = 3 + + def __init__(self, sh_cmd, title=None, env=None, d=None): + self.command = d and d.getVar('OE_TERMINAL_CUSTOMCMD', True) + if self.command: + if not '{command}' in self.command: + self.command += ' {command}' + Terminal.__init__(self, sh_cmd, title, env, d) + logger.warn('Custom terminal was started.') + else: + logger.debug(1, 'No custom terminal (OE_TERMINAL_CUSTOMCMD) set') + raise UnsupportedTerminal('OE_TERMINAL_CUSTOMCMD not set') + + +def prioritized(): + return Registry.prioritized() + +def spawn_preferred(sh_cmd, title=None, env=None, d=None): + """Spawn the first supported terminal, by priority""" + for terminal in prioritized(): + try: + spawn(terminal.name, sh_cmd, title, env, d) + break + except UnsupportedTerminal: + continue + else: + raise NoSupportedTerminals() + +def spawn(name, sh_cmd, title=None, env=None, d=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(sh_cmd, title, env, d) + output = pipe.communicate()[0] + if pipe.returncode != 0: + raise ExecutionError(sh_cmd, pipe.returncode, output) + +def check_tmux_pane_size(tmux): + import subprocess as sub + try: + p = sub.Popen('%s list-panes -F "#{?pane_active,#{pane_height},}"' % tmux, + shell=True,stdout=sub.PIPE,stderr=sub.PIPE) + out, err = p.communicate() + size = int(out.strip()) + except OSError as exc: + import errno + if exc.errno == errno.ENOENT: + return None + else: + raise + if size/2 >= 19: + return True + return False + +def check_terminal_version(terminalName): + import subprocess as sub + try: + p = sub.Popen(['sh', '-c', '%s --version' % terminalName],stdout=sub.PIPE,stderr=sub.PIPE) + out, err = p.communicate() + ver_info = out.rstrip().split('\n') + except OSError as exc: + import errno + if exc.errno == errno.ENOENT: + return None + else: + raise + vernum = None + for ver in ver_info: + if ver.startswith('Konsole'): + vernum = ver.split(' ')[-1] + if ver.startswith('GNOME Terminal'): + vernum = ver.split(' ')[-1] + return vernum + +def distro_name(): + try: + p = Popen(['lsb_release', '-i']) + out, err = p.communicate() + distro = out.split(':')[1].strip().lower() + except: + distro = "unknown" + return distro |