diff mbox series

[1/2] GIT-VERSION-GEN: fix overriding version via environment

Message ID 20241219-b4-pks-git-version-via-environment-v1-1-9393af058240@pks.im (mailing list archive)
State Superseded
Headers show
Series GIT-VERSION-GEN: fix overriding values | expand

Commit Message

Patrick Steinhardt Dec. 19, 2024, 3:53 p.m. UTC
GIT-VERSION-GEN tries to derive the version that Git is being built from
via multiple different sources in the following order:

  1. A file called "version" in the source tree's root directory, if it
     exists.

  2. The current commit in case Git is built from a Git repository.

  3. Otherwise, we use a fallback version stored in a variable which is
     bumped whenever a new Git version is getting tagged.

It used to be possible to override the version by overriding the
`GIT_VERSION` Makefile variable (e.g. `make GIT_VERSION=foo`). This
worked somewhat by chance, only: `GIT-VERSION-GEN` would write the
actual Git version into `GIT-VERSION-FILE`, not the overridden value,
but when including the file into our Makefile we would not override the
`GIT_VERSION` variable because it has already been set by the user. And
because our Makefile used the variable to propagate the version to our
build tools instead of using `GIT-VERSION-FILE` the resulting build
artifacts used the overridden version.

But that subtle mechanism broke with 4838deab65 (Makefile: refactor
GIT-VERSION-GEN to be reusable, 2024-12-06) and subsequent commits
because the version information is not propagated via the Makefile
variable anymore, but instead via the files that `GIT-VERSION-GEN`
started to write. And as the script never knew about the `GIT_VERSION`
environment variable in the first place it uses one of the values listed
above instead of the overridden value.

Fix this issue by making `GIT-VERSION-GEN` handle the case where
`GIT_VERSION` has been set via the environment.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-VERSION-GEN | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Comments

Junio C Hamano Dec. 19, 2024, 6:49 p.m. UTC | #1
Patrick Steinhardt <ps@pks.im> writes:

> worked somewhat by chance, only: ...
>
> But that subtle mechanism broke with 4838deab65 (Makefile: refactor
> GIT-VERSION-GEN to be reusable, 2024-12-06) and subsequent commits

With such a nice analysis, it does not look like it was "by chance"
working, though ;-)  And ...

> because the version information is not propagated via the Makefile
> variable anymore, but instead via the files that `GIT-VERSION-GEN`
> started to write. And as the script never knew about the `GIT_VERSION`
> environment variable in the first place it uses one of the values listed
> above instead of the overridden value.
>
> Fix this issue by making `GIT-VERSION-GEN` handle the case where
> `GIT_VERSION` has been set via the environment.

... the "fix" sounds very much the logical and only correct
solution.

Thanks, queued.

>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  GIT-VERSION-GEN | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
> index de0e63bdfbac263884e2ea328cc2ef11ace7a238..787c6cfd04f0a43d0c1c8a6690185d26ccf2fc2f 100755
> --- a/GIT-VERSION-GEN
> +++ b/GIT-VERSION-GEN
> @@ -29,7 +29,10 @@ export GIT_CEILING_DIRECTORIES
>  
>  # First see if there is a version file (included in release tarballs),
>  # then try git-describe, then default.
> -if test -f "$SOURCE_DIR"/version
> +if test -n "$GIT_VERSION"
> +then
> +    VN="$GIT_VERSION"
> +elif test -f "$SOURCE_DIR"/version
>  then
>  	VN=$(cat "$SOURCE_DIR"/version) || VN="$DEF_VER"
>  elif {
Jeff King Dec. 20, 2024, 7:34 a.m. UTC | #2
On Thu, Dec 19, 2024 at 04:53:36PM +0100, Patrick Steinhardt wrote:

> But that subtle mechanism broke with 4838deab65 (Makefile: refactor
> GIT-VERSION-GEN to be reusable, 2024-12-06) and subsequent commits
> because the version information is not propagated via the Makefile
> variable anymore, but instead via the files that `GIT-VERSION-GEN`
> started to write. And as the script never knew about the `GIT_VERSION`
> environment variable in the first place it uses one of the values listed
> above instead of the overridden value.
> 
> Fix this issue by making `GIT-VERSION-GEN` handle the case where
> `GIT_VERSION` has been set via the environment.

I think this is the right direction, but there are two subtleties we
might want to address. The first is that you are adjusting $VN and not
$GIT_VERSION itself:

> diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
> index de0e63bdfbac263884e2ea328cc2ef11ace7a238..787c6cfd04f0a43d0c1c8a6690185d26ccf2fc2f 100755
> --- a/GIT-VERSION-GEN
> +++ b/GIT-VERSION-GEN
> @@ -29,7 +29,10 @@ export GIT_CEILING_DIRECTORIES
>  
>  # First see if there is a version file (included in release tarballs),
>  # then try git-describe, then default.
> -if test -f "$SOURCE_DIR"/version
> +if test -n "$GIT_VERSION"
> +then
> +    VN="$GIT_VERSION"
> +elif test -f "$SOURCE_DIR"/version
>  then
>  	VN=$(cat "$SOURCE_DIR"/version) || VN="$DEF_VER"
>  elif {

Later we process $VN into $GIT_VERSION, but after removing the leading
"v" (which would usually be there from the tag name):

  GIT_VERSION=$(expr "$VN" : v*'\(.*\)')

So if I do:

  make GIT_VERSION=very-special

with v2.47 I'd end up with the version "very-special". But after your
patch, it is "ery-special".

I'd guess it's unlikely to come up in practice, but if we are trying to
restore the old behavior, that's one difference.


The second is that the value is read from the environment, but make will
not always put its variables into the environment. Ones given on the
command line are, so:

  make GIT_VERSION=foo

works as before. But:

  echo 'GIT_VERSION = foo' >config.mak
  make

will not, as the variable isn't set in the environment. The invocation
of GIT-VERSION-GEN already passes along GIT_USER_AGENT explicitly:

  version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
          $(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
          @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi

Should we do the same thing for GIT_VERSION? And GIT_DATE, etc? If we're
going to do many of these, it might also be easier to just add "export
GIT_VERSION", etc, in the Makefile.

-Peff

PS I don't know if meson.build would need something similar. It does not
   even pass through GIT_USER_AGENT now.
Patrick Steinhardt Dec. 20, 2024, 8:45 a.m. UTC | #3
On Fri, Dec 20, 2024 at 02:34:37AM -0500, Jeff King wrote:
> On Thu, Dec 19, 2024 at 04:53:36PM +0100, Patrick Steinhardt wrote:
> > diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
> > index de0e63bdfbac263884e2ea328cc2ef11ace7a238..787c6cfd04f0a43d0c1c8a6690185d26ccf2fc2f 100755
> > --- a/GIT-VERSION-GEN
> > +++ b/GIT-VERSION-GEN
> > @@ -29,7 +29,10 @@ export GIT_CEILING_DIRECTORIES
> >  
> >  # First see if there is a version file (included in release tarballs),
> >  # then try git-describe, then default.
> > -if test -f "$SOURCE_DIR"/version
> > +if test -n "$GIT_VERSION"
> > +then
> > +    VN="$GIT_VERSION"
> > +elif test -f "$SOURCE_DIR"/version
> >  then
> >  	VN=$(cat "$SOURCE_DIR"/version) || VN="$DEF_VER"
> >  elif {
> 
> Later we process $VN into $GIT_VERSION, but after removing the leading
> "v" (which would usually be there from the tag name):
> 
>   GIT_VERSION=$(expr "$VN" : v*'\(.*\)')
> 
> So if I do:
> 
>   make GIT_VERSION=very-special
> 
> with v2.47 I'd end up with the version "very-special". But after your
> patch, it is "ery-special".
> 
> I'd guess it's unlikely to come up in practice, but if we are trying to
> restore the old behavior, that's one difference.

Ah, indeed, will fix.

> The second is that the value is read from the environment, but make will
> not always put its variables into the environment. Ones given on the
> command line are, so:
> 
>   make GIT_VERSION=foo
> 
> works as before. But:
> 
>   echo 'GIT_VERSION = foo' >config.mak
>   make
> 
> will not, as the variable isn't set in the environment. The invocation
> of GIT-VERSION-GEN already passes along GIT_USER_AGENT explicitly:
> 
>   version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
>           $(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
>           @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
> 
> Should we do the same thing for GIT_VERSION? And GIT_DATE, etc? If we're
> going to do many of these, it might also be easier to just add "export
> GIT_VERSION", etc, in the Makefile.

I guess. It'll become quite painful to do this at every callsite, so
I'll add another commit on top to introduce a call template that does
all of this for us.

> PS I don't know if meson.build would need something similar. It does not
>    even pass through GIT_USER_AGENT now.

It's easy enough to do, so why not?

Patrick
Jeff King Dec. 20, 2024, 8:56 a.m. UTC | #4
On Fri, Dec 20, 2024 at 09:45:36AM +0100, Patrick Steinhardt wrote:

> >   version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
> >           $(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
> >           @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
> > 
> > Should we do the same thing for GIT_VERSION? And GIT_DATE, etc? If we're
> > going to do many of these, it might also be easier to just add "export
> > GIT_VERSION", etc, in the Makefile.
> 
> I guess. It'll become quite painful to do this at every callsite, so
> I'll add another commit on top to introduce a call template that does
> all of this for us.

Is there any reason not to just do:

  export GIT_VERSION
  export GIT_DATE
  export GIT_BUILT_FROM_COMMIT
  export GIT_USER_AGENT

in shared.mak? Then you only have to do it once, and no need for
templates.

-Peff
Patrick Steinhardt Dec. 20, 2024, 9:31 a.m. UTC | #5
On Fri, Dec 20, 2024 at 03:56:26AM -0500, Jeff King wrote:
> On Fri, Dec 20, 2024 at 09:45:36AM +0100, Patrick Steinhardt wrote:
> 
> > >   version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
> > >           $(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
> > >           @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
> > > 
> > > Should we do the same thing for GIT_VERSION? And GIT_DATE, etc? If we're
> > > going to do many of these, it might also be easier to just add "export
> > > GIT_VERSION", etc, in the Makefile.
> > 
> > I guess. It'll become quite painful to do this at every callsite, so
> > I'll add another commit on top to introduce a call template that does
> > all of this for us.
> 
> Is there any reason not to just do:
> 
>   export GIT_VERSION
>   export GIT_DATE
>   export GIT_BUILT_FROM_COMMIT
>   export GIT_USER_AGENT
> 
> in shared.mak? Then you only have to do it once, and no need for
> templates.

You could do that, yeah, but the user needs to be aware that they can.
I'm happy to not go down that path and live with the above solution.
Alternatively, this would be what the call template would look like.

Patrick

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3392e1ce7e..a7cb885b67 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -211,12 +211,10 @@ XMLTO_EXTRA += --skip-validation
 XMLTO_EXTRA += -x manpage.xsl
 
 asciidoctor-extensions.rb: asciidoctor-extensions.rb.in FORCE
-	$(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ../GIT-VERSION-GEN "$(shell pwd)/.." $< $@+
-	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+	$(QUIET_GEN)$(call version_gen,$(shell pwd)/..,$<,$@)
 else
 asciidoc.conf: asciidoc.conf.in FORCE
-	$(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ../GIT-VERSION-GEN "$(shell pwd)/.." $< $@+
-	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+	$(QUIET_GEN)$(call version_gen,$(shell pwd)/..,$<,$@)
 endif
 
 ASCIIDOC_DEPS += docinfo.html
diff --git a/Makefile b/Makefile
index 79739a13d2..9cfe3d0aa9 100644
--- a/Makefile
+++ b/Makefile
@@ -593,7 +593,7 @@ include shared.mak
 
 GIT-VERSION-FILE: FORCE
 	@OLD=$$(cat $@ 2>/dev/null || :) && \
-	$(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" GIT-VERSION-FILE.in $@ && \
+	$(call version_gen,"$(shell pwd)",GIT-VERSION-FILE.in,$@) && \
 	NEW=$$(cat $@ 2>/dev/null || :) && \
 	if test "$$OLD" != "$$NEW"; then echo "$$NEW" >&2; fi
 -include GIT-VERSION-FILE
@@ -2512,8 +2512,7 @@ pager.sp pager.s pager.o: EXTRA_CPPFLAGS = \
 	-DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
 
 version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
-	$(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
-	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+	$(QUIET_GEN)$(call version_gen,"$(shell pwd)",$<,$@)
 
 version.sp version.s version.o: version-def.h
 
@@ -2554,8 +2553,7 @@ $(SCRIPT_SH_GEN) $(SCRIPT_LIB) : % : %.sh generate-script.sh GIT-BUILD-OPTIONS G
 	mv $@+ $@
 
 git.rc: git.rc.in GIT-VERSION-GEN GIT-VERSION-FILE
-	$(QUIET_GEN)$(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
-	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+	$(QUIET_GEN)$(call version_gen,"$(shell pwd)",$<,$@)
 
 git.res: git.rc GIT-PREFIX
 	$(QUIET_RC)$(RC) -i $< -o $@
diff --git a/shared.mak b/shared.mak
index 29bebd30d8..8e0a19691f 100644
--- a/shared.mak
+++ b/shared.mak
@@ -116,3 +116,14 @@ endef
 define libpath_template
 -L$(1) $(if $(filter-out -L,$(CC_LD_DYNPATH)),$(CC_LD_DYNPATH)$(1))
 endef
+
+# Populate build information into a file via GIT-VERSION-GEN. Requires the
+# absolute path to the root source directory as well as input and output files
+# as arguments, in that order.
+define version_gen
+GIT_BUILT_FROM_COMMIT="$(GIT_BUILT_FROM_COMMIT)" \
+GIT_DATE="$(GIT_DATE)" \
+GIT_USER_AGENT="$(GIT_USER_AGENT)" \
+GIT_VERSION="$(GIT_VERSION)" \
+$(SHELL_PATH) "$(1)/GIT-VERSION-GEN" "$(1)" "$(2)" "$(3)"
+endef
Jeff King Dec. 20, 2024, 11:17 a.m. UTC | #6
On Fri, Dec 20, 2024 at 10:31:30AM +0100, Patrick Steinhardt wrote:

> > > I guess. It'll become quite painful to do this at every callsite, so
> > > I'll add another commit on top to introduce a call template that does
> > > all of this for us.
> > 
> > Is there any reason not to just do:
> > 
> >   export GIT_VERSION
> >   export GIT_DATE
> >   export GIT_BUILT_FROM_COMMIT
> >   export GIT_USER_AGENT
> > 
> > in shared.mak? Then you only have to do it once, and no need for
> > templates.
> 
> You could do that, yeah, but the user needs to be aware that they can.
> I'm happy to not go down that path and live with the above solution.
> Alternatively, this would be what the call template would look like.

I meant that _we_ would mark those variables for export ourselves (in
shared.mak, which is used by all of our Makefiles, not the user's
config.mak).

I.e., this:

diff --git a/shared.mak b/shared.mak
index 29bebd30d8..4aa7dbf5e0 100644
--- a/shared.mak
+++ b/shared.mak
@@ -116,3 +116,5 @@ endef
 define libpath_template
 -L$(1) $(if $(filter-out -L,$(CC_LD_DYNPATH)),$(CC_LD_DYNPATH)$(1))
 endef
+
+export GIT_VERSION

which makes:

  echo 'GIT_VERSION = foo' >config.mak
  make

behave as it used to (when coupled with the fix you already sent to
respect the variable within the version-gen script).

-Peff
Patrick Steinhardt Dec. 20, 2024, 12:22 p.m. UTC | #7
On Fri, Dec 20, 2024 at 06:17:16AM -0500, Jeff King wrote:
> On Fri, Dec 20, 2024 at 10:31:30AM +0100, Patrick Steinhardt wrote:
> 
> > > > I guess. It'll become quite painful to do this at every callsite, so
> > > > I'll add another commit on top to introduce a call template that does
> > > > all of this for us.
> > > 
> > > Is there any reason not to just do:
> > > 
> > >   export GIT_VERSION
> > >   export GIT_DATE
> > >   export GIT_BUILT_FROM_COMMIT
> > >   export GIT_USER_AGENT
> > > 
> > > in shared.mak? Then you only have to do it once, and no need for
> > > templates.
> > 
> > You could do that, yeah, but the user needs to be aware that they can.
> > I'm happy to not go down that path and live with the above solution.
> > Alternatively, this would be what the call template would look like.
> 
> I meant that _we_ would mark those variables for export ourselves (in
> shared.mak, which is used by all of our Makefiles, not the user's
> config.mak).
> 
> I.e., this:
> 
> diff --git a/shared.mak b/shared.mak
> index 29bebd30d8..4aa7dbf5e0 100644
> --- a/shared.mak
> +++ b/shared.mak
> @@ -116,3 +116,5 @@ endef
>  define libpath_template
>  -L$(1) $(if $(filter-out -L,$(CC_LD_DYNPATH)),$(CC_LD_DYNPATH)$(1))
>  endef
> +
> +export GIT_VERSION
> 
> which makes:
> 
>   echo 'GIT_VERSION = foo' >config.mak
>   make
> 
> behave as it used to (when coupled with the fix you already sent to
> respect the variable within the version-gen script).

Ah, I misread "shared.mak" and thought you meant "config.mak". That
makes more sense then, thanks!

Patrick
diff mbox series

Patch

diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index de0e63bdfbac263884e2ea328cc2ef11ace7a238..787c6cfd04f0a43d0c1c8a6690185d26ccf2fc2f 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -29,7 +29,10 @@  export GIT_CEILING_DIRECTORIES
 
 # First see if there is a version file (included in release tarballs),
 # then try git-describe, then default.
-if test -f "$SOURCE_DIR"/version
+if test -n "$GIT_VERSION"
+then
+    VN="$GIT_VERSION"
+elif test -f "$SOURCE_DIR"/version
 then
 	VN=$(cat "$SOURCE_DIR"/version) || VN="$DEF_VER"
 elif {