terminal.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import logging
  2. import oe.classutils
  3. import shlex
  4. from bb.process import Popen, ExecutionError
  5. logger = logging.getLogger('BitBake.OE.Terminal')
  6. class UnsupportedTerminal(StandardError):
  7. pass
  8. class NoSupportedTerminals(StandardError):
  9. pass
  10. class Registry(oe.classutils.ClassRegistry):
  11. command = None
  12. def __init__(cls, name, bases, attrs):
  13. super(Registry, cls).__init__(name.lower(), bases, attrs)
  14. @property
  15. def implemented(cls):
  16. return bool(cls.command)
  17. class Terminal(Popen):
  18. __metaclass__ = Registry
  19. def __init__(self, sh_cmd, title=None, env=None):
  20. fmt_sh_cmd = self.format_command(sh_cmd, title)
  21. try:
  22. Popen.__init__(self, fmt_sh_cmd, env=env)
  23. except OSError as exc:
  24. import errno
  25. if exc.errno == errno.ENOENT:
  26. raise UnsupportedTerminal(self.name)
  27. else:
  28. raise
  29. def format_command(self, sh_cmd, title):
  30. fmt = {'title': title or 'Terminal', 'command': sh_cmd}
  31. if isinstance(self.command, basestring):
  32. return shlex.split(self.command.format(**fmt))
  33. else:
  34. return [element.format(**fmt) for element in self.command]
  35. class XTerminal(Terminal):
  36. def __init__(self, sh_cmd, title=None, env=None):
  37. Terminal.__init__(self, sh_cmd, title, env)
  38. if not os.environ.get('DISPLAY'):
  39. raise UnsupportedTerminal(self.name)
  40. class Gnome(XTerminal):
  41. command = 'gnome-terminal --disable-factory -t "{title}" -x {command}'
  42. priority = 2
  43. class Xfce(XTerminal):
  44. command = 'Terminal -T "{title}" -e "{command}"'
  45. priority = 2
  46. def __init__(self, command, title=None, env=None):
  47. # Upstream binary name is Terminal but Debian/Ubuntu use
  48. # xfce4-terminal to avoid possible(?) conflicts
  49. distro = distro_name()
  50. if distro == 'ubuntu' or distro == 'debian':
  51. cmd = 'xfce4-terminal -T "{title}" -e "{command}"'
  52. else:
  53. cmd = command
  54. XTerminal.__init__(self, cmd, title, env)
  55. class Konsole(XTerminal):
  56. command = 'konsole -T "{title}" -e {command}'
  57. priority = 2
  58. def __init__(self, sh_cmd, title=None, env=None):
  59. # Check version
  60. vernum = check_konsole_version("konsole")
  61. if vernum:
  62. if vernum.split('.')[0] == "2":
  63. logger.debug(1, 'Konsole from KDE 4.x will not work as devshell, skipping')
  64. raise UnsupportedTerminal(self.name)
  65. XTerminal.__init__(self, sh_cmd, title, env)
  66. class XTerm(XTerminal):
  67. command = 'xterm -T "{title}" -e {command}'
  68. priority = 1
  69. class Rxvt(XTerminal):
  70. command = 'rxvt -T "{title}" -e {command}'
  71. priority = 1
  72. class Screen(Terminal):
  73. command = 'screen -D -m -t "{title}" -S devshell {command}'
  74. def __init__(self, sh_cmd, title=None, env=None):
  75. Terminal.__init__(self, sh_cmd, title, env)
  76. logger.warn('Screen started. Please connect in another terminal with '
  77. '"screen -r devshell"')
  78. def prioritized():
  79. return Registry.prioritized()
  80. def spawn_preferred(sh_cmd, title=None, env=None):
  81. """Spawn the first supported terminal, by priority"""
  82. for terminal in prioritized():
  83. try:
  84. spawn(terminal.name, sh_cmd, title, env)
  85. break
  86. except UnsupportedTerminal:
  87. continue
  88. else:
  89. raise NoSupportedTerminals()
  90. def spawn(name, sh_cmd, title=None, env=None):
  91. """Spawn the specified terminal, by name"""
  92. logger.debug(1, 'Attempting to spawn terminal "%s"', name)
  93. try:
  94. terminal = Registry.registry[name]
  95. except KeyError:
  96. raise UnsupportedTerminal(name)
  97. pipe = terminal(sh_cmd, title, env)
  98. output = pipe.communicate()[0]
  99. if pipe.returncode != 0:
  100. raise ExecutionError(sh_cmd, pipe.returncode, output)
  101. def check_konsole_version(konsole):
  102. import subprocess as sub
  103. try:
  104. p = sub.Popen(['sh', '-c', '%s --version' % konsole],stdout=sub.PIPE,stderr=sub.PIPE)
  105. out, err = p.communicate()
  106. ver_info = out.rstrip().split('\n')
  107. except OSError as exc:
  108. import errno
  109. if exc.errno == errno.ENOENT:
  110. return None
  111. else:
  112. raise
  113. vernum = None
  114. for ver in ver_info:
  115. if ver.startswith('Konsole'):
  116. vernum = ver.split(' ')[-1]
  117. return vernum
  118. def distro_name():
  119. try:
  120. p = Popen(['lsb_release', '-i'])
  121. out, err = p.communicate()
  122. distro = out.split(':')[1].strip().lower()
  123. except:
  124. distro = "unknown"
  125. return distro