Simon Josefsson wrote:
> > Maybe a doc/git-version-gen.texi is the answer here...  and thinking
> > through the concepts a bit more.

I replied:
> Let me start doing that.
> ...
> Next, what are the problems?

Spent the day thinking through the concepts and fixing all the problems.

* The comment about .version in git-version-gen was misplaced. .version
  is unrelated to git-version-gen; in fact, it is possible to use
  git-version-gen without a .version.

* The description of .tarball-version was wrong: "present only in a
  distribution tarball, and not in a checked-out repository." Wrong
  because when the maintainer wants to make a release with a specific
  version number, they do that by creating .tarball-version in their
  git checkout.

* The recommendation to use git-version-gen via m4_esyscmd was wrong,
  because the goal of that snippet is described as
  "Use the following line in your configure.ac, so that $(VERSION) will
   automatically be up-to-date each time configure is run"
  and what the snippet actually does is to invoke git-version-gen when
  autoconf is run, not when configure is run.

* The Makefile.am snippet with dist-hook was misleading, because one
  could think that it creates the file $(top_srcdir)/.tarball-version
  (which it doesn't — fortunately).

* The Makefile.am snippet regarding .version was wrong on multiple
  accounts:
  1) Since the purpose of this file is to be a stamp file, it needs
     to be updated when the value of $(VERSION) in config.status
     has changed. This was not done, and this was the cause for the
     several out-of-date version references in the past releases
     of coreutils and gettext.
  2) Since the moment $(VERSION) can change is when config.status
     is regenerated, it would be overkill to check for this situation
     in a Makefile rule. The relevant moment for checking it is when
     config.status is being run.


2025-01-22  Bruno Haible  <br...@clisp.org>

        doc: Document the workflows of the .tarball-version and .version files.
        * doc/package-version.texi: New file.
        * doc/gnulib.texi (Build Infrastructure Modules): Include it.
        * build-aux/git-version-gen: Fix comments: Fix description of
        .tarball-version. Recommend to invoke git-version-gen at configure time,
        not at autoconf time. Remove description of .version. Rename Makefile
        target 'tarball-version' to 'dist-tarball-version'.
        * top/GNUmakefile: Improve comments.

2025-01-22  Bruno Haible  <br...@clisp.org>

        New module 'version-stamp'.
        * m4/version-stamp.m4: New file.
        * modules/version-stamp: New file.

2025-01-22  Bruno Haible  <br...@clisp.org>

        New module 'package-version'.
        * m4/init-package-version.m4: New file, from GNU libunistring.
        * modules/package-version: New file.
        * modules/git-version-gen (Depends-on): Add it.

>From e518788ad085e02b046e42889039a1f671e4619a Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 22 Jan 2025 21:21:59 +0100
Subject: [PATCH 1/3] New module 'package-version'.

* m4/init-package-version.m4: New file, from GNU libunistring.
* modules/package-version: New file.
* modules/git-version-gen (Depends-on): Add it.
---
 ChangeLog                  |   7 +++
 m4/init-package-version.m4 | 124 +++++++++++++++++++++++++++++++++++++
 modules/git-version-gen    |   1 +
 modules/package-version    |  19 ++++++
 4 files changed, 151 insertions(+)
 create mode 100644 m4/init-package-version.m4
 create mode 100644 modules/package-version

diff --git a/ChangeLog b/ChangeLog
index 7c402b5a0b..87087d2d3b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2025-01-22  Bruno Haible  <br...@clisp.org>
+
+	New module 'package-version'.
+	* m4/init-package-version.m4: New file, from GNU libunistring.
+	* modules/package-version: New file.
+	* modules/git-version-gen (Depends-on): Add it.
+
 2025-01-19  Bruno Haible  <br...@clisp.org>
 
 	attribute: Add note about ongoing standardization of some attributes.
diff --git a/m4/init-package-version.m4 b/m4/init-package-version.m4
new file mode 100644
index 0000000000..525dfe5bf5
--- /dev/null
+++ b/m4/init-package-version.m4
@@ -0,0 +1,124 @@
+# init-package-version.m4
+# serial 3
+dnl Copyright (C) 1992-2025 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+# Make it possible to pass version numbers extracted from a file in
+# $(srcdir) to autoconf.
+#
+# Autoconf insists on passing the package name and version number to
+# every generated .h file and every Makefile. This was a reasonable
+# design at times when a version number was changed only once a month.
+# Nowadays, people often assign a new version number once a week, or
+# even change it each time a 'git' commit is made. Regenerating all
+# the files that depend on configure.ac (aclocal.m4, configure,
+# config.status, config.h, all Makefiles) may take 15 minutes. These
+# delays can severely hamper development.
+#
+# An alternative is to store the version number in a file in $(srcdir)
+# that is separate from configure.ac. It can be a data file, a shell
+# script, a .m4 file, or other. The essential point is that the maintainer
+# is responsible for creating Makefile dependencies to this version file
+# for every file that needs to be rebuilt when the version changes. This
+# typically includes
+#   - distributable documentation files that carry the version number,
+# but does not include
+#   - aclocal.m4, configure, config.status, config.h, all Makefiles,
+#   - executables.
+#
+# autoconf and automake make it hard to follow this approach:
+#
+# - If AC_INIT is used with arguments, there is a chicken-and-egg problem:
+#   The arguments need to be read from a file in $(srcdir). The location
+#   of $(srcdir) is only determined by AC_CONFIG_SRCDIR. AC_CONFIG_SRCDIR
+#   can only appear after AC_INIT (otherwise aclocal gives an error:
+#   "error: m4_defn: undefined macro: _m4_divert_diversion").
+#   Furthermore, the arguments passed to AC_INIT must be literals; for
+#   example, the assignment to PACKAGE_VERSION looks like this:
+#     [PACKAGE_VERSION=']AC_PACKAGE_VERSION[']
+#
+# - If AC_INIT is used without arguments:
+#   Automake provides its own variables, PACKAGE and VERSION, and uses them
+#   instead of PACKAGE_NAME and PACKAGE_VERSION that come from Autoconf.
+#   - If AM_INIT_AUTOMAKE is used with two arguments, automake options
+#     like 'silent-rules' cannot be specified.
+#   - If AM_INIT_AUTOMAKE is used in its one-argument form or without
+#     arguments at all, it triggers an error
+#     "error: AC_INIT should be called with package and version arguments".
+#   - If AM_INIT_AUTOMAKE is used in its one-argument form or without
+#     arguments at all, and _AC_INIT_PACKAGE is used before it, with
+#     the package and version number from the file as arguments, we get
+#     a warning: "warning: AC_INIT: not a literal: $VERSION_NUMBER".
+#     The arguments passed to _AC_INIT_PACKAGE must be literals.
+#
+# With the macro defined in this file, the approach can be coded like this:
+#
+#   AC_INIT
+#   AC_CONFIG_SRCDIR(WITNESS)
+#   . $srcdir/../version.sh
+#   gl_INIT_PACKAGE(PACKAGE, $VERSION_NUMBER)
+#   AM_INIT_AUTOMAKE([OPTIONS])
+#
+# and after changing version.sh, the developer can directly configure and build:
+#
+#   make distclean
+#   ./configure
+#   make
+#
+# Some other packages use another approach:
+#
+#   AC_INIT(PACKAGE,
+#           m4_normalize(m4_esyscmd([. ./version.sh; echo $VERSION_NUMBER])))
+#   AC_CONFIG_SRCDIR(WITNESS)
+#   AM_INIT_AUTOMAKE([OPTIONS])
+#
+# but here, after changing version.sh, the developer must first regenerate the
+# configure file:
+#
+#   make distclean
+#   ./autogen.sh --skip-gnulib
+#   ./configure
+#   make
+#
+
+# gl_INIT_PACKAGE(PACKAGE-NAME, VERSION)
+# --------------------------------------
+# followed by an AM_INIT_AUTOMAKE invocation,
+# is like calling AM_INIT_AUTOMAKE(PACKAGE-NAME, VERSION)
+# except that it can use computed non-literal arguments.
+AC_DEFUN([gl_INIT_PACKAGE],
+[
+  AC_BEFORE([$0], [AM_INIT_AUTOMAKE])
+  dnl Redefine AM_INIT_AUTOMAKE.
+  m4_define([gl_AM_INIT_AUTOMAKE],
+    m4_bpatsubst(m4_dquote(
+        m4_bpatsubst(m4_dquote(
+            m4_bpatsubst(m4_dquote(
+                m4_defn([AM_INIT_AUTOMAKE])),
+              [AC_PACKAGE_NAME], [gl_INIT_DUMMY])),
+          [AC_PACKAGE_TARNAME], [gl_INIT_EMPTY])),
+      [AC_PACKAGE_VERSION], [gl_INIT_DUMMY])
+    [AC_SUBST([PACKAGE], [$1])
+     AC_SUBST([VERSION], [$2])
+    ])
+  m4_define([AM_INIT_AUTOMAKE],
+    m4_defn([gl_RPL_INIT_AUTOMAKE]))
+])
+m4_define([gl_INIT_EMPTY], [])
+dnl Automake 1.16.4 no longer accepts an empty value for gl_INIT_DUMMY.
+dnl But a macro that later expands to empty works.
+m4_define([gl_INIT_DUMMY], [gl_INIT_DUMMY2])
+m4_define([gl_INIT_DUMMY2], [])
+AC_DEFUN([gl_RPL_INIT_AUTOMAKE], [
+  m4_ifval([$2],
+    [m4_fatal([After gl_INIT_PACKAGE, the two-argument form of AM_INIT_AUTOMAKE cannot be used.])])
+  gl_AM_INIT_AUTOMAKE([$1 no-define])
+  m4_if(m4_index([ $1 ], [ no-define ]), [-1],
+    [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+     AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])
+    ])
+])
diff --git a/modules/git-version-gen b/modules/git-version-gen
index a783ac570d..ee02ef4ee5 100644
--- a/modules/git-version-gen
+++ b/modules/git-version-gen
@@ -5,6 +5,7 @@ Files:
 build-aux/git-version-gen
 
 Depends-on:
+package-version
 
 configure.ac:
 
diff --git a/modules/package-version b/modules/package-version
new file mode 100644
index 0000000000..fafa8b058a
--- /dev/null
+++ b/modules/package-version
@@ -0,0 +1,19 @@
+Description:
+Support for a computed version string.
+
+Files:
+m4/init-package-version.m4
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+
+Include:
+
+License:
+GPLed build tool
+
+Maintainer:
+Bruno Haible
-- 
2.43.0

>From 85599643e2fbf70f7f0bd58831993132ef335705 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 22 Jan 2025 21:25:27 +0100
Subject: [PATCH 2/3] New module 'version-stamp'.

* m4/version-stamp.m4: New file.
* modules/version-stamp: New file.
---
 ChangeLog             |  6 ++++++
 m4/version-stamp.m4   | 35 +++++++++++++++++++++++++++++++++++
 modules/version-stamp | 19 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 m4/version-stamp.m4
 create mode 100644 modules/version-stamp

diff --git a/ChangeLog b/ChangeLog
index 87087d2d3b..ef6f39e3a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2025-01-22  Bruno Haible  <br...@clisp.org>
+
+	New module 'version-stamp'.
+	* m4/version-stamp.m4: New file.
+	* modules/version-stamp: New file.
+
 2025-01-22  Bruno Haible  <br...@clisp.org>
 
 	New module 'package-version'.
diff --git a/m4/version-stamp.m4 b/m4/version-stamp.m4
new file mode 100644
index 0000000000..81e9f15d26
--- /dev/null
+++ b/m4/version-stamp.m4
@@ -0,0 +1,35 @@
+# version-stamp.m4
+# serial 1
+dnl Copyright (C) 2025 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+# Manages a stamp file, that keeps track when $(VERSION) was last changed.
+#
+# gl_CONFIG_VERSION_STAMP
+# needs to be invoked near the end of the package's top-level configure.ac,
+# before AC_OUTPUT.
+# It makes sure that during the build,
+#   - $(top_srcdir)/.version exists, and
+#   - when $(VERSION) is changed, $(top_srcdir)/.version gets modified.
+#
+# $(top_srcdir)/.version is a stamp file. Its contents wouldn't matter,
+# except that for detecting the change, we store the value of $(VERSION)
+# in it (but we could just as well store it in a different file).
+AC_DEFUN([gl_CONFIG_VERSION_STAMP],
+[
+  AC_CONFIG_COMMANDS([version-timestamp],
+    [if test -f "$ac_top_srcdir/.version" \
+        && test `cat "$ac_top_srcdir/.version"` = "$gl_version"; then
+       # The value of $(VERSION) is the same as last time.
+       :
+     else
+       # The value of $(VERSION) has changed. Update the stamp.
+       echo "$gl_version" > "$ac_top_srcdir/.version"
+     fi
+    ],
+    [gl_version="$VERSION"])
+])
diff --git a/modules/version-stamp b/modules/version-stamp
new file mode 100644
index 0000000000..6257221f6f
--- /dev/null
+++ b/modules/version-stamp
@@ -0,0 +1,19 @@
+Description:
+Optimized rebuilding of artifacts that depend on $(VERSION).
+
+Files:
+m4/version-stamp.m4
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+
+Include:
+
+License:
+GPLed build tool
+
+Maintainer:
+Bruno Haible
-- 
2.43.0

>From 7e2fe1fb9abf29e80a26a43b2cee047985ef3c1c Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 22 Jan 2025 21:31:52 +0100
Subject: [PATCH 3/3] doc: Document the workflows of the .tarball-version and
 .version files.

* doc/package-version.texi: New file.
* doc/gnulib.texi (Build Infrastructure Modules): Include it.
* build-aux/git-version-gen: Fix comments: Fix description of
.tarball-version. Recommend to invoke git-version-gen at configure time,
not at autoconf time. Remove description of .version. Rename Makefile
target 'tarball-version' to 'dist-tarball-version'.
* top/GNUmakefile: Improve comments.
---
 ChangeLog                 |  11 ++
 build-aux/git-version-gen |  47 ++++----
 doc/gnulib.texi           |   3 +
 doc/package-version.texi  | 238 ++++++++++++++++++++++++++++++++++++++
 top/GNUmakefile           |   3 +
 5 files changed, 278 insertions(+), 24 deletions(-)
 create mode 100644 doc/package-version.texi

diff --git a/ChangeLog b/ChangeLog
index ef6f39e3a9..e5419275d4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2025-01-22  Bruno Haible  <br...@clisp.org>
+
+	doc: Document the workflows of the .tarball-version and .version files.
+	* doc/package-version.texi: New file.
+	* doc/gnulib.texi (Build Infrastructure Modules): Include it.
+	* build-aux/git-version-gen: Fix comments: Fix description of
+	.tarball-version. Recommend to invoke git-version-gen at configure time,
+	not at autoconf time. Remove description of .version. Rename Makefile
+	target 'tarball-version' to 'dist-tarball-version'.
+	* top/GNUmakefile: Improve comments.
+
 2025-01-22  Bruno Haible  <br...@clisp.org>
 
 	New module 'version-stamp'.
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
index d936fab979..4535beb99b 100755
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Print a version string.
-scriptversion=2025-01-14.15; # UTC
+scriptversion=2025-01-22.18; # UTC
 
 # Copyright (C) 2007-2025 Free Software Foundation, Inc.
 #
@@ -34,15 +34,15 @@ scriptversion=2025-01-14.15; # UTC
 # In order to use intra-version strings in your project, you will need some
 # separate generated version string files:
 #
-# .tarball-version - present only in a distribution tarball, and not in
-#   a checked-out repository.  Created with contents that were learned at
-#   the last time autoconf was run, and used by git-version-gen.  Must not
-#   be present in either $(srcdir) or $(builddir) for git-version-gen to
-#   give accurate answers during normal development with a checked out tree,
-#   but must be present in a tarball when there is no version control system.
-#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
-#   hooks to force a reconfigure at distribution time to get the value
-#   correct, without penalizing normal development with extra reconfigures.
+# .tarball-version - contains the version number assigned by the maintainer.
+#   Present or missing in a checked-out repository, at the discretion of the
+#   maintainer/contributor.  Present in a distribution tarball, because the
+#   tarball does not include the version control history.
+#   Used by git-version-gen as an override.
+#   Cannot be used in any dependencies (since it may be absent).
+#   GNUmakefile has hooks to force a reconfigure at "make dist" time to get
+#   the value correct, without penalizing normal development with extra
+#   reconfigures.
 #
 # .tarball-version-git - a file committed to git containing a single
 #   line with the string $Format:%(describe)$ and that the file is
@@ -62,27 +62,26 @@ scriptversion=2025-01-14.15; # UTC
 # .tarball-version is never generated in a VC'd directory, so needn't
 # be listed there.
 #
-# Use the following line in your configure.ac, so that $(VERSION) will
+# Use the following snippet in your configure.ac, so that $(VERSION) will
 # automatically be up-to-date each time configure is run (and note that
 # since configure.ac no longer includes a version string, Makefile rules
 # should not depend on configure.ac for version updates).
 #
-# AC_INIT([GNU project],
-#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
-#         [bug-project@example])
+# AC_INIT
+# AC_CONFIG_SRCDIR([@var{unique-file-in-source-dir}])
+# AC_CONFIG_AUX_DIR([build-aux])
+# VERSION_NUMBER=`cd $srcdir \
+#                 && build-aux/git-version-gen .tarball-version \
+#                    | sed -e 's/dirty$/modified/'`
+# gl_INIT_PACKAGE([@var{package}], [$VERSION_NUMBER])
+# AM_INIT_AUTOMAKE([@var{options}])
 #
-# Then use the following lines in your Makefile.am, so that .version
-# will be present for dependencies, and so that .version and
+# Then use the following lines in your Makefile.am, so that
 # .tarball-version will exist in distribution tarballs.
 #
-# EXTRA_DIST = $(top_srcdir)/.version
-# BUILT_SOURCES = $(top_srcdir)/.version
-# $(top_srcdir)/.version:
-#	echo '$(VERSION)' > $@-t
-#	mv $@-t $@
-# dist-hook: tarball-version
-# .PHONY: tarball-version
-# tarball-version:
+# dist-hook: dist-tarball-version
+# .PHONY: dist-tarball-version
+# dist-tarball-version:
 #	echo '$(VERSION)' > $(distdir)/.tarball-version
 #
 # To setup support for "git archive" tarballs, use the following:
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index 5eed50d4e0..6a7ba18f5f 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -8192,6 +8192,7 @@
 * warnings::
 * manywarnings::
 * Running self-tests under valgrind::
+* Package version management::
 * VCS To ChangeLog::
 * gitlog-to-changelog::
 @end menu
@@ -8210,6 +8211,8 @@
 
 @include valgrind-tests.texi
 
+@include package-version.texi
+
 @include vcs-to-changelog.texi
 
 @include gitlog-to-changelog.texi
diff --git a/doc/package-version.texi b/doc/package-version.texi
new file mode 100644
index 0000000000..fb050a8fcf
--- /dev/null
+++ b/doc/package-version.texi
@@ -0,0 +1,238 @@
+@node Package version management
+@section Package version management
+
+@c Copyright (C) 2007--2025 Free Software Foundation, Inc.
+
+@c Permission is granted to copy, distribute and/or modify this document
+@c under the terms of the GNU Free Documentation License, Version 1.3 or
+@c any later version published by the Free Software Foundation; with no
+@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
+@c copy of the license is at <https://www.gnu.org/licenses/fdl-1.3.en.html>.
+
+The goals of a package maintainer,
+when dealing with the version number of a package, are:
+
+@itemize
+@item
+Programs should identify themselves with that version number
+when invoked as @command{@var{program} --version}.
+
+@item
+Every package tarball should contain the version number, in up to four ways:
+@itemize
+@item
+The tarball name should be @code{@var{package}-@var{version}.tar.gz}.
+@item
+The tarball should unpack into a directory
+named @code{@var{package}-@var{version}}.
+@item
+Programs built from that tarball should identify themselves
+when invoked as @command{@var{program} --version}.
+@item
+Documentation (especially in PDF format) and man pages
+may contain the version number, as an important bit of meta information.
+@end itemize
+
+@item
+The package maintainer should be able to
+easily set the version number before making a release,
+and doing so should be quick.
+
+@item
+When a contributor is
+building from the git repository (as opposed to a tarball),
+the version number should, by default, reflect the git commit,
+in order to avoid confusion w.r.t. official releases.
+@end itemize
+
+Here's how the GNU Build System and Gnulib achieve these goals.
+
+@menu
+* Setting the package version::
+* Propagating the package version::
+* Using the package version::
+@end menu
+
+@node Setting the package version
+@subsection Setting the package version
+
+@cindex @code{.tarball-version}
+When the maintainer has decided about the version number
+for the next build (and likely also the next tarball),
+they implement that decision by storing this version number
+in a file named @file{.tarball-version} at the top level of the package.
+This is a simple one-line text file.
+
+The maintainer can, alternatively, decide to
+let the git version be the version number.
+To implement this choice,
+they remove the file @file{.tarball-version} at the top level of the package.
+
+@mindex git-version-gen
+The actual version number comes from an invocation of
+the program @code{git-version-gen},
+part of the Gnulib module @code{git-version-gen}.
+It looks at the file @file{.tarball-version}, if that exists,
+and at the current checkout, otherwise.
+
+The file @file{.tarball-version} should not be put under version control.
+Therefore you may want to list it
+in the package's top-level @code{.gitignore} file.
+
+But you need a @code{Makefile.am} rule that will
+make sure that @file{.tarball-version} will exist in distribution tarballs:
+
+@example
+dist-hook: dist-tarball-version
+.PHONY: dist-tarball-version
+dist-tarball-version:
+	echo '$(VERSION)' > $(distdir)/.tarball-version
+@end example
+
+Typically, the maintainer will set the version number in a clean directory
+(i.e. after ``make distclean'').
+@mindex gnumakefile
+In order to adapt to alternative workflows,
+the file @file{GNUmakefile}, part of the Gnulib module @code{gnumakefile},
+contains a rule that will print a warning or possibly call @code{autoreconf}
+if it finds that the version has changed but has not yet been propagated.
+Reminder: Not every package supports @code{autoreconf}.
+
+@node Propagating the package version
+@subsection Propagating the package version
+
+At the end of a @file{configure} run,
+@code{config.status} is run,
+that creates various files with embedded pieces of information.
+It thus propagates the values of various variables
+to various files in the build tree
+(most notably, @code{Makefile}s and @code{config.h}).
+
+One such propagated value is
+the value of the Automake-defined variable @code{$(VERSION)}.
+(There is also the Autoconf-defined variable @code{$(PACKAGE_VERSION)},
+but nothing except Automake ought to use it.)
+
+For @code{$(VERSION)} to have a sensible value,
+a few lines are needed in @code{configure.ac}.
+
+The recommended code pattern is
+@example
+AC_INIT
+AC_CONFIG_SRCDIR([@var{unique-file-in-source-dir}])
+AC_CONFIG_AUX_DIR([build-aux])
+VERSION_NUMBER=`cd $srcdir \
+                && build-aux/git-version-gen .tarball-version \
+                   | sed -e 's/dirty$/modified/'`
+gl_INIT_PACKAGE([@var{package}], [$VERSION_NUMBER])
+AM_INIT_AUTOMAKE([@var{options}])
+@end example
+@noindent
+With this code pattern,
+the contents of the file @file{.tarball-version} and the git status
+are considered when @code{configure} is run.
+
+Two older code patterns are deprecated, because they read
+the contents of the file @file{.tarball-version} and the git status
+when @code{autoconf} is run, not when @code{configure} is run.
+These older code patterns thus require a longer turnaround cycle
+when the maintainer has changed the version number.
+
+The first such old code pattern
+is to set the version number directly in @code{configure.ac}:
+@example
+AC_INIT([@var{package}, @var{version}])
+AC_CONFIG_SRCDIR([@var{unique-file-in-source-dir}])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([@var{options}])
+@end example
+
+The second such old code pattern
+is to invoke @code{git-version-gen} at @code{autoconf} time:
+@example
+AC_INIT([@var{package}],
+        m4_esyscmd([build-aux/git-version-gen .tarball-version \
+                    | sed -e 's/dirty$/modified/'])])
+AC_CONFIG_SRCDIR([@var{unique-file-in-source-dir}])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([@var{options}])
+@end example
+
+@mindex package-version
+The macro @code{gl_INIT_PACKAGE}
+is defined in the Gnulib module @code{package-version}.
+
+@node Using the package version
+@subsection Using the package version during the build
+
+@subsubheading The unoptimized way
+
+Once propagated through @code{config.status},
+the value of @code{$(VERSION)} can appear anywhere in built files.
+For built files that are
+distributed (that is, not erased by ``make distclean''),
+it is important to add a dependency in the @code{Makefile.am},
+so that the file gets rebuilt:
+@example
+@var{built-file}: $(top_builddir)/config.status
+@end example
+
+For files that are erased by ``make distclean'', this is not really necessary,
+because
+@itemize
+@item
+In the main workflow, where the developer
+changes the version only between ``make distclean'' and ``./configure'',
+the built files have been remade.
+@item
+Most @code{.o} files depend on @code{config.h},
+which is rebuilt when @code{configure} runs.
+@end itemize
+
+Thus, only in packages that don't use Gnulib would a dependency such as
+@example
+hello-hello.$(OBJEXT): $(top_builddir)/config.status
+@end example
+@noindent
+be needed.
+
+@subsubheading The optimized way
+
+Notice that
+@code{config.status} usually changes much more often than the version number.
+Therefore, for built files which depend
+@emph{only} on @code{$(VERSION)} and not on other variables,
+the following optimized technique can be used.
+
+The technique consists of keeping a file named @code{$(top_srcdir)/.version},
+which is a timestamp file.
+Its modification time represents the last time
+the value of @code{$(VERSION)} was changed.
+Its contents is an undocumented implementation detail.
+
+With such a file, the dependency in @code{Makefile.am} becomes:
+@example
+@var{built-file}: $(top_srcdir)/.version
+@end example
+
+In order to prepare for using @code{$(top_srcdir)/.version},
+three modifications in the package are needed:
+@itemize
+@item
+In the top-level @code{configure.ac} file,
+add an invocation of the macro @code{gl_CONFIG_VERSION_STAMP},
+near the end (before @code{AC_OUTPUT}).
+This macro is defined in file @file{m4/version-stamp.m4},
+part of the Gnulib module @code{version-stamp}.
+@item
+In the top-level @code{Makefile.am}, add:
+@example
+EXTRA_DIST += $(top_srcdir)/.version
+BUILT_SOURCES += $(top_srcdir)/.version
+@end example
+@noindent
+so that the file @code{.version} will be present in tarballs
+and so that ``make maintainer-clean'' will erase it.
+@item
+Add @code{.version} to the package's top-level @code{.gitignore} file.
+@end itemize
diff --git a/top/GNUmakefile b/top/GNUmakefile
index d85da0bed4..44419bfcb0 100644
--- a/top/GNUmakefile
+++ b/top/GNUmakefile
@@ -44,6 +44,9 @@ include $(srcdir)/maint.mk
 
 # Ensure that $(VERSION) is up to date for dist-related targets, but not
 # for others: rerunning autoreconf and recompiling everything isn't cheap.
+# This is not part of the essential workflow with .tarball-version.  Rather,
+# it is meant to help the maintainer who has changed the current version
+# but not done a "make distclean".
 _have-git-version-gen := \
   $(shell test -f $(srcdir)/$(_build-aux)/git-version-gen && echo yes)
 ifeq ($(_have-git-version-gen)0,yes$(MAKELEVEL))
-- 
2.43.0

Reply via email to