Better error message if an implementation isn't in the cache.
[0compile.git] / build.py
blobf10155abf183d67cea482b736fda43cf0877b6d5
1 # Copyright (C) 2006, Thomas Leonard
2 # See http://0install.net/0compile.html
4 import sys, os, __main__, time
5 from os.path import join
6 from logging import info
7 from xml.dom import minidom
9 from support import *
11 def env(name, value):
12 info('Setting %s="%s"', name, value)
13 os.environ[name] = value
15 def do_env_binding(binding, path):
16 os.environ[binding.name] = binding.get_value(path,
17 os.environ.get(binding.name, None))
18 info("%s=%s", binding.name, os.environ[binding.name])
20 def do_build_internal(args):
21 """build-internal"""
22 import getpass, socket, time
24 buildenv = BuildEnv()
26 builddir = os.path.realpath('build')
27 ensure_dir(buildenv.metadir)
29 # Create build-environment.xml file
30 root = buildenv.doc.documentElement
31 info = buildenv.doc.createElementNS(XMLNS_0COMPILE, 'build-info')
32 root.appendChild(buildenv.doc.createTextNode(' '))
33 root.appendChild(info)
34 root.appendChild(buildenv.doc.createTextNode('\n'))
35 info.setAttributeNS(None, 'time', time.strftime('%Y-%m-%d %H:%M').strip())
36 info.setAttributeNS(None, 'host', socket.getfqdn())
37 info.setAttributeNS(None, 'user', getpass.getuser())
38 uname = os.uname()
39 info.setAttributeNS(None, 'arch', '%s-%s' % (uname[0], uname[4]))
40 buildenv.doc.writexml(file(join(buildenv.metadir, 'build-environment.xml'), 'w'))
42 # Create local binary interface file
43 src_iface = iface_cache.get_interface(buildenv.interface)
44 write_sample_interface(src_iface, buildenv.local_iface_file,
45 buildenv.chosen_impl(buildenv.interface))
47 # Create the patch
48 orig_impl = buildenv.chosen_impl(buildenv.interface)
49 patch_file = join(buildenv.distdir, '0install/from-%s.patch' % orig_impl.get_version())
50 if os.path.isdir('src'):
51 orig_src = lookup(orig_impl.id)
52 # (ignore errors; will already be shown on stderr)
53 os.system("diff -urN '%s' src > %s" %
54 (orig_src.replace('\\', '\\\\').replace("'", "\\'"),
55 patch_file))
56 if os.path.getsize(patch_file) == 0:
57 os.unlink(patch_file)
58 elif os.path.exists(patch_file):
59 os.unlink(patch_file)
61 env('BUILDDIR', builddir)
62 env('DISTDIR', buildenv.distdir)
63 env('SRCDIR', buildenv.srcdir)
64 os.chdir(builddir)
66 for needed_iface in buildenv.interfaces:
67 impl = buildenv.chosen_impl(needed_iface)
68 assert impl
69 for dep in impl.dependencies.values():
70 dep_iface = buildenv.interfaces[dep.interface]
71 for b in dep.bindings:
72 if isinstance(b, EnvironmentBinding):
73 dep_impl = buildenv.chosen_impl(dep.interface)
74 do_env_binding(b, lookup(dep_impl.id))
76 if args == ['--shell']:
77 spawn_and_check(find_in_path('sh'), [])
78 else:
79 command = buildenv.root_impl.metadata['command']
80 print "Executing: " + command
81 os.system(command)
83 def do_build(args):
84 """build [ --nosandbox ] [ shell ]"""
85 buildenv = BuildEnv()
87 builddir = os.path.realpath('build')
89 ensure_dir(builddir)
90 ensure_dir(buildenv.distdir)
92 if args[:1] == ['--nosandbox']:
93 return do_build_internal(args[1:])
95 tmpdir = tempfile.mkdtemp(prefix = '0compile-')
96 try:
97 my_dir = os.path.dirname(__file__)
98 readable = ['.', my_dir]
99 writable = ['build', buildenv.distdir, tmpdir]
100 env('TMPDIR', tmpdir)
101 env('PATH', join(my_dir, 'bin') + ':' + os.environ['PATH'])
103 readable.append(get_cached_iface_path(buildenv.interface))
105 for iface in buildenv.interfaces:
106 readable.append(lookup(buildenv.chosen_impl(iface).id))
108 options = []
109 if __main__.options.verbose:
110 options.append('--verbose')
111 spawn_maybe_sandboxed(readable, writable, tmpdir, sys.executable, [sys.argv[0]] + options + ['build', '--nosandbox'] + args)
112 finally:
113 info("Deleting temporary directory '%s'" % tmpdir)
114 shutil.rmtree(tmpdir)
116 def write_sample_interface(iface, path, src_impl):
117 impl = minidom.getDOMImplementation()
119 XMLNS_IFACE = namespaces.XMLNS_IFACE
121 doc = impl.createDocument(XMLNS_IFACE, "interface", None)
123 root = doc.documentElement
124 root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', XMLNS_IFACE)
126 def addSimple(parent, name, text = None):
127 elem = doc.createElementNS(XMLNS_IFACE, name)
129 parent.appendChild(doc.createTextNode('\n' + ' ' * (1 + depth(parent))))
130 parent.appendChild(elem)
131 if text:
132 elem.appendChild(doc.createTextNode(text))
133 return elem
135 def close(element):
136 element.appendChild(doc.createTextNode('\n' + ' ' * depth(element)))
138 addSimple(root, 'name', iface.name)
139 addSimple(root, 'summary', iface.summary)
140 addSimple(root, 'description', iface.description)
141 feed_for = addSimple(root, 'feed-for')
142 feed_for.setAttributeNS(None, 'interface', iface.uri)
144 group = addSimple(root, 'group')
145 main = src_impl.metadata.get('binary-main')
146 if main:
147 group.setAttributeNS(None, 'main', main)
149 uname = os.uname()
150 target_os, target_machine = uname[0], uname[-1]
151 if target_machine in ('i585', 'i686'):
152 target_machine = 'i486' # (sensible default)
154 group.setAttributeNS(None, 'arch', '%s-%s' % (target_os, target_machine))
155 impl_elem = addSimple(group, 'implementation')
156 impl_elem.setAttributeNS(None, 'version', src_impl.get_version())
157 impl_elem.setAttributeNS(None, 'id', '..')
158 impl_elem.setAttributeNS(None, 'released', time.strftime('%Y-%m-%d'))
159 close(group)
160 close(root)
162 doc.writexml(file(path, 'w'))
164 __main__.commands.append(do_build)