summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2014-05-27 16:09:16 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-05-28 08:27:00 +0100
commitcd7b437d4b6800aab47ea9d9b00bbac1bb270bc5 (patch)
tree9f6590f7b0d8d7257f066f648712c9db9da0e504
parent3bf24188b6735bc156f0e2f0acde0277b8443737 (diff)
downloadpoky-cd7b437d4b6800aab47ea9d9b00bbac1bb270bc5.tar.gz
devshell: Add interactive python shell
Being able to interact with the python context in the Bitbake task execution environment has long been desireable. This patch introduces such a mechanism. Executing "bitbake X -c devpyshell" will open a terminal connected to a python interactive interpretor in the task context so for example you can run commands like "d.getVar('WORKDIR')" This version now includes readline support for command history and various other bug fixes such as exiting cleanly compared to previous versions. (From OE-Core rev: 36734f34fe6e4b91e293234687e63c02f5b3117e) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/classes/devshell.bbclass121
-rwxr-xr-xscripts/oepydevshell-internal.py92
2 files changed, 213 insertions, 0 deletions
diff --git a/meta/classes/devshell.bbclass b/meta/classes/devshell.bbclass
index 92edb9ef25..41164a3f33 100644
--- a/meta/classes/devshell.bbclass
+++ b/meta/classes/devshell.bbclass
@@ -31,3 +31,124 @@ python () {
31 d.setVarFlag("do_devshell", "manualfakeroot", "1") 31 d.setVarFlag("do_devshell", "manualfakeroot", "1")
32 d.delVarFlag("do_devshell", "fakeroot") 32 d.delVarFlag("do_devshell", "fakeroot")
33} 33}
34
35def devpyshell(d):
36
37 import code
38 import select
39 import signal
40 import termios
41
42 m, s = os.openpty()
43 sname = os.ttyname(s)
44
45 def noechoicanon(fd):
46 old = termios.tcgetattr(fd)
47 old[3] = old[3] &~ termios.ECHO &~ termios.ICANON
48 # &~ termios.ISIG
49 termios.tcsetattr(fd, termios.TCSADRAIN, old)
50
51 # No echo or buffering over the pty
52 noechoicanon(s)
53
54 pid = os.fork()
55 if pid:
56 os.close(m)
57 oe_terminal("oepydevshell-internal.py %s %d" % (sname, pid), 'OpenEmbedded Developer PyShell', d)
58 os._exit(0)
59 else:
60 os.close(s)
61
62 os.dup2(m, sys.stdin.fileno())
63 os.dup2(m, sys.stdout.fileno())
64 os.dup2(m, sys.stderr.fileno())
65
66 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
67 sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
68
69 bb.utils.nonblockingfd(sys.stdout)
70 bb.utils.nonblockingfd(sys.stderr)
71 bb.utils.nonblockingfd(sys.stdin)
72
73 _context = {
74 "os": os,
75 "bb": bb,
76 "time": time,
77 "d": d,
78 }
79
80 ps1 = "pydevshell> "
81 ps2 = "... "
82 buf = []
83 more = False
84
85 i = code.InteractiveInterpreter(locals=_context)
86 print("OE PyShell (PN = %s)\n" % d.getVar("PN", True))
87
88 def prompt(more):
89 if more:
90 prompt = ps2
91 else:
92 prompt = ps1
93 sys.stdout.write(prompt)
94
95 # Restore Ctrl+C since bitbake masks this
96 def signal_handler(signal, frame):
97 raise KeyboardInterrupt
98 signal.signal(signal.SIGINT, signal_handler)
99
100 child = None
101
102 prompt(more)
103 while True:
104 try:
105 try:
106 (r, _, _) = select.select([sys.stdin], [], [], 1)
107 if not r:
108 continue
109 line = sys.stdin.readline().strip()
110 if not line:
111 prompt(more)
112 continue
113 except EOFError as e:
114 sys.stdout.write("\n")
115 except (OSError, IOError) as e:
116 if e.errno == 11:
117 continue
118 if e.errno == 5:
119 return
120 raise
121 else:
122 if not child:
123 child = int(line)
124 continue
125 buf.append(line)
126 source = "\n".join(buf)
127 more = i.runsource(source, "<pyshell>")
128 if not more:
129 buf = []
130 prompt(more)
131 except KeyboardInterrupt:
132 i.write("\nKeyboardInterrupt\n")
133 buf = []
134 more = False
135 prompt(more)
136 except SystemExit:
137 # Easiest way to ensure everything exits
138 os.kill(child, signal.SIGTERM)
139 break
140
141python do_devpyshell() {
142 import signal
143
144 try:
145 devpyshell(d)
146 except SystemExit:
147 # Stop the SIGTERM above causing an error exit code
148 return
149 finally:
150 return
151}
152addtask devpyshell after do_patch
153
154do_devpyshell[nostamp] = "1"
diff --git a/scripts/oepydevshell-internal.py b/scripts/oepydevshell-internal.py
new file mode 100755
index 0000000000..f7b2e4e0bf
--- /dev/null
+++ b/scripts/oepydevshell-internal.py
@@ -0,0 +1,92 @@
1#!/usr/bin/env python
2
3import os
4import sys
5import time
6import select
7import fcntl
8import termios
9import readline
10import signal
11
12def nonblockingfd(fd):
13 fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
14
15def echonocbreak(fd):
16 old = termios.tcgetattr(fd)
17 old[3] = old[3] | termios.ECHO | termios.ICANON
18 termios.tcsetattr(fd, termios.TCSADRAIN, old)
19
20def cbreaknoecho(fd):
21 old = termios.tcgetattr(fd)
22 old[3] = old[3] &~ termios.ECHO &~ termios.ICANON
23 termios.tcsetattr(fd, termios.TCSADRAIN, old)
24
25if len(sys.argv) != 3:
26 print("Incorrect parameters")
27 sys.exit(1)
28
29pty = open(sys.argv[1], "w+b", 0)
30parent = int(sys.argv[2])
31
32# Don't buffer output by line endings
33sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
34sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
35nonblockingfd(pty)
36nonblockingfd(sys.stdin)
37
38
39histfile = os.path.expanduser("~/.oedevpyshell-history")
40readline.parse_and_bind("tab: complete")
41try:
42 readline.read_history_file(histfile)
43except IOError:
44 pass
45
46try:
47
48 i = ""
49 o = ""
50 # Need cbreak/noecho whilst in select so we trigger on any keypress
51 cbreaknoecho(sys.stdin.fileno())
52 # Send our PID to the other end so they can kill us.
53 pty.write(str(os.getpid()) + "\n")
54 while True:
55 try:
56 writers = []
57 if i:
58 writers.append(sys.stdout)
59 (ready, _, _) = select.select([pty, sys.stdin], writers , [], 0)
60 try:
61 if pty in ready:
62 i = i + pty.read()
63 if i:
64 # Write a page at a time to avoid overflowing output
65 # d.keys() is a good way to do that
66 sys.stdout.write(i[:4096])
67 i = i[4096:]
68 if sys.stdin in ready:
69 echonocbreak(sys.stdin.fileno())
70 o = raw_input()
71 cbreaknoecho(sys.stdin.fileno())
72 pty.write(o + "\n")
73 except (IOError, OSError) as e:
74 if e.errno == 11:
75 continue
76 if e.errno == 5:
77 sys.exit(0)
78 raise
79 except EOFError:
80 sys.exit(0)
81 except KeyboardInterrupt:
82 os.kill(parent, signal.SIGINT)
83
84except SystemExit:
85 pass
86except Exception as e:
87 import traceback
88 print("Exception in oepydehshell-internal: " + str(e))
89 traceback.print_exc()
90 time.sleep(5)
91finally:
92 readline.write_history_file(histfile)