1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: MIT
5#
6
7inherit terminal
8
9DEVSHELL = "${SHELL}"
10
11PATH:prepend:task-devshell = "${COREBASE}/scripts/git-intercept:"
12
13python do_devshell () {
14    if d.getVarFlag("do_devshell", "manualfakeroot"):
15       d.prependVar("DEVSHELL", "pseudo ")
16       fakeenv = d.getVar("FAKEROOTENV").split()
17       for f in fakeenv:
18            k = f.split("=")
19            d.setVar(k[0], k[1])
20            d.appendVar("OE_TERMINAL_EXPORTS", " " + k[0])
21       d.delVarFlag("do_devshell", "fakeroot")
22
23    oe_terminal(d.getVar('DEVSHELL'), 'OpenEmbedded Developer Shell', d)
24}
25
26addtask devshell after do_patch do_prepare_recipe_sysroot
27
28# The directory that the terminal starts in
29DEVSHELL_STARTDIR ?= "${S}"
30do_devshell[dirs] = "${DEVSHELL_STARTDIR}"
31do_devshell[nostamp] = "1"
32do_devshell[network] = "1"
33
34# devshell and fakeroot/pseudo need careful handling since only the final
35# command should run under fakeroot emulation, any X connection should
36# be done as the normal user. We therfore carefully construct the envionment
37# manually
38python () {
39    if d.getVarFlag("do_devshell", "fakeroot"):
40       # We need to signal our code that we want fakeroot however we
41       # can't manipulate the environment and variables here yet (see YOCTO #4795)
42       d.setVarFlag("do_devshell", "manualfakeroot", "1")
43       d.delVarFlag("do_devshell", "fakeroot")
44}
45
46def pydevshell(d):
47
48    import code
49    import select
50    import signal
51    import termios
52
53    m, s = os.openpty()
54    sname = os.ttyname(s)
55
56    def noechoicanon(fd):
57        old = termios.tcgetattr(fd)
58        old[3] = old[3] &~ termios.ECHO &~ termios.ICANON
59        # &~ termios.ISIG
60        termios.tcsetattr(fd, termios.TCSADRAIN, old)
61
62    # No echo or buffering over the pty
63    noechoicanon(s)
64
65    pid = os.fork()
66    if pid:
67        os.close(m)
68        oe_terminal("oepydevshell-internal.py %s %d" % (sname, pid), 'OpenEmbedded Developer PyShell', d)
69        os._exit(0)
70    else:
71        os.close(s)
72
73        os.dup2(m, sys.stdin.fileno())
74        os.dup2(m, sys.stdout.fileno())
75        os.dup2(m, sys.stderr.fileno())
76
77        bb.utils.nonblockingfd(sys.stdout)
78        bb.utils.nonblockingfd(sys.stderr)
79        bb.utils.nonblockingfd(sys.stdin)
80
81        _context = {
82            "os": os,
83            "bb": bb,
84            "time": time,
85            "d": d,
86        }
87
88        ps1 = "pydevshell> "
89        ps2 = "... "
90        buf = []
91        more = False
92
93        i = code.InteractiveInterpreter(locals=_context)
94        print("OE PyShell (PN = %s)\n" % d.getVar("PN"))
95
96        def prompt(more):
97            if more:
98                prompt = ps2
99            else:
100                prompt = ps1
101            sys.stdout.write(prompt)
102            sys.stdout.flush()
103
104        # Restore Ctrl+C since bitbake masks this
105        def signal_handler(signal, frame):
106            raise KeyboardInterrupt
107        signal.signal(signal.SIGINT, signal_handler)
108
109        child = None
110
111        prompt(more)
112        while True:
113            try:
114                try:
115                    (r, _, _) = select.select([sys.stdin], [], [], 1)
116                    if not r:
117                        continue
118                    line = sys.stdin.readline().strip()
119                    if not line:
120                        prompt(more)
121                        continue
122                except EOFError as e:
123                    sys.stdout.write("\n")
124                    sys.stdout.flush()
125                except (OSError, IOError) as e:
126                    if e.errno == 11:
127                        continue
128                    if e.errno == 5:
129                        return
130                    raise
131                else:
132                    if not child:
133                        child = int(line)
134                        continue
135                    buf.append(line)
136                    source = "\n".join(buf)
137                    more = i.runsource(source, "<pyshell>")
138                    if not more:
139                        buf = []
140                    sys.stderr.flush()
141                    prompt(more)
142            except KeyboardInterrupt:
143                i.write("\nKeyboardInterrupt\n")
144                buf = []
145                more = False
146                prompt(more)
147            except SystemExit:
148                # Easiest way to ensure everything exits
149                os.kill(child, signal.SIGTERM)
150                break
151
152python do_pydevshell() {
153    import signal
154
155    try:
156        pydevshell(d)
157    except SystemExit:
158        # Stop the SIGTERM above causing an error exit code
159        return
160    finally:
161        return
162}
163addtask pydevshell after do_patch
164
165do_pydevshell[nostamp] = "1"
166do_pydevshell[network] = "1"
167