Update freeradius for radiusd.pid change
[moonshot.git] / debian-builder
1 #!/usr/bin/python
2
3 '''A script to build Debian packages using sbuild from a source
4 directory rather than a source package. Also, if multiple packages are
5 built, previous packages are available to satisfy
6 dependencies. Requires that the build-place be available from the
7 chroot.
8 '''
9
10 from contextlib import contextmanager
11 import os, subprocess, exceptions
12 import re
13 import sys
14 from optparse import OptionParser
15 from shutil import copy
16
17 import debian.changelog, debian.deb822
18
19 # These variables can be overridden by options. If packages is not
20 # set, then it is read from the source_packages file
21 packages = []  # Set of packages to build
22 distribution = "sid"
23 build_place = os.path.join(os.getcwd(), 'debian_build')
24
25 class CommandError(exceptions.StandardError):
26     pass
27
28 @contextmanager
29 def current_directory(dir):
30     "Change the current directory as a context manager; when the context exits, return."
31     cwd = os.getcwd()
32     os.chdir(dir)
33     yield
34     os.chdir(cwd)
35
36
37 def run_cmd(args, **kwords):
38     rcode =  subprocess.call( args, **kwords)
39     if rcode <> 0:
40         raise CommandError(args)
41
42 def command_output(args) :
43     p = subprocess.Popen(args, stdout=subprocess.PIPE)
44     output = p.communicate()
45     output = output[0]
46     if p.returncode != 0:
47         raise CommandError(args)
48     return output
49
50 def dsc_files(dsc) :
51     '''Describe all the files included in dsc, wich is a string name
52     of a dsc package. dsc itself is included'''
53     package = debian.deb822.Dsc(open(dsc))
54     internal_files = map( lambda(x): x['name'], package['Files'])
55     internal_files.append(dsc)
56     return internal_files
57
58 def build(package):
59     with current_directory(package):
60         cl = debian.changelog.Changelog(open('debian/changelog'))
61         package_name = cl.package
62         package_version = re.sub('^\d+:','',str(cl.version))
63         orig_tgz = package_name+'_'+ cl.upstream_version + ".orig.tar.gz"
64         dsc_name = package_name+"_"+package_version + ".dsc"
65         print "==> Package: ", package_name
66         source_format = command_output(('dpkg-source', '--print-format', '.'))
67         if "native" not in source_format:
68             run_cmd( ('pristine-tar', 'checkout', '../'+orig_tgz))
69     package_path = os.path.split(package)
70     with current_directory(os.path.join ('.', *package_path[0:len(package_path)-1])) :
71         package_path = package_path[len(package_path)-1]
72         try:
73             run_cmd(("dpkg-source", '-b', '-i', package_path))
74             for f in dsc_files(dsc_name):
75                 copy(f, build_place)
76         finally:
77             for f in dsc_files(dsc_name):
78                 try: os.unlink(f)
79                 except OSError: pass
80     with current_directory(build_place):
81         sb = ['sbuild', '-n', '-d', distribution, '--setup-hook',
82     build_place + '/add_source']
83         if sbuild_opts is not None: sb += sbuild_opts
84         sb.append(dsc_name)
85         print " ".join(sb)
86         sys.stdout.flush()
87         run_cmd(sb)
88
89 def gen_package_files() :
90     '''Generate package files in build_place and a script
91     build_place/add_source that can be used as a sbuild setup hook to
92     include the package files. Unfortunately, apt doesn't have a
93     mechanism for asserting that a package file is trusted when it is
94     local. We could generate a unique gpg key, generate signed
95     releases files and trust that key. It's easier to simply touch the
96     release.gpg in apt's lists directory, which turns out to do what
97     we need.'''
98     # Rather than substuting the release file directly, we create gpg
99     # release files for any  local package list. That's easier than
100     # encoding apt's ideas about what characters to escape.
101     script = '''#!/bin/sh
102     set -e
103     sudo -u root /usr/local/sbin/add-source {build_place}
104     '''.format (
105         build_place = build_place
106         )
107     f = open(build_place + "/add_source", "w")
108     f.write(script)
109     f.close()
110     with current_directory(build_place):
111         run_cmd(('chmod', 'a+x', 'add_source'))
112         run_cmd( 'dpkg-scanpackages . >Packages',
113                  shell = True)
114
115
116 def read_packages():
117     '''Read in the packages file from source_packages
118     '''
119     try: pf = open("source_packages")
120     except IOError:
121         print "Error: source_packages file not found"
122         exit(1)
123     def is_comment(line):
124         if re.match("^\\s*#", line): return False
125         if "#" in line: raise ValueError(
126             "Source package line contains a comment but not at beginning")
127         return True
128     return map(lambda(x): x.rstrip(),
129         filter(is_comment, pf.readlines()))
130
131
132 # main program
133 opt = OptionParser()
134 opt.add_option('-b', '--build-place',
135                dest='build_place', help="Write resulting packages to BUILD_PLACE",
136                default=build_place)
137 opt.add_option('-d', '--distribution',
138                help="Set the Debian distribution to DISTRIBUTION",
139                dest="distribution",
140                default=distribution
141                )
142 opt.add_option('-s', '--sbuild-opt',
143                action='append', dest='sbuild_opts',
144                help='Specify an option to be sent to sbuild')
145 opt.add_option('--tar-file',
146                dest='tar_file',
147                help = 'Tar up resulting packages in given tar file',
148                default = None)
149 opt.usage = "%prog [options] [packages]"
150 (options, packages) = opt.parse_args()
151 build_place = options.build_place
152 distribution = options.distribution
153 sbuild_opts = options.sbuild_opts
154 tar_file = options.tar_file
155
156
157 if len(packages) == 0: packages = read_packages()
158 try:
159     os.makedirs(build_place)
160 except OSError: pass
161
162 code = 0
163 try:
164     for p in packages:
165         gen_package_files()
166         build(p)
167 except CommandError as c:
168     print "Error:" + str(c.args)
169     code = 1
170 finally:
171     if tar_file is not None:
172         with current_directory(build_place):
173             run_cmd('tar -cf '+tar_file+' .',
174                     shell=True)
175
176 sys.exit(code)