]> arthur.barton.de Git - bup.git/commitdiff
Move bup to cmd/ and symlink ./bup to cmd/bup
authorRob Browning <rlb@defaultvalue.org>
Sat, 12 Oct 2019 21:47:16 +0000 (16:47 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sun, 15 Dec 2019 20:56:37 +0000 (14:56 -0600)
Move main.py to cmd/bup and make ./bup a permanent symlink to it.
Make the installed bup executable a relative symlink to the
LIBDIR/cmd/bup file.  Always use the location of the bup executable as
a way to locate the other subcommands and the lib and resource
directories, either when running from the source tree or from an
install tree.

This work finishes the switch to have all the commands to rely on the
bup-python wrapper so that we can establish some norms we'll need to
support Python 3.  It should also allow us to simplify the main.py
startup process.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
.gitignore
Makefile
bup [new symlink]
cmd/bup [new file with mode: 0755]
lib/bup/t/tclient.py
lib/bup/t/tgit.py
main.py [deleted file]

index d82e27a4430f9c5cc1bec61413359d1f82fbad11..8ea45fef03408e9cc39a17320838bc093d0eba0c 100644 (file)
@@ -1,4 +1,3 @@
-/bup
 /cmd/bup-*
 randomgen
 memtest
index 338d5958785f443cf7dba9aa2f8038b5f6e60b96..d285f3ac71e3e7083679653e77fe5f21a829af82 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -50,22 +50,13 @@ bup_cmds := cmd/bup-python \
   $(patsubst cmd/%-cmd.py,cmd/bup-%,$(wildcard cmd/*-cmd.py)) \
   $(patsubst cmd/%-cmd.sh,cmd/bup-%,$(wildcard cmd/*-cmd.sh))
 
-bup_deps := bup lib/bup/_checkout.py lib/bup/_helpers$(SOEXT) $(bup_cmds)
+bup_deps := lib/bup/_checkout.py lib/bup/_helpers$(SOEXT) $(bup_cmds)
 
 all: $(bup_deps) Documentation/all $(current_sampledata)
 
-bup:
-       ln -s main.py bup
-
 $(current_sampledata):
        t/configure-sampledata --setup
 
-define install-python-bin
-  set -e; \
-  sed -e '1 s|.*|#!$(bup_python)|; 2,/^# end of bup preamble$$/d' $1 > $2; \
-  chmod 0755 $2;
-endef
-
 PANDOC ?= $(shell type -p pandoc)
 
 ifeq (,$(PANDOC))
@@ -98,8 +89,11 @@ install: all
        test -z "$(man_roff)" || $(INSTALL) -m 0644 $(man_roff) $(dest_mandir)/man1
        test -z "$(man_html)" || install -d $(dest_docdir)
        test -z "$(man_html)" || $(INSTALL) -m 0644 $(man_html) $(dest_docdir)
-       $(call install-python-bin,bup,"$(dest_bindir)/bup")
+       $(INSTALL) -pm 0755 cmd/bup $(dest_libdir)/cmd/
        $(INSTALL) -pm 0755 cmd/bup-* $(dest_libdir)/cmd/
+       cd "$(dest_bindir)" && \
+         ln -sf "$$($(bup_python) -c 'import os; print(os.path.relpath("$(abspath $(dest_libdir))/cmd/bup"))')"
+       set -e; \
        $(INSTALL) -pm 0644 \
                lib/bup/*.py \
                $(dest_libdir)/bup
@@ -321,7 +315,6 @@ clean: Documentation/clean cmd/bup-python
        rm -f *.o lib/*/*.o *.so lib/*/*.so *.dll lib/*/*.dll *.exe \
                .*~ *~ */*~ lib/*/*~ lib/*/*/*~ \
                *.pyc */*.pyc lib/*/*.pyc lib/*/*/*.pyc \
-               bup \
                randomgen memtest \
                testfs.img lib/bup/t/testfs.img
        for x in $$(ls cmd/*-cmd.py cmd/*-cmd.sh | grep -vF python-cmd.sh | cut -b 5-); do \
diff --git a/bup b/bup
new file mode 120000 (symlink)
index 0000000..f4083c1
--- /dev/null
+++ b/bup
@@ -0,0 +1 @@
+cmd/bup
\ No newline at end of file
diff --git a/cmd/bup b/cmd/bup
new file mode 100755 (executable)
index 0000000..e442541
--- /dev/null
+++ b/cmd/bup
@@ -0,0 +1,274 @@
+#!/bin/sh
+"""": # -*-python-*- # -*-python-*-
+set -e
+top="$(pwd)"
+cmdpath="$0"
+# loop because macos doesn't have recursive readlink/realpath utils
+while test -L "$cmdpath"; do
+    link="$(readlink "$cmdpath")"
+    cd "$(dirname "$cmdpath")"
+    cmdpath="$link"
+done
+script_home="$(cd "$(dirname "$cmdpath")" && pwd -P)"
+cd "$top"
+exec "$script_home/bup-python" "$0" ${1+"$@"}
+"""
+# end of bup preamble
+
+from __future__ import absolute_import, print_function
+import errno, re, sys, os, subprocess, signal, getopt
+
+if sys.version_info[0] != 2 \
+   and not os.environ.get('BUP_ALLOW_UNEXPECTED_PYTHON_VERSION') == 'true':
+    print('error: bup may crash with python versions other than 2, or eat your data',
+          file=sys.stderr)
+    sys.exit(2)
+
+from subprocess import PIPE
+from sys import stderr, stdout
+import select
+
+argv = sys.argv
+exe = os.path.realpath(argv[0])
+exepath = os.path.split(exe)[0] or '.'
+
+# fix the PYTHONPATH to include our lib dir
+if os.path.exists("%s/../bup/." % exepath):
+    # Everything is relative to exepath (i.e. LIBDIR/cmd/)
+    cmdpath = exepath
+    libpath = os.path.join(exepath, '..')
+    resourcepath = libpath
+else:
+    # running from the src directory without being installed first
+    cmdpath = exepath
+    libpath = os.path.join(exepath, '../lib')
+    resourcepath = libpath
+sys.path[:0] = [libpath]
+os.environ['PYTHONPATH'] = libpath + ':' + os.environ.get('PYTHONPATH', '')
+os.environ['BUP_MAIN_EXE'] = os.path.abspath(exe)
+os.environ['BUP_RESOURCE_PATH'] = resourcepath
+
+
+from bup import compat, helpers
+from bup.compat import add_ex_tb, add_ex_ctx, wrap_main
+from bup.helpers import atoi, columnate, debug1, log, merge_dict, tty_width
+
+
+def usage(msg=""):
+    log('Usage: bup [-?|--help] [-d BUP_DIR] [--debug] [--profile] '
+        '<command> [options...]\n\n')
+    common = dict(
+        ftp = 'Browse backup sets using an ftp-like client',
+        fsck = 'Check backup sets for damage and add redundancy information',
+        fuse = 'Mount your backup sets as a filesystem',
+        help = 'Print detailed help for the given command',
+        index = 'Create or display the index of files to back up',
+        on = 'Backup a remote machine to the local one',
+        restore = 'Extract files from a backup set',
+        save = 'Save files into a backup set (note: run "bup index" first)',
+        tag = 'Tag commits for easier access',
+        web = 'Launch a web server to examine backup sets',
+    )
+
+    log('Common commands:\n')
+    for cmd,synopsis in sorted(common.items()):
+        log('    %-10s %s\n' % (cmd, synopsis))
+    log('\n')
+    
+    log('Other available commands:\n')
+    cmds = []
+    for c in sorted(os.listdir(cmdpath) + os.listdir(exepath)):
+        if c.startswith('bup-') and c.find('.') < 0:
+            cname = c[4:]
+            if cname not in common:
+                cmds.append(c[4:])
+    log(columnate(cmds, '    '))
+    log('\n')
+    
+    log("See 'bup help COMMAND' for more information on " +
+        "a specific command.\n")
+    if msg:
+        log("\n%s\n" % msg)
+    sys.exit(99)
+
+
+if len(argv) < 2:
+    usage()
+
+# Handle global options.
+try:
+    optspec = ['help', 'version', 'debug', 'profile', 'bup-dir=']
+    global_args, subcmd = getopt.getopt(argv[1:], '?VDd:', optspec)
+except getopt.GetoptError as ex:
+    usage('error: %s' % ex.msg)
+
+help_requested = None
+do_profile = False
+
+for opt in global_args:
+    if opt[0] in ['-?', '--help']:
+        help_requested = True
+    elif opt[0] in ['-V', '--version']:
+        subcmd = ['version']
+    elif opt[0] in ['-D', '--debug']:
+        helpers.buglvl += 1
+        os.environ['BUP_DEBUG'] = str(helpers.buglvl)
+    elif opt[0] in ['--profile']:
+        do_profile = True
+    elif opt[0] in ['-d', '--bup-dir']:
+        os.environ['BUP_DIR'] = opt[1]
+    else:
+        usage('error: unexpected option "%s"' % opt[0])
+
+# Make BUP_DIR absolute, so we aren't affected by chdir (i.e. save -C, etc.).
+if 'BUP_DIR' in os.environ:
+    os.environ['BUP_DIR'] = os.path.abspath(os.environ['BUP_DIR'])
+
+if len(subcmd) == 0:
+    if help_requested:
+        subcmd = ['help']
+    else:
+        usage()
+
+if help_requested and subcmd[0] != 'help':
+    subcmd = ['help'] + subcmd
+
+if len(subcmd) > 1 and subcmd[1] == '--help' and subcmd[0] != 'help':
+    subcmd = ['help', subcmd[0]] + subcmd[2:]
+
+subcmd_name = subcmd[0]
+if not subcmd_name:
+    usage()
+
+def subpath(s):
+    sp = os.path.join(exepath, 'bup-%s' % s)
+    if not os.path.exists(sp):
+        sp = os.path.join(cmdpath, 'bup-%s' % s)
+    return sp
+
+subcmd[0] = subpath(subcmd_name)
+if not os.path.exists(subcmd[0]):
+    usage('error: unknown command "%s"' % subcmd_name)
+
+already_fixed = atoi(os.environ.get('BUP_FORCE_TTY'))
+if subcmd_name in ['mux', 'ftp', 'help']:
+    already_fixed = True
+fix_stdout = not already_fixed and os.isatty(1)
+fix_stderr = not already_fixed and os.isatty(2)
+
+if fix_stdout or fix_stderr:
+    tty_env = merge_dict(os.environ,
+                         {'BUP_FORCE_TTY': str((fix_stdout and 1 or 0)
+                                               + (fix_stderr and 2 or 0))})
+else:
+    tty_env = os.environ
+
+
+sep_rx = re.compile(br'([\r\n])')
+
+def print_clean_line(dest, content, width, sep=None):
+    """Write some or all of content, followed by sep, to the dest fd after
+    padding the content with enough spaces to fill the current
+    terminal width or truncating it to the terminal width if sep is a
+    carriage return."""
+    global sep_rx
+    assert sep in (b'\r', b'\n', None)
+    if not content:
+        if sep:
+            os.write(dest, sep)
+        return
+    for x in content:
+        assert not sep_rx.match(x)
+    content = b''.join(content)
+    if sep == b'\r' and len(content) > width:
+        content = content[width:]
+    os.write(dest, content)
+    if len(content) < width:
+        os.write(dest, b' ' * (width - len(content)))
+    if sep:
+        os.write(dest, sep)
+
+def filter_output(src_out, src_err, dest_out, dest_err):
+    """Transfer data from src_out to dest_out and src_err to dest_err via
+    print_clean_line until src_out and src_err close."""
+    global sep_rx
+    assert not isinstance(src_out, bool)
+    assert not isinstance(src_err, bool)
+    assert not isinstance(dest_out, bool)
+    assert not isinstance(dest_err, bool)
+    assert src_out is not None or src_err is not None
+    assert (src_out is None) == (dest_out is None)
+    assert (src_err is None) == (dest_err is None)
+    pending = {}
+    pending_ex = None
+    try:
+        fds = tuple([x for x in (src_out, src_err) if x is not None])
+        while fds:
+            ready_fds, _, _ = select.select(fds, [], [])
+            width = tty_width()
+            for fd in ready_fds:
+                buf = os.read(fd, 4096)
+                dest = dest_out if fd == src_out else dest_err
+                if not buf:
+                    fds = tuple([x for x in fds if x is not fd])
+                    print_clean_line(dest, pending.pop(fd, []), width)
+                else:
+                    split = sep_rx.split(buf)
+                    while len(split) > 1:
+                        content, sep = split[:2]
+                        split = split[2:]
+                        print_clean_line(dest,
+                                         pending.pop(fd, []) + [content],
+                                         width,
+                                         sep)
+                    assert(len(split) == 1)
+                    if split[0]:
+                        pending.setdefault(fd, []).extend(split)
+    except BaseException as ex:
+        pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
+    try:
+        # Try to finish each of the streams
+        for fd, pending_items in compat.items(pending):
+            dest = dest_out if fd == src_out else dest_err
+            try:
+                print_clean_line(dest, pending_items, width)
+            except (EnvironmentError, EOFError) as ex:
+                pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
+    except BaseException as ex:
+        pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
+    if pending_ex:
+        raise pending_ex
+
+def run_subcmd(subcmd):
+
+    c = (do_profile and [sys.executable, '-m', 'cProfile'] or []) + subcmd
+    if not (fix_stdout or fix_stderr):
+        os.execvp(c[0], c)
+
+    p = None
+    try:
+        p = subprocess.Popen(c,
+                             stdout=PIPE if fix_stdout else sys.stdout,
+                             stderr=PIPE if fix_stderr else sys.stderr,
+                             env=tty_env, bufsize=4096, close_fds=True)
+        # Assume p will receive these signals and quit, which will
+        # then cause us to quit.
+        for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGQUIT):
+            signal.signal(sig, signal.SIG_IGN)
+
+        filter_output(fix_stdout and p.stdout.fileno() or None,
+                      fix_stderr and p.stderr.fileno() or None,
+                      fix_stdout and sys.stdout.fileno() or None,
+                      fix_stderr and sys.stderr.fileno() or None)
+        return p.wait()
+    except BaseException as ex:
+        add_ex_tb(ex)
+        try:
+            if p and p.poll() == None:
+                os.kill(p.pid, signal.SIGTERM)
+                p.wait()
+        except BaseException as kill_ex:
+            raise add_ex_ctx(add_ex_tb(kill_ex), ex)
+        raise ex
+        
+wrap_main(lambda : run_subcmd(subcmd))
index f529c229e6004bdc5d4b1788879f4e8355f86f28..2ea4dca0d0f6b23251a0ab3fb2cfdfb8867f7001 100644 (file)
@@ -17,7 +17,7 @@ def randbytes(sz):
 
 
 top_dir = os.path.realpath('../../..')
-bup_exe = top_dir + '/bup'
+bup_exe = top_dir + '/cmd/bup'
 
 s1 = randbytes(10000)
 s2 = randbytes(10000)
index bcc58bd01819cfa815492a5b8743899f1231d7e9..2cf82302c499c8489cf2d5ed92f64a0f82d1acdb 100644 (file)
@@ -12,7 +12,7 @@ from buptest import no_lingering_errors, test_tempdir
 
 
 top_dir = os.path.realpath('../../..')
-bup_exe = top_dir + '/bup'
+bup_exe = top_dir + '/cmd/bup'
 
 
 def exc(*cmd):
diff --git a/main.py b/main.py
deleted file mode 100755 (executable)
index 05e62c4..0000000
--- a/main.py
+++ /dev/null
@@ -1,266 +0,0 @@
-#!/bin/sh
-"""": # -*-python-*- # -*-python-*-
-bup_python="$(dirname "$0")/cmd/bup-python" || exit $?
-exec "$bup_python" "$0" ${1+"$@"}
-"""
-# end of bup preamble
-
-from __future__ import absolute_import, print_function
-import errno, re, sys, os, subprocess, signal, getopt
-
-if sys.version_info[0] != 2 \
-   and not os.environ.get('BUP_ALLOW_UNEXPECTED_PYTHON_VERSION') == 'true':
-    print('error: bup may crash with python versions other than 2, or eat your data',
-          file=sys.stderr)
-    sys.exit(2)
-
-from subprocess import PIPE
-from sys import stderr, stdout
-import select
-
-argv = sys.argv
-exe = os.path.realpath(argv[0])
-exepath = os.path.split(exe)[0] or '.'
-exeprefix = os.path.split(os.path.abspath(exepath))[0]
-
-# fix the PYTHONPATH to include our lib dir
-if os.path.exists("%s/lib/bup/cmd/." % exeprefix):
-    # installed binary in /.../bin.
-    # eg. /usr/bin/bup means /usr/lib/bup/... is where our libraries are.
-    cmdpath = "%s/lib/bup/cmd" % exeprefix
-    libpath = "%s/lib/bup" % exeprefix
-    resourcepath = libpath
-else:
-    # running from the src directory without being installed first
-    cmdpath = os.path.join(exepath, 'cmd')
-    libpath = os.path.join(exepath, 'lib')
-    resourcepath = libpath
-sys.path[:0] = [libpath]
-os.environ['PYTHONPATH'] = libpath + ':' + os.environ.get('PYTHONPATH', '')
-os.environ['BUP_MAIN_EXE'] = os.path.abspath(exe)
-os.environ['BUP_RESOURCE_PATH'] = resourcepath
-
-
-from bup import compat, helpers
-from bup.compat import add_ex_tb, add_ex_ctx, wrap_main
-from bup.helpers import atoi, columnate, debug1, log, merge_dict, tty_width
-
-
-def usage(msg=""):
-    log('Usage: bup [-?|--help] [-d BUP_DIR] [--debug] [--profile] '
-        '<command> [options...]\n\n')
-    common = dict(
-        ftp = 'Browse backup sets using an ftp-like client',
-        fsck = 'Check backup sets for damage and add redundancy information',
-        fuse = 'Mount your backup sets as a filesystem',
-        help = 'Print detailed help for the given command',
-        index = 'Create or display the index of files to back up',
-        on = 'Backup a remote machine to the local one',
-        restore = 'Extract files from a backup set',
-        save = 'Save files into a backup set (note: run "bup index" first)',
-        tag = 'Tag commits for easier access',
-        web = 'Launch a web server to examine backup sets',
-    )
-
-    log('Common commands:\n')
-    for cmd,synopsis in sorted(common.items()):
-        log('    %-10s %s\n' % (cmd, synopsis))
-    log('\n')
-    
-    log('Other available commands:\n')
-    cmds = []
-    for c in sorted(os.listdir(cmdpath) + os.listdir(exepath)):
-        if c.startswith('bup-') and c.find('.') < 0:
-            cname = c[4:]
-            if cname not in common:
-                cmds.append(c[4:])
-    log(columnate(cmds, '    '))
-    log('\n')
-    
-    log("See 'bup help COMMAND' for more information on " +
-        "a specific command.\n")
-    if msg:
-        log("\n%s\n" % msg)
-    sys.exit(99)
-
-
-if len(argv) < 2:
-    usage()
-
-# Handle global options.
-try:
-    optspec = ['help', 'version', 'debug', 'profile', 'bup-dir=']
-    global_args, subcmd = getopt.getopt(argv[1:], '?VDd:', optspec)
-except getopt.GetoptError as ex:
-    usage('error: %s' % ex.msg)
-
-help_requested = None
-do_profile = False
-
-for opt in global_args:
-    if opt[0] in ['-?', '--help']:
-        help_requested = True
-    elif opt[0] in ['-V', '--version']:
-        subcmd = ['version']
-    elif opt[0] in ['-D', '--debug']:
-        helpers.buglvl += 1
-        os.environ['BUP_DEBUG'] = str(helpers.buglvl)
-    elif opt[0] in ['--profile']:
-        do_profile = True
-    elif opt[0] in ['-d', '--bup-dir']:
-        os.environ['BUP_DIR'] = opt[1]
-    else:
-        usage('error: unexpected option "%s"' % opt[0])
-
-# Make BUP_DIR absolute, so we aren't affected by chdir (i.e. save -C, etc.).
-if 'BUP_DIR' in os.environ:
-    os.environ['BUP_DIR'] = os.path.abspath(os.environ['BUP_DIR'])
-
-if len(subcmd) == 0:
-    if help_requested:
-        subcmd = ['help']
-    else:
-        usage()
-
-if help_requested and subcmd[0] != 'help':
-    subcmd = ['help'] + subcmd
-
-if len(subcmd) > 1 and subcmd[1] == '--help' and subcmd[0] != 'help':
-    subcmd = ['help', subcmd[0]] + subcmd[2:]
-
-subcmd_name = subcmd[0]
-if not subcmd_name:
-    usage()
-
-def subpath(s):
-    sp = os.path.join(exepath, 'bup-%s' % s)
-    if not os.path.exists(sp):
-        sp = os.path.join(cmdpath, 'bup-%s' % s)
-    return sp
-
-subcmd[0] = subpath(subcmd_name)
-if not os.path.exists(subcmd[0]):
-    usage('error: unknown command "%s"' % subcmd_name)
-
-already_fixed = atoi(os.environ.get('BUP_FORCE_TTY'))
-if subcmd_name in ['mux', 'ftp', 'help']:
-    already_fixed = True
-fix_stdout = not already_fixed and os.isatty(1)
-fix_stderr = not already_fixed and os.isatty(2)
-
-if fix_stdout or fix_stderr:
-    tty_env = merge_dict(os.environ,
-                         {'BUP_FORCE_TTY': str((fix_stdout and 1 or 0)
-                                               + (fix_stderr and 2 or 0))})
-else:
-    tty_env = os.environ
-
-
-sep_rx = re.compile(br'([\r\n])')
-
-def print_clean_line(dest, content, width, sep=None):
-    """Write some or all of content, followed by sep, to the dest fd after
-    padding the content with enough spaces to fill the current
-    terminal width or truncating it to the terminal width if sep is a
-    carriage return."""
-    global sep_rx
-    assert sep in (b'\r', b'\n', None)
-    if not content:
-        if sep:
-            os.write(dest, sep)
-        return
-    for x in content:
-        assert not sep_rx.match(x)
-    content = b''.join(content)
-    if sep == b'\r' and len(content) > width:
-        content = content[width:]
-    os.write(dest, content)
-    if len(content) < width:
-        os.write(dest, b' ' * (width - len(content)))
-    if sep:
-        os.write(dest, sep)
-
-def filter_output(src_out, src_err, dest_out, dest_err):
-    """Transfer data from src_out to dest_out and src_err to dest_err via
-    print_clean_line until src_out and src_err close."""
-    global sep_rx
-    assert not isinstance(src_out, bool)
-    assert not isinstance(src_err, bool)
-    assert not isinstance(dest_out, bool)
-    assert not isinstance(dest_err, bool)
-    assert src_out is not None or src_err is not None
-    assert (src_out is None) == (dest_out is None)
-    assert (src_err is None) == (dest_err is None)
-    pending = {}
-    pending_ex = None
-    try:
-        fds = tuple([x for x in (src_out, src_err) if x is not None])
-        while fds:
-            ready_fds, _, _ = select.select(fds, [], [])
-            width = tty_width()
-            for fd in ready_fds:
-                buf = os.read(fd, 4096)
-                dest = dest_out if fd == src_out else dest_err
-                if not buf:
-                    fds = tuple([x for x in fds if x is not fd])
-                    print_clean_line(dest, pending.pop(fd, []), width)
-                else:
-                    split = sep_rx.split(buf)
-                    while len(split) > 1:
-                        content, sep = split[:2]
-                        split = split[2:]
-                        print_clean_line(dest,
-                                         pending.pop(fd, []) + [content],
-                                         width,
-                                         sep)
-                    assert(len(split) == 1)
-                    if split[0]:
-                        pending.setdefault(fd, []).extend(split)
-    except BaseException as ex:
-        pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
-    try:
-        # Try to finish each of the streams
-        for fd, pending_items in compat.items(pending):
-            dest = dest_out if fd == src_out else dest_err
-            try:
-                print_clean_line(dest, pending_items, width)
-            except (EnvironmentError, EOFError) as ex:
-                pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
-    except BaseException as ex:
-        pending_ex = add_ex_ctx(add_ex_tb(ex), pending_ex)
-    if pending_ex:
-        raise pending_ex
-
-def run_subcmd(subcmd):
-
-    c = (do_profile and [sys.executable, '-m', 'cProfile'] or []) + subcmd
-    if not (fix_stdout or fix_stderr):
-        os.execvp(c[0], c)
-
-    p = None
-    try:
-        p = subprocess.Popen(c,
-                             stdout=PIPE if fix_stdout else sys.stdout,
-                             stderr=PIPE if fix_stderr else sys.stderr,
-                             env=tty_env, bufsize=4096, close_fds=True)
-        # Assume p will receive these signals and quit, which will
-        # then cause us to quit.
-        for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGQUIT):
-            signal.signal(sig, signal.SIG_IGN)
-
-        filter_output(fix_stdout and p.stdout.fileno() or None,
-                      fix_stderr and p.stderr.fileno() or None,
-                      fix_stdout and sys.stdout.fileno() or None,
-                      fix_stderr and sys.stderr.fileno() or None)
-        return p.wait()
-    except BaseException as ex:
-        add_ex_tb(ex)
-        try:
-            if p and p.poll() == None:
-                os.kill(p.pid, signal.SIGTERM)
-                p.wait()
-        except BaseException as kill_ex:
-            raise add_ex_ctx(add_ex_tb(kill_ex), ex)
-        raise ex
-        
-wrap_main(lambda : run_subcmd(subcmd))