util: make the qsort_r check work on macOS This performs a compile-check by using CMake support, to differentiate the GNU version from the BSD version of qsort_r. Module taken from 4f252abea5f1d17c60f6ff115c9c44cc0b6f1df6, which I've checked against CMake 2.8.11.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b71d903..d9c0a1f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,7 @@ INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckSymbolExists)
INCLUDE(CheckStructHasMember)
+INCLUDE(CheckPrototypeDefinition) # Added in CMake 3.0
INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgLibraries)
INCLUDE(FindThreads)
diff --git a/cmake/Modules/CheckPrototypeDefinition.c.in b/cmake/Modules/CheckPrototypeDefinition.c.in
new file mode 100644
index 0000000..a97344a
--- /dev/null
+++ b/cmake/Modules/CheckPrototypeDefinition.c.in
@@ -0,0 +1,29 @@
+@CHECK_PROTOTYPE_DEFINITION_HEADER@
+
+static void cmakeRequireSymbol(int dummy, ...) {
+ (void) dummy;
+}
+
+static void checkSymbol(void) {
+#ifndef @CHECK_PROTOTYPE_DEFINITION_SYMBOL@
+ cmakeRequireSymbol(0, &@CHECK_PROTOTYPE_DEFINITION_SYMBOL@);
+#endif
+}
+
+@CHECK_PROTOTYPE_DEFINITION_PROTO@ {
+ return @CHECK_PROTOTYPE_DEFINITION_RETURN@;
+}
+
+#ifdef __CLASSIC_C__
+int main() {
+ int ac;
+ char*av[];
+#else
+int main(int ac, char *av[]) {
+#endif
+ checkSymbol();
+ if (ac > 1000) {
+ return *av[0];
+ }
+ return 0;
+}
diff --git a/cmake/Modules/CheckPrototypeDefinition.cmake b/cmake/Modules/CheckPrototypeDefinition.cmake
new file mode 100644
index 0000000..244b9b5
--- /dev/null
+++ b/cmake/Modules/CheckPrototypeDefinition.cmake
@@ -0,0 +1,96 @@
+# - Check if the protoype we expect is correct.
+# check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE)
+# FUNCTION - The name of the function (used to check if prototype exists)
+# PROTOTYPE- The prototype to check.
+# RETURN - The return value of the function.
+# HEADER - The header files required.
+# VARIABLE - The variable to store the result.
+# Example:
+# check_prototype_definition(getpwent_r
+# "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)"
+# "NULL"
+# "unistd.h;pwd.h"
+# SOLARIS_GETPWENT_R)
+# The following variables may be set before calling this macro to
+# modify the way the check is run:
+#
+# CMAKE_REQUIRED_FLAGS = string of compile command line flags
+# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+# CMAKE_REQUIRED_INCLUDES = list of include directories
+# CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+
+#=============================================================================
+# Copyright 2005-2009 Kitware, Inc.
+# Copyright 2010-2011 Andreas Schneider <asn@cryptomilk.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+#
+
+get_filename_component(__check_proto_def_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
+
+function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE)
+
+ if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$")
+ set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n")
+
+ set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS})
+ if (CMAKE_REQUIRED_LIBRARIES)
+ set(CHECK_PROTOTYPE_DEFINITION_LIBS
+ "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
+ else(CMAKE_REQUIRED_LIBRARIES)
+ set(CHECK_PROTOTYPE_DEFINITION_LIBS)
+ endif(CMAKE_REQUIRED_LIBRARIES)
+ if (CMAKE_REQUIRED_INCLUDES)
+ set(CMAKE_SYMBOL_EXISTS_INCLUDES
+ "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+ else(CMAKE_REQUIRED_INCLUDES)
+ set(CMAKE_SYMBOL_EXISTS_INCLUDES)
+ endif(CMAKE_REQUIRED_INCLUDES)
+
+ foreach(_FILE ${_HEADER})
+ set(CHECK_PROTOTYPE_DEFINITION_HEADER
+ "${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n")
+ endforeach(_FILE)
+
+ set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION})
+ set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE})
+ set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN})
+
+ configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in"
+ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY)
+
+ file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE)
+
+ try_compile(${_VARIABLE}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c
+ COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+ CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS}
+ "${CHECK_PROTOTYPE_DEFINITION_LIBS}"
+ "${CMAKE_SYMBOL_EXISTS_INCLUDES}"
+ OUTPUT_VARIABLE OUTPUT)
+
+ if (${_VARIABLE})
+ set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}")
+ message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True")
+ file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n"
+ "${OUTPUT}\n\n")
+ else (${_VARIABLE})
+ message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False")
+ set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}")
+ file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n"
+ "${OUTPUT}\n\n${_SOURCE}\n\n")
+ endif (${_VARIABLE})
+ endif("${_VARIABLE}" MATCHES "^${_VARIABLE}$")
+
+endfunction(CHECK_PROTOTYPE_DEFINITION)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index be733cc..157ce80 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -58,10 +58,19 @@ IF (HAVE_FUTIMENS)
SET(GIT_USE_FUTIMENS 1)
ENDIF ()
-CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
-IF (HAVE_QSORT_R)
- ADD_DEFINITIONS(-DHAVE_QSORT_R)
-ENDIF ()
+CHECK_PROTOTYPE_DEFINITION(qsort_r
+ "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
+ "" "stdlib.h" HAVE_QSORT_R_BSD)
+IF (HAVE_QSORT_R_BSD)
+ ADD_DEFINITIONS(-DHAVE_QSORT_R_BSD)
+ENDIF()
+
+CHECK_PROTOTYPE_DEFINITION(qsort_r
+ "void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)"
+ "" "stdlib.h" HAVE_QSORT_R_GNU)
+IF (HAVE_QSORT_R_GNU)
+ ADD_DEFINITIONS(-DHAVE_QSORT_R_GNU)
+ENDIF()
CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
IF (HAVE_QSORT_S)
diff --git a/src/util.c b/src/util.c
index bf778a9..79b362f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -647,7 +647,7 @@ size_t git__unescape(char *str)
return (pos - str);
}
-#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD))
+#if defined(HAVE_QSORT_S) || defined(HAVE_QSORT_R_BSD)
typedef struct {
git__sort_r_cmp cmp;
void *payload;
@@ -664,10 +664,10 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{
-#if defined(HAVE_QSORT_R) && defined(BSD)
+#if defined(HAVE_QSORT_R_BSD)
git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
-#elif defined(HAVE_QSORT_R) && defined(__GLIBC__)
+#elif defined(HAVE_QSORT_R_GNU)
qsort_r(els, nel, elsize, cmp, payload);
#elif defined(HAVE_QSORT_S)
git__qsort_r_glue glue = { cmp, payload };