In freeradius-server, added custom logging (disabled) on debian branch
[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 = "unstable"
23 build_place = os.path.join(os.getcwd(), 'debian_build')
24
25 suffix_map = {
26     'wheezy': 'deb70',
27     'jessie': 'deb80',
28     'unstable': 'sid',
29     'sid': 'sid'}
30
31 class CommandError(exceptions.StandardError):
32     pass
33
34 @contextmanager
35 def current_directory(dir):
36     "Change the current directory as a context manager; when the context exits, return."
37     cwd = os.getcwd()
38     os.chdir(dir)
39     yield
40     os.chdir(cwd)
41
42
43 def run_cmd(args, **kwords):
44     rcode =  subprocess.call( args, **kwords)
45     if rcode <> 0:
46         raise CommandError(args)
47
48 def command_output(args) :
49     p = subprocess.Popen(args, stdout=subprocess.PIPE)
50     output = p.communicate()
51     output = output[0]
52     if p.returncode != 0:
53         raise CommandError(args)
54     return output
55
56 def dsc_files(dsc) :
57     '''Describe all the files included in dsc, wich is a string name
58     of a dsc package. dsc itself is included'''
59     package = debian.deb822.Dsc(open(dsc))
60     internal_files = map( lambda(x): x['name'], package['Files'])
61     internal_files.append(dsc)
62     return internal_files
63
64 def build(package):
65     with current_directory(package):
66         cl = debian.changelog.Changelog(open('debian/changelog'))
67         package_name = cl.package
68         package_version = re.sub('^\d+:','',str(cl.version))
69         orig_tar = package_name+'_'+ cl.upstream_version + ".orig.tar"
70         dsc_name = package_name+"_"+package_version + '~' + version_suffix+ ".dsc"
71         run_cmd(['dch', '-b', '-v' +str(cl.version)+'~'+version_suffix, '-D'+distribution, 'Autobuilt package'])
72         print "==> Package: ", package_name
73         source_format = command_output(('dpkg-source', '--print-format', '.'))
74         if "native" not in source_format:
75             run_cmd( ('git', 'fetch', 'origin', 'pristine-tar:pristine-tar'))
76             for file in command_output(( 'pristine-tar', 'list')).split("\n"):
77                 if file.startswith(orig_tar): orig_tar = file
78             run_cmd( ('pristine-tar', 'checkout', '../'+orig_tar))
79     package_path = os.path.split(package)
80     with current_directory(os.path.join ('.', *package_path[0:len(package_path)-1])) :
81         package_path = package_path[len(package_path)-1]
82         try:
83             run_cmd(("dpkg-source", '-b', '-i', package_path))
84             for f in dsc_files(dsc_name):
85                 copy(f, build_place)
86         finally:
87             for f in dsc_files(dsc_name):
88                 try: os.unlink(f)
89                 except OSError: pass
90             with current_directory(package_path): run_cmd('git checkout debian/changelog', shell=True)
91     with current_directory(build_place):
92         sb = ['sbuild', '-n', '-d', distribution, '--setup-hook',
93     build_place + '/add_source']
94         if sbuild_opts is not None: sb += sbuild_opts
95         sb.append(dsc_name)
96         print " ".join(sb)
97         sys.stdout.flush()
98         run_cmd(sb)
99
100 def gen_package_files() :
101     '''Generate package files in build_place and a script
102     build_place/add_source that can be used as a sbuild setup hook to
103     include the package files.  Use the sbuild key to sign our packages'''
104     script = '''#!/bin/sh
105     set -e
106     sudo -u root /usr/local/sbin/add-source {build_place}
107     '''.format (
108         build_place = build_place
109         )
110     f = open(build_place + "/add_source", "w")
111     f.write(script)
112     f.close()
113     with current_directory(build_place):
114         run_cmd(('chmod', 'a+x', 'add_source'))
115         run_cmd( 'dpkg-scanpackages . >Packages',
116                  shell = True)
117         run_cmd('apt-ftparchive   release . >Release', shell=True)
118         try: os.unlink('Release.gpg')
119         except OSError: pass
120         run_cmd( 'gpg -sabt -o Release.gpg --secret-keyring /var/lib/sbuild/apt-keys/sbuild-key.sec --keyring /var/lib/sbuild/apt-keys/sbuild-key.pub Release', shell=True)
121
122
123 def read_packages():
124     '''Read in the packages file from source_packages
125     '''
126     try: pf = open("source_packages")
127     except IOError:
128         print "Error: source_packages file not found"
129         exit(1)
130     def is_comment(line):
131         if re.match("^\\s*#", line): return False
132         if "#" in line: raise ValueError(
133             "Source package line contains a comment but not at beginning")
134         return True
135     return map(lambda(x): x.rstrip(),
136         filter(is_comment, pf.readlines()))
137
138
139 # main program
140 opt = OptionParser()
141 opt.add_option('-b', '--build-place',
142                dest='build_place', help="Write resulting packages to BUILD_PLACE",
143                default=build_place)
144 opt.add_option('--version-suffix', dest ='version_suffix')
145 opt.add_option('-d', '--distribution',
146                help="Set the Debian distribution to DISTRIBUTION",
147                dest="distribution",
148                default=distribution
149                )
150 opt.add_option('-s', '--sbuild-opt',
151                action='append', dest='sbuild_opts',
152                help='Specify an option to be sent to sbuild')
153 opt.add_option('--tar-file',
154                dest='tar_file',
155                help = 'Tar up resulting packages in given tar file',
156                default = None)
157 opt.usage = "%prog [options] [packages]"
158 (options, packages) = opt.parse_args()
159 build_place = options.build_place
160 distribution = options.distribution
161 version_suffix = options.version_suffix
162 if not version_suffix: version_suffix = suffix_map.get(distribution, '0')
163 sbuild_opts = options.sbuild_opts
164 tar_file = options.tar_file
165
166
167 if len(packages) == 0: packages = read_packages()
168 try:
169     os.makedirs(build_place)
170 except OSError: pass
171
172 code = 0
173 try:
174     for p in packages:
175         gen_package_files()
176         build(p)
177 except CommandError as c:
178     print "Error:" + str(c.args)
179     code = 1
180 finally:
181     if tar_file is not None:
182         with current_directory(build_place):
183             run_cmd('tar -cf '+tar_file+' .',
184                     shell=True)
185
186 sys.exit(code)