Instead of hard-coding the GNU compile commands, set the command as an
[0compile.git] / support.py
blob1929c86e983d67804fdfa386b48def29b65dc21d
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'
17 def lookup(id):
18 if id.startswith('/'):
19 if os.path.isdir(id):
20 return id
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)
28 return uri
29 else:
30 path = basedir.load_first_cache(namespaces.config_site, 'interfaces', escape(uri))
31 if path and os.path.isfile(path):
32 return path
33 raise SafeException("Interface '%s' not found in cache. Hint: try '0compile setup'" % uri)
35 def ensure_dir(d):
36 if os.path.isdir(d): return
37 if os.path.exists(d):
38 raise SafeException("'%s' exitst, but is not a directory!" % d)
39 os.mkdir(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):
45 return path
46 return None
48 def spawn_and_check(prog, args):
49 status = os.spawnv(os.P_WAIT, prog, [prog] + args)
50 if status > 0:
51 raise SafeException("Program '%s' failed with exit code %d" % (prog, status))
52 elif status < 0:
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)
58 assert pid == child
59 if os.WIFEXITED(status):
60 exit_code = os.WEXITSTATUS(status)
61 if exit_code == 0:
62 return
63 else:
64 raise SafeException('Command failed with exit status %d' % exit_code)
65 else:
66 raise SafeException('Command failed with signal %d' % WTERMSIG(status))
68 def get_env_doc():
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:
77 yield x
79 def spawn_maybe_sandboxed(readable, writable, tmpdir, prog, args):
80 child = os.fork()
81 if child == 0:
82 try:
83 try:
84 exec_maybe_sandboxed(readable, writable, tmpdir, prog, args)
85 except:
86 traceback.print_exc()
87 finally:
88 print >>sys.stderr, "Exec failed"
89 os._exit(1)
90 wait_for_child(child)
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')
99 if _pola_run is None:
100 os.execlp(prog, prog, *args)
101 # We have pola-shell :-)
102 pola_args = ['--prog', prog, '-B']
103 for a in args:
104 pola_args += ['-a', a]
105 for r in readable:
106 pola_args += ['-f', r]
107 for w in writable:
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']
116 def __init__(self):
117 self.doc = get_env_doc()
118 root = self.doc.documentElement
119 self.interface = root.getAttributeNS(None, 'interface')
120 assert self.interface
122 self.interfaces = {}
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')
133 else:
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
140 return impls[0]
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)
169 return iface
171 def depth(node):
172 root = node.ownerDocument.documentElement
173 depth = 0
174 while node and node is not root:
175 node = node.parentNode
176 depth += 1
177 return depth