Skip to content

Commit 3c45c72

Browse files
committed
Fix ext/odbc and ext/pdo_odbc
- Updated FindODBC module - Configuration options synced - User experience annoyances reduced a bit - Configuration headers synced for in-php-src or standalone extension builds
1 parent e5eb087 commit 3c45c72

File tree

6 files changed

+450
-203
lines changed

6 files changed

+450
-203
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ jobs:
8080
libfreetype-dev \
8181
libldap2-dev \
8282
unixodbc-dev \
83-
libodbc2 \
8483
freetds-dev \
8584
libsnmp-dev \
8685
snmp \

cmake/cmake/modules/FindODBC.cmake

Lines changed: 262 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,183 @@
11
#[=============================================================================[
22
Find the ODBC library.
33
4-
Module defines the following `IMPORTED` target(s):
4+
This module is based on the upstream
5+
[FindODBC](https://cmake.org/cmake/help/latest/module/FindODBC.html) with some
6+
enhancements and adjustments for the PHP build workflow.
57
6-
* `ODBC::ODBC` - The package library, if found.
8+
Modifications from upstream:
79
8-
Result variables:
10+
* New result variables:
911
10-
* `ODBC_FOUND` - Whether the package has been found.
11-
* `ODBC_INCLUDE_DIRS` - Include directories needed to use this package.
12-
* `ODBC_LIBRARIES` - Libraries needed to link to the package library.
13-
* `ODBC_VERSION` - Package version, if found.
12+
* `ODBC_DRIVER`
1413
15-
Cache variables:
14+
Name of the found driver, if any. For example, `unixODBC`, `iODBC`.
1615
17-
* `ODBC_INCLUDE_DIR` - Directory containing package library headers.
18-
* `ODBC_LIBRARY` - The path to the package library.
16+
* `ODBC_VERSION`
1917
20-
Hints:
18+
Version of the found ODBC library if it was retrieved from config utilities.
2119
22-
* The `ODBC_ROOT` variable adds custom search path.
23-
* The `ODBC_TYPE` variable adds ODBC library name to look for.
20+
* New hints:
21+
22+
* `ODBC_USE_DRIVER`
23+
24+
Set to `unixODBC` or `iODBC` to limit searching for specific ODBC driver
25+
instead of any driver.
26+
27+
* Added pkg-config integration.
28+
29+
* It fixes the limitation where the upstream module can't (yet) select which
30+
specific ODBC driver to use. Except on Windows, where the driver searching is
31+
the same as upstream.
32+
33+
* Added package meta-data for FeatureSummary (not relevant for upstream module).
2434
#]=============================================================================]
2535

2636
include(FeatureSummary)
27-
include(FindPackageHandleStandardArgs)
2837

29-
set_package_properties(
30-
ODBC
31-
PROPERTIES
32-
URL "https://en.wikipedia.org/wiki/Open_Database_Connectivity"
33-
DESCRIPTION "Open Database Connectivity library"
34-
)
38+
# Define internal variables
39+
set(_odbc_include_paths)
40+
set(_odbc_lib_paths)
41+
set(_odbc_lib_names)
42+
set(_odbc_required_libs_names)
43+
set(_odbc_config_names)
44+
set(_reason)
3545

36-
set(_reason "")
46+
### Try Windows Kits ##########################################################
47+
if(WIN32)
48+
# List names of ODBC libraries on Windows
49+
if(NOT MINGW)
50+
set(ODBC_LIBRARY odbc32.lib)
51+
else()
52+
set(ODBC_LIBRARY libodbc32.a)
53+
endif()
54+
set(_odbc_lib_names odbc32;)
3755

38-
# Use pkgconf, if available on the system.
39-
find_package(PkgConfig QUIET)
40-
pkg_check_modules(PC_ODBC QUIET odbc)
56+
# List additional libraries required to use ODBC library
57+
if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
58+
set(_odbc_required_libs_names odbccp32;ws2_32)
59+
elseif(MINGW)
60+
set(_odbc_required_libs_names odbccp32)
61+
endif()
62+
endif()
4163

42-
find_path(
43-
ODBC_INCLUDE_DIR
44-
NAMES sql.h
45-
PATHS ${PC_ODBC_INCLUDE_DIRS}
46-
DOC "Directory containing ODBC library headers"
47-
)
64+
### Try unixODBC or iODBC config program ######################################
65+
if(UNIX)
66+
if(ODBC_USE_DRIVER MATCHES "^(unixODBC|unixodbc|UNIXODBC)$")
67+
set(_odbc_config_names odbc_config)
68+
elseif(ODBC_USE_DRIVER MATCHES "^(iODBC|iodbc|IODBC)$")
69+
set(_odbc_config_names iodbc-config)
70+
else()
71+
set(_odbc_config_names odbc_config iodbc-config)
72+
endif()
4873

49-
if(NOT ODBC_INCLUDE_DIR)
50-
string(APPEND _reason "ODBC sql.h not found. ")
74+
find_program(ODBC_CONFIG
75+
NAMES ${_odbc_config_names}
76+
DOC "Path to unixODBC or iODBC config program")
77+
mark_as_advanced(ODBC_CONFIG)
5178
endif()
5279

53-
if(NOT ODBC_TYPE)
54-
set(ODBC_TYPE "")
80+
if(UNIX AND ODBC_CONFIG)
81+
# unixODBC and iODBC accept unified command line options
82+
execute_process(COMMAND ${ODBC_CONFIG} --cflags
83+
OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE)
84+
execute_process(COMMAND ${ODBC_CONFIG} --libs
85+
OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE)
86+
87+
# Collect paths of include directories from CFLAGS
88+
separate_arguments(_cflags NATIVE_COMMAND "${_cflags}")
89+
foreach(arg IN LISTS _cflags)
90+
if("${arg}" MATCHES "^-I(.*)$")
91+
list(APPEND _odbc_include_paths "${CMAKE_MATCH_1}")
92+
endif()
93+
endforeach()
94+
unset(_cflags)
95+
96+
# Collect paths of library names and directories from LIBS
97+
separate_arguments(_libs NATIVE_COMMAND "${_libs}")
98+
foreach(arg IN LISTS _libs)
99+
if("${arg}" MATCHES "^-L(.*)$")
100+
list(APPEND _odbc_lib_paths "${CMAKE_MATCH_1}")
101+
elseif("${arg}" MATCHES "^-l(.*)$")
102+
set(_lib_name ${CMAKE_MATCH_1})
103+
string(REGEX MATCH "odbc" _is_odbc ${_lib_name})
104+
if(_is_odbc)
105+
list(APPEND _odbc_lib_names ${_lib_name})
106+
else()
107+
list(APPEND _odbc_required_libs_names ${_lib_name})
108+
endif()
109+
unset(_lib_name)
110+
endif()
111+
endforeach()
112+
unset(_libs)
55113
endif()
56114

57-
find_library(
58-
ODBC_LIBRARY
59-
NAMES ${ODBC_TYPE} odbc
60-
PATHS ${PC_ODBC_LIBRARY_DIRS}
61-
DOC "The path to the ODBC library"
62-
)
115+
### Try pkg-config ############################################################
116+
if(NOT ODBC_CONFIG)
117+
find_package(PkgConfig QUIET)
118+
if(PKG_CONFIG_FOUND)
119+
if(ODBC_USE_DRIVER MATCHES "^(unixODBC|unixodbc|UNIXODBC)$")
120+
pkg_check_modules(PC_ODBC QUIET odbc)
121+
elseif(ODBC_USE_DRIVER MATCHES "^(iODBC|iodbc|IODBC)$")
122+
pkg_check_modules(PC_ODBC QUIET libiodbc)
123+
else()
124+
pkg_search_module(PC_ODBC QUIET odbc libiodbc)
125+
endif()
126+
endif()
127+
endif()
128+
129+
### Try unixODBC or iODBC in include/lib filesystems ##########################
130+
if(UNIX AND NOT ODBC_CONFIG)
131+
if(ODBC_USE_DRIVER MATCHES "^(unixODBC|unixodbc|UNIXODBC)$")
132+
set(_odbc_lib_names odbc;unixodbc;)
133+
elseif(ODBC_USE_DRIVER MATCHES "^(iODBC|iodbc|IODBC)$")
134+
set(_odbc_lib_names iodbc;)
135+
else()
136+
# List names of both ODBC libraries, unixODBC and iODBC
137+
set(_odbc_lib_names odbc;iodbc;unixodbc;)
138+
endif()
139+
endif()
63140

141+
### Find include directories ##################################################
142+
find_path(ODBC_INCLUDE_DIR
143+
NAMES sql.h
144+
PATHS ${_odbc_include_paths}
145+
HINTS ${PC_ODBC_INCLUDE_DIRS})
146+
147+
if(NOT ODBC_INCLUDE_DIR AND WIN32)
148+
set(ODBC_INCLUDE_DIR "")
149+
endif()
150+
151+
### Find libraries ############################################################
64152
if(NOT ODBC_LIBRARY)
65-
string(APPEND _reason "ODBC library not found. ")
153+
find_library(ODBC_LIBRARY
154+
NAMES ${_odbc_lib_names}
155+
PATHS ${_odbc_lib_paths}
156+
PATH_SUFFIXES odbc
157+
HINTS ${PC_ODBC_LIBRARY_DIRS})
158+
159+
foreach(_lib IN LISTS _odbc_required_libs_names)
160+
find_library(_lib_path
161+
NAMES ${_lib}
162+
PATHS ${_odbc_lib_paths} # system parths or collected from ODBC_CONFIG
163+
PATH_SUFFIXES odbc)
164+
if(_lib_path)
165+
list(APPEND _odbc_required_libs_paths ${_lib_path})
166+
endif()
167+
unset(_lib_path CACHE)
168+
endforeach()
66169
endif()
67170

68-
# Get version.
171+
# Unset internal lists as no longer used
172+
unset(_odbc_include_paths)
173+
unset(_odbc_lib_paths)
174+
unset(_odbc_lib_names)
175+
unset(_odbc_required_libs_names)
176+
unset(_odbc_config_names)
177+
178+
### Get version ###############################################################
69179
block(PROPAGATE ODBC_VERSION)
70-
# ODBC headers don't provide version. Try pkgconf version, if found.
180+
# ODBC headers don't provide version. Try pkg-confing version, if found.
71181
if(PC_ODBC_VERSION)
72182
cmake_path(
73183
COMPARE
@@ -79,36 +189,126 @@ block(PROPAGATE ODBC_VERSION)
79189
set(ODBC_VERSION ${PC_ODBC_VERSION})
80190
endif()
81191
endif()
192+
193+
if(NOT ODBC_VERSION AND ODBC_CONFIG)
194+
execute_process(
195+
COMMAND ${ODBC_CONFIG} --version
196+
OUTPUT_VARIABLE _odbc_version
197+
OUTPUT_STRIP_TRAILING_WHITESPACE
198+
ERROR_QUIET
199+
)
200+
if(_odbc_version MATCHES "[0-9]+\.[0-9.]*")
201+
set(ODBC_VERSION ${_odbc_version})
202+
endif()
203+
endif()
82204
endblock()
83205

84-
mark_as_advanced(ODBC_LIBRARY ODBC_INCLUDE_DIR)
206+
### Set result variables ######################################################
207+
set(_odbc_required_vars ODBC_LIBRARY)
208+
if(NOT WIN32)
209+
list(APPEND _odbc_required_vars ODBC_INCLUDE_DIR)
210+
endif()
211+
212+
if(NOT ODBC_INCLUDE_DIR)
213+
string(APPEND _reason "ODBC sql.h not found. ")
214+
endif()
85215

216+
if(NOT ODBC_LIBRARY)
217+
string(APPEND _reason "ODBC library not found. ")
218+
endif()
219+
220+
include(FindPackageHandleStandardArgs)
86221
find_package_handle_standard_args(
87222
ODBC
88-
REQUIRED_VARS
89-
ODBC_LIBRARY
90-
ODBC_INCLUDE_DIR
223+
REQUIRED_VARS ${_odbc_required_vars}
91224
VERSION_VAR ODBC_VERSION
92225
REASON_FAILURE_MESSAGE "${_reason}"
93226
)
94227

228+
unset(_odbc_required_vars)
95229
unset(_reason)
96230

97-
if(NOT ODBC_FOUND)
98-
return()
99-
endif()
231+
mark_as_advanced(ODBC_LIBRARY ODBC_INCLUDE_DIR)
100232

101233
set(ODBC_INCLUDE_DIRS ${ODBC_INCLUDE_DIR})
102-
set(ODBC_LIBRARIES ${ODBC_LIBRARY})
103-
104-
if(NOT TARGET ODBC::ODBC)
105-
add_library(ODBC::ODBC UNKNOWN IMPORTED)
106-
107-
set_target_properties(
108-
ODBC::ODBC
109-
PROPERTIES
110-
IMPORTED_LOCATION "${ODBC_LIBRARY}"
111-
INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}"
112-
INTERFACE_COMPILE_OPTIONS "${PC_ODBC_CFLAGS_OTHER}"
113-
)
234+
list(APPEND ODBC_LIBRARIES ${ODBC_LIBRARY})
235+
list(APPEND ODBC_LIBRARIES ${_odbc_required_libs_paths})
236+
237+
### Import targets ############################################################
238+
if(ODBC_FOUND)
239+
if(NOT TARGET ODBC::ODBC)
240+
if(IS_ABSOLUTE "${ODBC_LIBRARY}")
241+
add_library(ODBC::ODBC UNKNOWN IMPORTED)
242+
set_target_properties(ODBC::ODBC PROPERTIES
243+
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
244+
IMPORTED_LOCATION "${ODBC_LIBRARY}")
245+
else()
246+
add_library(ODBC::ODBC INTERFACE IMPORTED)
247+
set_target_properties(ODBC::ODBC PROPERTIES
248+
IMPORTED_LIBNAME "${ODBC_LIBRARY}")
249+
endif()
250+
set_target_properties(ODBC::ODBC PROPERTIES
251+
INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}")
252+
253+
if(_odbc_required_libs_paths)
254+
set_property(TARGET ODBC::ODBC APPEND PROPERTY
255+
INTERFACE_LINK_LIBRARIES "${_odbc_required_libs_paths}")
256+
endif()
257+
endif()
258+
259+
if(NOT ODBC_DRIVER)
260+
if(ODBC_CONFIG)
261+
execute_process(
262+
COMMAND ${ODBC_CONFIG}
263+
OUTPUT_VARIABLE _output
264+
ERROR_VARIABLE _output
265+
OUTPUT_STRIP_TRAILING_WHITESPACE
266+
ERROR_QUIET
267+
)
268+
if(_output MATCHES "^iODBC")
269+
set(ODBC_DRIVER "iODBC")
270+
elseif(_output MATCHES "^Usage: odbc_config")
271+
set(ODBC_DRIVER "unixODBC")
272+
endif()
273+
unset(_output)
274+
elseif(PC_ODBC_FOUND)
275+
if(PC_ODBC_MODULE_NAME STREQUAL "libiodbc")
276+
set(ODBC_DRIVER "iODBC")
277+
elseif(PC_ODBC_MODULE_NAME STREQUAL "odbc")
278+
set(ODBC_DRIVER "unixODBC")
279+
endif()
280+
elseif(WIN32)
281+
set(ODBC_DRIVER "Windows")
282+
endif()
283+
284+
if(NOT ODBC_DRIVER)
285+
if(ODBC_LIBRARY MATCHES "libiodbc")
286+
set(ODBC_DRIVER "iODBC")
287+
elseif(ODBC_LIBRARY MATCHES "odbc")
288+
set(ODBC_DRIVER "unixODBC")
289+
endif()
290+
endif()
291+
endif()
292+
endif()
293+
294+
unset(_odbc_required_libs_paths)
295+
296+
### Set package metadata ######################################################
297+
if(ODBC_DRIVER STREQUAL "unixODBC" OR ODBC_USE_DRIVER STREQUAL "unixODBC")
298+
set(_odbc_url "https://www.unixodbc.org/")
299+
set(_odbc_description "Open Database Connectivity library for *nix systems")
300+
elseif(ODBC_DRIVER STREQUAL "iODBC" OR ODBC_USE_DRIVER STREQUAL "iODBC")
301+
set(_odbc_url "https://www.iodbc.org")
302+
set(_odbc_description "Independent Open Database Connectivity library")
303+
else()
304+
set(_odbc_url "https://en.wikipedia.org/wiki/Open_Database_Connectivity")
305+
set(_odbc_description "Open Database Connectivity library")
114306
endif()
307+
set_package_properties(
308+
ODBC
309+
PROPERTIES
310+
URL "${_odbc_url}"
311+
DESCRIPTION "${_odbc_description}"
312+
)
313+
unset(_odbc_url)
314+
unset(_odbc_description)

0 commit comments

Comments
 (0)