From f2d394387b974774320dfa0aa2b69f74a49a8030 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 22 Apr 2007 15:33:42 +0000 Subject: [PATCH] Added --wrapper option for debugging and tracing. git-svn-id: file:///home/talex/Backups/sf.net/Subversion/zero-install/trunk/0launch@1681 9f8c893c-44ee-0310-b757-c8ca8341c71e --- 0launch.1 | 26 ++++++++++++++++++++++++++ zeroinstall/injector/autopolicy.py | 4 ++-- zeroinstall/injector/cli.py | 7 ++++--- zeroinstall/injector/run.py | 31 +++++++++++++++++++++++++------ zeroinstall/zerostore/unpack.py | 23 +++++++++-------------- 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/0launch.1 b/0launch.1 index acf9a07..f32cafe 100644 --- a/0launch.1 +++ b/0launch.1 @@ -170,6 +170,32 @@ More verbose output. Use twice for even more verbose output. \fB-V\fP, \fB--version\fP Display version information. +.TP +\fB-w\fP, \fB--wrapper=COMMAND\fP +Instead of executing the chosen program directly, run \fBCOMMAND PROGRAM ARGS\fP. +This is useful for running debuggers and tracing tools on the program (rather +than on 0launch!). Note that the wrapper is executed in the environment selected +by the program; hence, this mechanism cannot be used for sandboxing. See the +DEBUGGING section below. + +.SH DEBUGGING TIPS + +.PP +To debug 0launch itself, use the --verbose and --console options. For example: + +.B $ 0launch -vvc http://myprog + +.PP +To trace or debug programs run by 0launch, use the --wrapper option. +For example, to run \fBmyprog --help\fP, displaying all calls to open(2): + +.B $ 0launch --wrapper="strace -e open" http://myprog --help + +If your program is interpreted (e.g. a Python program), and you wish to debug +the interpreter running it, you can do it like this: + +.B $ 0launch --wrapper="gdb --args python" http://myprog --help + .SH FILES Configuration files (see freedesktop.org basedir spec): diff --git a/zeroinstall/injector/autopolicy.py b/zeroinstall/injector/autopolicy.py index 104256f..cec0ab2 100644 --- a/zeroinstall/injector/autopolicy.py +++ b/zeroinstall/injector/autopolicy.py @@ -67,11 +67,11 @@ class AutoPolicy(policy.Policy): raise NeedDownload(download_source.url) return policy.Policy.begin_archive_download(self, download_source, success_callback, force = force) - def execute(self, prog_args, main = None): + def execute(self, prog_args, main = None, wrapper = None): self.start_downloading_impls() self.handler.wait_for_downloads() if not self.download_only: - run.execute(self, prog_args, dry_run = self.dry_run, main = main) + run.execute(self, prog_args, dry_run = self.dry_run, main = main, wrapper = wrapper) else: info("Downloads done (download-only mode)") diff --git a/zeroinstall/injector/cli.py b/zeroinstall/injector/cli.py index 7aafed1..4e1a6ce 100755 --- a/zeroinstall/injector/cli.py +++ b/zeroinstall/injector/cli.py @@ -162,7 +162,7 @@ def _normal_mode(options, args): if options.get_selections: _get_selections(policy) else: - policy.execute(args[1:], main = options.main) + policy.execute(args[1:], main = options.main, wrapper = options.wrapper) assert options.dry_run or options.download_only return @@ -204,7 +204,7 @@ def _normal_mode(options, args): doc.writexml(sys.stdout) sys.stdout.write('\n') elif not options.download_only: - run.execute_selections(sels, prog_args, options.dry_run, options.main) + run.execute_selections(sels, prog_args, options.dry_run, options.main, options.wrapper) else: #program_log('download_and_execute ' + iface_uri) policy.download_and_execute(prog_args, refresh = bool(options.refresh), main = options.main) @@ -364,6 +364,7 @@ def main(command_args): parser.add_option("-s", "--source", help="select source code", action='store_true') parser.add_option("-v", "--verbose", help="more verbose output", action='count') parser.add_option("-V", "--version", help="display version information", action='store_true') + parser.add_option("-w", "--wrapper", help="execute program using a debugger, etc", metavar='COMMAND') parser.disable_interspersed_args() (options, args) = parser.parse_args(command_args) @@ -392,7 +393,7 @@ def main(command_args): elif options.set_selections: from zeroinstall.injector import selections, qdom, run sels = selections.Selections(qdom.parse(file(options.set_selections))) - run.execute_selections(sels, args, options.dry_run, options.main) + run.execute_selections(sels, args, options.dry_run, options.main, options.wrapper) elif getattr(options, 'import'): _import_interface(args) elif options.feed: diff --git a/zeroinstall/injector/run.py b/zeroinstall/injector/run.py index acd401b..7b4bbeb 100644 --- a/zeroinstall/injector/run.py +++ b/zeroinstall/injector/run.py @@ -16,7 +16,7 @@ def do_env_binding(binding, path): os.environ.get(binding.name, None)) info("%s=%s", binding.name, os.environ[binding.name]) -def execute(policy, prog_args, dry_run = False, main = None): +def execute(policy, prog_args, dry_run = False, main = None, wrapper = None): """Execute program. On success, doesn't return. On failure, raises an Exception. Returns normally only for a successful dry run. @@ -35,13 +35,13 @@ def execute(policy, prog_args, dry_run = False, main = None): do_env_binding(b, policy.get_implementation_path(dep_impl)) root_impl = policy.get_implementation(iface) - _execute(root_impl, prog_args, dry_run, main) + _execute(root_impl, prog_args, dry_run, main, wrapper) def _get_implementation_path(id): if id.startswith('/'): return id return iface_cache.stores.lookup(id) -def execute_selections(selections, prog_args, dry_run = False, main = None): +def execute_selections(selections, prog_args, dry_run = False, main = None, wrapper = None): """Execute program. On success, doesn't return. On failure, raises an Exception. Returns normally only for a successful dry run. @@ -56,11 +56,12 @@ def execute_selections(selections, prog_args, dry_run = False, main = None): do_env_binding(b, _get_implementation_path(dep_impl.id)) root_impl = sels[selections.interface] - _execute(root_impl, prog_args, dry_run, main) + _execute(root_impl, prog_args, dry_run, main, wrapper) -def test_selections(selections, prog_args, dry_run, main): +def test_selections(selections, prog_args, dry_run, main, wrapper = None): """Run the program in a child process, collecting stdout and stderr. @return: the output produced by the process + @since: 0.27 """ args = [] import tempfile @@ -96,7 +97,7 @@ def test_selections(selections, prog_args, dry_run, main): return results -def _execute(root_impl, prog_args, dry_run, main): +def _execute(root_impl, prog_args, dry_run, main, wrapper): assert root_impl is not None if main is None: @@ -116,6 +117,10 @@ def _execute(root_impl, prog_args, dry_run, main): raise SafeException("File '%s' does not exist.\n" "(implementation '%s' + program '%s')" % (prog_path, root_impl.id, main)) + if wrapper: + prog_args = ['-c', wrapper + ' "$@"', '-', prog_path] + list(prog_args) + prog_path = '/bin/sh' + if dry_run: print "Would execute:", prog_path else: @@ -126,3 +131,17 @@ def _execute(root_impl, prog_args, dry_run, main): os.execl(prog_path, prog_path, *prog_args) except OSError, ex: raise SafeException("Failed to run '%s': %s" % (prog_path, str(ex))) + +def find_in_path(prog): + """Search $PATH for prog. + If prog is an absolute path, return it unmodified. + @param prog: name of executable to find + @return: the full path of prog, or None if not found + @since: 0.27 + """ + if os.path.isabs(prog): return prog + for d in os.environ['PATH'].split(':'): + path = os.path.join(d, prog) + if os.path.isfile(path): + return path + return None diff --git a/zeroinstall/zerostore/unpack.py b/zeroinstall/zerostore/unpack.py index 4964f54..bfe20dd 100644 --- a/zeroinstall/zerostore/unpack.py +++ b/zeroinstall/zerostore/unpack.py @@ -11,6 +11,7 @@ import sha import re from logging import debug, info, warn from zeroinstall import SafeException +from zeroinstall.injector.run import find_in_path _cpio_version = None def _get_cpio_version(): @@ -53,13 +54,7 @@ def recent_gnu_tar(): debug("Recent GNU tar = %s", recent_gnu_tar) return recent_gnu_tar -def _find_in_path(prog): - for d in os.environ['PATH'].split(':'): - path = os.path.join(d, prog) - if os.path.isfile(path): - return path - return None -_pola_run = _find_in_path('pola-run') +_pola_run = find_in_path('pola-run') if _pola_run: info('Found pola-run: %s', _pola_run) else: @@ -84,30 +79,30 @@ def check_type_ok(mime_type): @raise SafeException: if the needed software is not available""" assert mime_type if mime_type == 'application/x-rpm': - if not _find_in_path('rpm2cpio'): + if not find_in_path('rpm2cpio'): raise SafeException("This package looks like an RPM, but you don't have the rpm2cpio command " "I need to extract it. Install the 'rpm' package first (this works even if " "you're on a non-RPM-based distribution such as Debian).") elif mime_type == 'application/x-deb': - if not _find_in_path('ar'): + if not find_in_path('ar'): raise SafeException("This package looks like a Debian package, but you don't have the 'ar' command " "I need to extract it. Install the package containing it (sometimes called 'binutils') " "first. This works even if you're on a non-Debian-based distribution such as Red Hat).") elif mime_type == 'application/x-bzip-compressed-tar': - if not _find_in_path('bunzip2'): + if not find_in_path('bunzip2'): raise SafeException("This package looks like a bzip2-compressed package, but you don't have the 'bunzip2' command " "I need to extract it. Install the package containing it (it's probably called 'bzip2') " "first.") elif mime_type == 'application/zip': - if not _find_in_path('unzip'): + if not find_in_path('unzip'): raise SafeException("This package looks like a zip-compressed archive, but you don't have the 'unzip' command " "I need to extract it. Install the package containing it first.") elif mime_type == 'application/vnd.ms-cab-compressed': - if not _find_in_path('cabextract'): + if not find_in_path('cabextract'): raise SafeException("This package looks like a Microsoft Cabinet archive, but you don't have the 'cabextract' command " "I need to extract it. Install the package containing it first.") elif mime_type == 'application/x-lzma-compressed-tar': - if not _find_in_path('unlzma'): + if not find_in_path('unlzma'): raise SafeException("This package looks like an LZMA archive, but you don't have the 'unlzma' command " "I need to extract it. Install the package containing it (it's probably called 'lzma') first.") elif mime_type in ('application/x-compressed-tar', 'application/x-tar'): @@ -119,7 +114,7 @@ def check_type_ok(mime_type): def _exec_maybe_sandboxed(writable, prog, *args): """execlp prog, with (only) the 'writable' directory writable if sandboxing is available. If no sandbox is available, run without a sandbox.""" - prog_path = _find_in_path(prog) + prog_path = find_in_path(prog) if not prog_path: raise Exception("'%s' not found in $PATH" % prog) if _pola_run is None: os.execlp(prog_path, prog_path, *args) -- 2.11.4.GIT