1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5FindODBC
6--------
7
8.. versionadded:: 3.12
9
10Find an Open Database Connectivity (ODBC) include directory and library.
11
12On Windows, when building with Visual Studio, this module assumes the ODBC
13library is provided by the available Windows SDK.
14
15On Unix, this module allows to search for ODBC library provided by
16unixODBC or iODBC implementations of ODBC API.
17This module reads hint about location of the config program:
18
19.. variable:: ODBC_CONFIG
20
21  Location of odbc_config or iodbc-config program
22
23Otherwise, this module tries to find the config program,
24first from unixODBC, then from iODBC.
25If no config program found, this module searches for ODBC header
26and library in list of known locations.
27
28Imported targets
29^^^^^^^^^^^^^^^^
30
31This module defines the following :prop_tgt:`IMPORTED` targets:
32
33.. variable:: ODBC::ODBC
34
35  Imported target for using the ODBC library, if found.
36
37Result variables
38^^^^^^^^^^^^^^^^
39
40.. variable:: ODBC_FOUND
41
42  Set to true if ODBC library found, otherwise false or undefined.
43
44.. variable:: ODBC_INCLUDE_DIRS
45
46  Paths to include directories listed in one variable for use by ODBC client.
47  May be empty on Windows, where the include directory corresponding to the
48  expected Windows SDK is already available in the compilation environment.
49
50.. variable:: ODBC_LIBRARIES
51
52  Paths to libraries to linked against to use ODBC.
53  May just a library name on Windows, where the library directory corresponding
54  to the expected Windows SDK is already available in the compilation environment.
55
56.. variable:: ODBC_CONFIG
57
58  Path to unixODBC or iODBC config program, if found or specified.
59
60Cache variables
61^^^^^^^^^^^^^^^
62
63For users who wish to edit and control the module behavior, this module
64reads hints about search locations from the following variables:
65
66.. variable:: ODBC_INCLUDE_DIR
67
68  Path to ODBC include directory with ``sql.h`` header.
69
70.. variable:: ODBC_LIBRARY
71
72  Path to ODBC library to be linked.
73
74These variables should not be used directly by project code.
75
76Limitations
77^^^^^^^^^^^
78
79On Windows, this module does not search for iODBC.
80On Unix, there is no way to prefer unixODBC over iODBC, or vice versa,
81other than providing the config program location using the ``ODBC_CONFIG``.
82This module does not allow to search for a specific ODBC driver.
83
84#]=======================================================================]
85
86# Define lists used internally
87set(_odbc_include_paths)
88set(_odbc_lib_paths)
89set(_odbc_lib_names)
90set(_odbc_required_libs_names)
91
92### Try Windows Kits ##########################################################
93if(WIN32)
94  # List names of ODBC libraries on Windows
95  if(NOT MINGW)
96    set(ODBC_LIBRARY odbc32.lib)
97  else()
98    set(ODBC_LIBRARY libodbc32.a)
99  endif()
100  set(_odbc_lib_names odbc32;)
101
102  # List additional libraries required to use ODBC library
103  if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
104    set(_odbc_required_libs_names odbccp32;ws2_32)
105  elseif(MINGW)
106    set(_odbc_required_libs_names odbccp32)
107  endif()
108endif()
109
110### Try unixODBC or iODBC config program ######################################
111if (UNIX)
112  find_program(ODBC_CONFIG
113    NAMES odbc_config iodbc-config
114    DOC "Path to unixODBC or iODBC config program")
115  mark_as_advanced(ODBC_CONFIG)
116endif()
117
118if (UNIX AND ODBC_CONFIG)
119  # unixODBC and iODBC accept unified command line options
120  execute_process(COMMAND ${ODBC_CONFIG} --cflags
121    OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE)
122  execute_process(COMMAND ${ODBC_CONFIG} --libs
123    OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE)
124
125  # Collect paths of include directories from CFLAGS
126  separate_arguments(_cflags NATIVE_COMMAND "${_cflags}")
127  foreach(arg IN LISTS _cflags)
128    if("${arg}" MATCHES "^-I(.*)$")
129      list(APPEND _odbc_include_paths "${CMAKE_MATCH_1}")
130    endif()
131  endforeach()
132  unset(_cflags)
133
134  # Collect paths of library names and directories from LIBS
135  separate_arguments(_libs NATIVE_COMMAND "${_libs}")
136  foreach(arg IN LISTS _libs)
137    if("${arg}" MATCHES "^-L(.*)$")
138      list(APPEND _odbc_lib_paths "${CMAKE_MATCH_1}")
139    elseif("${arg}" MATCHES "^-l(.*)$")
140      set(_lib_name ${CMAKE_MATCH_1})
141      string(REGEX MATCH "odbc" _is_odbc ${_lib_name})
142      if(_is_odbc)
143        list(APPEND _odbc_lib_names ${_lib_name})
144      else()
145        list(APPEND _odbc_required_libs_names ${_lib_name})
146      endif()
147      unset(_lib_name)
148    endif()
149  endforeach()
150  unset(_libs)
151endif()
152
153### Try unixODBC or iODBC in include/lib filesystems ##########################
154if (UNIX AND NOT ODBC_CONFIG)
155  # List names of both ODBC libraries, unixODBC and iODBC
156  set(_odbc_lib_names odbc;iodbc;unixodbc;)
157endif()
158
159### Find include directories ##################################################
160find_path(ODBC_INCLUDE_DIR
161  NAMES sql.h
162  PATHS ${_odbc_include_paths})
163
164if(NOT ODBC_INCLUDE_DIR AND WIN32)
165  set(ODBC_INCLUDE_DIR "")
166endif()
167
168### Find libraries ############################################################
169if(NOT ODBC_LIBRARY)
170  find_library(ODBC_LIBRARY
171    NAMES ${_odbc_lib_names}
172    PATHS ${_odbc_lib_paths}
173    PATH_SUFFIXES odbc)
174
175  foreach(_lib IN LISTS _odbc_required_libs_names)
176    find_library(_lib_path
177      NAMES ${_lib}
178      PATHS ${_odbc_lib_paths} # system parths or collected from ODBC_CONFIG
179      PATH_SUFFIXES odbc)
180    if(_lib_path)
181      list(APPEND _odbc_required_libs_paths ${_lib_path})
182    endif()
183    unset(_lib_path CACHE)
184  endforeach()
185endif()
186
187# Unset internal lists as no longer used
188unset(_odbc_include_paths)
189unset(_odbc_lib_paths)
190unset(_odbc_lib_names)
191unset(_odbc_required_libs_names)
192
193### Set result variables ######################################################
194set(_odbc_required_vars ODBC_LIBRARY)
195if(NOT WIN32)
196  list(APPEND _odbc_required_vars ODBC_INCLUDE_DIR)
197endif()
198
199include(FindPackageHandleStandardArgs)
200find_package_handle_standard_args(ODBC DEFAULT_MSG ${_odbc_required_vars})
201
202unset(_odbc_required_vars)
203
204mark_as_advanced(ODBC_LIBRARY ODBC_INCLUDE_DIR)
205
206set(ODBC_INCLUDE_DIRS ${ODBC_INCLUDE_DIR})
207list(APPEND ODBC_LIBRARIES ${ODBC_LIBRARY})
208list(APPEND ODBC_LIBRARIES ${_odbc_required_libs_paths})
209
210### Import targets ############################################################
211if(ODBC_FOUND)
212  if(NOT TARGET ODBC::ODBC)
213    if(IS_ABSOLUTE "${ODBC_LIBRARY}")
214      add_library(ODBC::ODBC UNKNOWN IMPORTED)
215      set_target_properties(ODBC::ODBC PROPERTIES
216        IMPORTED_LINK_INTERFACE_LANGUAGES "C"
217        IMPORTED_LOCATION "${ODBC_LIBRARY}")
218    else()
219      add_library(ODBC::ODBC INTERFACE IMPORTED)
220      set_target_properties(ODBC::ODBC PROPERTIES
221        IMPORTED_LIBNAME "${ODBC_LIBRARY}")
222    endif()
223    set_target_properties(ODBC::ODBC PROPERTIES
224      INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}")
225
226    if(_odbc_required_libs_paths)
227      set_property(TARGET ODBC::ODBC APPEND PROPERTY
228        INTERFACE_LINK_LIBRARIES "${_odbc_required_libs_paths}")
229    endif()
230  endif()
231endif()
232
233unset(_odbc_required_libs_paths)
234