[Git][NTPsec/ntpsec][master] 2 commits: NEWS - add notices on dodged CVEs.

Eric S. Raymond gitlab at mg.gitlab.com
Tue Mar 13 17:41:46 UTC 2018


Eric S. Raymond pushed to branch master at NTPsec / ntpsec


Commits:
c6256c2e by Eric S. Raymond at 2018-03-13T17:37:49Z
NEWS - add notices on dodged CVEs.

- - - - -
3aae297a by Eric S. Raymond at 2018-03-13T17:38:36Z
Revert "Use python in wscript instead of autorevision.sh"

Too close to release. Maybe I need to start using caffeine...

- - - - -


19 changed files:

- .gitignore
- INSTALL
- NEWS
- VERSION
- devel/TODO
- devel/hacking.txt
- devel/ifdex-ignores
- devel/make-tarball
- devel/release.nix
- ntpd/ntpd.c
- ntpd/wscript
- ntptime/ntptime.c
- pylib/util.py.in → pylib/util.py
- pylib/wscript
- tests/pylib/test_util.py.in → tests/pylib/test_util.py
- tests/wscript
- + wafhelpers/autorevision.sh
- wafhelpers/options.py
- wscript


Changes:

=====================================
.gitignore
=====================================
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
+wafhelpers/.autorevision-cache
 .lock-waf*
 .waf*
 *.pyc
+ntpd/version.h
 build


=====================================
INSTALL
=====================================
--- a/INSTALL
+++ b/INSTALL
@@ -166,6 +166,28 @@ Linux File Hierarchy Standard.
 You should have neither issue if you install from an OS distribution's
 binary package.
 
+== Caveat for Crossbuilders ==
+
+If you see a build failure with the message "error: No repo or cache
+detected.", you have collided with an unusual feature of our build recipe.
+
+To generate the version.h file, the uses a script called
+'autorevision.sh' which needs one of two preconditions.  Either (1)
+you are building in a local repository clone, or (2) you are building
+from an unpacked release tarball containing a file
+wafhelpers/.autorevision-cache containing version information.
+
+The motivation for this behavior is to have a version string that
+makes sense on every build from either the repository or a release
+tarball.  It means, however, that if you try to build when waf can see
+neither of these things, you'll get the "No repo or cache detected"
+error.  This can happen when atrempting to use a remote buildroot for
+cross-compilation or other purposes
+
+There are a couple of possible workarounds.  One is to do your remote build
+from a release tarball.  Another is to do a ./waf configure and build in the
+source directory before attempting a buildroot build.
+
 == Installation Names ==
 
 By default, `waf install' will install the package's files in


=====================================
NEWS
=====================================
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,14 @@ Digests longer then 20 bytes will be truncated.
 
 We have merged Classic's fix for CVE-2018-7182.
 
+The following NTP Classic CVEs announced in February 2018 do not affect NTPsec:
+
+* CVE-2016-1549: Sybil vulnerability: ephemeral association attack
+* CVE-2018-7170: Multiple authenticated ephemeral associations
+* CVE-2018-7184: Interleaved symmetric mode cannot recover from bad state
+* CVE-2018-7185: Unauthenticated packet can reset authenticated interleaved association
+* CVE-2018-7183: ntpq:decodearr() can write beyond its buffer limit
+
 We have dropped support for Broadcast servers.
 
 It is now possible to unpeer refclocks using a type/unit specification


=====================================
VERSION
=====================================
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0
+1.0.1


=====================================
devel/TODO
=====================================
--- a/devel/TODO
+++ b/devel/TODO
@@ -104,6 +104,10 @@ Waf detect need configure:
   after a git pull.  (From email on 23 May 2017)
 
 Version string cleanup:
+  This is tangled up with EPOCH
+  configure has --build-version-tag=
+  configure sets up NTPSEC_VERSION_STRING
+  ntptime is the only useage.
   The version string should change if I make an edit and rebuild
     currently it only changes when something in git changes
   We need to be sure not to break the stable checksum feature.


=====================================
devel/hacking.txt
=====================================
--- a/devel/hacking.txt
+++ b/devel/hacking.txt
@@ -377,7 +377,7 @@ a large common include file. These includes live in docs/includes
 and are probably what you need to edit if you're updating anything
 that appears on a man page.
 
-=== Version number ===
+=== Version string ===
 
 We use a variant of three part Semantic Versioning, of the form X.Y.Z.
 X, Y, and Z are non-negative decimal integers.
@@ -411,20 +411,6 @@ Note that this is a different numbering system from NTP Classic. In
 their A.B.C numbers, A was the protocol version, B was the major, and
 C was the minor.  They also use release-candidate suffixes.
 
-== Version string ==
-
-We use the BUILD_EPOCH as described in packaging/packaging.txt for the
-timestamp (converted to RFC 3339 format) part of the version string reported in
-ntpd and other binaries.
-
-Release builds have version strings of the form:
-
-<base version number> <BUILD_EPOCH in RFC 3339 format>
-
-Development builds have version strings of the form:
-
-<base version number>+ <BUILD_EPOCH in RFC 3339 format> (git rev <git shorthash>)
-
 == Contribution workflow and conventions ==
 
 Please work on one piece of conceptual work at a time.


=====================================
devel/ifdex-ignores
=====================================
--- a/devel/ifdex-ignores
+++ b/devel/ifdex-ignores
@@ -87,6 +87,7 @@ TEST_LIBPARSE
 
 # Purely internal symbols
 CONFIG_H
+AUTOREVISION_H
 BITSPERCHAR		# Only used in the Arcron refclock
 BACKTRACE_MAXFRAME	# Internal to the ISC backtrace code
 BACKTRACE_LIBC


=====================================
devel/make-tarball
=====================================
--- a/devel/make-tarball
+++ b/devel/make-tarball
@@ -5,6 +5,9 @@
 # a file named ntpsec-$V.tar.gz.  The name of this tarball is echoed to
 # standard output as part of the success message.
 #
+# The tricky part is that it has to include the autorevision cache file,
+# which can't be checked into the repo or there'd be an infinite loop.
+#
 # Do not try running this outside of devel/
 #
 # *Do* configure and test-build before running it.
@@ -15,6 +18,12 @@ then
     exit 1
 fi
 
+if [ ! -r ../wafhelpers/.autorevision-cache ]
+then
+    echo "Autorevision cache file does not exist, waf build and try again"
+    exit 1
+fi
+
 set -e
 
 if [ "$1" != "" ]
@@ -26,7 +35,7 @@ fi
 
 # Build the tarball
 rm -fr .tmp
-(cd ..; git ls-files; find build -print | grep '\.[0-9]$') >MANIFEST
+(cd ..; git ls-files; find build -print | grep '\.[0-9]$'; echo "wafhelpers/.autorevision-cache") >MANIFEST
 (cd ..; tar --transform="s:^:ntpsec-${V}/:" -T devel/MANIFEST -czf ntpsec-${V}.tar.gz)
 rm MANIFEST
 mv ../ntpsec-${V}.tar.gz .


=====================================
devel/release.nix
=====================================
--- a/devel/release.nix
+++ b/devel/release.nix
@@ -28,6 +28,10 @@ in with derivationOptions; rec {
     distPhase = ''
       runHook preDist
 
+      if [ -n "''${versionSuffix}" ]; then
+        distFlags="--build-version-tag=$versionSuffix $distFlags"
+      fi
+
       echo "dist flags: $distFlags ''${distFlagsArray[@]}"
       python waf dist $distFlags "''${distFlagsArray[@]}"
       
@@ -56,6 +60,10 @@ in with derivationOptions; rec {
           configureFlags="''${prefixKey:---prefix=}$prefix $configureFlags"
         fi
 
+        if [ -n "$versionSuffix" ]; then
+          configureFlags="--build-version-tag=$versionSuffix $configureFlags"
+        fi
+
         echo "configure flags: $configureFlags ''${configureFlagsArray[@]}"
         python waf configure $configureFlags "''${configureFlagsArray[@]}"
 


=====================================
ntpd/ntpd.c
=====================================
--- a/ntpd/ntpd.c
+++ b/ntpd/ntpd.c
@@ -32,6 +32,8 @@
 
 #include "recvbuff.h"
 
+#include "version.h"
+
 void catchQuit (int sig);
 static volatile int signo = 0;
 /* In an ideal world, 'finish_safe()' would declared as noreturn... */
@@ -483,7 +485,7 @@ const char *ntpd_version(void)
 {
     static char versionbuf[64];
     snprintf(versionbuf, sizeof(versionbuf),
-	     "ntpd ntpsec-%s", NTPSEC_VERSION);
+	     "ntpd ntpsec-%s+%d %s", VERSION, VCS_TICK, VCS_DATE);
     return versionbuf;
 }
 


=====================================
ntpd/wscript
=====================================
--- a/ntpd/wscript
+++ b/ntpd/wscript
@@ -2,6 +2,8 @@
 def build(ctx):
     srcnode = ctx.srcnode.abspath()
     bldnode = ctx.bldnode.abspath()
+    target3 = ctx.srcnode.make_node('ntpd/version.h')
+    target4 = ctx.srcnode.make_node('wafhelpers/.autorevision-cache')
 
     if ctx.variant == "host":
         bison_source = ["ntp_parser.y"]
@@ -15,7 +17,15 @@ def build(ctx):
             target="bison_obj",
         )
 
-        # Generate Bison file first.
+        ctx(
+            cwd=srcnode,
+            rule='VCS_EXTRA=`cat ${SRC[0]}` wafhelpers/autorevision.sh '
+                 '-o ${TGT[1].abspath()} -e VERSION -t h >${TGT[0].abspath()}',
+            source=["../VERSION", '../wafhelpers/autorevision.sh'],
+            target=[target3, target4],
+        )
+
+        # Generate Bison and version.h files first.
         ctx.add_group()
 
         keyword_gen_source = ["keyword-gen.c", ]


=====================================
ntptime/ntptime.c
=====================================
--- a/ntptime/ntptime.c
+++ b/ntptime/ntptime.c
@@ -410,7 +410,7 @@ main(
 		}
 		if (json)
 		    /* hack to avoid trailing comma - not semantically needed */
-		    printf("\"version\":\"ntpsec-%s\"}\n", NTPSEC_VERSION);
+		    fputs("\"version\":\""  NTPSEC_VERSION_STRING "\"}\n", stdout);
 		exit(EXIT_SUCCESS);
 	}
 


=====================================
pylib/util.py.in → pylib/util.py
=====================================
--- a/pylib/util.py.in
+++ b/pylib/util.py
@@ -13,6 +13,7 @@ import socket
 import sys
 import time
 import ntp.ntpc
+import ntp.version
 import ntp.magic
 import ntp.control
 
@@ -106,7 +107,9 @@ def safeargcast(arg, castfunc, errtext, usage):
 
 def stdversion():
     "Returns the NTPsec version string in a standard format"
-    return "ntpsec-%s" % "@NTPSEC_VERSION@"
+    return "ntpsec-%s+%s %s" % (ntp.version.VERSION,
+                                ntp.version.VCS_TICK,
+                                ntp.version.VCS_DATE)
 
 
 def rfc3339(t):


=====================================
pylib/wscript
=====================================
--- a/pylib/wscript
+++ b/pylib/wscript
@@ -16,21 +16,15 @@ def build(ctx):
     bldnode = ctx.bldnode.make_node('pylib')
     target1 = bldnode.make_node('control.py')
     target2 = bldnode.make_node('magic.py')
-
-    ctx(
-        features="subst",
-        source='util.py.in',
-        target='util.py'
-    )
-    # Force early creation of util.py
-    ctx.add_group()
-
+    target3 = bldnode.make_node('version.py')
+    target4 = ctx.srcnode.make_node('wafhelpers/.autorevision-cache')
     sources = srcnode.ant_glob('*.py')
     builds = [x.get_bld() for x in sources]
 
     # Remove generated files to ensure they are properly updated
     ctx.exec_command("rm -f %s" % target1.abspath())
     ctx.exec_command("rm -f %s" % target2.abspath())
+    ctx.exec_command("rm -f %s" % target3.abspath())
 
     # Make sure Python sees .py as well as .pyc/.pyo
     ctx(
@@ -55,12 +49,21 @@ def build(ctx):
         target=target2,
     )
 
+    ctx(
+        before=['pyc', 'pyo'],
+        cwd=srcnode,
+        rule='VCS_EXTRA=`cat ${SRC[0]}` ../wafhelpers/autorevision.sh '
+             '-o ${TGT[1].abspath()} -e VERSION -t python >${TGT[0].abspath()}',
+        source=["../VERSION", '../wafhelpers/autorevision.sh'],
+        target=[target3, target4],
+    )
+
     # Force early creation of generated files
     ctx.add_group()
 
     ctx(
         features='py',
-        source=builds+[target1, target2],
+        source=builds+[target1, target2, target3],
         install_from=bldnode,
         install_path='${PYTHONDIR}/ntp'
     )


=====================================
tests/pylib/test_util.py.in → tests/pylib/test_util.py
=====================================
--- a/tests/pylib/test_util.py.in
+++ b/tests/pylib/test_util.py
@@ -96,8 +96,10 @@ class TestPylibUtilMethods(unittest.TestCase):
     def test_stdversion(self):
         f = ntp.util.stdversion
 
-        ver = "@NTPSEC_VERSION@"
-        self.assertEqual(f(), "ntpsec-" + ver)
+        ver = str(ntp.version.VERSION)
+        tick = str(ntp.version.VCS_TICK)
+        date = str(ntp.version.VCS_DATE)
+        self.assertEqual(f(), "ntpsec-" + ver + "+" + tick + " " + date)
 
     def test_rfc3339(self):
         f = ntp.util.rfc3339


=====================================
tests/wscript
=====================================
--- a/tests/wscript
+++ b/tests/wscript
@@ -126,14 +126,6 @@ def build(ctx):
             pass
         os.symlink(pypath.abspath(), linkpath.abspath())
 
-    ctx(
-        features="subst",
-        source="pylib/test_util.py.in",
-        target="pylib/test_util.py"
-    )
-    # Force early creation of pylib/test_util.py
-    ctx.add_group()
-
     pytests = ["pylib/test_util.py",
                "pylib/test_agentx_packet.py",
                "pylib/test_packet.py",


=====================================
wafhelpers/autorevision.sh
=====================================
--- /dev/null
+++ b/wafhelpers/autorevision.sh
@@ -0,0 +1,1341 @@
+#!/bin/sh
+
+# Copyright (c) 2012 - 2016 dak180 and contributors. See
+# https://opensource.org/licenses/mit-license.php or the included
+# COPYING.md for licence terms.
+#
+# autorevision - extracts metadata about the head version from your
+# repository.
+
+# shellcheck disable=SC2154
+
+# Usage message.
+arUsage() {
+	tee >&2 << EOF
+usage: autorevision {-t output-type | -s symbol} [-o cache-file [-f] ] [-e name] [-U] [-V]
+	Options include:
+	-t output-type		= specify output type
+	-s symbol		= specify symbol output
+	-o cache-file		= specify cache file location
+	-f			= force the use of cache data
+	-e name			= set a different output name for VCS_EXTRA
+	-U			= check for untracked files in svn
+	-V			= emit version and exit
+	-?			= help message
+
+The following are valid output types:
+	c			= C/C++ file
+	clojure			= clojure file
+	cmake 			= CMake script file
+	csharp			= CSharp properties file
+	h			= Header for use with c/c++
+	hpp			= Alternate C++ header strings with namespace
+	ini			= INI file
+	java			= Java file
+	javaprop		= Java properties file
+	js			= javascript file
+	json			= JSON file
+	lua			= Lua file
+	m4			= m4 file
+	matlab			= matlab file
+	octave			= octave file
+	php			= PHP file
+	pl			= Perl file
+	py			= Python file
+	rpm			= rpm file
+	scheme			= scheme file
+	sh			= Bash sytax
+	swift			= Swift file
+	tex			= (La)TeX file
+	xcode			= Header useful for populating info.plist files
+
+
+The following are valid symbols:
+	VCS_TYPE
+	VCS_BASENAME
+	VCS_UUID
+	VCS_NUM
+	VCS_DATE
+	VCS_BRANCH
+	VCS_TAG
+	VCS_TICK
+	VCS_EXTRA
+	VCS_FULL_HASH
+	VCS_SHORT_HASH
+	VCS_WC_MODIFIED
+	VCS_ACTION_STAMP
+EOF
+	exit 1
+}
+
+# Config
+ARVERSION="1.19"
+while getopts ":t:o:s:e:VfU" OPTION; do
+	case "${OPTION}" in
+		t)
+			AFILETYPE="${OPTARG}"
+		;;
+		o)
+			CACHEFILE="${OPTARG}"
+		;;
+		f)
+			CACHEFORCE="1"
+		;;
+		s)
+			VAROUT="${OPTARG}"
+		;;
+		e)
+			EXTRA_NAME="${OPTARG}"
+		;;
+		U)
+			UNTRACKEDFILES="1"
+		;;
+		V)
+			echo "autorevision ${ARVERSION}"
+			exit 0
+		;;
+		?)
+			# If an unknown flag is used (or -?):
+			arUsage
+		;;
+	esac
+done
+
+if [ ! -z "${VAROUT}" ] && [ ! -z "${AFILETYPE}" ]; then
+	# If both -s and -t are specified:
+	echo "error: Improper argument combination." 1>&2
+	exit 1
+elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then
+	# If neither -s or -t are specified:
+	arUsage
+elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+	# If -f is specified without -o:
+	arUsage
+elif [ ! -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+	# If we are forced to use the cache but it does not exist.
+	echo "error: Cache forced but no cache found." 1>&2
+	exit 1
+fi
+
+# Only use the local keyword if it is there (ksh we are looking at
+# you).
+
+if [ "$(command -v local 2> /dev/null)" = "local" ]; then
+	LOCAL="local"
+elif command -v typeset > /dev/null 2>&1; then
+	LOCAL="typeset"
+else
+	LOCAL=""
+fi
+
+# Make sure that the path we are given is one we can source
+# (dash, we are looking at you).
+if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then
+	CACHEFILE="./${CACHEFILE}"
+fi
+
+GENERATED_HEADER="Generated by autorevision - do not hand-hack!"
+: "${EXTRA_NAME:="VCS_EXTRA"}"
+
+# Functions to extract data from different repo types.
+# For git repos
+# shellcheck disable=SC2039,SC2164,SC2155
+gitRepo() {
+	${LOCAL} oldPath="${PWD}"
+
+	cd "$(git rev-parse --show-toplevel)"
+
+	VCS_TYPE="git"
+
+	VCS_BASENAME="$(basename "${PWD}")"
+
+	${LOCAL} currentRev="$(git rev-parse HEAD)"
+
+	VCS_UUID="$(git rev-list --max-parents=0 --date-order --reverse "${currentRev}" 2>/dev/null | sed -n 1p)"
+	if [ -z "${VCS_UUID}" ]; then
+		VCS_UUID="$(git rev-list --topo-order "${currentRev}" | tail -n 1)"
+	fi
+
+	# Is the working copy clean?
+	test -z "$(git status --untracked-files=normal --porcelain)"
+	VCS_WC_MODIFIED="${?}"
+
+	# Enumeration of changesets
+	VCS_NUM="$(git rev-list --count "${currentRev}" 2>/dev/null)"
+	if [ -z "${VCS_NUM}" ]; then
+		echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2
+		VCS_NUM="$(git rev-list HEAD | wc -l)"
+	fi
+
+	# This may be a git-svn remote.  If so, report the Subversion revision.
+	if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]; then
+		# The full revision hash
+		VCS_FULL_HASH="$(git rev-parse "${currentRev}")"
+
+		# The short hash
+		VCS_SHORT_HASH="$(git rev-parse --short "${currentRev}")"
+	else
+		# The git-svn revision number
+		VCS_FULL_HASH="$(git svn find-rev "${currentRev}")"
+		VCS_SHORT_HASH="${VCS_FULL_HASH}"
+	fi
+
+	# Current branch
+	VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined "${currentRev}" 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')"
+
+	# Cache the description
+	${LOCAL} DESCRIPTION="$(git describe --long --tags "${currentRev}" 2>/dev/null)"
+
+	# Current or last tag ancestor (empty if no tags)
+	VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" -e 's:-[0-9]*$::')"
+
+	# Distance to last tag or an alias of VCS_NUM if there is no tag
+	if [ ! -z "${DESCRIPTION}" ]; then
+		VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")"
+	else
+		VCS_TICK="${VCS_NUM}"
+	fi
+
+	# Date of the current commit
+	VCS_DATE="$(TZ=UTC git show -s --date=iso-strict-local --pretty=format:%cd "${currentRev}" 2>/dev/null | sed -e 's|+00:00|Z|')"
+	if [ -z "${VCS_DATE}" ]; then
+		echo "warning: Action stamps require git version 2.7+." 1>&2
+		VCS_DATE="$(git log -1 --pretty=format:%ci "${currentRev}" | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')"
+		${LOCAL} ASdis="1"
+	fi
+
+	# Action Stamp
+	if [ -z "${ASdis}" ]; then
+		VCS_ACTION_STAMP="${VCS_DATE}!$(git show -s --pretty=format:%cE "${currentRev}")"
+	else
+		VCS_ACTION_STAMP=""
+	fi
+
+	cd "${oldPath}"
+}
+
+# For hg repos
+# shellcheck disable=SC2039,SC2164
+hgRepo() {
+	${LOCAL} oldPath="${PWD}"
+
+	cd "$(hg root)"
+
+	VCS_TYPE="hg"
+
+	VCS_BASENAME="$(basename "${PWD}")"
+
+	VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')"
+
+	# Is the working copy clean?
+	test -z "$(hg status -duram)"
+	VCS_WC_MODIFIED="${?}"
+
+	# Enumeration of changesets
+	VCS_NUM="$(hg id -n | tr -d '+')"
+
+	# The full revision hash
+	VCS_FULL_HASH="$(hg log -r "${VCS_NUM}" -l 1 --template '{node}\n')"
+
+	# The short hash
+	VCS_SHORT_HASH="$(hg id -i | tr -d '+')"
+
+	# Current bookmark (bookmarks are roughly equivalent to git's branches)
+	# or branch if no bookmark
+	VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)"
+	# Fall back to the branch if there are no bookmarks
+	if [ -z "${VCS_BRANCH}" ]; then
+		VCS_BRANCH="$(hg id -b)"
+	fi
+
+	# Current or last tag ancestor (excluding auto tags, empty if no tags)
+	VCS_TAG="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' -e 's:qparent::' -e "s:$(hg --config 'extensions.color=' --config 'extensions.mq=' --color never qtop 2>/dev/null)::" | cut -d ' ' -f 1)"
+
+	# Distance to last tag or an alias of VCS_NUM if there is no tag
+	if [ ! -z "${VCS_TAG}" ]; then
+		VCS_TICK="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttagdistance}\n' 2>/dev/null)"
+	else
+		VCS_TICK="${VCS_NUM}"
+	fi
+
+	# Date of the current commit
+	VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')"
+
+	# Action Stamp
+	VCS_ACTION_STAMP="$(TZ=UTC hg log -r "${VCS_NUM}" -l 1 --template '{date|localdate|rfc3339date}\n' 2>/dev/null | sed -e 's|+00:00|Z|')!$(hg log -r "${VCS_NUM}" -l 1 --template '{author|email}\n' 2>/dev/null)"
+
+	cd "${oldPath}"
+}
+
+# For bzr repos
+# shellcheck disable=SC2039,SC2164
+bzrRepo() {
+	${LOCAL} oldPath="${PWD}"
+
+	cd "$(bzr root)"
+
+	VCS_TYPE="bzr"
+
+	VCS_BASENAME="$(basename "${PWD}")"
+
+	# Currently unimplemented because more investigation is needed.
+	VCS_UUID=""
+
+	# Is the working copy clean?
+	bzr version-info --custom --template='{clean}\n' | grep -q '1'
+	VCS_WC_MODIFIED="${?}"
+
+	# Enumeration of changesets
+	VCS_NUM="$(bzr revno)"
+
+	# The full revision hash
+	VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')"
+
+	# The short hash
+	VCS_SHORT_HASH="${VCS_NUM}"
+
+	# Nick of the current branch
+	VCS_BRANCH="$(bzr nick)"
+
+	# Current or last tag ancestor (excluding auto tags, empty if no tags)
+	VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)"
+
+	# Distance to last tag or an alias of VCS_NUM if there is no tag
+	if [ ! -z "${VCS_TAG}" ]; then
+		VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')"
+	else
+		VCS_TICK="${VCS_NUM}"
+	fi
+
+	# Date of the current commit
+	VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' -e 's: ::')"
+
+	# Action Stamp
+	# Currently unimplemented because more investigation is needed.
+	VCS_ACTION_STAMP=""
+
+	cd "${oldPath}"
+}
+
+# For svn repos
+# shellcheck disable=SC2039,SC2164,SC2155
+svnRepo() {
+	${LOCAL} oldPath="${PWD}"
+
+	VCS_TYPE="svn"
+
+	case "${PWD}" in
+	/*trunk*|/*branches*|/*tags*)
+		${LOCAL} fn="${PWD}"
+		while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do
+			${LOCAL} fn="$(dirname "${fn}")"
+		done
+		${LOCAL} fn="$(dirname "${fn}")"
+		if [ "${fn}" = '/' ]; then
+			VCS_BASENAME="$(basename "${PWD}")"
+		else
+			VCS_BASENAME="$(basename "${fn}")"
+		fi
+		;;
+	*) VCS_BASENAME="$(basename "${PWD}")" ;;
+	esac
+
+	VCS_UUID="$(svn info --xml | sed -n -e 's:<uuid>::' -e 's:</uuid>::p')"
+
+	# Cache svnversion output
+	${LOCAL} SVNVERSION="$(svnversion)"
+
+	# Is the working copy clean?
+	echo "${SVNVERSION}" | grep -q "M"
+	case "${?}" in
+		0)
+			VCS_WC_MODIFIED="1"
+		;;
+		1)
+			if [ ! -z "${UNTRACKEDFILES}" ]; then
+			# `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to.
+				if [ -z "$(svn status)" ]; then
+					VCS_WC_MODIFIED="0"
+				else
+					VCS_WC_MODIFIED="1"
+				fi
+			else
+				VCS_WC_MODIFIED="0"
+			fi
+		;;
+	esac
+
+	# Enumeration of changesets
+	VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')"
+
+	# The full revision hash
+	VCS_FULL_HASH="${SVNVERSION}"
+
+	# The short hash
+	VCS_SHORT_HASH="${VCS_NUM}"
+
+	# Current branch
+	case "${PWD}" in
+	/*trunk*|/*branches*|/*tags*)
+		${LOCAL} lastbase=""
+		${LOCAL} fn="${PWD}"
+		while :
+		do
+			base="$(basename "${fn}")"
+			if [ "${base}" = 'trunk' ]; then
+				VCS_BRANCH='trunk'
+				break
+			elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then
+				VCS_BRANCH="${lastbase}"
+				break
+			elif [ "${base}" = '/' ]; then
+				VCS_BRANCH=""
+				break
+			fi
+			${LOCAL} lastbase="${base}"
+			${LOCAL} fn="$(dirname "${fn}")"
+		done
+		;;
+	*) VCS_BRANCH="" ;;
+	esac
+
+	# Current or last tag ancestor (empty if no tags). But "current
+	# tag" can't be extracted reliably because Subversion doesn't
+	# have tags the way other VCSes do.
+	VCS_TAG=""
+	VCS_TICK=""
+
+	# Date of the current commit
+	VCS_DATE="$(svn info --xml | sed -n -e 's:<date>::' -e 's:</date>::p')"
+
+	# Action Stamp
+	VCS_ACTION_STAMP="${VCS_DATE}!$(svn log --xml -l 1 -r "${VCS_SHORT_HASH}" | sed -n -e 's:<author>::' -e 's:</author>::p')"
+
+	cd "${oldPath}"
+}
+
+
+# Functions to output data in different formats.
+# For bash output
+# First in list because it is used by autorevision
+shOutput() {
+	tee << EOF
+# ${GENERATED_HEADER}
+
+VCS_TYPE="${VCS_TYPE}"
+VCS_BASENAME="${VCS_BASENAME}"
+VCS_UUID="${VCS_UUID}"
+VCS_NUM="${VCS_NUM}"
+VCS_DATE="${VCS_DATE}"
+VCS_BRANCH="${VCS_BRANCH}"
+VCS_TAG="${VCS_TAG}"
+VCS_TICK="${VCS_TICK}"
+${EXTRA_NAME}="${VCS_EXTRA}"
+
+VCS_ACTION_STAMP="${VCS_ACTION_STAMP}"
+VCS_FULL_HASH="${VCS_FULL_HASH}"
+VCS_SHORT_HASH="${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED="${VCS_WC_MODIFIED}"
+
+# end
+EOF
+}
+
+# For source C output
+cOutput() {
+	tee << EOF
+/* ${GENERATED_HEADER} */
+
+const char *VCS_TYPE         = "${VCS_TYPE}";
+const char *VCS_BASENAME     = "${VCS_BASENAME}";
+const char *VCS_UUID         = "${VCS_UUID}";
+const int VCS_NUM            = ${VCS_NUM};
+const char *VCS_DATE         = "${VCS_DATE}";
+const char *VCS_BRANCH       = "${VCS_BRANCH}";
+const char *VCS_TAG          = "${VCS_TAG}";
+const int VCS_TICK           = ${VCS_TICK};
+const char *${EXTRA_NAME}        = "${VCS_EXTRA}";
+
+const char *VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}";
+const char *VCS_FULL_HASH    = "${VCS_FULL_HASH}";
+const char *VCS_SHORT_HASH   = "${VCS_SHORT_HASH}";
+
+const int VCS_WC_MODIFIED     = ${VCS_WC_MODIFIED};
+
+/* end */
+EOF
+}
+
+# For Cmake output
+cmakeOutput() {
+	tee << EOF
+# ${GENERATED_HEADER}
+
+set(VCS_TYPE ${VCS_TYPE})
+set(VCS_BASENAME ${VCS_BASENAME})
+set(VCS_UUID ${VCS_UUID})
+set(VCS_NUM ${VCS_NUM})
+set(VCS_DATE ${VCS_DATE})
+set(VCS_BRANCH ${VCS_BRANCH})
+set(VCS_TAG ${VCS_TAG})
+set(VCS_TICK ${VCS_TICK})
+set(${EXTRA_NAME} ${VCS_EXTRA})
+
+set(VCS_ACTION_STAMP ${VCS_ACTION_STAMP})
+set(VCS_FULL_HASH ${VCS_FULL_HASH})
+set(VCS_SHORT_HASH ${VCS_SHORT_HASH})
+
+set(VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+
+# end
+EOF
+}
+
+# For header output
+hOutput() {
+	tee << EOF
+/* ${GENERATED_HEADER} */
+#ifndef AUTOREVISION_H
+#define AUTOREVISION_H
+
+#define VCS_TYPE		"${VCS_TYPE}"
+#define VCS_BASENAME	"${VCS_BASENAME}"
+#define VCS_UUID		"${VCS_UUID}"
+#define VCS_NUM			${VCS_NUM}
+#define VCS_DATE		"${VCS_DATE}"
+#define VCS_BRANCH		"${VCS_BRANCH}"
+#define VCS_TAG			"${VCS_TAG}"
+#define VCS_TICK		${VCS_TICK}
+#define ${EXTRA_NAME}		"${VCS_EXTRA}"
+
+#define VCS_ACTION_STAMP	"${VCS_ACTION_STAMP}"
+#define VCS_FULL_HASH		"${VCS_FULL_HASH}"
+#define VCS_SHORT_HASH		"${VCS_SHORT_HASH}"
+
+#define VCS_WC_MODIFIED		${VCS_WC_MODIFIED}
+
+#endif
+
+/* end */
+EOF
+}
+
+# A header output for use with xcode to populate info.plist strings
+xcodeOutput() {
+	tee << EOF
+/* ${GENERATED_HEADER} */
+#ifndef AUTOREVISION_H
+#define AUTOREVISION_H
+
+#define VCS_TYPE		${VCS_TYPE}
+#define VCS_BASENAME	${VCS_BASENAME}
+#define VCS_UUID		${VCS_UUID}
+#define VCS_NUM			${VCS_NUM}
+#define VCS_DATE		${VCS_DATE}
+#define VCS_BRANCH		${VCS_BRANCH}
+#define VCS_TAG			${VCS_TAG}
+#define VCS_TICK		${VCS_TICK}
+#define ${EXTRA_NAME}		${VCS_EXTRA}
+
+#define VCS_ACTION_STAMP	${VCS_ACTION_STAMP}
+#define VCS_FULL_HASH		${VCS_FULL_HASH}
+#define VCS_SHORT_HASH		${VCS_SHORT_HASH}
+
+#define VCS_WC_MODIFIED		${VCS_WC_MODIFIED}
+
+#endif
+
+/* end */
+EOF
+}
+
+# For Swift output
+swiftOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	# For values that may not exist depending on the type of repo we
+	# have read from, set them to `nil` when they are empty.
+	if [ -z "${VCS_UUID}" ]; then
+		VCS_UUID="nil"
+	else
+		VCS_UUID="\"${VCS_UUID}\""
+	fi
+	if [ -z "${VCS_TAG}" ]; then
+		VCS_TAG="nil"
+	else
+		VCS_TAG="\"${VCS_TAG}\""
+	fi
+	: "${VCS_TICK:="nil"}"
+	if [ -z "${VCS_EXTRA}" ]; then
+		VCS_EXTRA="nil"
+	else
+		VCS_EXTRA="\"${VCS_EXTRA}\""
+	fi
+	if [ -z "${VCS_ACTION_STAMP}" ]; then
+		VCS_ACTION_STAMP="nil"
+	else
+		VCS_ACTION_STAMP="\"${VCS_ACTION_STAMP}\""
+	fi
+	tee << EOF
+/* ${GENERATED_HEADER} */
+
+let VCS_TYPE			= "${VCS_TYPE}"
+let VCS_BASENAME		= "${VCS_BASENAME}"
+let VCS_UUID:	String?	= ${VCS_UUID}
+let VCS_NUM:	Int		= ${VCS_NUM}
+let VCS_DATE			= "${VCS_DATE}"
+let VCS_BRANCH:	String	= "${VCS_BRANCH}"
+let VCS_TAG:	String?	= ${VCS_TAG}
+let VCS_TICK:	Int?	= ${VCS_TICK}
+let ${EXTRA_NAME}:	String?	= ${VCS_EXTRA}
+
+let VCS_ACTION_STAMP:	String?	= ${VCS_ACTION_STAMP}
+let VCS_FULL_HASH:		String	= "${VCS_FULL_HASH}"
+let VCS_SHORT_HASH:		String	= "${VCS_SHORT_HASH}"
+
+let VCS_WC_MODIFIED:	Bool	= ${VCS_WC_MODIFIED}
+
+/* end */
+EOF
+}
+
+# For Python output
+pyOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) PY_VCS_WC_MODIFIED="False" ;;
+		1) PY_VCS_WC_MODIFIED="True" ;;
+	esac
+	tee << EOF
+# -*- coding: utf-8 -*-
+# ${GENERATED_HEADER}
+
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+${EXTRA_NAME} = "${VCS_EXTRA}"
+
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED = ${PY_VCS_WC_MODIFIED}
+
+# end
+EOF
+}
+
+# For Perl output
+plOutput() {
+	tee << EOF
+# ${GENERATED_HEADER}
+
+\$VCS_TYPE = '${VCS_TYPE}';
+\$VCS_BASENAME = '${VCS_BASENAME}';
+\$VCS_UUID = '${VCS_UUID}';
+\$VCS_NUM = ${VCS_NUM};
+\$VCS_DATE = '${VCS_DATE}';
+\$VCS_BRANCH = '${VCS_BRANCH}';
+\$VCS_TAG = '${VCS_TAG}';
+\$VCS_TICK = ${VCS_TICK};
+\$${EXTRA_NAME} = '${VCS_EXTRA}';
+
+\$VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+\$VCS_FULL_HASH = '${VCS_FULL_HASH}';
+\$VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+\$VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+# end
+1;
+EOF
+}
+
+# For lua output
+luaOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	tee << EOF
+-- ${GENERATED_HEADER}
+
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+${EXTRA_NAME} = "${VCS_EXTRA}"
+
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
+
+-- end
+EOF
+}
+
+# For php output
+phpOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	tee << EOF
+<?php
+# ${GENERATED_HEADER}
+
+return array(
+	"VCS_TYPE" => "${VCS_TYPE}",
+	"VCS_BASENAME" => "${VCS_BASENAME}",
+	"VCS_UUID" => "${VCS_UUID}",
+	"VCS_NUM" => ${VCS_NUM},
+	"VCS_DATE" => "${VCS_DATE}",
+	"VCS_BRANCH" => "${VCS_BRANCH}",
+	"VCS_TAG" => "${VCS_TAG}",
+	"VCS_TICK" => ${VCS_TICK},
+	"${EXTRA_NAME}" => "${VCS_EXTRA}",
+	"VCS_ACTION_STAMP" => "${VCS_ACTION_STAMP}",
+	"VCS_FULL_HASH" => "${VCS_FULL_HASH}",
+	"VCS_SHORT_HASH" => "${VCS_SHORT_HASH}",
+	"VCS_WC_MODIFIED" => ${VCS_WC_MODIFIED}
+);
+
+# end
+?>
+EOF
+}
+
+# For ini output
+iniOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	tee << EOF
+; ${GENERATED_HEADER}
+[VCS]
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+${EXTRA_NAME} = "${VCS_EXTRA}"
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
+; end
+EOF
+}
+
+# For javascript output
+jsOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		1) VCS_WC_MODIFIED="true" ;;
+		0) VCS_WC_MODIFIED="false" ;;
+	esac
+	tee << EOF
+/** ${GENERATED_HEADER} */
+
+var autorevision = {
+	VCS_TYPE: "${VCS_TYPE}",
+	VCS_BASENAME: "${VCS_BASENAME}",
+	VCS_UUID: "${VCS_UUID}",
+	VCS_NUM: ${VCS_NUM},
+	VCS_DATE: "${VCS_DATE}",
+	VCS_BRANCH: "${VCS_BRANCH}",
+	VCS_TAG: "${VCS_TAG}",
+	VCS_TICK: ${VCS_TICK},
+	${EXTRA_NAME}: "${VCS_EXTRA}",
+
+	VCS_ACTION_STAMP: "${VCS_ACTION_STAMP}",
+	VCS_FULL_HASH: "${VCS_FULL_HASH}",
+	VCS_SHORT_HASH: "${VCS_SHORT_HASH}",
+
+	VCS_WC_MODIFIED: ${VCS_WC_MODIFIED}
+};
+
+/** Node.js compatibility */
+if (typeof module !== 'undefined') {
+	module.exports = autorevision;
+}
+
+/** end */
+EOF
+}
+
+# For JSON output
+jsonOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		1) VCS_WC_MODIFIED="true" ;;
+		0) VCS_WC_MODIFIED="false" ;;
+	esac
+	tee << EOF
+{
+	"_comment": "${GENERATED_HEADER}",
+	"VCS_TYPE": "${VCS_TYPE}",
+	"VCS_BASENAME": "${VCS_BASENAME}",
+	"VCS_UUID": "${VCS_UUID}",
+	"VCS_NUM": ${VCS_NUM},
+	"VCS_DATE": "${VCS_DATE}",
+	"VCS_BRANCH":"${VCS_BRANCH}",
+	"VCS_TAG": "${VCS_TAG}",
+	"VCS_TICK": ${VCS_TICK},
+	"${EXTRA_NAME}": "${VCS_EXTRA}",
+
+	"VCS_ACTION_STAMP": "${VCS_ACTION_STAMP}",
+	"VCS_FULL_HASH": "${VCS_FULL_HASH}",
+	"VCS_SHORT_HASH": "${VCS_SHORT_HASH}",
+
+	"VCS_WC_MODIFIED": ${VCS_WC_MODIFIED}
+}
+EOF
+}
+
+# For Java output
+javaOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		1) VCS_WC_MODIFIED="true" ;;
+		0) VCS_WC_MODIFIED="false" ;;
+	esac
+	tee << EOF
+/* ${GENERATED_HEADER} */
+
+public class autorevision {
+    public static final String VCS_TYPE = "${VCS_TYPE}";
+    public static final String VCS_BASENAME = "${VCS_BASENAME}";
+    public static final String VCS_UUID = "${VCS_UUID}";
+    public static final long VCS_NUM = ${VCS_NUM};
+    public static final String VCS_DATE = "${VCS_DATE}";
+    public static final String VCS_BRANCH = "${VCS_BRANCH}";
+    public static final String VCS_TAG = "${VCS_TAG}";
+    public static final long VCS_TICK = ${VCS_TICK};
+    public static final String ${EXTRA_NAME} = "${VCS_EXTRA}";
+
+    public static final String VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}";
+    public static final String VCS_FULL_HASH = "${VCS_FULL_HASH}";
+    public static final String VCS_SHORT_HASH = "${VCS_SHORT_HASH}";
+
+    public static final boolean VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+}
+EOF
+}
+
+csharpOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		1) VCS_WC_MODIFIED="true" ;;
+		0) VCS_WC_MODIFIED="false" ;;
+	esac
+	if [ "${EXTRA_NAME}" = "VCS_EXTRA" ]; then
+        EXTRA_NAME="VcsExtra"
+    fi
+    tee << EOF
+/* ${GENERATED_HEADER} */
+
+namespace AutoRevision
+{
+    public class VersionInfo
+    {
+        public static string VcsType = "${VCS_TYPE}";
+        public static string VcsBasename = "${VCS_BASENAME}";
+        public static string VcsUuid = "${VCS_UUID}";
+        public static string VcsNum = "${VCS_NUM}";
+        public static string VcsDate = "${VCS_DATE}";
+        public static string VcsBranch = "${VCS_DATE}";
+        public static string VcsTag = "${VCS_TAG}";
+        public static string VcsTick = "${VCS_TICK}";
+        public static string ${EXTRA_NAME} = "${VCS_EXTRA}";
+        public static string VcsActionStamp = "${VCS_ACTION_STAMP}";
+        public static string VcsFullHash = "${VCS_FULL_HASH}";
+        public static string VcsShortHash = "${VCS_SHORT_HASH}";
+        public static string VcsWcModified = "${VCS_WC_MODIFIED}";
+    }
+}
+EOF
+}
+
+# For Java properties output
+javapropOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		1) VCS_WC_MODIFIED="true" ;;
+		0) VCS_WC_MODIFIED="false" ;;
+	esac
+	tee << EOF
+# ${GENERATED_HEADER}
+
+VCS_TYPE=${VCS_TYPE}
+VCS_BASENAME=${VCS_BASENAME}
+VCS_UUID=${VCS_UUID}
+VCS_NUM=${VCS_NUM}
+VCS_DATE=${VCS_DATE}
+VCS_BRANCH=${VCS_BRANCH}
+VCS_TAG=${VCS_TAG}
+VCS_TICK=${VCS_TICK}
+${EXTRA_NAME}=${VCS_EXTRA}
+
+VCS_ACTION_STAMP=${VCS_ACTION_STAMP}
+VCS_FULL_HASH=${VCS_FULL_HASH}
+VCS_SHORT_HASH=${VCS_SHORT_HASH}
+
+VCS_WC_MODIFIED=${VCS_WC_MODIFIED}
+EOF
+}
+
+# For m4 output
+m4Output() {
+	tee << EOF
+dnl ${GENERATED_HEADER}
+define(\`VCS_TYPE', \`${VCS_TYPE}')dnl
+define(\`VCS_BASENAME', \`${VCS_BASENAME}')dnl
+define(\`VCS_UUID', \`${VCS_UUID}')dnl
+define(\`VCS_NUM', \`${VCS_NUM}')dnl
+define(\`VCS_DATE', \`${VCS_DATE}')dnl
+define(\`VCS_BRANCH', \`${VCS_BRANCH}')dnl
+define(\`VCS_TAG', \`${VCS_TAG}')dnl
+define(\`VCS_TICK', \`${VCS_TICK}')dnl
+define(\`${EXTRA_NAME}', \`${VCS_EXTRA}')dnl
+define(\`VCS_ACTIONSTAMP', \`${VCS_ACTION_STAMP}')dnl
+define(\`VCS_FULLHASH', \`${VCS_FULL_HASH}')dnl
+define(\`VCS_SHORTHASH', \`${VCS_SHORT_HASH}')dnl
+define(\`VCS_WC_MODIFIED', \`${VCS_WC_MODIFIED}')dnl
+EOF
+}
+
+# For (La)TeX output
+texOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	if [ "${EXTRA_NAME}" = "VCS_EXTRA" ]; then
+		EXTRA_NAME="vcsExtra"
+	fi
+	tee << EOF
+% ${GENERATED_HEADER}
+\def \vcsType {${VCS_TYPE}}
+\def \vcsBasename {${VCS_BASENAME}}
+\def \vcsUUID {${VCS_UUID}}
+\def \vcsNum {${VCS_NUM}}
+\def \vcsDate {${VCS_DATE}}
+\def \vcsBranch {${VCS_BRANCH}}
+\def \vcsTag {${VCS_TAG}}
+\def \vcsTick {${VCS_TICK}}
+\def \\${EXTRA_NAME} {${VCS_EXTRA}}
+\def \vcsACTIONSTAMP {${VCS_ACTION_STAMP}}
+\def \vcsFullHash {${VCS_FULL_HASH}}
+\def \vcsShortHash {${VCS_SHORT_HASH}}
+\def \vcsWCModified {${VCS_WC_MODIFIED}}
+\endinput
+EOF
+}
+
+# For scheme output
+schemeOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="#f" ;;
+		1) VCS_WC_MODIFIED="#t" ;;
+	esac
+	tee << EOF
+;; ${GENERATED_HEADER}
+(define VCS_TYPE        "${VCS_TYPE}")
+(define VCS_BASENAME    "${VCS_BASENAME}")
+(define VCS_UUID        "${VCS_UUID}")
+(define VCS_NUM         ${VCS_NUM})
+(define VCS_DATE        "${VCS_DATE}")
+(define VCS_BRANCH      "${VCS_BRANCH}")
+(define VCS_TAG         "${VCS_TAG}")
+(define VCS_TICK        ${VCS_TICK})
+(define ${EXTRA_NAME}       "${VCS_EXTRA}")
+
+(define VCS_ACTION_STAMP   "${VCS_ACTION_STAMP}")
+(define VCS_FULL_HASH   "${VCS_FULL_HASH}")
+(define VCS_SHORT_HASH  "${VCS_SHORT_HASH}")
+
+(define VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+;; end
+EOF
+}
+
+# For clojure output
+clojureOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="false" ;;
+		1) VCS_WC_MODIFIED="true" ;;
+	esac
+	tee << EOF
+;; ${GENERATED_HEADER}
+(def VCS_TYPE        "${VCS_TYPE}")
+(def VCS_BASENAME    "${VCS_BASENAME}")
+(def VCS_UUID        "${VCS_UUID}")
+(def VCS_NUM         ${VCS_NUM})
+(def VCS_DATE        "${VCS_DATE}")
+(def VCS_BRANCH      "${VCS_BRANCH}")
+(def VCS_TAG         "${VCS_TAG}")
+(def VCS_TICK        ${VCS_TICK})
+(def ${EXTRA_NAME}       "${VCS_EXTRA}")
+
+(def VCS_ACTION_STAMP   "${VCS_ACTION_STAMP}")
+(def VCS_FULL_HASH      "${VCS_FULL_HASH}")
+(def VCS_SHORT_HASH     "${VCS_SHORT_HASH}")
+
+(def VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+;; end
+EOF
+}
+
+# For rpm spec file output
+rpmOutput() {
+	tee << EOF
+# ${GENERATED_HEADER}
+$([ "${VCS_TYPE}" ] && echo "%define vcs_type		${VCS_TYPE}")
+$([ "${VCS_BASENAME}" ] && echo "%define vcs_basename		${VCS_BASENAME}")
+$([ "${VCS_UUID}" ] && echo "%define vcs_uuid		${VCS_UUID}")
+$([ "${VCS_NUM}" ] && echo "%define vcs_num			${VCS_NUM}")
+$([ "${VCS_DATE}" ] && echo "%define vcs_date		${VCS_DATE}")
+$([ "${VCS_BRANCH}" ] && echo "%define vcs_branch		${VCS_BRANCH}")
+$([ "${VCS_TAG}" ] && echo "%define vcs_tag			${VCS_TAG}")
+$([ "${VCS_TICK}" ] && echo "%define vcs_tick		${VCS_TICK}")
+$([ "${VCS_EXTRA}" ] && echo "%define ${EXTRA_NAME}		${VCS_EXTRA}")
+
+$([ "${VCS_ACTION_STAMP}" ] && echo "%define vcs_action_stamp		${VCS_ACTION_STAMP}")
+$([ "${VCS_FULL_HASH}" ] && echo "%define vcs_full_hash		${VCS_FULL_HASH}")
+$([ "${VCS_SHORT_HASH}" ] && echo "%define vcs_short_hash		${VCS_SHORT_HASH}")
+
+$([ "${VCS_WC_MODIFIED}" ] && echo "%define vcs_wc_modified		${VCS_WC_MODIFIED}")
+# end
+EOF
+}
+
+# For C++ Header output
+# shellcheck disable=SC2155,SC2039
+hppOutput() {
+	${LOCAL} NAMESPACE="$(echo "${VCS_BASENAME}" | sed -e 's:_::g' | tr '[:lower:]' '[:upper:]')"
+	tee << EOF
+/* ${GENERATED_HEADER} */
+
+#ifndef ${NAMESPACE}_AUTOREVISION_H
+#define ${NAMESPACE}_AUTOREVISION_H
+
+#include <string>
+
+namespace $(echo "${NAMESPACE}" | tr '[:upper:]' '[:lower:]')
+{
+	const std::string VCS_TYPE		= "${VCS_TYPE}";
+	const std::string VCS_BASENAME	= "${VCS_BASENAME}";
+	const std::string VCS_UUID		= "${VCS_UUID}";
+	const int VCS_NUM				= ${VCS_NUM};
+	const std::string VCS_DATE		= "${VCS_DATE}";
+	const std::string VCS_BRANCH	= "${VCS_BRANCH}";
+	const std::string VCS_TAG		= "${VCS_TAG}";
+	const int VCS_TICK				= ${VCS_TICK};
+	const std::string ${EXTRA_NAME}		= "${VCS_EXTRA}";
+
+	const std::string VCS_ACTION_STAMP	= "${VCS_ACTION_STAMP}";
+	const std::string VCS_FULL_HASH		= "${VCS_FULL_HASH}";
+	const std::string VCS_SHORT_HASH	= "${VCS_SHORT_HASH}";
+
+	const int VCS_WC_MODIFIED			= ${VCS_WC_MODIFIED};
+}
+
+#endif
+
+/* end */
+EOF
+}
+
+# For Matlab output
+matlabOutput() {
+	case "${VCS_WC_MODIFIED}" in
+		0) VCS_WC_MODIFIED="FALSE" ;;
+		1) VCS_WC_MODIFIED="TRUE" ;;
+	esac
+	tee << EOF
+% ${GENERATED_HEADER}
+
+VCS_TYPE = '${VCS_TYPE}';
+VCS_BASENAME = '${VCS_BASENAME}';
+VCS_UUID = '${VCS_UUID}';
+VCS_NUM = ${VCS_NUM};
+VCS_DATE = '${VCS_DATE}';
+VCS_BRANCH = '${VCS_BRANCH}';
+VCS_TAG = '${VCS_TAG}';
+VCS_TICK = ${VCS_TICK};
+${EXTRA_NAME} = '${VCS_EXTRA}';
+
+VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+VCS_FULL_HASH = '${VCS_FULL_HASH}';
+VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+% end
+EOF
+}
+
+# For Octave output
+octaveOutput() {
+	tee << EOF
+% ${GENERATED_HEADER}
+
+VCS_TYPE = '${VCS_TYPE}';
+VCS_BASENAME = '${VCS_BASENAME}';
+VCS_UUID = '${VCS_UUID}';
+VCS_NUM = ${VCS_NUM};
+VCS_DATE = '${VCS_DATE}';
+VCS_BRANCH = '${VCS_BRANCH}';
+VCS_TAG = '${VCS_TAG}';
+VCS_TICK = ${VCS_TICK};
+${EXTRA_NAME} = '${VCS_EXTRA}';
+
+VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+VCS_FULL_HASH = '${VCS_FULL_HASH}';
+VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+% end
+EOF
+}
+
+
+# Helper functions
+# Count path segments
+# shellcheck disable=SC2039
+pathSegment() {
+	${LOCAL} pathz="${1}"
+	${LOCAL} depth="0"
+
+	if [ ! -z "${pathz}" ]; then
+		# Continue until we are at / or there are no path separators left.
+		while [ ! "${pathz}" = "/" ] && [ ! "${pathz}" = "$(echo "${pathz}" | sed -e 's:/::')" ]; do
+			pathz="$(dirname "${pathz}")"
+			depth="$((depth+1))"
+		done
+	fi
+	echo "${depth}"
+}
+
+# Largest of four numbers
+# shellcheck disable=SC2039
+multiCompare() {
+	${LOCAL} larger="${1}"
+	${LOCAL} numA="${2}"
+	${LOCAL} numB="${3}"
+	${LOCAL} numC="${4}"
+
+	[ "${numA}" -gt "${larger}" ] && larger="${numA}"
+	[ "${numB}" -gt "${larger}" ] && larger="${numB}"
+	[ "${numC}" -gt "${larger}" ] && larger="${numC}"
+	echo "${larger}"
+}
+
+# Test for repositories
+# shellcheck disable=SC2155,SC2039
+repoTest() {
+	REPONUM="0"
+	if command -v git > /dev/null 2>&1; then
+		${LOCAL} gitPath="$(git rev-parse --show-toplevel 2>/dev/null)"
+		${LOCAL} gitDepth="$(pathSegment "${gitPath}")"
+		if [ ! -z "${gitPath}" ]; then
+			REPONUM="$((REPONUM+1))"
+		fi
+	else
+		${LOCAL} gitDepth="0"
+	fi
+	if command -v hg > /dev/null 2>&1; then
+		${LOCAL} hgPath="$(hg root 2>/dev/null)"
+		${LOCAL} hgDepth="$(pathSegment "${hgPath}")"
+		if [ ! -z "${hgPath}" ]; then
+			REPONUM="$((REPONUM+1))"
+		fi
+	else
+		${LOCAL} hgDepth="0"
+	fi
+	if command -v bzr > /dev/null 2>&1; then
+		${LOCAL} bzrPath="$(bzr root 2>/dev/null)"
+		${LOCAL} bzrDepth="$(pathSegment "${bzrPath}")"
+		if [ ! -z "${bzrPath}" ]; then
+			REPONUM="$((REPONUM+1))"
+		fi
+	else
+		${LOCAL} bzrDepth="0"
+	fi
+	if command -v svn > /dev/null 2>&1; then
+		${LOCAL} stringz="<wcroot-abspath>"
+		${LOCAL} stringx="</wcroot-abspath>"
+		${LOCAL} svnPath="$(svn info --xml 2>/dev/null | sed -n -e "s:${stringz}::" -e "s:${stringx}::p")"
+		# An old enough svn will not be able give us a path; default
+		# to 1 for that case.
+		if [ ! -z "${svnPath}" ]; then
+			${LOCAL} svnDepth="$(pathSegment "${svnPath}")"
+			REPONUM="$((REPONUM+1))"
+		elif [ -z "${svnPath}" ] && [ -d ".svn" ]; then
+			${LOCAL} svnDepth="1"
+			REPONUM="$((REPONUM+1))"
+		else
+			${LOCAL} svnDepth="0"
+		fi
+	else
+		${LOCAL} svnDepth="0"
+	fi
+
+	# Do not do more work then we have to.
+	if [ "${REPONUM}" = "0" ]; then
+		return 0
+	fi
+
+	# Figure out which repo is the deepest and use it.
+	${LOCAL} wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")"
+	if [ "${wonRepo}" = "${gitDepth}" ]; then
+		gitRepo
+	elif [ "${wonRepo}" = "${hgDepth}" ]; then
+		hgRepo
+	elif [ "${wonRepo}" = "${bzrDepth}" ]; then
+		bzrRepo
+	elif [ "${wonRepo}" = "${svnDepth}" ]; then
+		svnRepo
+	fi
+}
+
+
+
+# Detect which repos we are in and gather data.
+# shellcheck source=/dev/null
+if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+	# When requested only read from the cache to populate our symbols.
+	. "${CACHEFILE}"
+else
+	# If a value is not set through the environment set VCS_EXTRA to nothing.
+	: "${VCS_EXTRA:=""}"
+	repoTest
+
+	if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then
+		# We are not in a repo; try to use a previously generated cache to populate our symbols.
+		. "${CACHEFILE}"
+		# Do not overwrite the cache if we know we are not going to write anything new.
+		CACHEFORCE="1"
+	elif [ "${REPONUM}" = "0" ]; then
+		echo "error: No repo or cache detected." 1>&2
+		exit 1
+	fi
+fi
+
+
+# -s output is handled here.
+if [ ! -z "${VAROUT}" ]; then
+	if [ "${VAROUT}" = "VCS_TYPE" ]; then
+		echo "${VCS_TYPE}"
+	elif [ "${VAROUT}" = "VCS_BASENAME" ]; then
+		echo "${VCS_BASENAME}"
+	elif [ "${VAROUT}" = "VCS_NUM" ]; then
+		echo "${VCS_NUM}"
+	elif [ "${VAROUT}" = "VCS_DATE" ]; then
+		echo "${VCS_DATE}"
+	elif [ "${VAROUT}" = "VCS_BRANCH" ]; then
+		echo "${VCS_BRANCH}"
+	elif [ "${VAROUT}" = "VCS_TAG" ]; then
+		echo "${VCS_TAG}"
+	elif [ "${VAROUT}" = "VCS_TICK" ]; then
+		echo "${VCS_TICK}"
+	elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then
+		echo "${VCS_FULL_HASH}"
+	elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then
+		echo "${VCS_SHORT_HASH}"
+	elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then
+		echo "${VCS_WC_MODIFIED}"
+	elif [ "${VAROUT}" = "VCS_ACTION_STAMP" ]; then
+		echo "${VCS_ACTION_STAMP}"
+	else
+		echo "error: Not a valid output symbol." 1>&2
+		exit 1
+	fi
+fi
+
+
+# Detect requested output type and use it.
+if [ ! -z "${AFILETYPE}" ]; then
+	if [ "${AFILETYPE}" = "c" ]; then
+		cOutput
+	elif [ "${AFILETYPE}" = "h" ]; then
+		hOutput
+	elif [ "${AFILETYPE}" = "xcode" ]; then
+		xcodeOutput
+	elif [ "${AFILETYPE}" = "swift" ]; then
+		swiftOutput
+	elif [ "${AFILETYPE}" = "sh" ]; then
+		shOutput
+	elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then
+		pyOutput
+	elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then
+		plOutput
+	elif [ "${AFILETYPE}" = "lua" ]; then
+		luaOutput
+	elif [ "${AFILETYPE}" = "php" ]; then
+		phpOutput
+	elif [ "${AFILETYPE}" = "ini" ]; then
+		iniOutput
+	elif [ "${AFILETYPE}" = "js" ]; then
+		jsOutput
+	elif [ "${AFILETYPE}" = "json" ]; then
+		jsonOutput
+	elif [ "${AFILETYPE}" = "java" ]; then
+		javaOutput
+	elif [ "${AFILETYPE}" = "javaprop" ]; then
+		javapropOutput
+	elif [ "${AFILETYPE}" = "csharp" ]; then
+		csharpOutput
+	elif [ "${AFILETYPE}" = "tex" ]; then
+		texOutput
+	elif [ "${AFILETYPE}" = "m4" ]; then
+		m4Output
+	elif [ "${AFILETYPE}" = "scheme" ]; then
+		schemeOutput
+	elif [ "${AFILETYPE}" = "clojure" ]; then
+		clojureOutput
+	elif [ "${AFILETYPE}" = "rpm" ]; then
+		rpmOutput
+	elif [ "${AFILETYPE}" = "hpp" ]; then
+		hppOutput
+	elif [ "${AFILETYPE}" = "matlab" ]; then
+		matlabOutput
+	elif [ "${AFILETYPE}" = "octave" ]; then
+		octaveOutput
+	elif [ "${AFILETYPE}" = "cmake" ]; then
+		cmakeOutput
+	else
+		echo "error: Not a valid output type." 1>&2
+		exit 1
+	fi
+fi
+
+
+# If requested, make a cache file.
+if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then
+	EXTRA_NAME="VCS_EXTRA"
+	shOutput > "${CACHEFILE}.tmp"
+
+	# Check to see if there have been any actual changes.
+	if [ ! -f "${CACHEFILE}" ]; then
+		mv -f "${CACHEFILE}.tmp" "${CACHEFILE}"
+	elif cmp -s "${CACHEFILE}.tmp" "${CACHEFILE}"; then
+		rm -f "${CACHEFILE}.tmp"
+	else
+		mv -f "${CACHEFILE}.tmp" "${CACHEFILE}"
+	fi
+fi


=====================================
wafhelpers/options.py
=====================================
--- a/wafhelpers/options.py
+++ b/wafhelpers/options.py
@@ -61,7 +61,7 @@ def options_cmd(ctx, config):
 
     grp = ctx.add_option_group("NTP developer configure options")
     grp.add_option('--build-version-tag', type='string',
-                   help="Append a tag to the version string (unused)")
+                   help="Append a tag to the version string.")
     grp.add_option('--cflags', type='string', action="callback",
                    callback=callback_flags,
                    help="Users should use CFLAGS in their environment.")


=====================================
wscript
=====================================
--- a/wscript
+++ b/wscript
@@ -1,9 +1,7 @@
 from __future__ import print_function
 
-from datetime import datetime
 import itertools
 import os
-import re
 import shlex
 import sys
 import time
@@ -201,29 +199,24 @@ def configure(ctx):
     if ctx.options.disable_manpage:
         ctx.env.DISABLE_MANPAGE = True
 
-    source_date_epoch = os.getenv('SOURCE_DATE_EPOCH', None)
-    if ctx.options.build_epoch is not None:
-        build_epoch = ctx.options.build_epoch
-        ctx.define("BUILD_EPOCH", build_epoch, comment="Using --build-epoch")
-    elif source_date_epoch:
-        if not source_date_epoch.isdigit():
-            ctx.fatal("ERROR: malformed SOURCE_DATE_EPOCH")
-        build_epoch = int(source_date_epoch)
-        ctx.define("BUILD_EPOCH", build_epoch, comment="Using SOURCE_DATE_EPOCH")
-    else:
-        build_epoch = int(time.time())
-        ctx.define("BUILD_EPOCH", build_epoch, comment="Using default")
-
-    build_epoch_formatted = datetime.utcfromtimestamp(build_epoch).strftime("%Y-%m-%dT%H:%M:%SZ")
     if ((os.path.exists(".git") and
             ctx.find_program("git", var="BIN_GIT", mandatory=False))):
-        cmd = ctx.env.BIN_GIT + shlex.split("log -1 --format=%h")
-        git_short_hash = ctx.cmd_and_log(cmd).strip()
+        ctx.start_msg("DEVEL: Getting revision")
+        cmd = shlex.split("git log -1 --format=%H")
+        ctx.env.NTPSEC_REVISION = ctx.cmd_and_log(cmd).strip()
+        ctx.end_msg(ctx.env.NTPSEC_REVISION)
 
-        ctx.env.NTPSEC_VERSION += "+ %s (git rev %s)" % (build_epoch_formatted, git_short_hash)
-    else:
-        ctx.env.NTPSEC_VERSION += " %s" % build_epoch_formatted
-    ctx.define("NTPSEC_VERSION", ctx.env.NTPSEC_VERSION)
+    ctx.start_msg("Building version")
+    ctx.env.NTPSEC_VERSION_STRING = ctx.env.NTPSEC_VERSION
+
+    if ctx.env.NTPSEC_REVISION:
+        ctx.env.NTPSEC_VERSION_STRING += "-%s" % ctx.env.NTPSEC_REVISION[:7]
+
+    if ctx.options.build_version_tag:
+        ctx.env.NTPSEC_VERSION_STRING += "-%s" % ctx.options.build_version_tag
+
+    ctx.define("NTPSEC_VERSION_STRING", ctx.env.NTPSEC_VERSION_STRING)
+    ctx.end_msg(ctx.env.NTPSEC_VERSION_STRING)
 
     # We require some things that C99 doesn't enable, like pthreads.
     # These flags get propagated to both the host and main parts of the build.
@@ -858,6 +851,19 @@ int main(int argc, char **argv) {
                 msg("WARNING: This system has a 32-bit time_t.")
                 msg("WARNING: Your ntpd will fail on 2038-01-19T03:14:07Z.")
 
+    source_date_epoch = os.getenv('SOURCE_DATE_EPOCH', None)
+    if ctx.options.build_epoch is not None:
+        ctx.define("BUILD_EPOCH", ctx.options.build_epoch,
+                   comment="Using --build-epoch")
+    elif source_date_epoch:
+        if not source_date_epoch.isdigit():
+            msg("ERROR: malformed SOURCE_DATE_EPOCH")
+            sys.exit(1)
+        ctx.define("BUILD_EPOCH", int(source_date_epoch),
+                   comment="Using SOURCE_DATE_EPOCH")
+    else:
+        ctx.define("BUILD_EPOCH", int(time.time()), comment="Using default")
+
     # before write_config()
     droproot_type = ""
     if ctx.is_defined("HAVE_LINUX_CAPABILITY"):
@@ -981,6 +987,8 @@ def afterparty(ctx):
     # Note that this setup is applied to the build tree, not the
     # source tree.  Only the build-tree copies of the programs are
     # expected to work.
+    if ctx.cmd == 'clean':
+        ctx.exec_command("rm -f ntpd/version.h ")
     for x in ("ntpclients", "tests/pylib",):
         # List used to be longer...
         path_build = ctx.bldnode.make_node("pylib")



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/7f83b7f0cdd9480160a9655c3319b8272fda024d...3aae297ad674a904529f32a619476f935d3813e6

---
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/7f83b7f0cdd9480160a9655c3319b8272fda024d...3aae297ad674a904529f32a619476f935d3813e6
You're receiving this email because of your account on gitlab.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20180313/611f7148/attachment.html>


More information about the vc mailing list