1 # Copyright (C) 2006, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 import os
, sys
, tempfile
, shutil
, traceback
5 from xml
.dom
import minidom
, XMLNS_NAMESPACE
, Node
7 from zeroinstall
.injector
.model
import Interface
, Implementation
, Dependency
, EnvironmentBinding
, escape
8 from zeroinstall
.injector
import namespaces
, basedir
, reader
9 from zeroinstall
.injector
.iface_cache
import iface_cache
10 from zeroinstall
import SafeException
11 from zeroinstall
.injector
import run
12 from zeroinstall
.zerostore
import Stores
14 ENV_FILE
= '0compile-env.xml'
15 XMLNS_0COMPILE
= 'http://zero-install.sourceforge.net/2006/namespaces/0compile'
18 if id.startswith('/'):
21 raise SafeException("Directory '%s' no longer exists. Try '0compile setup'" % id)
22 return iface_cache
.stores
.lookup(id)
24 def get_cached_iface_path(uri
):
25 if uri
.startswith('/'):
26 if not os
.path
.isfile(uri
):
27 raise SafeException("Local source interface '%s' does not exist!" % uri
)
30 path
= basedir
.load_first_cache(namespaces
.config_site
, 'interfaces', escape(uri
))
31 if path
and os
.path
.isfile(path
):
33 raise SafeException("Interface '%s' not found in cache. Hint: try '0compile setup'" % uri
)
36 if os
.path
.isdir(d
): return
38 raise SafeException("'%s' exitst, but is not a directory!" % d
)
41 def find_in_path(prog
):
42 for d
in os
.environ
['PATH'].split(':'):
43 path
= os
.path
.join(d
, prog
)
44 if os
.path
.isfile(path
):
48 def spawn_and_check(prog
, args
):
49 status
= os
.spawnv(os
.P_WAIT
, prog
, [prog
] + args
)
51 raise SafeException("Program '%s' failed with exit code %d" % (prog
, status
))
53 raise SafeException("Program '%s' failed with signal %d" % (prog
, -status
))
55 def wait_for_child(child
):
56 """Wait for child to exit and reap it. Throw an exception if it doesn't return success."""
57 pid
, status
= os
.waitpid(child
, 0)
59 if os
.WIFEXITED(status
):
60 exit_code
= os
.WEXITSTATUS(status
)
64 raise SafeException('Command failed with exit status %d' % exit_code
)
66 raise SafeException('Command failed with signal %d' % WTERMSIG(status
))
69 if not os
.path
.isfile(ENV_FILE
):
70 raise SafeException("Run 0compile from a directory containing a '%s' file" % ENV_FILE
)
71 return minidom
.parse(ENV_FILE
)
73 def children(parent
, uri
, name
):
74 """Yield all direct children with the given name."""
75 for x
in parent
.childNodes
:
76 if x
.nodeType
== Node
.ELEMENT_NODE
and x
.namespaceURI
== uri
and x
.localName
== name
:
79 def spawn_maybe_sandboxed(readable
, writable
, tmpdir
, prog
, args
):
84 exec_maybe_sandboxed(readable
, writable
, tmpdir
, prog
, args
)
88 print >>sys
.stderr
, "Exec failed"
92 def exec_maybe_sandboxed(readable
, writable
, tmpdir
, prog
, args
):
93 """execl prog, with (only) the 'writable' directories writable if sandboxing is available.
94 The readable directories will be readable, as well as various standard locations.
95 If no sandbox is available, run without a sandbox."""
96 assert prog
.startswith('/')
97 _pola_run
= find_in_path('pola-run')
100 os
.execlp(prog
, prog
, *args
)
101 # We have pola-shell :-)
102 pola_args
= ['--prog', prog
, '-B']
104 pola_args
+= ['-a', a
]
106 pola_args
+= ['-f', r
]
108 pola_args
+= ['-fw', w
]
109 pola_args
+= ['-tw', '/tmp', tmpdir
]
110 os
.environ
['TMPDIR'] = '/tmp'
111 os
.execl(_pola_run
, _pola_run
, *pola_args
)
113 class BuildEnv(object):
114 __slots__
= ['doc', 'interface', 'interfaces', 'root_impl', 'srcdir']
117 self
.doc
= get_env_doc()
118 root
= self
.doc
.documentElement
119 self
.interface
= root
.getAttributeNS(None, 'interface')
120 assert self
.interface
123 for child
in children(root
, XMLNS_0COMPILE
, 'interface'):
124 iface
= self
.interface_from_elem(child
)
125 assert iface
.uri
not in self
.interfaces
126 self
.interfaces
[iface
.uri
] = iface
128 assert self
.interface
in self
.interfaces
129 self
.root_impl
= self
.chosen_impl(self
.interface
)
131 if os
.path
.isdir('src'):
132 self
.srcdir
= os
.path
.realpath('src')
134 self
.srcdir
= lookup(self
.root_impl
.id)
136 def chosen_impl(self
, uri
):
137 assert uri
in self
.interfaces
138 impls
= self
.interfaces
[uri
].implementations
.values()
139 assert len(impls
) == 1
142 def interface_from_elem(self
, elem
):
143 uri
= elem
.getAttributeNS(None, 'uri')
145 iface
= Interface(uri
)
147 impl_elems
= list(children(elem
, XMLNS_0COMPILE
, 'implementation'))
148 assert len(impl_elems
) == 1
149 impl_elem
= impl_elems
[0]
151 impl
= iface
.get_impl(impl_elem
.getAttributeNS(None, 'id'))
152 impl
.main
= impl_elem
.getAttributeNS(None, 'main') or None
153 impl
.version
= reader
.parse_version(impl_elem
.getAttributeNS(None, 'version'))
155 for x
in impl_elem
.attributes
.values():
156 impl
.metadata
[x
.name
] = x
.value
158 for dep_elem
in children(impl_elem
, XMLNS_0COMPILE
, 'requires'):
159 dep_uri
= dep_elem
.getAttributeNS(None, 'interface')
160 dep
= Dependency(dep_uri
)
161 impl
.dependencies
[dep_uri
] = dep
163 for e
in children(dep_elem
, XMLNS_0COMPILE
, 'environment'):
164 env
= EnvironmentBinding(e
.getAttributeNS(None, 'name'),
165 e
.getAttributeNS(None, 'insert'),
166 e
.getAttributeNS(None, 'default') or None)
167 dep
.bindings
.append(env
)
172 root
= node
.ownerDocument
.documentElement
174 while node
and node
is not root
:
175 node
= node
.parentNode