Merge lp:~jelmer/bzr-builddeb/auto-apply-quilt into lp:bzr-builddeb

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/bzr-builddeb/auto-apply-quilt
Merge into: lp:bzr-builddeb
Diff against target: 607 lines (+361/-58)
9 files modified
builder.py (+19/-4)
cmds.py (+61/-44)
debian/changelog (+6/-1)
merge_quilt.py (+24/-0)
quilt.py (+151/-0)
tests/__init__.py (+3/-0)
tests/test_quilt.py (+87/-0)
tests/test_util.py (+5/-5)
util.py (+5/-4)
To merge this branch: bzr merge lp:~jelmer/bzr-builddeb/auto-apply-quilt
Reviewer Review Type Date Requested Status
Bzr-builddeb-hackers Pending
Review via email: mp+87295@code.launchpad.net

This proposal has been superseded by a proposal from 2012-01-02.

Description of the change

Automatically apply patches for 3.0 (quilt) packages in 'bzr bd' or 'bzr bd-do'.

To post a comment you must log in.
685. By Jelmer Vernooij

Fix changelog.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'builder.py'
2--- builder.py 2009-12-01 17:14:15 +0000
3+++ builder.py 2012-01-02 22:04:32 +0000
4@@ -28,10 +28,13 @@
5 NoSourceDirError,
6 BuildFailedError,
7 )
8+from bzrlib.plugins.builddeb.quilt import quilt_push_all
9 from bzrlib.plugins.builddeb.util import (
10- get_parent_dir,
11- subprocess_setup,
12- )
13+ get_parent_dir,
14+ tree_get_source_format,
15+ subprocess_setup,
16+ FORMAT_3_0_QUILT,
17+ )
18
19
20 class DebBuild(object):
21@@ -71,8 +74,20 @@
22 if self.use_existing:
23 raise NoSourceDirError
24
25- def export(self):
26+ def export(self, apply_quilt_patches=True):
27 self.distiller.distill(self.target_dir)
28+ if apply_quilt_patches:
29+ self._apply_quilt_patches()
30+
31+ def _apply_quilt_patches(self):
32+ if not os.path.isfile(os.path.join(self.target_dir, "debian/patches/series")):
33+ return
34+ format_path = os.path.join(self.target_dir, "debian/source/format")
35+ if not os.path.isfile(format_path):
36+ return
37+ with file(format_path, 'r') as f:
38+ if f.read().strip() == FORMAT_3_0_QUILT:
39+ quilt_push_all(os.path.abspath(self.target_dir))
40
41 def build(self):
42 """This builds the package using the supplied command."""
43
44=== modified file 'cmds.py'
45--- cmds.py 2012-01-02 18:01:36 +0000
46+++ cmds.py 2012-01-02 22:04:32 +0000
47@@ -82,6 +82,48 @@
48 type=str)
49
50
51+def _get_changelog_info(tree, last_version=None, package=None, distribution=None):
52+ from bzrlib.plugins.builddeb.util import (
53+ find_changelog,
54+ find_last_distribution,
55+ lookup_distribution,
56+ )
57+ from bzrlib.plugins.builddeb.errors import (
58+ MissingChangelogError,
59+ )
60+ current_version = last_version
61+ try:
62+ (changelog, top_level) = find_changelog(tree, False, max_blocks=2)
63+ if last_version is None:
64+ current_version = changelog.version.upstream_version
65+ if package is None:
66+ package = changelog.package
67+ if distribution is None:
68+ distribution = find_last_distribution(changelog)
69+ if distribution is not None:
70+ note(gettext("Using distribution %s") % distribution)
71+ except MissingChangelogError:
72+ top_level = False
73+ changelog = None
74+ if distribution is None:
75+ note("No distribution specified, and no changelog, "
76+ "assuming 'debian'")
77+ distribution = "debian"
78+ if package is None:
79+ raise BzrCommandError("You did not specify --package, and "
80+ "there is no changelog from which to determine the "
81+ "package name, which is needed to know the name to "
82+ "give the .orig.tar.gz. Please specify --package.")
83+ distribution = distribution.lower()
84+ distribution_name = lookup_distribution(distribution)
85+ if distribution_name is None:
86+ raise BzrCommandError(gettext("Unknown target distribution: %s") \
87+ % distribution)
88+ return (current_version, package, distribution, distribution_name,
89+ changelog, top_level)
90+
91+
92+
93 class cmd_builddeb(Command):
94 """Builds a Debian package from a branch.
95
96@@ -633,46 +675,6 @@
97 return self._fetch_tarball(package, version, orig_dir,
98 locations, v3)
99
100- def _get_changelog_info(self, tree, last_version, package, distribution):
101- from bzrlib.plugins.builddeb.util import (
102- find_changelog,
103- find_last_distribution,
104- lookup_distribution,
105- )
106- from bzrlib.plugins.builddeb.errors import (
107- MissingChangelogError,
108- )
109- current_version = last_version
110- try:
111- (changelog, top_level) = find_changelog(tree, False, max_blocks=2)
112- if last_version is None:
113- current_version = changelog.version.upstream_version
114- if package is None:
115- package = changelog.package
116- if distribution is None:
117- distribution = find_last_distribution(changelog)
118- if distribution is not None:
119- note(gettext("Using distribution %s") % distribution)
120- except MissingChangelogError:
121- top_level = False
122- changelog = None
123- if distribution is None:
124- note("No distribution specified, and no changelog, "
125- "assuming 'debian'")
126- distribution = "debian"
127- if package is None:
128- raise BzrCommandError("You did not specify --package, and "
129- "there is no changelog from which to determine the "
130- "package name, which is needed to know the name to "
131- "give the .orig.tar.gz. Please specify --package.")
132- distribution = distribution.lower()
133- distribution_name = lookup_distribution(distribution)
134- if distribution_name is None:
135- raise BzrCommandError(gettext("Unknown target distribution: %s") \
136- % distribution)
137- return (current_version, package, distribution, distribution_name,
138- changelog, top_level)
139-
140 def run(self, location=None, upstream_branch=None, version=None,
141 distribution=None, package=None,
142 directory=".", revision=None, merge_type=None,
143@@ -695,7 +697,7 @@
144 from bzrlib.plugins.builddeb.util import (
145 FORMAT_3_0_QUILT,
146 FORMAT_3_0_NATIVE,
147- get_source_format,
148+ tree_get_source_format,
149 guess_build_type,
150 tree_contains_upstream_source,
151 )
152@@ -710,7 +712,7 @@
153 "command.")
154 config = debuild_config(tree, tree)
155 (current_version, package, distribution, distribution_name,
156- changelog, top_level) = self._get_changelog_info(tree, last_version,
157+ changelog, top_level) = _get_changelog_info(tree, last_version,
158 package, distribution)
159 contains_upstream_source = tree_contains_upstream_source(tree)
160 if changelog is None:
161@@ -822,7 +824,7 @@
162 revisions=upstream_revisions)
163 else:
164 raise
165- source_format = get_source_format(tree)
166+ source_format = tree_get_source_format(tree)
167 v3 = (source_format in [
168 FORMAT_3_0_QUILT, FORMAT_3_0_NATIVE])
169 tarball_filenames = self._get_tarballs(config, tree, package,
170@@ -1160,10 +1162,25 @@
171 from bzrlib.plugins.builddeb.hooks import run_hook
172 from bzrlib.plugins.builddeb.util import (
173 find_changelog,
174+ guess_build_type,
175+ tree_contains_upstream_source,
176 )
177 t = WorkingTree.open_containing('.')[0]
178+ self.add_cleanup(t.lock_read().unlock)
179 config = debuild_config(t, t)
180- if config.build_type != BUILD_TYPE_MERGE:
181+ (current_version, package, distribution, distribution_name,
182+ changelog, top_level) = _get_changelog_info(t)
183+ contains_upstream_source = tree_contains_upstream_source(t)
184+ if changelog is None:
185+ changelog_version = None
186+ else:
187+ changelog_version = changelog.version
188+ build_type = config.build_type
189+ if build_type is None:
190+ build_type = guess_build_type(t, changelog_version,
191+ contains_upstream_source)
192+
193+ if build_type != BUILD_TYPE_MERGE:
194 raise BzrCommandError(gettext("This command only works for merge "
195 "mode packages. See /usr/share/doc/bzr-builddeb"
196 "/user_manual/merge.html for more information."))
197
198=== modified file 'debian/changelog'
199--- debian/changelog 2012-01-02 18:01:36 +0000
200+++ debian/changelog 2012-01-02 22:04:32 +0000
201@@ -5,8 +5,13 @@
202 commands like 'bzr bash-completion'. LP: #903650
203 * Provide merge-package functionality as a hook for 'bzr merge'.
204 LP: #486075, LP: #910900
205+ * Automatically apply quilt patches. LP: #616791
206+ * Automatically apply v3 quilt patches for 'bzr builddeb' and 'bzr bd-
207+ do'. LP: #616791
208+ * Automatically apply patches for 3.0 (quilt) packages in 'bzr bd-do'
209+ and 'bzr bd'. LP: #616791
210
211- -- Jelmer Vernooij <jelmer@debian.org> Mon, 02 Jan 2012 17:57:33 +0100
212+ -- Jelmer Vernooij <jelmer@debian.org> Mon, 02 Jan 2012 23:02:44 +0100
213
214 bzr-builddeb (2.8.0) unstable; urgency=low
215
216
217=== modified file 'merge_quilt.py'
218--- merge_quilt.py 2011-12-20 14:44:17 +0000
219+++ merge_quilt.py 2012-01-02 22:04:32 +0000
220@@ -21,3 +21,27 @@
221 """Quilt patch handling."""
222
223 from __future__ import absolute_import
224+import tempfile
225+
226+from bzrlib import trace
227+from bzrlib.plugins.builddeb.quilt import quilt_pop_all
228+
229+
230+def tree_unapply_patches(orig_tree):
231+ """Return a tree with patches unapplied.
232+
233+ :param tree: Tree from which to unapply quilt patches
234+ :return: Tuple with tree and temp path.
235+ The tree is a tree with unapplied patches; either a checkout of
236+ tree or tree itself if there were no patches
237+ """
238+ series_file_id = orig_tree.path2id("debian/patches/series")
239+ if series_file_id is None:
240+ # No quilt patches
241+ return orig_tree, None
242+
243+ target_dir = tempfile.mkdtemp()
244+ tree = orig_tree.branch.create_checkout(target_dir, lightweight=True)
245+ trace.warning("Applying quilt patches for %r in %s", orig_tree, target_dir)
246+ quilt_pop_all(working_dir=tree.basedir)
247+ return tree, target_dir
248
249=== added file 'quilt.py'
250--- quilt.py 1970-01-01 00:00:00 +0000
251+++ quilt.py 2012-01-02 22:04:32 +0000
252@@ -0,0 +1,151 @@
253+# quilt.py -- Quilt patch handling
254+# Copyright (C) 2011 Canonical Ltd.
255+#
256+# This file is part of bzr-builddeb.
257+#
258+# bzr-builddeb is free software; you can redistribute it and/or modify
259+# it under the terms of the GNU General Public License as published by
260+# the Free Software Foundation; either version 2 of the License, or
261+# (at your option) any later version.
262+#
263+# bzr-builddeb is distributed in the hope that it will be useful,
264+# but WITHOUT ANY WARRANTY; without even the implied warranty of
265+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
266+# GNU General Public License for more details.
267+#
268+# You should have received a copy of the GNU General Public License
269+# along with bzr-builddeb; if not, write to the Free Software
270+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
271+#
272+
273+"""Quilt patch handling."""
274+
275+from __future__ import absolute_import
276+
277+import errno
278+import os
279+import signal
280+import subprocess
281+from bzrlib import (
282+ errors,
283+ trace,
284+ )
285+
286+
287+class QuiltError(errors.BzrError):
288+
289+ _fmt = "An error (%(retcode)d) occurred running quilt: %(msg)s"
290+
291+ def __init__(self, retcode, msg):
292+ self.retcode = retcode
293+ self.msg = msg
294+
295+
296+def run_quilt(args, working_dir, series_file=None, patches_dir=None, quiet=None):
297+ """Run quilt.
298+
299+ :param args: Arguments to quilt
300+ :param working_dir: Working dir
301+ :param series_file: Optional path to the series file
302+ :param patches_dir: Optional path to the patches
303+ :param quilt: Whether to be quiet (quilt stderr not to terminal)
304+ :raise QuiltError: When running quilt fails
305+ """
306+ def subprocess_setup():
307+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
308+ env = {}
309+ if patches_dir is not None:
310+ env["QUILT_PATCHES"] = patches_dir
311+ else:
312+ env["QUILT_PATCHES"] = os.path.join(working_dir, "debian", "patches")
313+ if series_file is not None:
314+ env["QUILT_SERIES"] = series_file
315+ else:
316+ env["QUILT_SERIES"] = os.path.join(env["QUILT_PATCHES"], "series")
317+ # Hide output if -q is in use.
318+ if quiet is None:
319+ quiet = trace.is_quiet()
320+ if quiet:
321+ stderr = subprocess.STDOUT
322+ else:
323+ stderr = subprocess.PIPE
324+ command = ["quilt"] + args
325+ trace.mutter("running: %r", command)
326+ if not os.path.isdir(working_dir):
327+ raise AssertionError("%s is not a valid directory" % working_dir)
328+ try:
329+ proc = subprocess.Popen(command, cwd=working_dir, env=env,
330+ stdin=subprocess.PIPE, preexec_fn=subprocess_setup,
331+ stdout=subprocess.PIPE, stderr=stderr)
332+ except OSError, e:
333+ if e.errno != errno.ENOENT:
334+ raise
335+ raise errors.BzrError("quilt is not installed, please install it")
336+ output = proc.communicate()
337+ if proc.returncode not in (0, 2):
338+ raise QuiltError(proc.returncode, output[1])
339+ if output[0] is None:
340+ return ""
341+ return output[0]
342+
343+
344+def quilt_pop_all(working_dir, patches_dir=None, series_file=None, quiet=None):
345+ """Pop all patches.
346+
347+ :param working_dir: Directory to work in
348+ :param patches_dir: Optional patches directory
349+ :param series_file: Optional series file
350+ """
351+ return run_quilt(["pop", "-a", "-v"], working_dir=working_dir, patches_dir=patches_dir, series_file=series_file, quiet=quiet)
352+
353+
354+def quilt_push_all(working_dir, patches_dir=None, series_file=None, quiet=None):
355+ """Push all patches.
356+
357+ :param working_dir: Directory to work in
358+ :param patches_dir: Optional patches directory
359+ :param series_file: Optional series file
360+ """
361+ return run_quilt(["push", "-a", "-v"], working_dir=working_dir, patches_dir=patches_dir, series_file=series_file, quiet=quiet)
362+
363+
364+def quilt_applied(working_dir, patches_dir=None, series_file=None):
365+ """Find the list of applied quilt patches.
366+
367+ :param working_dir: Directory to work in
368+ :param patches_dir: Optional patches directory
369+ :param series_file: Optional series file
370+ """
371+ try:
372+ return run_quilt(["applied"], working_dir=working_dir, patches_dir=patches_dir, series_file=series_file).splitlines()
373+ except QuiltError, e:
374+ if e.retcode == 1:
375+ return []
376+ raise
377+
378+
379+def quilt_unapplied(working_dir, patches_dir=None, series_file=None):
380+ """Find the list of unapplied quilt patches.
381+
382+ :param working_dir: Directory to work in
383+ :param patches_dir: Optional patches directory
384+ :param series_file: Optional series file
385+ """
386+ try:
387+ return run_quilt(["unapplied"], working_dir=working_dir,
388+ patches_dir=patches_dir, series_file=series_file).splitlines()
389+ except QuiltError, e:
390+ if e.retcode == 1:
391+ return []
392+ raise
393+
394+
395+def quilt_series(working_dir, patches_dir=None, series_file=None):
396+ """Find the list of patches.
397+
398+ :param working_dir: Directory to work in
399+ :param patches_dir: Optional patches directory
400+ :param series_file: Optional series file
401+ """
402+ return run_quilt(["series"], working_dir=working_dir, patches_dir=patches_dir, series_file=series_file).splitlines()
403+
404
405=== modified file 'tests/__init__.py'
406--- tests/__init__.py 2011-12-20 14:44:17 +0000
407+++ tests/__init__.py 2012-01-02 22:04:32 +0000
408@@ -37,11 +37,13 @@
409 from bzrlib.tests import TestUtil, multiply_tests
410 try:
411 from bzrlib.tests.features import (
412+ ExecutableFeature,
413 ModuleAvailableFeature,
414 UnicodeFilenameFeature,
415 )
416 except ImportError: # bzr < 2.5
417 from bzrlib.tests import (
418+ ExecutableFeature,
419 ModuleAvailableFeature,
420 UnicodeFilenameFeature,
421 )
422@@ -135,6 +137,7 @@
423 'test_merge_package',
424 'test_merge_quilt',
425 'test_merge_upstream',
426+ 'test_quilt',
427 'test_repack_tarball_extra',
428 'test_revspec',
429 'test_source_distiller',
430
431=== added file 'tests/test_quilt.py'
432--- tests/test_quilt.py 1970-01-01 00:00:00 +0000
433+++ tests/test_quilt.py 2012-01-02 22:04:32 +0000
434@@ -0,0 +1,87 @@
435+# Copyright (C) 2011 Canonical Ltd
436+#
437+# This file is part of bzr-builddeb.
438+#
439+# bzr-builddeb is free software; you can redistribute it and/or modify
440+# it under the terms of the GNU General Public License as published by
441+# the Free Software Foundation; either version 2 of the License, or
442+# (at your option) any later version.
443+#
444+# bzr-builddeb is distributed in the hope that it will be useful,
445+# but WITHOUT ANY WARRANTY; without even the implied warranty of
446+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
447+# GNU General Public License for more details.
448+#
449+# You should have received a copy of the GNU General Public License
450+# along with bzr-builddeb; if not, write to the Free Software
451+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
452+#
453+
454+"""Tests for the quilt code."""
455+
456+import os
457+
458+from bzrlib.plugins.builddeb.tests import ExecutableFeature
459+from bzrlib.plugins.builddeb.quilt import (
460+ quilt_pop_all,
461+ quilt_applied,
462+ quilt_unapplied,
463+ quilt_push_all,
464+ quilt_series,
465+ )
466+
467+from bzrlib.tests import TestCaseWithTransport
468+
469+quilt_feature = ExecutableFeature('quilt')
470+
471+TRIVIAL_PATCH = """--- /dev/null 2012-01-02 01:09:10.986490031 +0100
472++++ a 2012-01-02 20:03:59.710666215 +0100
473+@@ -0,0 +1 @@
474++a
475+"""
476+
477+class QuiltTests(TestCaseWithTransport):
478+
479+ _test_needs_features = [quilt_feature]
480+
481+ def make_empty_quilt_dir(self, path):
482+ source = self.make_branch_and_tree(path)
483+ self.build_tree([os.path.join(path, n) for n in ['debian/',
484+ 'debian/patches/']])
485+ self.build_tree_contents([
486+ (os.path.join(path, "debian/patches/series"), "\n")])
487+ source.add(["debian", "debian/patches", "debian/patches/series"])
488+ return source
489+
490+ def test_series_all_empty(self):
491+ self.make_empty_quilt_dir("source")
492+ self.assertEquals([], quilt_series("source"))
493+
494+ def test_series_all(self):
495+ self.make_empty_quilt_dir("source")
496+ self.build_tree_contents([
497+ ("source/debian/patches/series", "patch1.diff\n"),
498+ ("source/debian/patches/patch1.diff", TRIVIAL_PATCH)])
499+ self.assertEquals(["patch1.diff"], quilt_series("source"))
500+
501+ def test_push_all_empty(self):
502+ self.make_empty_quilt_dir("source")
503+ quilt_push_all("source", quiet=True)
504+
505+ def test_poph_all_empty(self):
506+ self.make_empty_quilt_dir("source")
507+ quilt_pop_all("source", quiet=True)
508+
509+ def test_applied_empty(self):
510+ self.make_empty_quilt_dir("source")
511+ self.build_tree_contents([
512+ ("source/debian/patches/series", "patch1.diff\n"),
513+ ("source/debian/patches/patch1.diff", "foob ar")])
514+ self.assertEquals([], quilt_applied("source"))
515+
516+ def test_unapplied(self):
517+ self.make_empty_quilt_dir("source")
518+ self.build_tree_contents([
519+ ("source/debian/patches/series", "patch1.diff\n"),
520+ ("source/debian/patches/patch1.diff", "foob ar")])
521+ self.assertEquals(["patch1.diff"], quilt_unapplied("source"))
522
523=== modified file 'tests/test_util.py'
524--- tests/test_util.py 2011-09-09 13:20:42 +0000
525+++ tests/test_util.py 2012-01-02 22:04:32 +0000
526@@ -63,7 +63,6 @@
527 _find_previous_upload,
528 find_thanks,
529 get_commit_info_from_changelog,
530- get_source_format,
531 guess_build_type,
532 lookup_distribution,
533 move_file_if_different,
534@@ -74,6 +73,7 @@
535 suite_to_distribution,
536 tarball_name,
537 tree_contains_upstream_source,
538+ tree_get_source_format,
539 write_if_different,
540 )
541
542@@ -773,27 +773,27 @@
543
544 def test_no_source_format_file(self):
545 tree = self.make_branch_and_tree('.')
546- self.assertEquals("1.0", get_source_format(tree))
547+ self.assertEquals("1.0", tree_get_source_format(tree))
548
549 def test_source_format_newline(self):
550 tree = self.make_branch_and_tree('.')
551 self.build_tree_contents([("debian/", ), ("debian/source/",),
552 ("debian/source/format", "3.0 (native)\n")])
553 tree.add(["debian", "debian/source", "debian/source/format"])
554- self.assertEquals("3.0 (native)", get_source_format(tree))
555+ self.assertEquals("3.0 (native)", tree_get_source_format(tree))
556
557 def test_source_format(self):
558 tree = self.make_branch_and_tree('.')
559 self.build_tree_contents([("debian/",), ("debian/source/",),
560 ("debian/source/format", "3.0 (quilt)")])
561 tree.add(["debian", "debian/source", "debian/source/format"])
562- self.assertEquals("3.0 (quilt)", get_source_format(tree))
563+ self.assertEquals("3.0 (quilt)", tree_get_source_format(tree))
564
565 def test_source_format_file_unversioned(self):
566 tree = self.make_branch_and_tree('.')
567 self.build_tree_contents([("debian/",), ("debian/source/",),
568 ("debian/source/format", "3.0 (quilt)")])
569- self.assertEquals("3.0 (quilt)", get_source_format(tree))
570+ self.assertEquals("3.0 (quilt)", tree_get_source_format(tree))
571
572
573 class GuessBuildTypeTests(TestCaseWithTransport):
574
575=== modified file 'util.py'
576--- util.py 2011-10-28 16:32:42 +0000
577+++ util.py 2012-01-02 22:04:32 +0000
578@@ -648,16 +648,17 @@
579 return (len(present_files - packaging_files) > 0)
580
581
582-def get_source_format(tree):
583+def tree_get_source_format(tree):
584 """Retrieve the source format name from a package.
585
586 :param path: Path to the package
587 :return: String with package format
588 """
589 filename = "debian/source/format"
590- if not tree.has_filename(filename):
591+ file_id = tree.path2id(filename)
592+ if file_id is None:
593 return FORMAT_1_0
594- text = tree.get_file_text(tree.path2id(filename), filename)
595+ text = tree.get_file_text(file_id, filename)
596 return text.strip()
597
598
599@@ -677,7 +678,7 @@
600 :param contains_upstream_source: Whether this branch contains the upstream source.
601 :return: A build_type value.
602 """
603- source_format = get_source_format(tree)
604+ source_format = tree_get_source_format(tree)
605 if source_format in NATIVE_SOURCE_FORMATS:
606 format_native = True
607 elif source_format in NORMAL_SOURCE_FORMATS:

Subscribers

People subscribed via source and target branches