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
20 # These variables can be overridden by options. If packages is not
21 # set, then it is read from the source_packages file
22 packages = [] # Set of packages to build
24 build_place = os.path.join(os.getcwd(), 'debian_build')
26 class CommandError(exceptions.StandardError):
30 def current_directory(dir):
31 "Change the current directory as a context manager; when the context exits, return."
38 def run_cmd(args, **kwords):
39 rcode = subprocess.call( args, **kwords)
41 raise CommandError(args)
43 def command_output(args) :
44 p = subprocess.Popen(args, stdout=subprocess.PIPE)
45 output = p.communicate()
48 raise CommandError(args)
52 '''Describe all the files included in dsc, wich is a string name
53 of a dsc package. dsc itself is included'''
54 package = debian.deb822.Dsc(file(dsc))
55 internal_files = map( lambda(x): x['name'], package['Files'])
56 internal_files.append(dsc)
60 with current_directory(package):
61 cl = debian.changelog.Changelog(file('debian/changelog'))
62 package_name = cl.package
63 package_version = str(cl.version)
64 orig_tgz = package_name+'_'+ cl.upstream_version + ".orig.tar.gz"
65 dsc_name = package_name+"_"+package_version + ".dsc"
66 print "==> Package: ", package_name
67 source_format = command_output(('dpkg-source', '--print-format', '.'))
68 if "native" not in source_format:
69 run_cmd( ('pristine-tar', 'checkout', '../'+orig_tgz))
70 package_path = os.path.split(package)
71 with current_directory(os.path.join ('.', *package_path[0:len(package_path)-1])) :
72 package_path = package_path[len(package_path)-1]
74 run_cmd(("dpkg-source", '-b', '-i', package_path))
75 for f in dsc_files(dsc_name):
78 for f in dsc_files(dsc_name):
81 with current_directory(build_place):
82 sb = ['sbuild', '-n', '-d', distribution, '--setup-hook',
83 build_place + '/add_source']
84 if sbuild_opts is not None: sb += sbuild_opts
90 def gen_package_files() :
91 '''Generate package files in build_place and a script
92 build_place/add_source that can be used as a sbuild setup hook to
93 include the package files. Unfortunately, apt doesn't have a
94 mechanism for asserting that a package file is trusted when it is
95 local. We could generate a unique gpg key, generate signed
96 releases files and trust that key. It's easier to simply touch the
97 release.gpg in apt's lists directory, which turns out to do what
99 # Rather than substuting the release file directly, we create gpg
100 # release files for any local package list. That's easier than
101 # encoding apt's ideas about what characters to escape.
102 script = '''#!/bin/sh
104 echo deb file:{build_place} ./ >>/etc/apt/sources.list
106 touch $(ls /var/lib/apt/lists/_*Packages \
107 |sed -e s:Packages\$:Release.gpg:)
109 build_place = build_place
111 f = file(build_place + "/add_source", "w")
114 with current_directory(build_place):
115 run_cmd(('chmod', 'a+x', 'add_source'))
116 run_cmd( 'dpkg-scanpackages . >Packages',
121 '''Read in the packages file from source_packages
123 try: pf = file("source_packages")
125 print "Error: source_packages file not found"
127 def is_comment(line):
128 if re.match("^\\s*#", line): return False
129 if "#" in line: raise ValueError(
130 "Source package line contains a comment but not at beginning")
132 return map(lambda(x): x.rstrip(),
133 filter(is_comment, pf.readlines()))
138 opt.add_option('-b', '--build-place',
139 dest='build_place', help="Write resulting packages to BUILD_PLACE",
141 opt.add_option('-d', '--distribution',
142 help="Set the Debian distribution to DISTRIBUTION",
146 opt.add_option('-s', '--sbuild-opt',
147 action='append', dest='sbuild_opts',
148 help='Specify an option to be sent to sbuild')
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
155 if len(packages) == 0: packages = read_packages()
158 os.makedirs(build_place)
165 except CommandError as c:
166 print "Error:" + str(c.args)