From mboxrd@z Thu Jan 1 00:00:00 1970 X-GM-THRID: 6739560601010307072 X-Received: by 2002:ac2:4853:: with SMTP id 19mr14139351lfy.69.1569176238708; Sun, 22 Sep 2019 11:17:18 -0700 (PDT) X-BeenThere: isar-users@googlegroups.com Received: by 2002:a2e:934e:: with SMTP id m14ls1620233ljh.4.gmail; Sun, 22 Sep 2019 11:17:18 -0700 (PDT) X-Google-Smtp-Source: APXvYqxdGdiuVYtxZ5DkP20q/mLrl/vy3IagAd32DLdw4ED2dbqqsbPt/JRxKhO/6GApFUSnkJkN X-Received: by 2002:a2e:a0c5:: with SMTP id f5mr3446198ljm.114.1569176238012; Sun, 22 Sep 2019 11:17:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1569176238; cv=none; d=google.com; s=arc-20160816; b=UZnoBxLbwHErBQp8OeWqMLnJUsRwlEkdM07Yd7y3ljnVex38KPOvvQ973IpV2uORfU +yFEuJpsi1frHmxjQcyC8KeqPX88TjZrt3lbBVyPxWTIYof3tEJ4YlIzRrXInf/EWlTd ycpfMcKGaqbXyyycOKqFRNfea7Eq7AEdWsV1hECA8Pgfpeo05iAYvn9rYU/zZkmraLWV hk1vp+xuMSttgJfikrdGWAmwCLqKd8G+1QSlGoAs9kEmNT/et3AA/5SzrMUW5kZMXZIu oUtVuJo/eJpA5NW/DvG+c64sEmhR8VixQj04RkQPhQqldUOUSefFm7+InvO45yc1lwt9 fGgQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:references:in-reply-to:message-id:date :subject:to:from; bh=jPz/li0JcQsFatPIqFM1kQjkKCBm2Mv2omjGP/3MLXk=; b=UWvjPfH4jzx43dnzV/FNOoPqKn8K05cM8Nj4tC4BgKdGekTxWO/BoXKoQ8+io47XSQ xyZ61A88CX6Nma6N7181QXRLYH94k9mKLcDdqO0P0el6iAJKqjki5pURespdQXzX9xFS NSDECwfVWSdJsQJoqtfnHWrdFRnAq6U77ejqCZwUtnJD2M+QjoUg1PlUjBA9ynVIW0GG Asi3cBYOImq5jGR7UYg2+jD0BnU0u2BkQOv2z8jd0DwmRybbtuILPss9vynsjgp96YuV ybvciz4qwteDDcGkXt+fRHiRo1C2eFXLL/EuI6FunjcF+At+HKikPLXnd6YfLilPhT9+ DfCQ== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=pass (google.com: domain of jan.kiszka@siemens.com designates 194.138.37.39 as permitted sender) smtp.mailfrom=jan.kiszka@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Return-Path: Received: from lizzard.sbs.de (lizzard.sbs.de. [194.138.37.39]) by gmr-mx.google.com with ESMTPS id y6si509632lji.0.2019.09.22.11.17.17 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Sep 2019 11:17:17 -0700 (PDT) Received-SPF: pass (google.com: domain of jan.kiszka@siemens.com designates 194.138.37.39 as permitted sender) client-ip=194.138.37.39; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of jan.kiszka@siemens.com designates 194.138.37.39 as permitted sender) smtp.mailfrom=jan.kiszka@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from mail1.sbs.de (mail1.sbs.de [192.129.41.35]) by lizzard.sbs.de (8.15.2/8.15.2) with ESMTPS id x8MIHHwM029184 (version=TLSv1.2 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Sun, 22 Sep 2019 20:17:17 +0200 Received: from md1f2u6c.ad001.siemens.net ([139.22.37.1]) by mail1.sbs.de (8.15.2/8.15.2) with ESMTP id x8MIHB9D005761 for ; Sun, 22 Sep 2019 20:17:17 +0200 From: Jan Kiszka To: isar-users Subject: [PATCH v5 25/27] Add OE class and library elements for terminal support Date: Sun, 22 Sep 2019 20:17:09 +0200 Message-Id: X-Mailer: git-send-email 2.16.4 In-Reply-To: References: In-Reply-To: References: X-TUID: W2iS8CqdDSiW From: Jan Kiszka This imports the terminal class from OE core revision 95ad56262963 along with its dependencies under meta/lib/oe and a helper script. Files are almost unmodified, just augmented by a note for their origin, SPDX tags where missing and freed of three whitespace issues. Signed-off-by: Jan Kiszka --- meta/classes/terminal.bbclass | 112 +++++++++++++ meta/lib/oe/classutils.py | 49 ++++++ meta/lib/oe/data.py | 53 ++++++ meta/lib/oe/maketype.py | 112 +++++++++++++ meta/lib/oe/terminal.py | 315 ++++++++++++++++++++++++++++++++++++ meta/lib/oe/types.py | 187 +++++++++++++++++++++ scripts/oe-gnome-terminal-phonehome | 14 ++ 7 files changed, 842 insertions(+) create mode 100644 meta/classes/terminal.bbclass create mode 100644 meta/lib/oe/classutils.py create mode 100644 meta/lib/oe/data.py create mode 100644 meta/lib/oe/maketype.py create mode 100644 meta/lib/oe/terminal.py create mode 100644 meta/lib/oe/types.py create mode 100755 scripts/oe-gnome-terminal-phonehome diff --git a/meta/classes/terminal.bbclass b/meta/classes/terminal.bbclass new file mode 100644 index 0000000..8436d7f --- /dev/null +++ b/meta/classes/terminal.bbclass @@ -0,0 +1,112 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: MIT +# + +OE_TERMINAL ?= 'auto' +OE_TERMINAL[type] = 'choice' +OE_TERMINAL[choices] = 'auto none \ + ${@oe_terminal_prioritized()}' + +OE_TERMINAL_EXPORTS += 'EXTRA_OEMAKE CACHED_CONFIGUREVARS CONFIGUREOPTS EXTRA_OECONF' +OE_TERMINAL_EXPORTS[type] = 'list' + +XAUTHORITY ?= "${HOME}/.Xauthority" +SHELL ?= "bash" + +def oe_terminal_prioritized(): + import oe.terminal + return " ".join(o.name for o in oe.terminal.prioritized()) + +def emit_terminal_func(command, envdata, d): + import bb.build + cmd_func = 'do_terminal' + + envdata.setVar(cmd_func, 'exec ' + command) + envdata.setVarFlag(cmd_func, 'func', '1') + + runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}" + runfile = runfmt.format(func=cmd_func, task=cmd_func, taskfunc=cmd_func, pid=os.getpid()) + runfile = os.path.join(d.getVar('T'), runfile) + bb.utils.mkdirhier(os.path.dirname(runfile)) + + with open(runfile, 'w') as script: + script.write(bb.build.shell_trap_code()) + bb.data.emit_func(cmd_func, script, envdata) + script.write(cmd_func) + script.write("\n") + os.chmod(runfile, 0o755) + + return runfile + +def oe_terminal(command, title, d): + import oe.data + import oe.terminal + + envdata = bb.data.init() + + for v in os.environ: + envdata.setVar(v, os.environ[v]) + envdata.setVarFlag(v, 'export', '1') + + for export in oe.data.typed_value('OE_TERMINAL_EXPORTS', d): + value = d.getVar(export) + if value is not None: + os.environ[export] = str(value) + envdata.setVar(export, str(value)) + envdata.setVarFlag(export, 'export', '1') + if export == "PSEUDO_DISABLED": + if "PSEUDO_UNLOAD" in os.environ: + del os.environ["PSEUDO_UNLOAD"] + envdata.delVar("PSEUDO_UNLOAD") + + # Add in all variables from the user's original environment which + # haven't subsequntly been set/changed + origbbenv = d.getVar("BB_ORIGENV", False) or {} + for key in origbbenv: + if key in envdata: + continue + value = origbbenv.getVar(key) + if value is not None: + os.environ[key] = str(value) + envdata.setVar(key, str(value)) + envdata.setVarFlag(key, 'export', '1') + + # Use original PATH as a fallback + path = d.getVar('PATH') + ":" + origbbenv.getVar('PATH') + os.environ['PATH'] = path + envdata.setVar('PATH', path) + + # A complex PS1 might need more escaping of chars. + # Lets not export PS1 instead. + envdata.delVar("PS1") + + # Replace command with an executable wrapper script + command = emit_terminal_func(command, envdata, 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: + oe.terminal.spawn(terminal, command, title, None, d) + return + except oe.terminal.UnsupportedTerminal: + bb.warn('Unsupported terminal "%s", defaulting to "auto"' % + terminal) + except oe.terminal.ExecutionError as exc: + bb.fatal('Unable to spawn terminal %s: %s' % (terminal, exc)) + + try: + oe.terminal.spawn_preferred(command, title, None, d) + except oe.terminal.NoSupportedTerminals as nosup: + nosup.terms.remove("false") + cmds = '\n\t'.join(nosup.terms).replace("{command}", + "do_terminal").replace("{title}", title) + bb.fatal('No valid terminal found, unable to open devshell.\n' + + 'Tried the following commands:\n\t%s' % cmds) + except oe.terminal.ExecutionError as exc: + bb.fatal('Unable to spawn terminal %s: %s' % (terminal, exc)) + +oe_terminal[vardepsexclude] = "BB_ORIGENV" diff --git a/meta/lib/oe/classutils.py b/meta/lib/oe/classutils.py new file mode 100644 index 0000000..d879191 --- /dev/null +++ b/meta/lib/oe/classutils.py @@ -0,0 +1,49 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# + +class ClassRegistryMeta(type): + """Give each ClassRegistry their own registry""" + def __init__(cls, name, bases, attrs): + cls.registry = {} + type.__init__(cls, name, bases, attrs) + +class ClassRegistry(type, metaclass=ClassRegistryMeta): + """Maintain a registry of classes, indexed by name. + +Note that this implementation requires that the names be unique, as it uses +a dictionary to hold the classes by name. + +The name in the registry can be overridden via the 'name' attribute of the +class, and the 'priority' attribute controls priority. The prioritized() +method returns the registered classes in priority order. + +Subclasses of ClassRegistry may define an 'implemented' property to exert +control over whether the class will be added to the registry (e.g. to keep +abstract base classes out of the registry).""" + priority = 0 + def __init__(cls, name, bases, attrs): + super(ClassRegistry, cls).__init__(name, bases, attrs) + try: + if not cls.implemented: + return + except AttributeError: + pass + + try: + cls.name + except AttributeError: + cls.name = name + cls.registry[cls.name] = cls + + @classmethod + def prioritized(tcls): + return sorted(list(tcls.registry.values()), + key=lambda v: (v.priority, v.name), reverse=True) + + def unregister(cls): + for key in cls.registry.keys(): + if cls.registry[key] is cls: + del cls.registry[key] diff --git a/meta/lib/oe/data.py b/meta/lib/oe/data.py new file mode 100644 index 0000000..b832e9b --- /dev/null +++ b/meta/lib/oe/data.py @@ -0,0 +1,53 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import json +import oe.maketype + +def typed_value(key, d): + """Construct a value for the specified metadata variable, using its flags + to determine the type and parameters for construction.""" + var_type = d.getVarFlag(key, 'type') + flags = d.getVarFlags(key) + if flags is not None: + flags = dict((flag, d.expand(value)) + for flag, value in list(flags.items())) + else: + flags = {} + + try: + return oe.maketype.create(d.getVar(key) or '', var_type, **flags) + except (TypeError, ValueError) as exc: + bb.msg.fatal("Data", "%s: %s" % (key, str(exc))) + +def export2json(d, json_file, expand=True, searchString="",replaceString=""): + data2export = {} + keys2export = [] + + for key in d.keys(): + if key.startswith("_"): + continue + elif key.startswith("BB"): + continue + elif key.startswith("B_pn"): + continue + elif key.startswith("do_"): + continue + elif d.getVarFlag(key, "func"): + continue + + keys2export.append(key) + + for key in keys2export: + try: + data2export[key] = d.getVar(key, expand).replace(searchString,replaceString) + except bb.data_smart.ExpansionError: + data2export[key] = '' + except AttributeError: + pass + + with open(json_file, "w") as f: + json.dump(data2export, f, skipkeys=True, indent=4, sort_keys=True) diff --git a/meta/lib/oe/maketype.py b/meta/lib/oe/maketype.py new file mode 100644 index 0000000..969a22b --- /dev/null +++ b/meta/lib/oe/maketype.py @@ -0,0 +1,112 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# +"""OpenEmbedded variable typing support + +Types are defined in the metadata by name, using the 'type' flag on a +variable. Other flags may be utilized in the construction of the types. See +the arguments of the type's factory for details. +""" + +import inspect +import oe.types as types +try: + # Python 3.7+ + from collections.abc import Callable +except ImportError: + # Python < 3.7 + from collections import Callable + +available_types = {} + +class MissingFlag(TypeError): + """A particular flag is required to construct the type, but has not been + provided.""" + def __init__(self, flag, type): + self.flag = flag + self.type = type + TypeError.__init__(self) + + def __str__(self): + return "Type '%s' requires flag '%s'" % (self.type, self.flag) + +def factory(var_type): + """Return the factory for a specified type.""" + if var_type is None: + raise TypeError("No type specified. Valid types: %s" % + ', '.join(available_types)) + try: + return available_types[var_type] + except KeyError: + raise TypeError("Invalid type '%s':\n Valid types: %s" % + (var_type, ', '.join(available_types))) + +def create(value, var_type, **flags): + """Create an object of the specified type, given the specified flags and + string value.""" + obj = factory(var_type) + objflags = {} + for flag in obj.flags: + if flag not in flags: + if flag not in obj.optflags: + raise MissingFlag(flag, var_type) + else: + objflags[flag] = flags[flag] + + return obj(value, **objflags) + +def get_callable_args(obj): + """Grab all but the first argument of the specified callable, returning + the list, as well as a list of which of the arguments have default + values.""" + if type(obj) is type: + obj = obj.__init__ + + sig = inspect.signature(obj) + args = list(sig.parameters.keys()) + defaults = list(s for s in sig.parameters.keys() if sig.parameters[s].default != inspect.Parameter.empty) + flaglist = [] + if args: + if len(args) > 1 and args[0] == 'self': + args = args[1:] + flaglist.extend(args) + + optional = set() + if defaults: + optional |= set(flaglist[-len(defaults):]) + return flaglist, optional + +def factory_setup(name, obj): + """Prepare a factory for use.""" + args, optional = get_callable_args(obj) + extra_args = args[1:] + if extra_args: + obj.flags, optional = extra_args, optional + obj.optflags = set(optional) + else: + obj.flags = obj.optflags = () + + if not hasattr(obj, 'name'): + obj.name = name + +def register(name, factory): + """Register a type, given its name and a factory callable. + + Determines the required and optional flags from the factory's + arguments.""" + factory_setup(name, factory) + available_types[factory.name] = factory + + +# Register all our included types +for name in dir(types): + if name.startswith('_'): + continue + + obj = getattr(types, name) + if not isinstance(obj, Callable): + continue + + register(name, obj) diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py new file mode 100644 index 0000000..0d875c3 --- /dev/null +++ b/meta/lib/oe/terminal.py @@ -0,0 +1,315 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# +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): + def __init__(self, terms): + self.terms = terms + + +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, 'cwd': os.getcwd() } + if isinstance(self.command, str): + 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}" -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','') + + XTerminal.__init__(self, sh_cmd, title, env, d) + +class Mate(XTerminal): + command = 'mate-terminal --disable-factory -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 --separate --workdir . -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}' + elif vernum and LooseVersion(vernum) < '16.08.1': + # Konsole pre 16.08.01 Has nofork + self.command = 'konsole --nofork --workdir . -p tabtitle="{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.warning(msg) + +class TmuxRunning(Terminal): + """Open a new pane in the current running tmux window""" + name = 'tmux-running' + command = 'tmux split-window -c "{cwd}" "{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 or tmux < 1.9 version is being used') + + 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 -c "{cwd}" -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 -c "{cwd}" -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 -c "{{cwd}}" -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.warning(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') + if self.command: + if not '{command}' in self.command: + self.command += ' {command}' + Terminal.__init__(self, sh_cmd, title, env, d) + logger.warning('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 get_cmd_list(): + terms = Registry.prioritized() + cmds = [] + for term in terms: + if term.command: + cmds.append(term.command) + return cmds + +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(get_cmd_list()) + +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) + + # We need to know when the command completes but some terminals (at least + # gnome and tmux) gives us no way to do this. We therefore write the pid + # to a file using a "phonehome" wrapper script, then monitor the pid + # until it exits. + import tempfile + import time + pidfile = tempfile.NamedTemporaryFile(delete = False).name + try: + sh_cmd = bb.utils.which(os.getenv('PATH'), "oe-gnome-terminal-phonehome") + " " + pidfile + " " + sh_cmd + pipe = terminal(sh_cmd, title, env, d) + output = pipe.communicate()[0] + if output: + output = output.decode("utf-8") + if pipe.returncode != 0: + raise ExecutionError(sh_cmd, pipe.returncode, output) + + while os.stat(pidfile).st_size <= 0: + time.sleep(0.01) + continue + with open(pidfile, "r") as f: + pid = int(f.readline()) + finally: + os.unlink(pidfile) + + while True: + try: + os.kill(pid, 0) + time.sleep(0.1) + except OSError: + return + +def check_tmux_pane_size(tmux): + import subprocess as sub + # On older tmux versions (<1.9), return false. The reason + # is that there is no easy way to get the height of the active panel + # on current window without nested formats (available from version 1.9) + vernum = check_terminal_version("tmux") + if vernum and LooseVersion(vernum) < '1.9': + return False + 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 + + return size/2 >= 19 + +def check_terminal_version(terminalName): + import subprocess as sub + try: + cmdversion = '%s --version' % terminalName + if terminalName.startswith('tmux'): + cmdversion = '%s -V' % terminalName + newenv = os.environ.copy() + newenv["LANG"] = "C" + p = sub.Popen(['sh', '-c', cmdversion], stdout=sub.PIPE, stderr=sub.PIPE, env=newenv) + out, err = p.communicate() + ver_info = out.decode().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] + if ver.startswith('MATE Terminal'): + vernum = ver.split(' ')[-1] + if ver.startswith('tmux'): + vernum = ver.split()[-1] + if ver.startswith('tmux next-'): + vernum = ver.split()[-1][5:] + 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 diff --git a/meta/lib/oe/types.py b/meta/lib/oe/types.py new file mode 100644 index 0000000..ad8dd36 --- /dev/null +++ b/meta/lib/oe/types.py @@ -0,0 +1,187 @@ +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import errno +import re +import os + + +class OEList(list): + """OpenEmbedded 'list' type + + Acts as an ordinary list, but is constructed from a string value and a + separator (optional), and re-joins itself when converted to a string with + str(). Set the variable type flag to 'list' to use this type, and the + 'separator' flag may be specified (defaulting to whitespace).""" + + name = "list" + + def __init__(self, value, separator = None): + if value is not None: + list.__init__(self, value.split(separator)) + else: + list.__init__(self) + + if separator is None: + self.separator = " " + else: + self.separator = separator + + def __str__(self): + return self.separator.join(self) + +def choice(value, choices): + """OpenEmbedded 'choice' type + + Acts as a multiple choice for the user. To use this, set the variable + type flag to 'choice', and set the 'choices' flag to a space separated + list of valid values.""" + if not isinstance(value, str): + raise TypeError("choice accepts a string, not '%s'" % type(value)) + + value = value.lower() + choices = choices.lower() + if value not in choices.split(): + raise ValueError("Invalid choice '%s'. Valid choices: %s" % + (value, choices)) + return value + +class NoMatch(object): + """Stub python regex pattern object which never matches anything""" + def findall(self, string, flags=0): + return None + + def finditer(self, string, flags=0): + return None + + def match(self, flags=0): + return None + + def search(self, string, flags=0): + return None + + def split(self, string, maxsplit=0): + return None + + def sub(pattern, repl, string, count=0): + return None + + def subn(pattern, repl, string, count=0): + return None + +NoMatch = NoMatch() + +def regex(value, regexflags=None): + """OpenEmbedded 'regex' type + + Acts as a regular expression, returning the pre-compiled regular + expression pattern object. To use this type, set the variable type flag + to 'regex', and optionally, set the 'regexflags' type to a space separated + list of the flags to control the regular expression matching (e.g. + FOO[regexflags] += 'ignorecase'). See the python documentation on the + 're' module for a list of valid flags.""" + + flagval = 0 + if regexflags: + for flag in regexflags.split(): + flag = flag.upper() + try: + flagval |= getattr(re, flag) + except AttributeError: + raise ValueError("Invalid regex flag '%s'" % flag) + + if not value: + # Let's ensure that the default behavior for an undefined or empty + # variable is to match nothing. If the user explicitly wants to match + # anything, they can match '.*' instead. + return NoMatch + + try: + return re.compile(value, flagval) + except re.error as exc: + raise ValueError("Invalid regex value '%s': %s" % + (value, exc.args[0])) + +def boolean(value): + """OpenEmbedded 'boolean' type + + Valid values for true: 'yes', 'y', 'true', 't', '1' + Valid values for false: 'no', 'n', 'false', 'f', '0', None + """ + if value is None: + return False + + if isinstance(value, bool): + return value + + if not isinstance(value, str): + raise TypeError("boolean accepts a string, not '%s'" % type(value)) + + value = value.lower() + if value in ('yes', 'y', 'true', 't', '1'): + return True + elif value in ('no', 'n', 'false', 'f', '0'): + return False + raise ValueError("Invalid boolean value '%s'" % value) + +def integer(value, numberbase=10): + """OpenEmbedded 'integer' type + + Defaults to base 10, but this can be specified using the optional + 'numberbase' flag.""" + + return int(value, int(numberbase)) + +_float = float +def float(value, fromhex='false'): + """OpenEmbedded floating point type + + To use this type, set the type flag to 'float', and optionally set the + 'fromhex' flag to a true value (obeying the same rules as for the + 'boolean' type) if the value is in base 16 rather than base 10.""" + + if boolean(fromhex): + return _float.fromhex(value) + else: + return _float(value) + +def path(value, relativeto='', normalize='true', mustexist='false'): + value = os.path.join(relativeto, value) + + if boolean(normalize): + value = os.path.normpath(value) + + if boolean(mustexist): + try: + open(value, 'r') + except IOError as exc: + if exc.errno == errno.ENOENT: + raise ValueError("{0}: {1}".format(value, os.strerror(errno.ENOENT))) + + return value + +def is_x86(arch): + """ + Check whether arch is x86 or x86_64 + """ + if arch.startswith('x86_') or re.match('i.*86', arch): + return True + else: + return False + +def qemu_use_kvm(kvm, target_arch): + """ + Enable kvm if target_arch == build_arch or both of them are x86 archs. + """ + + use_kvm = False + if kvm and boolean(kvm): + build_arch = os.uname()[4] + if is_x86(build_arch) and is_x86(target_arch): + use_kvm = True + elif build_arch == target_arch: + use_kvm = True + return use_kvm diff --git a/scripts/oe-gnome-terminal-phonehome b/scripts/oe-gnome-terminal-phonehome new file mode 100755 index 0000000..5a321a2 --- /dev/null +++ b/scripts/oe-gnome-terminal-phonehome @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Imported from openembedded-core +# +# SPDX-License-Identifier: GPL-2.0-only +# +# Gnome terminal won't tell us which PID a given command is run as +# or allow a single instance so we can't tell when it completes. +# This allows us to figure out the PID of the target so we can tell +# when its done. +# +echo $$ > $1 +shift +exec $@ -- 2.16.4