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
10 from contextlib import contextmanager
11 import os, subprocess, exceptions
14 from optparse import OptionParser
15 from shutil import copy
17 import debian.changelog, debian.deb822
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
23 build_place = os.path.join(os.getcwd(), 'debian_build')
25 class CommandError(exceptions.StandardError):
29 def current_directory(dir):
30 "Change the current directory as a context manager; when the context exits, return."
37 def run_cmd(args, **kwords):
38 rcode = subprocess.call( args, **kwords)
40 raise CommandError(args)
42 def command_output(args) :
43 p = subprocess.Popen(args, stdout=subprocess.PIPE)
44 output = p.communicate()
47 raise CommandError(args)
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)
59 with current_directory(package):
60 cl = debian.changelog.Changelog(open('debian/changelog'))
61 package_name = cl.package
62 package_version = 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]
73 run_cmd(("dpkg-source", '-b', '-i', package_path))
74 for f in dsc_files(dsc_name):
77 for f in dsc_files(dsc_name):
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
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
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
103 echo deb file:{build_place} ./ >>/etc/apt/sources.list
105 touch $(ls /var/lib/apt/lists/_*Packages \
106 |sed -e s:Packages\$:Release.gpg:)
108 build_place = build_place
110 f = open(build_place + "/add_source", "w")
113 with current_directory(build_place):
114 run_cmd(('chmod', 'a+x', 'add_source'))
115 run_cmd( 'dpkg-scanpackages . >Packages',
120 '''Read in the packages file from source_packages
122 try: pf = open("source_packages")
124 print "Error: source_packages file not found"
126 def is_comment(line):
127 if re.match("^\\s*#", line): return False
128 if "#" in line: raise ValueError(
129 "Source package line contains a comment but not at beginning")
131 return map(lambda(x): x.rstrip(),
132 filter(is_comment, pf.readlines()))
137 opt.add_option('-b', '--build-place',
138 dest='build_place', help="Write resulting packages to BUILD_PLACE",
140 opt.add_option('-d', '--distribution',
141 help="Set the Debian distribution to DISTRIBUTION",
145 opt.add_option('-s', '--sbuild-opt',
146 action='append', dest='sbuild_opts',
147 help='Specify an option to be sent to sbuild')
148 opt.add_option('--tar-file',
150 help = 'Tar up resulting packages in given tar file',
152 opt.usage = "%prog [options] [packages]"
153 (options, packages) = opt.parse_args()
154 build_place = options.build_place
155 distribution = options.distribution
156 sbuild_opts = options.sbuild_opts
157 tar_file = options.tar_file
160 if len(packages) == 0: packages = read_packages()
162 os.makedirs(build_place)
170 except CommandError as c:
171 print "Error:" + str(c.args)
174 if tar_file is not None:
175 with current_directory(build_place):
176 run_cmd('tar -cf '+tar_file+' .',