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.
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
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
33 @param main: the name of the binary to run, or None to use the default
35 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
37 @precondition: C{policy.ready and policy.get_uncached_implementations() == []}
39 iface
= iface_cache
.get_interface(policy
.root
)
41 for impl
in policy
.implementation
.values():
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
)
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
):
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
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
.id), 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 '%s' does not exist.\n"
153 "(implementation '%s' + program '%s')" %
154 (prog_path
, root_impl
.id, main
))
156 prog_args
= ['-c', wrapper
+ ' "$@"', '-', prog_path
] + list(prog_args
)
157 prog_path
= '/bin/sh'
160 print "Would execute:", prog_path
, ' '.join(prog_args
)
162 info("Executing: %s", prog_path
)
166 os
.execl(prog_path
, prog_path
, *prog_args
)
168 raise SafeException("Failed to run '%s': %s" % (prog_path
, str(ex
)))