1# Licensed to the Apache Software Foundation (ASF) under one
2# or more contributor license agreements.  See the NOTICE file
3# distributed with this work for additional information
4# regarding copyright ownership.  The ASF licenses this file
5# to you under the Apache License, Version 2.0 (the
6# "License"); you may not use this file except in compliance
7# with the License.  You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing,
12# software distributed under the License is distributed on an
13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14# KIND, either express or implied.  See the License for the
15# specific language governing permissions and limitations
16# under the License.
17
18# - Find Arrow (arrow/api.h, libarrow.a, libarrow.so)
19# This module defines
20#  ARROW_FOUND, whether Arrow has been found
21#  ARROW_FULL_SO_VERSION, full shared object version of found Arrow "100.0.0"
22#  ARROW_IMPORT_LIB, path to libarrow's import library (Windows only)
23#  ARROW_INCLUDE_DIR, directory containing headers
24#  ARROW_LIBS, deprecated. Use ARROW_LIB_DIR instead
25#  ARROW_LIB_DIR, directory containing Arrow libraries
26#  ARROW_SHARED_IMP_LIB, deprecated. Use ARROW_IMPORT_LIB instead
27#  ARROW_SHARED_LIB, path to libarrow's shared library
28#  ARROW_SO_VERSION, shared object version of found Arrow such as "100"
29#  ARROW_STATIC_LIB, path to libarrow.a
30#  ARROW_VERSION, version of found Arrow
31#  ARROW_VERSION_MAJOR, major version of found Arrow
32#  ARROW_VERSION_MINOR, minor version of found Arrow
33#  ARROW_VERSION_PATCH, patch version of found Arrow
34
35if(DEFINED ARROW_FOUND)
36  return()
37endif()
38
39include(FindPkgConfig)
40include(FindPackageHandleStandardArgs)
41
42if(WIN32 AND NOT MINGW)
43  # This is used to handle builds using e.g. clang in an MSVC setting.
44  set(MSVC_TOOLCHAIN TRUE)
45else()
46  set(MSVC_TOOLCHAIN FALSE)
47endif()
48
49set(ARROW_SEARCH_LIB_PATH_SUFFIXES)
50if(CMAKE_LIBRARY_ARCHITECTURE)
51  list(APPEND ARROW_SEARCH_LIB_PATH_SUFFIXES "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
52endif()
53list(APPEND
54     ARROW_SEARCH_LIB_PATH_SUFFIXES
55     "lib64"
56     "lib32"
57     "lib"
58     "bin")
59set(ARROW_CONFIG_SUFFIXES
60    "_RELEASE"
61    "_RELWITHDEBINFO"
62    "_MINSIZEREL"
63    "_DEBUG"
64    "")
65if(CMAKE_BUILD_TYPE)
66  string(TOUPPER ${CMAKE_BUILD_TYPE} ARROW_CONFIG_SUFFIX_PREFERRED)
67  set(ARROW_CONFIG_SUFFIX_PREFERRED "_${ARROW_CONFIG_SUFFIX_PREFERRED}")
68  list(INSERT ARROW_CONFIG_SUFFIXES 0 "${ARROW_CONFIG_SUFFIX_PREFERRED}")
69endif()
70
71if(NOT DEFINED ARROW_MSVC_STATIC_LIB_SUFFIX)
72  if(MSVC_TOOLCHAIN)
73    set(ARROW_MSVC_STATIC_LIB_SUFFIX "_static")
74  else()
75    set(ARROW_MSVC_STATIC_LIB_SUFFIX "")
76  endif()
77endif()
78
79# Internal function.
80#
81# Set shared library name for ${base_name} to ${output_variable}.
82#
83# Example:
84#   arrow_build_shared_library_name(ARROW_SHARED_LIBRARY_NAME arrow)
85#   # -> ARROW_SHARED_LIBRARY_NAME=libarrow.so on Linux
86#   # -> ARROW_SHARED_LIBRARY_NAME=libarrow.dylib on macOS
87#   # -> ARROW_SHARED_LIBRARY_NAME=arrow.dll with MSVC on Windows
88#   # -> ARROW_SHARED_LIBRARY_NAME=libarrow.dll with MinGW on Windows
89function(arrow_build_shared_library_name output_variable base_name)
90  set(${output_variable}
91      "${CMAKE_SHARED_LIBRARY_PREFIX}${base_name}${CMAKE_SHARED_LIBRARY_SUFFIX}"
92      PARENT_SCOPE)
93endfunction()
94
95# Internal function.
96#
97# Set import library name for ${base_name} to ${output_variable}.
98# This is useful only for MSVC build. Import library is used only
99# with MSVC build.
100#
101# Example:
102#   arrow_build_import_library_name(ARROW_IMPORT_LIBRARY_NAME arrow)
103#   # -> ARROW_IMPORT_LIBRARY_NAME=arrow on Linux (meaningless)
104#   # -> ARROW_IMPORT_LIBRARY_NAME=arrow on macOS (meaningless)
105#   # -> ARROW_IMPORT_LIBRARY_NAME=arrow.lib with MSVC on Windows
106#   # -> ARROW_IMPORT_LIBRARY_NAME=libarrow.dll.a with MinGW on Windows
107function(arrow_build_import_library_name output_variable base_name)
108  set(${output_variable}
109      "${CMAKE_IMPORT_LIBRARY_PREFIX}${base_name}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
110      PARENT_SCOPE)
111endfunction()
112
113# Internal function.
114#
115# Set static library name for ${base_name} to ${output_variable}.
116#
117# Example:
118#   arrow_build_static_library_name(ARROW_STATIC_LIBRARY_NAME arrow)
119#   # -> ARROW_STATIC_LIBRARY_NAME=libarrow.a on Linux
120#   # -> ARROW_STATIC_LIBRARY_NAME=libarrow.a on macOS
121#   # -> ARROW_STATIC_LIBRARY_NAME=arrow.lib with MSVC on Windows
122#   # -> ARROW_STATIC_LIBRARY_NAME=libarrow.dll.a with MinGW on Windows
123function(arrow_build_static_library_name output_variable base_name)
124  set(${output_variable}
125      "${CMAKE_STATIC_LIBRARY_PREFIX}${base_name}${ARROW_MSVC_STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
126      PARENT_SCOPE)
127endfunction()
128
129# Internal function.
130#
131# Set macro value for ${macro_name} in ${header_content} to ${output_variable}.
132#
133# Example:
134#   arrow_extract_macro_value(version_major
135#                             "ARROW_VERSION_MAJOR"
136#                             "#define ARROW_VERSION_MAJOR 1.0.0")
137#   # -> version_major=1.0.0
138function(arrow_extract_macro_value output_variable macro_name header_content)
139  string(REGEX MATCH "#define +${macro_name} +[^\r\n]+" macro_definition
140               "${header_content}")
141  string(REGEX REPLACE "^#define +${macro_name} +(.+)$" "\\1" macro_value
142                       "${macro_definition}")
143  set(${output_variable}
144      "${macro_value}"
145      PARENT_SCOPE)
146endfunction()
147
148# Internal macro only for arrow_find_package.
149#
150# Find package in HOME.
151macro(arrow_find_package_home)
152  find_path(${prefix}_include_dir "${header_path}"
153            PATHS "${home}"
154            PATH_SUFFIXES "include"
155            NO_DEFAULT_PATH)
156  set(include_dir "${${prefix}_include_dir}")
157  set(${prefix}_INCLUDE_DIR
158      "${include_dir}"
159      PARENT_SCOPE)
160
161  if(MSVC_TOOLCHAIN)
162    set(CMAKE_SHARED_LIBRARY_SUFFIXES_ORIGINAL ${CMAKE_FIND_LIBRARY_SUFFIXES})
163    # .dll isn't found by find_library with MSVC because .dll isn't included in
164    # CMAKE_FIND_LIBRARY_SUFFIXES.
165    list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX}")
166  endif()
167  find_library(${prefix}_shared_lib
168               NAMES "${shared_lib_name}"
169               PATHS "${home}"
170               PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES}
171               NO_DEFAULT_PATH)
172  if(MSVC_TOOLCHAIN)
173    set(CMAKE_SHARED_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_ORIGINAL})
174  endif()
175  set(shared_lib "${${prefix}_shared_lib}")
176  set(${prefix}_SHARED_LIB
177      "${shared_lib}"
178      PARENT_SCOPE)
179  if(shared_lib)
180    add_library(${target_shared} SHARED IMPORTED)
181    set_target_properties(${target_shared} PROPERTIES IMPORTED_LOCATION "${shared_lib}")
182    if(include_dir)
183      set_target_properties(${target_shared} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
184                                                        "${include_dir}")
185    endif()
186    find_library(${prefix}_import_lib
187                 NAMES "${import_lib_name}"
188                 PATHS "${home}"
189                 PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES}
190                 NO_DEFAULT_PATH)
191    set(import_lib "${${prefix}_import_lib}")
192    set(${prefix}_IMPORT_LIB
193        "${import_lib}"
194        PARENT_SCOPE)
195    if(import_lib)
196      set_target_properties(${target_shared} PROPERTIES IMPORTED_IMPLIB "${import_lib}")
197    endif()
198  endif()
199
200  find_library(${prefix}_static_lib
201               NAMES "${static_lib_name}"
202               PATHS "${home}"
203               PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES}
204               NO_DEFAULT_PATH)
205  set(static_lib "${${prefix}_static_lib}")
206  set(${prefix}_STATIC_LIB
207      "${static_lib}"
208      PARENT_SCOPE)
209  if(static_lib)
210    add_library(${target_static} STATIC IMPORTED)
211    set_target_properties(${target_static} PROPERTIES IMPORTED_LOCATION "${static_lib}")
212    if(include_dir)
213      set_target_properties(${target_static} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
214                                                        "${include_dir}")
215    endif()
216  endif()
217endmacro()
218
219# Internal macro only for arrow_find_package.
220#
221# Find package by CMake package configuration.
222macro(arrow_find_package_cmake_package_configuration)
223  find_package(${cmake_package_name} CONFIG)
224  if(${cmake_package_name}_FOUND)
225    set(${prefix}_USE_CMAKE_PACKAGE_CONFIG
226        TRUE
227        PARENT_SCOPE)
228    if(TARGET ${target_shared})
229      foreach(suffix ${ARROW_CONFIG_SUFFIXES})
230        get_target_property(shared_lib ${target_shared} IMPORTED_LOCATION${suffix})
231        if(shared_lib)
232          # Remove shared library version:
233          #   libarrow.so.100.0.0 -> libarrow.so
234          # Because ARROW_HOME and pkg-config approaches don't add
235          # shared library version.
236          string(REGEX REPLACE "(${CMAKE_SHARED_LIBRARY_SUFFIX})[.0-9]+$" "\\1"
237                               shared_lib "${shared_lib}")
238          set(${prefix}_SHARED_LIB
239              "${shared_lib}"
240              PARENT_SCOPE)
241          break()
242        endif()
243      endforeach()
244    endif()
245    if(TARGET ${target_static})
246      foreach(suffix ${ARROW_CONFIG_SUFFIXES})
247        get_target_property(static_lib ${target_static} IMPORTED_LOCATION${suffix})
248        if(static_lib)
249          set(${prefix}_STATIC_LIB
250              "${static_lib}"
251              PARENT_SCOPE)
252          break()
253        endif()
254      endforeach()
255    endif()
256  endif()
257endmacro()
258
259# Internal macro only for arrow_find_package.
260#
261# Find package by pkg-config.
262macro(arrow_find_package_pkg_config)
263  pkg_check_modules(${prefix}_PC ${pkg_config_name})
264  if(${prefix}_PC_FOUND)
265    set(${prefix}_USE_PKG_CONFIG
266        TRUE
267        PARENT_SCOPE)
268
269    set(include_dir "${${prefix}_PC_INCLUDEDIR}")
270    set(lib_dir "${${prefix}_PC_LIBDIR}")
271    set(shared_lib_paths "${${prefix}_PC_LINK_LIBRARIES}")
272    # Use the first shared library path as the IMPORTED_LOCATION
273    # for ${target_shared}. This assumes that the first shared library
274    # path is the shared library path for this module.
275    list(GET shared_lib_paths 0 first_shared_lib_path)
276    # Use the rest shared library paths as the INTERFACE_LINK_LIBRARIES
277    # for ${target_shared}. This assumes that the rest shared library
278    # paths are dependency library paths for this module.
279    list(LENGTH shared_lib_paths n_shared_lib_paths)
280    if(n_shared_lib_paths LESS_EQUAL 1)
281      set(rest_shared_lib_paths)
282    else()
283      list(SUBLIST
284           shared_lib_paths
285           1
286           -1
287           rest_shared_lib_paths)
288    endif()
289
290    set(${prefix}_VERSION
291        "${${prefix}_PC_VERSION}"
292        PARENT_SCOPE)
293    set(${prefix}_INCLUDE_DIR
294        "${include_dir}"
295        PARENT_SCOPE)
296    set(${prefix}_SHARED_LIB
297        "${first_shared_lib_path}"
298        PARENT_SCOPE)
299
300    add_library(${target_shared} SHARED IMPORTED)
301    set_target_properties(${target_shared}
302                          PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}"
303                                     INTERFACE_LINK_LIBRARIES "${rest_shared_lib_paths}"
304                                     IMPORTED_LOCATION "${first_shared_lib_path}")
305    get_target_property(shared_lib ${target_shared} IMPORTED_LOCATION)
306
307    find_library(${prefix}_static_lib
308                 NAMES "${static_lib_name}"
309                 PATHS "${lib_dir}"
310                 NO_DEFAULT_PATH)
311    set(static_lib "${${prefix}_static_lib}")
312    set(${prefix}_STATIC_LIB
313        "${static_lib}"
314        PARENT_SCOPE)
315    if(static_lib)
316      add_library(${target_static} STATIC IMPORTED)
317      set_target_properties(${target_static}
318                            PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}"
319                                       IMPORTED_LOCATION "${static_lib}")
320    endif()
321  endif()
322endmacro()
323
324function(arrow_find_package
325         prefix
326         home
327         base_name
328         header_path
329         cmake_package_name
330         pkg_config_name)
331  arrow_build_shared_library_name(shared_lib_name ${base_name})
332  arrow_build_import_library_name(import_lib_name ${base_name})
333  arrow_build_static_library_name(static_lib_name ${base_name})
334
335  set(target_shared ${base_name}_shared)
336  set(target_static ${base_name}_static)
337
338  if(home)
339    arrow_find_package_home()
340    set(${prefix}_FIND_APPROACH
341        "HOME: ${home}"
342        PARENT_SCOPE)
343  else()
344    arrow_find_package_cmake_package_configuration()
345    if(${cmake_package_name}_FOUND)
346      set(${prefix}_FIND_APPROACH
347          "CMake package configuration: ${cmake_package_name}"
348          PARENT_SCOPE)
349    else()
350      arrow_find_package_pkg_config()
351      set(${prefix}_FIND_APPROACH
352          "pkg-config: ${pkg_config_name}"
353          PARENT_SCOPE)
354    endif()
355  endif()
356
357  if(NOT include_dir)
358    if(TARGET ${target_shared})
359      get_target_property(include_dir ${target_shared} INTERFACE_INCLUDE_DIRECTORIES)
360    elseif(TARGET ${target_static})
361      get_target_property(include_dir ${target_static} INTERFACE_INCLUDE_DIRECTORIES)
362    endif()
363  endif()
364  if(include_dir)
365    set(${prefix}_INCLUDE_DIR
366        "${include_dir}"
367        PARENT_SCOPE)
368  endif()
369
370  if(shared_lib)
371    get_filename_component(lib_dir "${shared_lib}" DIRECTORY)
372  elseif(static_lib)
373    get_filename_component(lib_dir "${static_lib}" DIRECTORY)
374  else()
375    set(lib_dir NOTFOUND)
376  endif()
377  set(${prefix}_LIB_DIR
378      "${lib_dir}"
379      PARENT_SCOPE)
380  # For backward compatibility
381  set(${prefix}_LIBS
382      "${lib_dir}"
383      PARENT_SCOPE)
384endfunction()
385
386if(NOT "$ENV{ARROW_HOME}" STREQUAL "")
387  file(TO_CMAKE_PATH "$ENV{ARROW_HOME}" ARROW_HOME)
388endif()
389arrow_find_package(ARROW
390                   "${ARROW_HOME}"
391                   arrow
392                   arrow/api.h
393                   Arrow
394                   arrow)
395
396if(ARROW_HOME)
397  if(ARROW_INCLUDE_DIR)
398    file(READ "${ARROW_INCLUDE_DIR}/arrow/util/config.h" ARROW_CONFIG_H_CONTENT)
399    arrow_extract_macro_value(ARROW_VERSION_MAJOR "ARROW_VERSION_MAJOR"
400                              "${ARROW_CONFIG_H_CONTENT}")
401    arrow_extract_macro_value(ARROW_VERSION_MINOR "ARROW_VERSION_MINOR"
402                              "${ARROW_CONFIG_H_CONTENT}")
403    arrow_extract_macro_value(ARROW_VERSION_PATCH "ARROW_VERSION_PATCH"
404                              "${ARROW_CONFIG_H_CONTENT}")
405    if("${ARROW_VERSION_MAJOR}" STREQUAL ""
406       OR "${ARROW_VERSION_MINOR}" STREQUAL ""
407       OR "${ARROW_VERSION_PATCH}" STREQUAL "")
408      set(ARROW_VERSION "0.0.0")
409    else()
410      set(ARROW_VERSION
411          "${ARROW_VERSION_MAJOR}.${ARROW_VERSION_MINOR}.${ARROW_VERSION_PATCH}")
412    endif()
413
414    arrow_extract_macro_value(ARROW_SO_VERSION_QUOTED "ARROW_SO_VERSION"
415                              "${ARROW_CONFIG_H_CONTENT}")
416    string(REGEX REPLACE "^\"(.+)\"$" "\\1" ARROW_SO_VERSION "${ARROW_SO_VERSION_QUOTED}")
417    arrow_extract_macro_value(ARROW_FULL_SO_VERSION_QUOTED "ARROW_FULL_SO_VERSION"
418                              "${ARROW_CONFIG_H_CONTENT}")
419    string(REGEX REPLACE "^\"(.+)\"$" "\\1" ARROW_FULL_SO_VERSION
420                         "${ARROW_FULL_SO_VERSION_QUOTED}")
421  endif()
422else()
423  if(ARROW_USE_CMAKE_PACKAGE_CONFIG)
424    find_package(Arrow CONFIG)
425  elseif(ARROW_USE_PKG_CONFIG)
426    pkg_get_variable(ARROW_SO_VERSION arrow so_version)
427    pkg_get_variable(ARROW_FULL_SO_VERSION arrow full_so_version)
428  endif()
429endif()
430
431set(ARROW_ABI_VERSION ${ARROW_SO_VERSION})
432
433mark_as_advanced(ARROW_ABI_VERSION
434                 ARROW_CONFIG_SUFFIXES
435                 ARROW_FULL_SO_VERSION
436                 ARROW_IMPORT_LIB
437                 ARROW_INCLUDE_DIR
438                 ARROW_LIBS
439                 ARROW_LIB_DIR
440                 ARROW_SEARCH_LIB_PATH_SUFFIXES
441                 ARROW_SHARED_IMP_LIB
442                 ARROW_SHARED_LIB
443                 ARROW_SO_VERSION
444                 ARROW_STATIC_LIB
445                 ARROW_VERSION
446                 ARROW_VERSION_MAJOR
447                 ARROW_VERSION_MINOR
448                 ARROW_VERSION_PATCH)
449
450find_package_handle_standard_args(
451  Arrow
452  REQUIRED_VARS # The first required variable is shown
453                # in the found message. So this list is
454                # not sorted alphabetically.
455                ARROW_INCLUDE_DIR ARROW_LIB_DIR ARROW_FULL_SO_VERSION ARROW_SO_VERSION
456  VERSION_VAR ARROW_VERSION)
457set(ARROW_FOUND ${Arrow_FOUND})
458
459if(Arrow_FOUND AND NOT Arrow_FIND_QUIETLY)
460  message(STATUS "Arrow version: ${ARROW_VERSION} (${ARROW_FIND_APPROACH})")
461  message(STATUS "Arrow SO and ABI version: ${ARROW_SO_VERSION}")
462  message(STATUS "Arrow full SO version: ${ARROW_FULL_SO_VERSION}")
463  message(STATUS "Found the Arrow core shared library: ${ARROW_SHARED_LIB}")
464  message(STATUS "Found the Arrow core import library: ${ARROW_IMPORT_LIB}")
465  message(STATUS "Found the Arrow core static library: ${ARROW_STATIC_LIB}")
466endif()
467