Allow testing against a dev 0compile checkout
[0release.git] / compile.py
blobaa80035b903f19ccd9fe1246238b7be787ceb483
1 # Copyright (C) 2009, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 import tempfile, shutil, os, sys
5 import ConfigParser
6 from logging import info
7 from zeroinstall.support import basedir
9 import support
11 class Compiler:
12 def __init__(self, options, src_feed_name):
13 self.src_feed_name = src_feed_name
14 self.src_feed = support.load_feed(src_feed_name)
15 self.archive_dir_public_url = options.archive_dir_public_url
16 assert options.archive_dir_public_url
18 self.config = ConfigParser.RawConfigParser()
20 # Start with a default configuration
21 self.config.add_section('global')
22 self.config.set('global', 'builders', 'host')
24 self.config.add_section('builder-host')
25 #self.config.set('builder-host', 'build', '0launch --not-before 0.10 http://0install.net/2007/interfaces/0release.xml --build-slave "$@"')
26 self.config.set('builder-host', 'build', '0launch --command=build-slave http://0install.net/2007/interfaces/0release.xml "$@"')
28 self.src_impl = support.get_singleton_impl(self.src_feed)
29 if self.src_impl.arch and self.src_impl.arch.endswith('-src'):
30 path = basedir.load_first_config('0install.net', '0release', 'builders.conf')
31 if path:
32 info("Loading configuration file '%s'", path)
33 self.config.read(path)
34 else:
35 info("No builders.conf configuration; will build a binary for this host only")
37 if options.builders is not None:
38 builders = options.builders
39 else:
40 builders = self.config.get('global', 'builders').strip()
41 if builders:
42 self.targets = [x.strip() for x in builders.split(',')]
43 info("%d build targets configured: %s", len(self.targets), self.targets)
44 else:
45 self.targets = []
46 info("No builders set; no binaries will be built")
47 else:
48 self.targets = []
50 # We run the build in a sub-process. The idea is that the build may need to run
51 # on a different machine.
52 def build_binaries(self):
53 if not self.targets: return
55 print "Source package, so generating binaries..."
57 archive_file = support.get_archive_basename(self.src_impl)
59 for target in self.targets:
60 start = self.get('builder-' + target, 'start', None)
61 command = self.config.get('builder-' + target, 'build')
62 stop = self.get('builder-' + target, 'stop', None)
64 binary_feed = 'binary-' + target + '.xml'
65 if os.path.exists(binary_feed):
66 print "Feed %s already exists; not rebuilding" % binary_feed
67 else:
68 print "\nBuilding binary with builder '%s' ...\n" % target
70 if start: support.show_and_run(start, [])
71 try:
72 support.show_and_run(command, [os.path.basename(self.src_feed_name), archive_file, self.archive_dir_public_url, binary_feed + '.new'])
73 finally:
74 if stop: support.show_and_run(stop, [])
76 bin_feed = support.load_feed(binary_feed + '.new')
77 bin_impl = support.get_singleton_impl(bin_feed)
78 bin_archive_file = support.get_archive_basename(bin_impl)
79 bin_size = bin_impl.download_sources[0].size
81 assert os.path.exists(bin_archive_file), "Compiled binary '%s' not found!" % os.path.abspath(bin_archive_file)
82 assert os.path.getsize(bin_archive_file) == bin_size, "Compiled binary '%s' has wrong size!" % os.path.abspath(bin_archive_file)
84 os.rename(binary_feed + '.new', binary_feed)
86 def get_binary_feeds(self):
87 return ['binary-%s.xml' % target for target in self.targets]
89 def get(self, section, option, default):
90 try:
91 return self.config.get(section, option)
92 except ConfigParser.NoOptionError:
93 return default
95 # This is the actual build process, running on the build machine
96 def build_slave(src_feed, archive_file, archive_dir_public_url, target_feed):
97 try:
98 COMPILE = [os.environ['0COMPILE']]
99 except KeyError:
100 print >>sys.stdout, ("\n\n*****\n"
101 "Please update your ~/.config/0install.net/0release/builders.conf file to use --command=build-slave."
102 "\n*****\n\n")
103 COMPILE = ['0launch', '--not-before=0.30', 'http://0install.net/2006/interfaces/0compile.xml']
105 feed = support.load_feed(src_feed)
107 src_feed = os.path.abspath(src_feed)
108 archive_file = os.path.abspath(archive_file)
109 target_feed = os.path.abspath(target_feed)
111 impl, = feed.implementations.values()
113 tmpdir = tempfile.mkdtemp(prefix = '0release-')
114 try:
115 os.chdir(tmpdir)
116 depdir = os.path.join(tmpdir, 'dependencies')
117 os.mkdir(depdir)
119 support.unpack_tarball(archive_file)
120 os.rename(impl.download_sources[0].extract, os.path.join(depdir, impl.id))
122 config = ConfigParser.RawConfigParser()
123 config.add_section('compile')
124 config.set('compile', 'download-base-url', archive_dir_public_url)
125 config.set('compile', 'version-modifier', '')
126 config.set('compile', 'interface', src_feed)
127 config.set('compile', 'selections', '')
128 config.set('compile', 'metadir', '0install')
129 stream = open(os.path.join(tmpdir, '0compile.properties'), 'w')
130 try:
131 config.write(stream)
132 finally:
133 stream.close()
135 support.check_call(COMPILE + ['build'], cwd = tmpdir)
136 support.check_call(COMPILE + ['publish', '--target-feed', target_feed], cwd = tmpdir)
138 # TODO: run unit-tests
140 feed = support.load_feed(target_feed)
141 impl = support.get_singleton_impl(feed)
142 archive_file = support.get_archive_basename(impl)
144 shutil.move(archive_file, os.path.join(os.path.dirname(target_feed), archive_file))
145 except:
146 print "\nLeaving temporary directory %s for inspection...\n" % tmpdir
147 raise
148 else:
149 shutil.rmtree(tmpdir)