Removed warning about old GnuPG.
[zeroinstall.git] / zeroinstall / injector / run.py
blob5aa38eff0fd2222b9def8f5bc643b26703486e5f
1 """
2 Executes a set of implementations as a program.
3 """
5 # Copyright (C) 2006, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 import os, sys
9 from logging import debug, info
11 from zeroinstall.injector.model import Interface, SafeException, EnvironmentBinding, DistributionImplementation, ZeroInstallImplementation
12 from zeroinstall.injector.iface_cache import iface_cache
14 def do_env_binding(binding, path):
15 os.environ[binding.name] = binding.get_value(path,
16 os.environ.get(binding.name, None))
17 info("%s=%s", binding.name, os.environ[binding.name])
19 def execute(policy, prog_args, dry_run = False, main = None, wrapper = None):
20 """Execute program. On success, doesn't return. On failure, raises an Exception.
21 Returns normally only for a successful dry run.
23 @precondition: C{policy.ready and policy.get_uncached_implementations() == []}
24 """
25 iface = iface_cache.get_interface(policy.root)
27 for needed_iface in policy.implementation:
28 impl = policy.implementation[needed_iface]
29 assert impl
30 _do_bindings(impl, impl.bindings)
31 for dep in impl.requires:
32 dep_iface = iface_cache.get_interface(dep.interface)
33 dep_impl = policy.get_implementation(dep_iface)
34 if isinstance(dep_impl, ZeroInstallImplementation):
35 _do_bindings(dep_impl, dep.bindings)
36 else:
37 debug("Implementation %s is native; no bindings needed", dep_impl)
39 root_impl = policy.get_implementation(iface)
40 _execute(root_impl, prog_args, dry_run, main, wrapper)
42 def _do_bindings(impl, bindings):
43 for b in bindings:
44 if isinstance(b, EnvironmentBinding):
45 do_env_binding(b, _get_implementation_path(impl.id))
47 def _get_implementation_path(id):
48 if id.startswith('/'): return id
49 return iface_cache.stores.lookup(id)
51 def execute_selections(selections, prog_args, dry_run = False, main = None, wrapper = None):
52 """Execute program. On success, doesn't return. On failure, raises an Exception.
53 Returns normally only for a successful dry run.
55 @since: 0.27
56 @precondition: All implementations are in the cache.
57 """
58 sels = selections.selections
59 for selection in sels.values():
60 _do_bindings(selection, selection.bindings)
61 for dep in selection.dependencies:
62 dep_impl = sels[dep.interface]
63 if not dep_impl.id.startswith('package:'):
64 _do_bindings(dep_impl, dep.bindings)
66 root_impl = sels[selections.interface]
67 _execute(root_impl, prog_args, dry_run, main, wrapper)
69 def test_selections(selections, prog_args, dry_run, main, wrapper = None):
70 """Run the program in a child process, collecting stdout and stderr.
71 @return: the output produced by the process
72 @since: 0.27
73 """
74 args = []
75 import tempfile
76 output = tempfile.TemporaryFile(prefix = '0launch-test')
77 try:
78 child = os.fork()
79 if child == 0:
80 # We are the child
81 try:
82 try:
83 os.dup2(output.fileno(), 1)
84 os.dup2(output.fileno(), 2)
85 execute_selections(selections, prog_args, dry_run, main)
86 except:
87 import traceback
88 traceback.print_exc()
89 finally:
90 sys.stdout.flush()
91 sys.stderr.flush()
92 os._exit(1)
94 info("Waiting for test process to finish...")
96 pid, status = os.waitpid(child, 0)
97 assert pid == child
99 output.seek(0)
100 results = output.read()
101 if status != 0:
102 results += "Error from child process: exit code = %d" % status
103 finally:
104 output.close()
106 return results
108 def _execute(root_impl, prog_args, dry_run, main, wrapper):
109 assert root_impl is not None
111 if root_impl.id.startswith('package:'):
112 main = main or root_impl.main
113 prog_path = main
114 else:
115 if main is None:
116 main = root_impl.main
117 elif main.startswith('/'):
118 main = main[1:]
119 elif root_impl.main:
120 main = os.path.join(os.path.dirname(root_impl.main), main)
121 if main:
122 prog_path = os.path.join(_get_implementation_path(root_impl.id), main)
124 if main is None:
125 raise SafeException("Implementation '%s' cannot be executed directly; it is just a library "
126 "to be used by other programs (or missing 'main' attribute)" %
127 root_impl)
129 if not os.path.exists(prog_path):
130 raise SafeException("File '%s' does not exist.\n"
131 "(implementation '%s' + program '%s')" %
132 (prog_path, root_impl.id, main))
133 if wrapper:
134 prog_args = ['-c', wrapper + ' "$@"', '-', prog_path] + list(prog_args)
135 prog_path = '/bin/sh'
137 if dry_run:
138 print "Would execute:", prog_path, ' '.join(prog_args)
139 else:
140 info("Executing: %s", prog_path)
141 sys.stdout.flush()
142 sys.stderr.flush()
143 try:
144 os.execl(prog_path, prog_path, *prog_args)
145 except OSError, ex:
146 raise SafeException("Failed to run '%s': %s" % (prog_path, str(ex)))