2 Executes a set of implementations as a program.
5 # Copyright (C) 2009, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 from zeroinstall
import _
10 from logging
import debug
, info
12 from zeroinstall
.injector
.model
import SafeException
, EnvironmentBinding
, ZeroInstallImplementation
13 from zeroinstall
.injector
.iface_cache
import iface_cache
15 def do_env_binding(binding
, path
):
16 """Update this process's environment by applying the binding.
17 @param binding: the binding to apply
18 @type binding: L{model.EnvironmentBinding}
19 @param path: the selected implementation
21 os
.environ
[binding
.name
] = binding
.get_value(path
,
22 os
.environ
.get(binding
.name
, None))
23 info("%s=%s", binding
.name
, os
.environ
[binding
.name
])
25 def execute(policy
, prog_args
, dry_run
= False, main
= None, wrapper
= None):
26 """Execute program. On success, doesn't return. On failure, raises an Exception.
27 Returns normally only for a successful dry run.
28 @param policy: a policy with the selected versions
29 @type policy: L{policy.Policy}
30 @param prog_args: arguments to pass to the program
31 @type prog_args: [str]
32 @param dry_run: if True, just print a message about what would have happened
34 @param main: the name of the binary to run, or None to use the default
36 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
38 @precondition: C{policy.ready and policy.get_uncached_implementations() == []}
41 for iface
, impl
in policy
.implementation
.iteritems():
43 _do_bindings(impl
, impl
.bindings
)
44 for dep
in policy
.solver
.requires
[iface
]:
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
)
50 debug(_("Implementation %s is native; no bindings needed"), dep_impl
)
52 root_iface
= iface_cache
.get_interface(policy
.root
)
53 root_impl
= policy
.get_implementation(root_iface
)
54 _execute(root_impl
, prog_args
, dry_run
, main
, wrapper
)
56 def _do_bindings(impl
, bindings
):
58 if isinstance(b
, EnvironmentBinding
):
59 do_env_binding(b
, _get_implementation_path(impl
))
61 def _get_implementation_path(impl
):
62 return impl
.local_path
or iface_cache
.stores
.lookup_any(impl
.digests
)
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
73 @param main: the name of the binary to run, or None to use the default
75 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
78 @precondition: All implementations are in the cache.
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
98 output
= tempfile
.TemporaryFile(prefix
= '0launch-test')
105 os
.dup2(output
.fileno(), 1)
106 os
.dup2(output
.fileno(), 2)
107 execute_selections(selections
, prog_args
, dry_run
, main
)
110 traceback
.print_exc()
116 info(_("Waiting for test process to finish..."))
118 pid
, status
= os
.waitpid(child
, 0)
122 results
= output
.read()
124 results
+= _("Error from child process: exit code = %d") % status
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
138 main
= root_impl
.main
139 elif main
.startswith('/'):
142 main
= os
.path
.join(os
.path
.dirname(root_impl
.main
), main
)
144 prog_path
= os
.path
.join(_get_implementation_path(root_impl
), main
)
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)") %
151 if not os
.path
.exists(prog_path
):
152 raise SafeException(_("File '%(program_path)s' does not exist.\n"
153 "(implementation '%(implementation_id)s' + program '%(main)s')") %
154 {'program_path': prog_path
, 'implementation_id': root_impl
.id,
157 prog_args
= ['-c', wrapper
+ ' "$@"', '-', prog_path
] + list(prog_args
)
158 prog_path
= '/bin/sh'
161 print _("Would execute: %s") % ' '.join([prog_path
] + prog_args
)
163 info(_("Executing: %s"), prog_path
)
167 os
.execl(prog_path
, prog_path
, *prog_args
)
169 raise SafeException(_("Failed to run '%(program_path)s': %(exception)s") % {'program_path': prog_path
, 'exception': str(ex
)})