Update swedish translations
[zeroinstall/solver.git] / zeroinstall / injector / run.py
bloba4a178e33de26df4ee881225d38d9667e6749a50
1 """
2 Executes a set of implementations as a program.
3 """
5 # Copyright (C) 2009, 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 SafeException, EnvironmentBinding, ZeroInstallImplementation
12 from zeroinstall.injector.iface_cache import iface_cache
14 def do_env_binding(binding, path):
15 """Update this process's environment by applying the binding.
16 @param binding: the binding to apply
17 @type binding: L{model.EnvironmentBinding}
18 @param path: the selected implementation
19 @type path: str"""
20 os.environ[binding.name] = binding.get_value(path,
21 os.environ.get(binding.name, None))
22 info("%s=%s", binding.name, os.environ[binding.name])
24 def execute(policy, prog_args, dry_run = False, main = None, wrapper = None):
25 """Execute program. On success, doesn't return. On failure, raises an Exception.
26 Returns normally only for a successful dry run.
27 @param policy: a policy with the selected versions
28 @type policy: L{policy.Policy}
29 @param prog_args: arguments to pass to the program
30 @type prog_args: [str]
31 @param dry_run: if True, just print a message about what would have happened
32 @type dry_run: bool
33 @param main: the name of the binary to run, or None to use the default
34 @type main: str
35 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
36 @type wrapper: str
37 @precondition: C{policy.ready and policy.get_uncached_implementations() == []}
38 """
39 iface = iface_cache.get_interface(policy.root)
41 for impl in policy.implementation.values():
42 assert impl
43 _do_bindings(impl, impl.bindings)
44 for dep in impl.requires:
45 dep_iface = iface_cache.get_interface(dep.interface)
46 dep_impl = policy.get_implementation(dep_iface)
47 if isinstance(dep_impl, ZeroInstallImplementation):
48 _do_bindings(dep_impl, dep.bindings)
49 else:
50 debug("Implementation %s is native; no bindings needed", dep_impl)
52 root_impl = policy.get_implementation(iface)
53 _execute(root_impl, prog_args, dry_run, main, wrapper)
55 def _do_bindings(impl, bindings):
56 for b in bindings:
57 if isinstance(b, EnvironmentBinding):
58 do_env_binding(b, _get_implementation_path(impl.id))
60 def _get_implementation_path(id):
61 if id.startswith('/'): return id
62 return iface_cache.stores.lookup(id)
64 def execute_selections(selections, prog_args, dry_run = False, main = None, wrapper = None):
65 """Execute program. On success, doesn't return. On failure, raises an Exception.
66 Returns normally only for a successful dry run.
67 @param selections: the selected versions
68 @type selections: L{selections.Selections}
69 @param prog_args: arguments to pass to the program
70 @type prog_args: [str]
71 @param dry_run: if True, just print a message about what would have happened
72 @type dry_run: bool
73 @param main: the name of the binary to run, or None to use the default
74 @type main: str
75 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
76 @type wrapper: str
77 @since: 0.27
78 @precondition: All implementations are in the cache.
79 """
80 sels = selections.selections
81 for selection in sels.values():
82 _do_bindings(selection, selection.bindings)
83 for dep in selection.dependencies:
84 dep_impl = sels[dep.interface]
85 if not dep_impl.id.startswith('package:'):
86 _do_bindings(dep_impl, dep.bindings)
88 root_impl = sels[selections.interface]
89 _execute(root_impl, prog_args, dry_run, main, wrapper)
91 def test_selections(selections, prog_args, dry_run, main, wrapper = None):
92 """Run the program in a child process, collecting stdout and stderr.
93 @return: the output produced by the process
94 @since: 0.27
95 """
96 args = []
97 import tempfile
98 output = tempfile.TemporaryFile(prefix = '0launch-test')
99 try:
100 child = os.fork()
101 if child == 0:
102 # We are the child
103 try:
104 try:
105 os.dup2(output.fileno(), 1)
106 os.dup2(output.fileno(), 2)
107 execute_selections(selections, prog_args, dry_run, main)
108 except:
109 import traceback
110 traceback.print_exc()
111 finally:
112 sys.stdout.flush()
113 sys.stderr.flush()
114 os._exit(1)
116 info("Waiting for test process to finish...")
118 pid, status = os.waitpid(child, 0)
119 assert pid == child
121 output.seek(0)
122 results = output.read()
123 if status != 0:
124 results += "Error from child process: exit code = %d" % status
125 finally:
126 output.close()
128 return results
130 def _execute(root_impl, prog_args, dry_run, main, wrapper):
131 assert root_impl is not None
133 if root_impl.id.startswith('package:'):
134 main = main or root_impl.main
135 prog_path = main
136 else:
137 if main is None:
138 main = root_impl.main
139 elif main.startswith('/'):
140 main = main[1:]
141 elif root_impl.main:
142 main = os.path.join(os.path.dirname(root_impl.main), main)
143 if main is not None:
144 prog_path = os.path.join(_get_implementation_path(root_impl.id), main)
146 if main is None:
147 raise SafeException("Implementation '%s' cannot be executed directly; it is just a library "
148 "to be used by other programs (or missing 'main' attribute)" %
149 root_impl)
151 if not os.path.exists(prog_path):
152 raise SafeException("File '%s' does not exist.\n"
153 "(implementation '%s' + program '%s')" %
154 (prog_path, root_impl.id, main))
155 if wrapper:
156 prog_args = ['-c', wrapper + ' "$@"', '-', prog_path] + list(prog_args)
157 prog_path = '/bin/sh'
159 if dry_run:
160 print "Would execute:", prog_path, ' '.join(prog_args)
161 else:
162 info("Executing: %s", prog_path)
163 sys.stdout.flush()
164 sys.stderr.flush()
165 try:
166 os.execl(prog_path, prog_path, *prog_args)
167 except OSError, ex:
168 raise SafeException("Failed to run '%s': %s" % (prog_path, str(ex)))