diff mbox series

[v4] compat: auto-detect if zlib has uncompress2()

Message ID xmqqh79t7sj4.fsf_-_@gitster.g (mailing list archive)
State Superseded
Headers show
Series [v4] compat: auto-detect if zlib has uncompress2() | expand

Commit Message

Junio C Hamano Jan. 24, 2022, 2:54 a.m. UTC
From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>

We have a copy of uncompress2() implementation in compat/ so that we
can build with an older version of zlib that lack the function, and
the build procedure selects if it is used via the NO_UNCOMPRESS2
$(MAKE) variable.  This is yet another "annoying" knob the porters
need to tweak on platforms that are not common enough to have the
default set in the config.mak.uname file.

Ævar came up with an idea to instead ask the system header <zlib.h>
to decide if we need the compatibility implementation.  We can
compile and link compat/zlib-uncompress2.c file unconditionally, but
conditionally hide the implementation behind #if/#endif when zlib
version is 1.2.9 or newer, and unconditionally archive the resulting
object file in the libgit.a to be picked up by the linker.

There are a few things to note in the shape of the code base after
this change:

 - We no longer use NO_UNCOMPRESS2 knob; if the system header
   <zlib.h> claims a version that is more cent than the library
   actually is, this would break, but it is easy to add it back when
   we find such a system.

 - The object file compat/zlib-uncompress2.o is always compiled and
   archived in libgit.a, just like a few other compat/ object files
   already are.

 - The inclusion of <zlib.h> is done in <git-compat-util.h>; we used
   to do so from <cache.h> which includes <git-compat-util.h> as the
   first thing it does, so from the *.c codes, there is no practical
   change.

 - Beat found a trick used by OpenSSL to avoid making the
   conditionally-compiled object truly empty (apparently because
   they had to deal with compilers that do not want to see an
   effectively empty input file).  Our compat/zlib-uncompress2.c
   file borrows the same trick for portabilty.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Beat Bolli <dev+git@drbeat.li>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * So, here is an updated one, still retaining the authorship but
   adjusting for the "no empty source" trick.  v3 seems to fail
   "win+VS build" due to the use of an extra $(ZLIB_COMPAT_OBJS)
   macro, and this iteration, which just uses LIB_OBJS as everybody
   else does, should be sufficient to avoid introducing such an
   issue.

   One thing that I found a bit iffy is the use of "force z_const to
   an empty string before including <zlib.h>".  This trick to work
   around too old a version of zlib (according to Carlo) was used
   only when compat/zlib-uncompress2.c included <zlib.h> via
   <reftable/system.h>, but never done when <cache.h> included
   <zlib.h>, which means that the two parts of the code could have
   been using incompatible definitions of the same structs (many
   struct definitions zlib uses have const members).  I opted to be
   "conservative" and choose to cast away z_const before
   <git-compat-util.> includes <zlib.h>, but we may want to drop it
   to see if anybody screams.

   In any case, I think it is way too late to put this in the final.
   We may want to reroll it once more to reintroduce NO_UNCOMPRESS2
   as an escape hatch for those with zlib.h headers that lie, but
   all of that can wait until the end of the next week.

 Makefile                  |  8 +-------
 cache.h                   |  1 -
 ci/lib.sh                 |  1 -
 compat/zlib-uncompress2.c | 11 ++++++-----
 config.mak.uname          |  7 -------
 configure.ac              | 13 -------------
 git-compat-util.h         | 12 ++++++++++++
 reftable/system.h         | 11 -----------
 8 files changed, 19 insertions(+), 45 deletions(-)

Comments

Ævar Arnfjörð Bjarmason Jan. 24, 2022, 9:27 a.m. UTC | #1
On Sun, Jan 23 2022, Junio C Hamano wrote:

> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>
> We have a copy of uncompress2() implementation in compat/ so that we
> can build with an older version of zlib that lack the function, and
> the build procedure selects if it is used via the NO_UNCOMPRESS2
> $(MAKE) variable.  This is yet another "annoying" knob the porters
> need to tweak on platforms that are not common enough to have the
> default set in the config.mak.uname file.
>
> Ævar came up with an idea to instead ask the system header <zlib.h>
> to decide if we need the compatibility implementation.  We can
> compile and link compat/zlib-uncompress2.c file unconditionally, but
> conditionally hide the implementation behind #if/#endif when zlib
> version is 1.2.9 or newer, and unconditionally archive the resulting
> object file in the libgit.a to be picked up by the linker.
>
> There are a few things to note in the shape of the code base after
> this change:
>
>  - We no longer use NO_UNCOMPRESS2 knob; if the system header
>    <zlib.h> claims a version that is more cent than the library
>    actually is, this would break, but it is easy to add it back when
>    we find such a system.
>
>  - The object file compat/zlib-uncompress2.o is always compiled and
>    archived in libgit.a, just like a few other compat/ object files
>    already are.
>
>  - The inclusion of <zlib.h> is done in <git-compat-util.h>; we used
>    to do so from <cache.h> which includes <git-compat-util.h> as the
>    first thing it does, so from the *.c codes, there is no practical
>    change.
>
>  - Beat found a trick used by OpenSSL to avoid making the
>    conditionally-compiled object truly empty (apparently because
>    they had to deal with compilers that do not want to see an
>    effectively empty input file).  Our compat/zlib-uncompress2.c
>    file borrows the same trick for portabilty.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> Helped-by: Beat Bolli <dev+git@drbeat.li>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>
>  * So, here is an updated one, still retaining the authorship but
>    adjusting for the "no empty source" trick.  v3 seems to fail
>    "win+VS build" due to the use of an extra $(ZLIB_COMPAT_OBJS)
>    macro, [...]

I hadn't noticed that "win+VS build" issue, but it's another issue with
the CMake.txt scraping of the Makefile. I.e. it scrapes $(LIB_OBJS) with
a regex, and as soon as another variable is used to append to it it
fails. It has special-casing for the $(COMPAT_OBJS) seen in the v3 diff
context.

All of which b.t.w. would be avoided with the approach I suggested in in
https://lore.kernel.org/git/patch-v2-3.3-cd62d8f92d1-20211101T191231Z-avarab@gmail.com/
of having it ask "make" for these values rather than scraping them.

>    and this iteration, which just uses LIB_OBJS as everybody
>    else does, should be sufficient to avoid introducing such an
>    issue.

The change you have here doesn't work because in relying on $(LIB_OBJS)
you broke the linking of libreftable.a, which doesn't link using
$(LIB_OBJS), but requires uncompress2().

I think you didn't test this on a system that doesn't have
uncompress3(). Testing it can be emulated by just naming it
uncompress3() or something, and making the "#if" macro check an "#if 1".

That linking issue can also be fixed, but overall I really don't see the
point of this complexity of insisting that this fallback function must
arrive via a compat object.

The approach I had in the v2 of just including these sources in the
top-level zlib.c just works, without any of the added build system
complexity.

So just taking the v2 would also work (perhaps with the config.mak.uname
changes), or this v4 with compat/zlib-uncompress2.o hardcoded in both
LIB_OBJS and REFTABLE_OBJS, so that the scraping in CMake.txt can
understand it.

>    One thing that I found a bit iffy is the use of "force z_const to
>    an empty string before including <zlib.h>".  This trick to work
>    around too old a version of zlib (according to Carlo) was used
>    only when compat/zlib-uncompress2.c included <zlib.h> via
>    <reftable/system.h>, but never done when <cache.h> included
>    <zlib.h>, which means that the two parts of the code could have
>    been using incompatible definitions of the same structs (many
>    struct definitions zlib uses have const members).  I opted to be
>    "conservative" and choose to cast away z_const before
>    <git-compat-util.> includes <zlib.h>, but we may want to drop it
>    to see if anybody screams.

Weren't any issues with this avoided because we didn't include the
reftable/* sources, but compiled them and then linked libreftable.a?
diff mbox series

Patch

diff --git c/Makefile w/Makefile
index 5580859afd..a32d278700 100644
--- c/Makefile
+++ w/Makefile
@@ -256,8 +256,6 @@  all::
 #
 # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 #
-# Define NO_UNCOMPRESS2 if your zlib does not have uncompress2.
-#
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
 # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
@@ -862,6 +860,7 @@  LIB_OBJS += commit-reach.o
 LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
+LIB_OBJS += compat/zlib-uncompress2.o
 LIB_OBJS += config.o
 LIB_OBJS += connect.o
 LIB_OBJS += connected.o
@@ -1726,11 +1725,6 @@  ifdef NO_DEFLATE_BOUND
 	BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 endif
 
-ifdef NO_UNCOMPRESS2
-	BASIC_CFLAGS += -DNO_UNCOMPRESS2
-	REFTABLE_OBJS += compat/zlib-uncompress2.o
-endif
-
 ifdef NO_POSIX_GOODIES
 	BASIC_CFLAGS += -DNO_POSIX_GOODIES
 endif
diff --git c/cache.h w/cache.h
index 281f00ab1b..3a0142aa56 100644
--- c/cache.h
+++ w/cache.h
@@ -18,7 +18,6 @@ 
 #include "repository.h"
 #include "mem-pool.h"
 
-#include <zlib.h>
 typedef struct git_zstream {
 	z_stream z;
 	unsigned long avail_in;
diff --git c/ci/lib.sh w/ci/lib.sh
index 9d28ab50fb..cbc2f8f1ca 100755
--- c/ci/lib.sh
+++ w/ci/lib.sh
@@ -197,7 +197,6 @@  esac
 case "$jobname" in
 linux32)
 	CC=gcc
-	MAKEFLAGS="$MAKEFLAGS NO_UNCOMPRESS2=1"
 	;;
 linux-musl)
 	CC=gcc
diff --git c/compat/zlib-uncompress2.c w/compat/zlib-uncompress2.c
index 722610b971..77a1b08048 100644
--- c/compat/zlib-uncompress2.c
+++ w/compat/zlib-uncompress2.c
@@ -1,3 +1,6 @@ 
+#include "git-compat-util.h"
+
+#if ZLIB_VERNUM < 0x1290
 /* taken from zlib's uncompr.c
 
    commit cacf7f1d4e3d44d871b605da3b647f07d718623f
@@ -8,16 +11,11 @@ 
 
 */
 
-#include "../reftable/system.h"
-#define z_const
-
 /*
  * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
-#include <zlib.h>
-
 /* clang-format off */
 
 /* ===========================================================================
@@ -93,3 +91,6 @@  int ZEXPORT uncompress2 (
 	   err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
 	   err;
 }
+#else
+static void *dummy_variable = &dummy_variable;
+#endif
diff --git c/config.mak.uname w/config.mak.uname
index c48db45106..92ea00c219 100644
--- c/config.mak.uname
+++ w/config.mak.uname
@@ -66,7 +66,6 @@  ifeq ($(uname_S),Linux)
 	# centos7/rhel7 provides gcc 4.8.5 and zlib 1.2.7.
 	ifneq ($(findstring .el7.,$(uname_R)),)
 		BASIC_CFLAGS += -std=c99
-		NO_UNCOMPRESS2 = YesPlease
 	endif
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
@@ -266,10 +265,6 @@  ifeq ($(uname_S),FreeBSD)
 	FILENO_IS_A_MACRO = UnfortunatelyYes
 endif
 ifeq ($(uname_S),OpenBSD)
-	# Versions < 7.0 need compatibility layer
-	ifeq ($(shell expr "$(uname_R)" : "[1-6]\."),2)
-		NO_UNCOMPRESS2 = UnfortunatelyYes
-	endif
 	NO_STRCASESTR = YesPlease
 	NO_MEMMEM = YesPlease
 	USE_ST_TIMESPEC = YesPlease
@@ -525,7 +520,6 @@  ifeq ($(uname_S),Interix)
 	endif
 endif
 ifeq ($(uname_S),Minix)
-	NO_UNCOMPRESS2 = YesPlease
 	NO_IPV6 = YesPlease
 	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 	NO_NSEC = YesPlease
@@ -581,7 +575,6 @@  ifeq ($(uname_S),NONSTOP_KERNEL)
 	NO_SETENV = YesPlease
 	NO_UNSETENV = YesPlease
 	NO_MKDTEMP = YesPlease
-	NO_UNCOMPRESS2 = YesPlease
 	# Currently libiconv-1.9.1.
 	OLD_ICONV = UnfortunatelyYes
 	NO_REGEX = NeedsStartEnd
diff --git c/configure.ac w/configure.ac
index d60d494ee4..5ee25ec95c 100644
--- c/configure.ac
+++ w/configure.ac
@@ -664,22 +664,9 @@  AC_LINK_IFELSE([ZLIBTEST_SRC],
 	NO_DEFLATE_BOUND=yes])
 LIBS="$old_LIBS"
 
-AC_DEFUN([ZLIBTEST_UNCOMPRESS2_SRC], [
-AC_LANG_PROGRAM([#include <zlib.h>],
- [uncompress2(NULL,NULL,NULL,NULL);])])
-AC_MSG_CHECKING([for uncompress2 in -lz])
-old_LIBS="$LIBS"
-LIBS="$LIBS -lz"
-AC_LINK_IFELSE([ZLIBTEST_UNCOMPRESS2_SRC],
-	[AC_MSG_RESULT([yes])],
-	[AC_MSG_RESULT([no])
-	NO_UNCOMPRESS2=yes])
-LIBS="$old_LIBS"
-
 GIT_UNSTASH_FLAGS($ZLIB_PATH)
 
 GIT_CONF_SUBST([NO_DEFLATE_BOUND])
-GIT_CONF_SUBST([NO_UNCOMPRESS2])
 
 #
 # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
diff --git c/git-compat-util.h w/git-compat-util.h
index 1229c8296b..ea111a7b48 100644
--- c/git-compat-util.h
+++ w/git-compat-util.h
@@ -1386,6 +1386,18 @@  void unleak_memory(const void *ptr, size_t len);
 #define UNLEAK(var) do {} while (0)
 #endif
 
+#define z_const
+#include <zlib.h>
+
+#if ZLIB_VERNUM < 0x1290
+/*
+ * This is uncompress2, which is only available in zlib >= 1.2.9
+ * (released as of early 2017). See compat/zlib-uncompress2.c.
+ */
+int uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source,
+		uLong *sourceLen);
+#endif
+
 /*
  * This include must come after system headers, since it introduces macros that
  * replace system names.
diff --git c/reftable/system.h w/reftable/system.h
index 4907306c0c..18f9207dfe 100644
--- c/reftable/system.h
+++ w/reftable/system.h
@@ -16,17 +16,6 @@  license that can be found in the LICENSE file or at
 #include "hash.h" /* hash ID, sizes.*/
 #include "dir.h" /* remove_dir_recursively, for tests.*/
 
-#include <zlib.h>
-
-#ifdef NO_UNCOMPRESS2
-/*
- * This is uncompress2, which is only available in zlib >= 1.2.9
- * (released as of early 2017)
- */
-int uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source,
-		uLong *sourceLen);
-#endif
-
 int hash_size(uint32_t id);
 
 #endif