2 Executes a set of implementations as a program.
5 # Copyright (C) 2006, 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 Interface
, SafeException
, EnvironmentBinding
, DistributionImplementation
, 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 needed_iface
in policy
.implementation
:
42 impl
= policy
.implementation
[needed_iface
]
44 _do_bindings(impl
, impl
.bindings
)
45 for dep
in impl
.requires
:
46 dep_iface
= iface_cache
.get_interface(dep
.interface
)
47 dep_impl
= policy
.get_implementation(dep_iface
)
48 if isinstance(dep_impl
, ZeroInstallImplementation
):
49 _do_bindings(dep_impl
, dep
.bindings
)
51 debug("Implementation %s is native; no bindings needed", dep_impl
)
53 root_impl
= policy
.get_implementation(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
.id))
61 def _get_implementation_path(id):
62 if id.startswith('/'): return id
63 return iface_cache
.stores
.lookup(id)
65 def execute_selections(selections
, prog_args
, dry_run
= False, main
= None, wrapper
= None):
66 """Execute program. On success, doesn't return. On failure, raises an Exception.
67 Returns normally only for a successful dry run.
68 @param selections: the selected versions
69 @type selections: L{selections.Selections}
70 @param prog_args: arguments to pass to the program
71 @type prog_args: [str]
72 @param dry_run: if True, just print a message about what would have happened
74 @param main: the name of the binary to run, or None to use the default
76 @param wrapper: a command to use to actually run the binary, or None to run the binary directly
79 @precondition: All implementations are in the cache.
81 sels
= selections
.selections
82 for selection
in sels
.values():
83 _do_bindings(selection
, selection
.bindings
)
84 for dep
in selection
.dependencies
:
85 dep_impl
= sels
[dep
.interface
]
86 if not dep_impl
.id.startswith('package:'):
87 _do_bindings(dep_impl
, dep
.bindings
)
89 root_impl
= sels
[selections
.interface
]
90 _execute(root_impl
, prog_args
, dry_run
, main
, wrapper
)
92 def test_selections(selections
, prog_args
, dry_run
, main
, wrapper
= None):
93 """Run the program in a child process, collecting stdout and stderr.
94 @return: the output produced by the process
99 output
= tempfile
.TemporaryFile(prefix
= '0launch-test')
106 os
.dup2(output
.fileno(), 1)
107 os
.dup2(output
.fileno(), 2)
108 execute_selections(selections
, prog_args
, dry_run
, main
)
111 traceback
.print_exc()
117 info("Waiting for test process to finish...")
119 pid
, status
= os
.waitpid(child
, 0)
123 results
= output
.read()
125 results
+= "Error from child process: exit code = %d" % status
131 def _execute(root_impl
, prog_args
, dry_run
, main
, wrapper
):
132 assert root_impl
is not None
134 if root_impl
.id.startswith('package:'):
135 main
= main
or root_impl
.main
139 main
= root_impl
.main
140 elif main
.startswith('/'):
143 main
= os
.path
.join(os
.path
.dirname(root_impl
.main
), main
)
145 prog_path
= os
.path
.join(_get_implementation_path(root_impl
.id), main
)
148 raise SafeException("Implementation '%s' cannot be executed directly; it is just a library "
149 "to be used by other programs (or missing 'main' attribute)" %
152 if not os
.path
.exists(prog_path
):
153 raise SafeException("File '%s' does not exist.\n"
154 "(implementation '%s' + program '%s')" %
155 (prog_path
, root_impl
.id, main
))
157 prog_args
= ['-c', wrapper
+ ' "$@"', '-', prog_path
] + list(prog_args
)
158 prog_path
= '/bin/sh'
161 print "Would execute:", prog_path
, ' '.join(prog_args
)
163 info("Executing: %s", prog_path
)
167 os
.execl(prog_path
, prog_path
, *prog_args
)
169 raise SafeException("Failed to run '%s': %s" % (prog_path
, str(ex
)))