Merge lp:~richard-wilbur/bzr/1537319-reproducible-builds into lp:bzr

Proposed by Richard Wilbur
Status: Work in progress
Proposed branch: lp:~richard-wilbur/bzr/1537319-reproducible-builds
Merge into: lp:bzr
Diff against target: 630 lines (+254/-84)
10 files modified
Makefile (+45/-34)
bzrlib/builtins.py (+2/-2)
bzrlib/cmd_version_info.py (+14/-1)
bzrlib/doc_generate/autodoc_bash_completion.py (+25/-13)
bzrlib/doc_generate/autodoc_man.py (+44/-16)
bzrlib/doc_generate/autodoc_rstx.py (+40/-15)
bzrlib/tests/test_version_info.py (+30/-1)
bzrlib/version_info_formats/__init__.py (+6/-1)
bzrlib/version_info_formats/format_source_date_epoch.py (+45/-0)
doc/en/Makefile (+3/-1)
To merge this branch: bzr merge lp:~richard-wilbur/bzr/1537319-reproducible-builds
Reviewer Review Type Date Requested Status
Vincent Ladeuil Pending
Review via email: mp+284564@code.launchpad.net

Description of the change

Changes to support GNU and debian reproducible builds effort: Add functionality to version-info to make populating SOURCE_DATE_EPOCH trivial on a bzr branch. Modify build and packaging code and rules to make bzr deliver reproducible builds for the same build environment.
Fixes lp:1537319

To post a comment you must log in.
Revision history for this message
Richard Wilbur (richard-wilbur) wrote :

I have nothing against 'bzr version-info --source-date-epoch' but I figured some folks might complain about the length of the required command line so I implemented (and documented) a hard-coded alias 'bzr sde' for that command. This is for those who value compactness over verbosity.

6615. By Richard Wilbur

Change makefile rules for reproducible input order. Use SOURCE_DATE_EPOCH for generating timestamps and datestamps, if available. Otherwise forgo timestamps and datestamps. Use SOURCE_DATE_EPOCH to clamp file modification times when generating tarball. Use root user and group as numeric id 0 when generating tarball.

6616. By Richard Wilbur

Fix creation of date and time stamps. man pages always have a datestamp in title so, in the absence of SOURCE_DATE_EPOCH, give the impossible date of 1 second before the UNIX epoch.

Revision history for this message
Vincent Ladeuil (vila) wrote :

Hmm.

This looks like a rabbit hole :-/

Overall, this is going in the right direction but I think this will end up in several MPs.

There is duplicated code you're modifying which is worth refactoring as are the duplicate doc Makefiles.

Not to mention the comments (I feel your pain :-/) you had to modify to maintain consistency.

And we need more tests :) At least a setup where we can build all docs and see the results to start with.

Revision history for this message
Richard Wilbur (richard-wilbur) wrote :
Download full text (3.6 KiB)

I'm happy to accommodate multiple merge proposals but am in need of counsel on how best to accomplish that. Should I shelve everything except one changeset, polish, push to launchpad, propose the merge, review, merge, and repeat? Using the same branch? Should I break the changes into separate branches?

I see the main changes here as:
revision 6614: Add explicit support to bzr for creating SOURCE_DATE_EPOCH from a bzr branch. (If no version information is available, presently returns 0 => *nix epoch. Would a different value be more suitable?)

revision 6615:
./Makefile: Change make rules to sort the file lists for constant file order in tar file creation. Also use numeric user and group ids, root/root => 0/0 and clamp modification time to SOURCE_DATE_EPOCH when creating tar files. (Set SOURCE_DATE_EPOCH with `bzr sde` if not already set.)

./doc/en/Makefile: Use (or set with `bzr sde`) SOURCE_DATE_EPOCH to create BUILD_DATE for sphinx documentation build.

./bzrlib/doc_generate/autodoc_bash_completion.py: Try to get SOURCE_DATE_EPOCH and, if found, use it to create timestamp and include 'Last commit: %' in output file. Retitled from 'Generation time' to 'Last commit' as it seemed more descriptive.

./bzrlib/doc_generate/autodoc_man.py: Try to get SOURCE_DATE_EPOCH and, if found, use it to create date/time stamps. Split preamble and head so that datestamp and timestamp are only output when SOURCE_DATE_EPOCH is available.

./bzrlib/doc_generate/autodoc_rstx.py: Try to get SOURCE_DATE_EPOCH and, if found, use it to create date/time stamps. Split preamble so that timestamp is output when SOURCE_DATE_EPOCH is available.

revision 6616: Fixed syntax of date/time stamp creation in bzrlib/doc_generate/autodoc_*.py. Changed man head to always use datestamp but in the absence of SOURCE_DATE_EPOCH, use 1 second before the *nix epoch (valid, impossible date).

1. I see the utility of factoring common code out of the bzrlib/doc_generate/autodoc_*.py into bzrlib/doc_generate/__init__.py
If we decide to substitute a flag date/time for missing SOURCE_DATE_EPOCH information, this will greatly simplify the process of factoring out the common code (more will be common), and it will also simplify the code which creates the documentation as there won't be as many conditionally included pieces.

If so, what would be a good, fixed value to use as a flag date/time? I notice that I already return "0" from `bzr sde` if there is no revision information on the branch and use "-1" in the event we are missing SOURCE_DATE_EPOCH in the man documentation generator. I think a case could be made for these being materially similar situations: no revision information available. So I think it would benefit us to use a consistent flag for this situation. I like "-1" as it should never occur as a valid source date. The only issue would possibly occur if SOURCE_DATE_EPOCH were to be treated as an unsigned integer, in which case "0" would be preferrable. I expect "0" is pretty much equally unlikely to be a valid source date. (I suppose in Python we are relatively unlikely to ever find SOURCE_DATE_EPOCH treated as an unsigned integer. If we are interested i...

Read more...

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Hi Richard,

Please split this up into separate merge proposals. At least for Debian, most of these changes are unnecessary. See https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/bzr.html

Revision history for this message
Richard Wilbur (richard-wilbur) wrote :

Separate merge proposals on the same branch or do I need to create separate branches?

Unmerged revisions

6616. By Richard Wilbur

Fix creation of date and time stamps. man pages always have a datestamp in title so, in the absence of SOURCE_DATE_EPOCH, give the impossible date of 1 second before the UNIX epoch.

6615. By Richard Wilbur

Change makefile rules for reproducible input order. Use SOURCE_DATE_EPOCH for generating timestamps and datestamps, if available. Otherwise forgo timestamps and datestamps. Use SOURCE_DATE_EPOCH to clamp file modification times when generating tarball. Use root user and group as numeric id 0 when generating tarball.

6614. By Richard Wilbur

Add source-date-epoch format for version-info command (and alias sde) to support reproducible builds.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2012-03-09 16:48:55 +0000
+++ Makefile 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2011 Canonical Ltd1# Copyright (C) 2005-2011, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -28,6 +28,10 @@
28PLUGIN_TARGET=plugin-release28PLUGIN_TARGET=plugin-release
29PYTHON_BUILDFLAGS=29PYTHON_BUILDFLAGS=
3030
31# Shorter replacement for $(sort $(wildcard <arg>)) as $(call sw,<arg>)
32sw = $(sort $(wildcard $(1)))
33
34
31.PHONY: all clean realclean extensions pyflakes api-docs check-nodocs check35.PHONY: all clean realclean extensions pyflakes api-docs check-nodocs check
3236
33all: extensions37all: extensions
@@ -106,10 +110,10 @@
106### Man-page Documentation ###110### Man-page Documentation ###
107111
108MAN_DEPENDENCIES = bzrlib/builtins.py \112MAN_DEPENDENCIES = bzrlib/builtins.py \
109 $(wildcard bzrlib/*.py) \113 $(call sw,bzrlib/*.py) \
110 $(wildcard bzrlib/*/*.py) \114 $(call sw,bzrlib/*/*.py) \
111 tools/generate_docs.py \115 tools/generate_docs.py \
112 $(wildcard $(addsuffix /*.txt, bzrlib/help_topics/en)) 116 $(call sw,$(addsuffix /*.txt, bzrlib/help_topics/en))
113117
114MAN_PAGES = man1/bzr.1118MAN_PAGES = man1/bzr.1
115man1/bzr.1: $(MAN_DEPENDENCIES)119man1/bzr.1: $(MAN_DEPENDENCIES)
@@ -145,7 +149,7 @@
145 doc/developers/Makefile \149 doc/developers/Makefile \
146 doc/developers/make.bat150 doc/developers/make.bat
147151
148NEWS_FILES = $(wildcard doc/en/release-notes/bzr-*.txt)152NEWS_FILES = $(call sw,doc/en/release-notes/bzr-*.txt)
149153
150doc/en/user-reference/index.txt: $(MAN_DEPENDENCIES)154doc/en/user-reference/index.txt: $(MAN_DEPENDENCIES)
151 $(PYTHON) tools/generate_docs.py -o $@ rstx155 $(PYTHON) tools/generate_docs.py -o $@ rstx
@@ -229,19 +233,19 @@
229 doc/en/tutorials/tutorial.txt \233 doc/en/tutorials/tutorial.txt \
230 doc/en/tutorials/using_bazaar_with_launchpad.txt \234 doc/en/tutorials/using_bazaar_with_launchpad.txt \
231 doc/en/tutorials/centralized_workflow.txt \235 doc/en/tutorials/centralized_workflow.txt \
232 $(wildcard doc/es/tutorials/*.txt) \236 $(call sw,doc/es/tutorials/*.txt) \
233 $(wildcard doc/ru/tutorials/*.txt) \237 $(call sw,doc/ru/tutorials/*.txt) \
234 doc/ja/tutorials/tutorial.txt \238 doc/ja/tutorials/tutorial.txt \
235 doc/ja/tutorials/using_bazaar_with_launchpad.txt \239 doc/ja/tutorials/using_bazaar_with_launchpad.txt \
236 doc/ja/tutorials/centralized_workflow.txt \240 doc/ja/tutorials/centralized_workflow.txt \
237 $(wildcard doc/*/mini-tutorial/index.txt) \241 $(call sw,doc/*/mini-tutorial/index.txt) \
238 $(wildcard doc/*/user-guide/index-plain.txt) \242 $(call sw,doc/*/user-guide/index-plain.txt) \
239 doc/en/admin-guide/index-plain.txt \243 doc/en/admin-guide/index-plain.txt \
240 $(wildcard doc/es/guia-usario/*.txt) \244 $(call sw,doc/es/guia-usario/*.txt) \
241 $(derived_txt_files) \245 $(derived_txt_files) \
242 doc/en/upgrade-guide/index.txt \246 doc/en/upgrade-guide/index.txt \
243 doc/index.txt \247 doc/index.txt \
244 $(wildcard doc/index.*.txt)248 $(call sw,doc/index.*.txt)
245txt_nohtml = \249txt_nohtml = \
246 doc/en/user-guide/index.txt \250 doc/en/user-guide/index.txt \
247 doc/es/user-guide/index.txt \251 doc/es/user-guide/index.txt \
@@ -253,16 +257,16 @@
253257
254non_txt_files = \258non_txt_files = \
255 doc/default.css \259 doc/default.css \
256 $(wildcard doc/*/bzr-en-quick-reference.svg) \260 $(call sw,doc/*/bzr-en-quick-reference.svg) \
257 $(wildcard doc/*/bzr-en-quick-reference.png) \261 $(call sw,doc/*/bzr-en-quick-reference.png) \
258 $(wildcard doc/*/bzr-en-quick-reference.pdf) \262 $(call sw,doc/*/bzr-en-quick-reference.pdf) \
259 $(wildcard doc/*/bzr-es-quick-reference.svg) \263 $(call sw,doc/*/bzr-es-quick-reference.svg) \
260 $(wildcard doc/*/bzr-es-quick-reference.png) \264 $(call sw,doc/*/bzr-es-quick-reference.png) \
261 $(wildcard doc/*/bzr-es-quick-reference.pdf) \265 $(call sw,doc/*/bzr-es-quick-reference.pdf) \
262 $(wildcard doc/*/bzr-ru-quick-reference.svg) \266 $(call sw,doc/*/bzr-ru-quick-reference.svg) \
263 $(wildcard doc/*/bzr-ru-quick-reference.png) \267 $(call sw,doc/*/bzr-ru-quick-reference.png) \
264 $(wildcard doc/*/bzr-ru-quick-reference.pdf) \268 $(call sw,doc/*/bzr-ru-quick-reference.pdf) \
265 $(wildcard doc/*/user-guide/images/*.png)269 $(call sw,doc/*/user-guide/images/*.png)
266270
267# doc/developers/*.txt files that should *not* be individually271# doc/developers/*.txt files that should *not* be individually
268# converted to HTML272# converted to HTML
@@ -292,20 +296,20 @@
292 doc/developers/status.txt \296 doc/developers/status.txt \
293 doc/developers/uncommit.txt297 doc/developers/uncommit.txt
294298
295dev_txt_all = $(wildcard $(addsuffix /*.txt, doc/developers))299dev_txt_all = $(call sw,$(addsuffix /*.txt, doc/developers))
296dev_txt_files = $(filter-out $(dev_txt_nohtml), $(dev_txt_all))300dev_txt_files = $(filter-out $(dev_txt_nohtml), $(dev_txt_all))
297dev_htm_files = $(patsubst %.txt, %.html, $(dev_txt_files)) 301dev_htm_files = $(patsubst %.txt, %.html, $(dev_txt_files))
298302
299doc/en/user-guide/index-plain.html: $(wildcard $(addsuffix /*.txt, doc/en/user-guide)) 303doc/en/user-guide/index-plain.html: $(call sw,$(addsuffix /*.txt, doc/en/user-guide))
300 $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@304 $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@
301305
302#doc/es/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/es/user-guide)) 306#doc/es/user-guide/index.html: $(call sw,$(addsuffix /*.txt, doc/es/user-guide))
303# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@307# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@
304#308#
305#doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide)) 309#doc/ru/user-guide/index.html: $(call sw,$(addsuffix /*.txt, doc/ru/user-guide))
306# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@310# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@
307#311#
308doc/en/admin-guide/index-plain.html: $(wildcard $(addsuffix /*.txt, doc/en/admin-guide)) 312doc/en/admin-guide/index-plain.html: $(call sw,$(addsuffix /*.txt, doc/en/admin-guide))
309 $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@313 $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@
310314
311doc/developers/%.html: doc/developers/%.txt315doc/developers/%.html: doc/developers/%.txt
@@ -323,7 +327,7 @@
323doc/en/release-notes/NEWS.txt: $(NEWS_FILES) tools/generate_release_notes.py327doc/en/release-notes/NEWS.txt: $(NEWS_FILES) tools/generate_release_notes.py
324 $(PYTHON) tools/generate_release_notes.py "$@" $(NEWS_FILES)328 $(PYTHON) tools/generate_release_notes.py "$@" $(NEWS_FILES)
325329
326upgrade_guide_dependencies = $(wildcard $(addsuffix /*.txt, doc/en/upgrade-guide)) 330upgrade_guide_dependencies = $(call sw,$(addsuffix /*.txt, doc/en/upgrade-guide))
327331
328doc/en/upgrade-guide/index.html: $(upgrade_guide_dependencies)332doc/en/upgrade-guide/index.html: $(upgrade_guide_dependencies)
329 $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@333 $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@
@@ -435,8 +439,9 @@
435update-pot: po/bzr.pot439update-pot: po/bzr.pot
436440
437TRANSLATABLE_PYFILES:=$(shell find bzrlib -name '*.py' \441TRANSLATABLE_PYFILES:=$(shell find bzrlib -name '*.py' \
438 | grep -v 'bzrlib/tests/' \442 | grep -v 'bzrlib/tests/' \
439 | grep -v 'bzrlib/doc' \443 | grep -v 'bzrlib/doc' \
444 | LC_ALL=C sort \
440 )445 )
441446
442po/bzr.pot: $(PYFILES) $(DOCFILES)447po/bzr.pot: $(PYFILES) $(DOCFILES)
@@ -453,6 +458,12 @@
453458
454.PHONY: dist check-dist-tarball459.PHONY: dist check-dist-tarball
455460
461SOURCE_DATE_EPOCH ?= $(shell bzr sde)
462TAR_OPTS = "--sort=name --owner=root --group=root --numeric-owner"
463ifdef SOURCE_DATE_EPOCH
464TAR_OPTS += "--mtime='$SOURCE_DATE_EPOCH' --clamp-mtime"
465endif
466
456# build a distribution source tarball467# build a distribution source tarball
457#468#
458# this method of copying the pyrex generated files is a bit ugly; it would be469# this method of copying the pyrex generated files is a bit ugly; it would be
@@ -467,7 +478,7 @@
467 $(MAKE) && \478 $(MAKE) && \
468 bzr export $$expdir && \479 bzr export $$expdir && \
469 cp bzrlib/*.c bzrlib/*.h $$expdir/bzrlib/. && \480 cp bzrlib/*.c bzrlib/*.h $$expdir/bzrlib/. && \
470 tar cfz $$tarball -C $$expbasedir bzr-$$version && \481 tar $(TAR_OPTS) cfz $$tarball -C $$expbasedir bzr-$$version && \
471 gpg --detach-sign $$tarball && \482 gpg --detach-sign $$tarball && \
472 rm -rf $$expbasedir483 rm -rf $$expbasedir
473484
474485
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2013-05-23 10:04:17 +0000
+++ bzrlib/builtins.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2012 Canonical Ltd1# Copyright (C) 2005-2012, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -6724,7 +6724,7 @@
6724 ('cmd_bundle_info', [], 'bzrlib.bundle.commands'),6724 ('cmd_bundle_info', [], 'bzrlib.bundle.commands'),
6725 ('cmd_config', [], 'bzrlib.config'),6725 ('cmd_config', [], 'bzrlib.config'),
6726 ('cmd_dpush', [], 'bzrlib.foreign'),6726 ('cmd_dpush', [], 'bzrlib.foreign'),
6727 ('cmd_version_info', [], 'bzrlib.cmd_version_info'),6727 ('cmd_version_info', ['sde'], 'bzrlib.cmd_version_info'),
6728 ('cmd_resolve', ['resolved'], 'bzrlib.conflicts'),6728 ('cmd_resolve', ['resolved'], 'bzrlib.conflicts'),
6729 ('cmd_conflicts', [], 'bzrlib.conflicts'),6729 ('cmd_conflicts', [], 'bzrlib.conflicts'),
6730 ('cmd_ping', [], 'bzrlib.smart.ping'),6730 ('cmd_ping', [], 'bzrlib.smart.ping'),
67316731
=== modified file 'bzrlib/cmd_version_info.py'
--- bzrlib/cmd_version_info.py 2011-12-30 12:52:54 +0000
+++ bzrlib/cmd_version_info.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2011 Canonical Ltd1# Copyright (C) 2005-2011, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -71,6 +71,14 @@
71 * {branch_nick} - branch nickname71 * {branch_nick} - branch nickname
72 * {clean} - 0 if the source tree contains uncommitted changes,72 * {clean} - 0 if the source tree contains uncommitted changes,
73 otherwise 173 otherwise 1
74
75 In order to support reproducible builds, you can set the
76 SOURCE_DATE_EPOCH to the timestamp of the last commit on a bzr
77 working tree as follows:
78
79 SOURCE_DATE_EPOCH=$(bzr version-info --source-date-epoch)
80 or using the alias
81 SOURCE_DATE_EPOCH=$(bzr sde)
74 """82 """
7583
76 takes_options = [RegistryOption('format',84 takes_options = [RegistryOption('format',
@@ -88,6 +96,7 @@
88 'revision',96 'revision',
89 ]97 ]
90 takes_args = ['location?']98 takes_args = ['location?']
99 aliases = ['sde']
91100
92 encoding_type = 'exact'101 encoding_type = 'exact'
93102
@@ -96,6 +105,10 @@
96 include_file_revisions=False, template=None,105 include_file_revisions=False, template=None,
97 revision=None):106 revision=None):
98107
108 if 'sde' == self.invoked_as:
109 format = \
110 version_info_formats.format_registry.get(key=
111 'source-date-epoch')
99 if revision and len(revision) > 1:112 if revision and len(revision) > 1:
100 raise errors.BzrCommandError(113 raise errors.BzrCommandError(
101 gettext('bzr version-info --revision takes exactly'114 gettext('bzr version-info --revision takes exactly'
102115
=== modified file 'bzrlib/doc_generate/autodoc_bash_completion.py'
--- bzrlib/doc_generate/autodoc_bash_completion.py 2011-12-19 13:23:58 +0000
+++ bzrlib/doc_generate/autodoc_bash_completion.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005 Canonical Ltd1# Copyright (C) 2005, 2016 Canonical Ltd
22
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -18,7 +18,8 @@
1818
19from __future__ import absolute_import19from __future__ import absolute_import
2020
21import time21import datetime
22import os
2223
23import bzrlib24import bzrlib
24import bzrlib.help25import bzrlib.help
@@ -30,24 +31,35 @@
3031
3132
32def infogen(options, outfile):33def infogen(options, outfile):
33 t = time.time()34 try:
34 tt = time.gmtime(t)35 tt = datetime.datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
35 params = \36 params = \
36 { "bzrcmd": options.bzr_name,37 { "bzrcmd": options.bzr_name,
37 "datestamp": time.strftime("%Y-%m-%d",tt),38 "datestamp": tt.strftime("%Y-%m-%d"),
38 "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt),39 "timestamp": tt.strftime("%Y-%m-%d %H:%M:%S +0000"),
39 "version": bzrlib.__version__,40 "version": bzrlib.__version__,
40 }41 }
42
43 except (KeyError, ValueError):
44 tt = None
45 params = \
46 { "bzrcmd": options.bzr_name,
47 "version": bzrlib.__version__,
48 }
4149
42 outfile.write(preamble % params)50 outfile.write(preamble % params)
51 if tt not None:
52 outfile.write(last_commit % params)
4353
4454
45preamble = """\55preamble = """\
46# bash completion functions for for Bazaar (%(bzrcmd)s)56# bash completion functions for Bazaar (%(bzrcmd)s)
47#57#
48# Large parts of this file are autogenerated from the internal58# Large parts of this file are autogenerated from the internal
49# Bazaar documentation and data structures.59# Bazaar documentation and data structures.
60"""
61
62last_commit = """\
50#63#
51# Generation time: %(timestamp)s64# Last commit: %(timestamp)s
52"""65"""
53
5466
=== modified file 'bzrlib/doc_generate/autodoc_man.py'
--- bzrlib/doc_generate/autodoc_man.py 2015-03-14 23:44:01 +0000
+++ bzrlib/doc_generate/autodoc_man.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2010 Canonical Ltd1# Copyright (C) 2005-2010, 2016 Canonical Ltd
22
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -15,18 +15,15 @@
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1616
17"""man.py - create man page from built-in bzr help and static text17"""man.py - create man page from built-in bzr help and static text
18
19TODO:
20 * use usage information instead of simple "bzr foo" in COMMAND OVERVIEW
21 * add command aliases
22"""18"""
2319
24from __future__ import absolute_import20from __future__ import absolute_import
2521
26PLUGINS_TO_DOCUMENT = ["launchpad"]22PLUGINS_TO_DOCUMENT = ["launchpad"]
2723
24import datetime
25import os
28import textwrap26import textwrap
29import time
3027
31import bzrlib28import bzrlib
32import bzrlib.help29import bzrlib.help
@@ -44,15 +41,35 @@
4441
45def infogen(options, outfile):42def infogen(options, outfile):
46 """Assembles a man page"""43 """Assembles a man page"""
47 t = time.time()44 try:
48 tt = time.gmtime(t)45 tt = datetime.datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
49 params = \46 params = \
50 { "bzrcmd": options.bzr_name,47 { "bzrcmd": options.bzr_name,
51 "datestamp": time.strftime("%Y-%m-%d",tt),48 "datestamp": tt.strftime("%Y-%m-%d"),
52 "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt),49 "timestamp": tt.strftime("%Y-%m-%d %H:%M:%S +0000"),
53 "version": bzrlib.__version__,50 "version": bzrlib.__version__,
54 }51 }
55 outfile.write(man_preamble % params)52
53 except (KeyError, ValueError):
54 tt = None
55 ep = datetime.datetime.utcfromtimestamp(-1)
56 params = \
57 { "bzrcmd": options.bzr_name,
58 # Linux man-pages convention: Title includes
59 # datestamp of last non-trivial revision. In absence
60 # of SOURCE_DATE_EPOCH use 1 second before UNIX
61 # epoch (impossible date).
62 "datestamp": ep.strftime("%Y-%m-%d"),
63 "version": bzrlib.__version__,
64 }
65 # Split part of man_preamble out that uses timestamp to allow
66 # reproducible builds in the absence of SOURCE_DATE_EPOCH.
67 outfile.write(man_prepreamble % params)
68 if tt not None:
69 # Uses timestamp, if available.
70 outfile.write(man_last_commit_preamble % params)
71 outfile.write(man_postpreamble % params)
72
56 outfile.write(man_escape(man_head % params))73 outfile.write(man_escape(man_head % params))
57 outfile.write(man_escape(getcommand_list(params)))74 outfile.write(man_escape(getcommand_list(params)))
58 outfile.write(man_escape(getcommand_help(params)))75 outfile.write(man_escape(getcommand_help(params)))
@@ -182,13 +199,24 @@
182 yield man_escape(desc) + "\n"199 yield man_escape(desc) + "\n"
183200
184201
185man_preamble = """\202# Split preamble to conditionally provide last commit time when
203# SOURCE_DATE_EPOCH provided by build system in order to provide
204# reproducible builds.
205man_prepreamble = """\
186.\\\"Man page for Bazaar (%(bzrcmd)s)206.\\\"Man page for Bazaar (%(bzrcmd)s)
187.\\\"207.\\\"
188.\\\" Large parts of this file are autogenerated from the output of208.\\\" Large parts of this file are autogenerated from the output of
189.\\\" \"%(bzrcmd)s help commands\"209.\\\" \"%(bzrcmd)s help commands\"
190.\\\" \"%(bzrcmd)s help <cmd>\"210.\\\" \"%(bzrcmd)s help <cmd>\"
191.\\\"211.\\\"
212"""
213
214man_last_commit_preamble = """\
215.\\\" Last commit: %(timestamp)s
216.\\\"
217"""
218
219man_postpreamble = """\
192220
193.ie \\n(.g .ds Aq \\(aq221.ie \\n(.g .ds Aq \\(aq
194.el .ds Aq '222.el .ds Aq '
195223
=== modified file 'bzrlib/doc_generate/autodoc_rstx.py'
--- bzrlib/doc_generate/autodoc_rstx.py 2015-11-15 02:30:05 +0000
+++ bzrlib/doc_generate/autodoc_rstx.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2006-2010 Canonical Ltd1# Copyright (C) 2006-2010, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,8 @@
2222
23from __future__ import absolute_import23from __future__ import absolute_import
2424
25import time25import datetime
26import os
2627
27import bzrlib28import bzrlib
28import bzrlib.help29import bzrlib.help
@@ -38,20 +39,34 @@
3839
39def infogen(options, outfile):40def infogen(options, outfile):
40 """Create manual in RSTX format"""41 """Create manual in RSTX format"""
41 t = time.time()42 try:
42 tt = time.gmtime(t)43 tt = datetime.datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
43 params = \44 params = \
44 { "bzrcmd": options.bzr_name,45 { "bzrcmd": options.bzr_name,
45 "datestamp": time.strftime("%Y-%m-%d",tt),46 "datestamp": tt.strftime("%Y-%m-%d"),
46 "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt),47 "timestamp": tt.strftime("%Y-%m-%d %H:%M:%S +0000"),
47 "version": bzrlib.__version__,48 "version": bzrlib.__version__,
48 }49 }
50
51 except (KeyError, ValueError):
52 tt = None
53 params = \
54 { "bzrcmd": options.bzr_name,
55 "version": bzrlib.__version__,
56 }
49 nominated_filename = getattr(options, 'filename', None)57 nominated_filename = getattr(options, 'filename', None)
50 if nominated_filename is None:58 if nominated_filename is None:
51 topic_dir = None59 topic_dir = None
52 else:60 else:
53 topic_dir = bzrlib.osutils.dirname(nominated_filename)61 topic_dir = bzrlib.osutils.dirname(nominated_filename)
54 outfile.write(rstx_preamble % params)62 # Split part of rstx_preamble out that uses timestamp to allow
63 # reproducible builds in the absence of SOURCE_DATE_EPOCH.
64 outfile.write(rstx_prepreamble % params)
65 if tt not None:
66 # Uses timestamp, if available.
67 outfile.write(rstx_last_commit_preamble % params)
68 outfile.write(rstx_postpreamble % params)
69
55 outfile.write(rstx_head % params)70 outfile.write(rstx_head % params)
56 outfile.write(_get_body(params, topic_dir))71 outfile.write(_get_body(params, topic_dir))
57 outfile.write(rstx_foot % params)72 outfile.write(rstx_foot % params)
@@ -139,14 +154,24 @@
139##154##
140# TEMPLATES155# TEMPLATES
141156
142rstx_preamble = """.. This file is autogenerated from the output of157# Split preamble to conditionally provide last commit time when
158# SOURCE_DATE_EPOCH provided by build system in order to provide
159# reproducible builds.
160rstx_prepreamble = """\
161.. This file is autogenerated from the output of
143.. %(bzrcmd)s help topics162.. %(bzrcmd)s help topics
144.. %(bzrcmd)s help commands163.. %(bzrcmd)s help commands
145.. %(bzrcmd)s help <cmd>164.. %(bzrcmd)s help <cmd>
146..165..
147166"""
148"""167
149168rstx_last_commit_preamble = """\
169.. Last commit: %(timestamp)s
170"""
171
172rstx_postpreamble = """\
173
174"""
150175
151rstx_head = """\176rstx_head = """\
152#####################177#####################
153178
=== modified file 'bzrlib/tests/test_version_info.py'
--- bzrlib/tests/test_version_info.py 2012-01-03 13:08:44 +0000
+++ bzrlib/tests/test_version_info.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2011 Canonical Ltd1# Copyright (C) 2005-2011, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
33from bzrlib.version_info_formats.format_custom import CustomVersionInfoBuilder33from bzrlib.version_info_formats.format_custom import CustomVersionInfoBuilder
34from bzrlib.version_info_formats.format_rio import RioVersionInfoBuilder34from bzrlib.version_info_formats.format_rio import RioVersionInfoBuilder
35from bzrlib.version_info_formats.format_python import PythonVersionInfoBuilder35from bzrlib.version_info_formats.format_python import PythonVersionInfoBuilder
36from bzrlib.version_info_formats.format_source_date_epoch import SourceDateEpochVersionInfoBuilder
3637
3738
38class VersionInfoTestCase(TestCaseWithTransport):39class VersionInfoTestCase(TestCaseWithTransport):
@@ -399,6 +400,34 @@
399 self.assertRaises(errors.NoTemplate, builder.generate, sio)400 self.assertRaises(errors.NoTemplate, builder.generate, sio)
400401
401402
403class SourceDateEpochVersionInfoTests(VersionInfoTestCase):
404
405 def test_custom_null(self):
406 """No revision yet on tree."""
407 sio = StringIO()
408 wt = self.make_branch_and_tree('branch')
409 builder = SourceDateEpochVersionInfoBuilder(wt.branch, working_tree=wt)
410 builder.generate(sio)
411 self.assertEquals("0", sio.getvalue())
412
413 def regen(self, wt):
414 sio = StringIO()
415 builder = SourceDateEpochVersionInfoBuilder(wt.branch, working_tree=wt)
416 builder.generate(sio)
417 val = sio.getvalue()
418 return val
419
420 def test_source_date_epoch(self):
421 """Should return seconds from *nix epoch till most recent revision."""
422 wt = self.create_branch()
423 # Get timestamp of most recent revision on tree.
424 br = wt.branch
425 revision_id = wt.last_revision()
426 last_rev = br.repository.get_revision(revision_id)
427 val = self.regen(wt)
428 self.assertEqual(val, '%0d' % last_rev.timestamp)
429
430
402class TestBuilder(version_info_formats.VersionInfoBuilder):431class TestBuilder(version_info_formats.VersionInfoBuilder):
403 pass432 pass
404433
405434
=== modified file 'bzrlib/version_info_formats/__init__.py'
--- bzrlib/version_info_formats/__init__.py 2011-12-30 13:02:27 +0000
+++ bzrlib/version_info_formats/__init__.py 2016-02-01 16:52:40 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006 Canonical Ltd1# Copyright (C) 2005, 2006, 2016 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -205,3 +205,8 @@
205 'bzrlib.version_info_formats.format_custom',205 'bzrlib.version_info_formats.format_custom',
206 'CustomVersionInfoBuilder',206 'CustomVersionInfoBuilder',
207 'Version info in Custom template-based format.')207 'Version info in Custom template-based format.')
208format_registry.register_lazy(
209 'source-date-epoch',
210 'bzrlib.version_info_formats.format_source_date_epoch',
211 'SourceDateEpochVersionInfoBuilder',
212 'SOURCE_DATE_EPOCH version info (for reproducible builds).')
208213
=== added file 'bzrlib/version_info_formats/format_source_date_epoch.py'
--- bzrlib/version_info_formats/format_source_date_epoch.py 1970-01-01 00:00:00 +0000
+++ bzrlib/version_info_formats/format_source_date_epoch.py 2016-02-01 16:52:40 +0000
@@ -0,0 +1,45 @@
1# Copyright (C) 2016 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""A generator which creates the SOURCE_DATE_EPOCH (seconds from the
18*nix epoch till the most recent revision commit) from the current tree
19info. This is used to aid the reproducible builds effort by replacing
20date and time stamps derived from the build date and time with
21information regarding the last revision of the source code.
22"""
23
24from __future__ import absolute_import
25
26from bzrlib.revision import (
27 NULL_REVISION,
28 )
29from bzrlib.version_info_formats import (
30 VersionInfoBuilder,
31 )
32
33
34class SourceDateEpochVersionInfoBuilder(VersionInfoBuilder):
35 """Create a version file which consists of the source epoch."""
36
37 def generate(self, to_file):
38 revision_id = self._get_revision_id()
39 if revision_id == NULL_REVISION:
40 sde = 0
41 else:
42 rev = self._branch.repository.get_revision(revision_id)
43 sde = rev.timestamp
44
45 to_file.write('%0d' % sde)
046
=== modified file 'doc/en/Makefile'
--- doc/en/Makefile 2012-03-09 16:48:55 +0000
+++ doc/en/Makefile 2016-02-01 16:52:40 +0000
@@ -2,7 +2,9 @@
2#2#
33
4# You can set these variables from the command line.4# You can set these variables from the command line.
5SPHINXOPTS =5SOURCE_DATE_EPOCH ?= $(shell bzr sde)
6BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "@$(SOURCE_DATE_EPOCH)")
7SPHINXOPTS = "-D today=\"$(BUILD_DATE)\""
6SPHINXBUILD = sphinx-build8SPHINXBUILD = sphinx-build
7PAPER =9PAPER =
810