FindZLIB: Needs version extraction update for zlib 1.3
Meson has a dependency detection module that invokes cmake find_package as one possible candidate for finding a dependency. With dependency('zlib')
we try to find cmake's find_package(ZLIB)
.
Calling CMake (['/usr/bin/cmake']) in /cygdrive/d/a/meson/meson/b 9863013ae1/meson-private/cmake_ZLIB with:
- "-G"
- "Ninja"
- "-DNAME=ZLIB"
- "-DARCHS="
- "-DVERSION="
- "-DCOMPS="
- "-DSTATIC=False"
- "--trace-expand"
- "--trace-format=json-v1"
- "--no-warn-unused-cli"
- "--trace-redirect=cmake_trace.txt"
- "-DCMAKE_TOOLCHAIN_FILE=/cygdrive/d/a/meson/meson/b 9863013ae1/meson-private/cmake_ZLIB/CMakeMesonToolchainFile.cmake"
- "."
- "-DCMAKE_PREFIX_PATH=/cygdrive/d/a/meson/meson/test cases/linuxlike/13 cmake dependency/cmake_fake1;/cygdrive/d/a/meson/meson/test cases/linuxlike/13 cmake dependency/cmake_fake2;/cygdrive/d/a/meson/meson/test cases/linuxlike/13 cmake dependency/cmake_pref_env"
Guessed CMake target 'ZLIB::ZLIB'
CMake TARGET:
-- name: ZLIB::ZLIB
-- type: UNKNOWN
-- imported: True
-- properties: {
'INTERFACE_INCLUDE_DIRECTORIES': ['/usr/include']
'IMPORTED_CONFIGURATIONS': ['RELEASE']
'IMPORTED_LOCATION_RELEASE': ['/usr/lib/libz.dll.a']
}
-- tline: CMake TRACE: /usr/share/cmake-3.25.3/Modules/FindZLIB.cmake:177 add_library(['ZLIB::ZLIB', 'UNKNOWN', 'IMPORTED'])
Include Dirs: ['/usr/include']
Compiler Options: []
Libraries: ['/usr/lib/libz.dll.a']
Run-time dependency zlib found: YES 1.3.#define ZLIB_VERSION "1.3"
(There is some boring unittest code later on that double checks the version result is correct, by passing '-DFOUND_ZLIB="${ZLIB_VERSION}"'
as a define to a test program that then complains if if(strcmp(ZLIB_VERSION, FOUND_ZLIB) != 0)
fails.)
Note the version detected is zlib 1.3.#define ZLIB_VERSION "1.3"
, including embedded double quotes.
A little bit of investigation reveals the problem: https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindZLIB.cmake#L177
if(ZLIB_INCLUDE_DIR AND EXISTS "${ZLIB_INCLUDE_DIR}/zlib.h")
file(STRINGS "${ZLIB_INCLUDE_DIR}/zlib.h" ZLIB_H REGEX "^#define ZLIB_VERSION \"[^\"]*\"$")
string(REGEX REPLACE "^.*ZLIB_VERSION \"([0-9]+).*$" "\\1" ZLIB_VERSION_MAJOR "${ZLIB_H}")
string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_MINOR "${ZLIB_H}")
string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_PATCH "${ZLIB_H}")
set(ZLIB_VERSION_STRING "${ZLIB_VERSION_MAJOR}.${ZLIB_VERSION_MINOR}.${ZLIB_VERSION_PATCH}")
# only append a TWEAK version if it exists:
set(ZLIB_VERSION_TWEAK "")
if( "${ZLIB_H}" MATCHES "ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+)")
set(ZLIB_VERSION_TWEAK "${CMAKE_MATCH_1}")
string(APPEND ZLIB_VERSION_STRING ".${ZLIB_VERSION_TWEAK}")
endif()
set(ZLIB_MAJOR_VERSION "${ZLIB_VERSION_MAJOR}")
set(ZLIB_MINOR_VERSION "${ZLIB_VERSION_MINOR}")
set(ZLIB_PATCH_VERSION "${ZLIB_VERSION_PATCH}")
set(ZLIB_VERSION ${ZLIB_VERSION_STRING})
endif()
This is some fragile regex parsing and ZLIB_VERSION_PATCH doesn't match the regex. Instead it gets left with the entire original string (?? is there no way to return the capture group itself rather than hope it replaces something?) and then concatenated together to form the erroneous version.
Indeed, the zlib 1.3 update drops its patch number entirely.
#define ZLIB_VERSION "1.3"
It would be best if cmake could learn to not rely on regular expressions for this sort of thing -- there is some similar code in meson which looks like this:
v, _ = self.clib_compiler.get_define('ZLIB_VERSION', '#include <zlib.h>', self.env, [], [self])
self.version = v.strip('"')
It works because a C define is either:
- useless for our purpose if it isn't a literal (evaluating complex nested preprocessor expansions that produce C code which computes the given string at compile time is... not happening without compiling and running a test program that does printf)
- going to be a string with only one guarantee: it begins and ends with a C string quote char
It doesn't matter how many components the version has -- this is handled by version comparison algorithms that natively understand version numbers, or possibly using zlib_dep.version().split('.')
which is also not regex based. And you can't end up with multiple copies of the input floating around.
If it's not feasible to do this without relying on a regex, then the regex should be fixed, at least.
Bonus points if the fix is a tech debt fix and works for more than the FindZLIB module. :)