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:
5FindICU
6-------
7
8.. versionadded:: 3.7
9
10Find the International Components for Unicode (ICU) libraries and
11programs.
12
13This module supports multiple components.
14Components can include any of: ``data``, ``i18n``, ``io``, ``le``,
15``lx``, ``test``, ``tu`` and ``uc``.
16
17Note that on Windows ``data`` is named ``dt`` and ``i18n`` is named
18``in``; any of the names may be used, and the appropriate
19platform-specific library name will be automatically selected.
20
21.. versionadded:: 3.11
22  Added support for static libraries on Windows.
23
24This module reports information about the ICU installation in
25several variables.  General variables::
26
27  ICU_VERSION - ICU release version
28  ICU_FOUND - true if the main programs and libraries were found
29  ICU_LIBRARIES - component libraries to be linked
30  ICU_INCLUDE_DIRS - the directories containing the ICU headers
31
32Imported targets::
33
34  ICU::<C>
35
36Where ``<C>`` is the name of an ICU component, for example
37``ICU::i18n``; ``<C>`` is lower-case.
38
39ICU programs are reported in::
40
41  ICU_GENCNVAL_EXECUTABLE - path to gencnval executable
42  ICU_ICUINFO_EXECUTABLE - path to icuinfo executable
43  ICU_GENBRK_EXECUTABLE - path to genbrk executable
44  ICU_ICU-CONFIG_EXECUTABLE - path to icu-config executable
45  ICU_GENRB_EXECUTABLE - path to genrb executable
46  ICU_GENDICT_EXECUTABLE - path to gendict executable
47  ICU_DERB_EXECUTABLE - path to derb executable
48  ICU_PKGDATA_EXECUTABLE - path to pkgdata executable
49  ICU_UCONV_EXECUTABLE - path to uconv executable
50  ICU_GENCFU_EXECUTABLE - path to gencfu executable
51  ICU_MAKECONV_EXECUTABLE - path to makeconv executable
52  ICU_GENNORM2_EXECUTABLE - path to gennorm2 executable
53  ICU_GENCCODE_EXECUTABLE - path to genccode executable
54  ICU_GENSPREP_EXECUTABLE - path to gensprep executable
55  ICU_ICUPKG_EXECUTABLE - path to icupkg executable
56  ICU_GENCMN_EXECUTABLE - path to gencmn executable
57
58ICU component libraries are reported in::
59
60  ICU_<C>_FOUND - ON if component was found; ``<C>`` is upper-case.
61  ICU_<C>_LIBRARIES - libraries for component; ``<C>`` is upper-case.
62
63ICU datafiles are reported in::
64
65  ICU_MAKEFILE_INC - Makefile.inc
66  ICU_PKGDATA_INC - pkgdata.inc
67
68This module reads hints about search results from::
69
70  ICU_ROOT - the root of the ICU installation
71
72The environment variable ``ICU_ROOT`` may also be used; the
73ICU_ROOT variable takes precedence.
74
75The following cache variables may also be set::
76
77  ICU_<P>_EXECUTABLE - the path to executable <P>; ``<P>`` is upper-case.
78  ICU_INCLUDE_DIR - the directory containing the ICU headers
79  ICU_<C>_LIBRARY - the library for component <C>; ``<C>`` is upper-case.
80
81.. note::
82
83  In most cases none of the above variables will require setting,
84  unless multiple ICU versions are available and a specific version
85  is required.
86
87Other variables one may set to control this module are::
88
89  ICU_DEBUG - Set to ON to enable debug output from FindICU.
90#]=======================================================================]
91
92# Written by Roger Leigh <[email protected]>
93
94set(icu_programs
95  gencnval
96  icuinfo
97  genbrk
98  icu-config
99  genrb
100  gendict
101  derb
102  pkgdata
103  uconv
104  gencfu
105  makeconv
106  gennorm2
107  genccode
108  gensprep
109  icupkg
110  gencmn)
111
112set(icu_data
113  Makefile.inc
114  pkgdata.inc)
115
116# The ICU checks are contained in a function due to the large number
117# of temporary variables needed.
118function(_ICU_FIND)
119  # Set up search paths, taking compiler into account.  Search ICU_ROOT,
120  # with ICU_ROOT in the environment as a fallback if unset.
121  if(ICU_ROOT)
122    list(APPEND icu_roots "${ICU_ROOT}")
123  else()
124    if(NOT "$ENV{ICU_ROOT}" STREQUAL "")
125      file(TO_CMAKE_PATH "$ENV{ICU_ROOT}" NATIVE_PATH)
126      list(APPEND icu_roots "${NATIVE_PATH}")
127      set(ICU_ROOT "${NATIVE_PATH}"
128          CACHE PATH "Location of the ICU installation" FORCE)
129    endif()
130  endif()
131
132  # Find include directory
133  list(APPEND icu_include_suffixes "include")
134  find_path(ICU_INCLUDE_DIR
135            NAMES "unicode/utypes.h"
136            HINTS ${icu_roots}
137            PATH_SUFFIXES ${icu_include_suffixes}
138            DOC "ICU include directory")
139  set(ICU_INCLUDE_DIR "${ICU_INCLUDE_DIR}" PARENT_SCOPE)
140
141  # Get version
142  if(ICU_INCLUDE_DIR AND EXISTS "${ICU_INCLUDE_DIR}/unicode/uvernum.h")
143    file(STRINGS "${ICU_INCLUDE_DIR}/unicode/uvernum.h" icu_header_str
144      REGEX "^#define[\t ]+U_ICU_VERSION[\t ]+\".*\".*")
145
146    string(REGEX REPLACE "^#define[\t ]+U_ICU_VERSION[\t ]+\"([^ \\n]*)\".*"
147      "\\1" icu_version_string "${icu_header_str}")
148    set(ICU_VERSION "${icu_version_string}")
149    set(ICU_VERSION "${icu_version_string}" PARENT_SCOPE)
150    unset(icu_header_str)
151    unset(icu_version_string)
152  endif()
153
154  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
155    # 64-bit binary directory
156    set(_bin64 "bin64")
157    # 64-bit library directory
158    set(_lib64 "lib64")
159  endif()
160
161
162  # Find all ICU programs
163  list(APPEND icu_binary_suffixes "${_bin64}" "bin" "sbin")
164  foreach(program ${icu_programs})
165    string(TOUPPER "${program}" program_upcase)
166    set(cache_var "ICU_${program_upcase}_EXECUTABLE")
167    set(program_var "ICU_${program_upcase}_EXECUTABLE")
168    find_program("${cache_var}"
169      NAMES "${program}"
170      HINTS ${icu_roots}
171      PATH_SUFFIXES ${icu_binary_suffixes}
172      DOC "ICU ${program} executable"
173      NO_PACKAGE_ROOT_PATH
174      )
175    mark_as_advanced("${cache_var}")
176    set("${program_var}" "${${cache_var}}" PARENT_SCOPE)
177  endforeach()
178
179  # Find all ICU libraries
180  list(APPEND icu_library_suffixes "${_lib64}" "lib")
181  set(ICU_REQUIRED_LIBS_FOUND ON)
182  set(static_prefix )
183  # static icu libraries compiled with MSVC have the prefix 's'
184  if(MSVC)
185    set(static_prefix "s")
186  endif()
187  foreach(component ${ICU_FIND_COMPONENTS})
188    string(TOUPPER "${component}" component_upcase)
189    set(component_cache "ICU_${component_upcase}_LIBRARY")
190    set(component_cache_release "${component_cache}_RELEASE")
191    set(component_cache_debug "${component_cache}_DEBUG")
192    set(component_found "ICU_${component_upcase}_FOUND")
193    set(component_found_compat "${component_upcase}_FOUND")
194    set(component_libnames "icu${component}")
195    set(component_debug_libnames "icu${component}d")
196
197    # Special case deliberate library naming mismatches between Unix
198    # and Windows builds
199    unset(component_libnames)
200    unset(component_debug_libnames)
201    list(APPEND component_libnames "icu${component}")
202    list(APPEND component_debug_libnames "icu${component}d")
203    if(component STREQUAL "data")
204      list(APPEND component_libnames "icudt")
205      # Note there is no debug variant at present
206      list(APPEND component_debug_libnames "icudtd")
207    endif()
208    if(component STREQUAL "dt")
209      list(APPEND component_libnames "icudata")
210      # Note there is no debug variant at present
211      list(APPEND component_debug_libnames "icudatad")
212    endif()
213    if(component STREQUAL "i18n")
214      list(APPEND component_libnames "icuin")
215      list(APPEND component_debug_libnames "icuind")
216    endif()
217    if(component STREQUAL "in")
218      list(APPEND component_libnames "icui18n")
219      list(APPEND component_debug_libnames "icui18nd")
220    endif()
221
222    if(static_prefix)
223      unset(static_component_libnames)
224      unset(static_component_debug_libnames)
225      foreach(component_libname ${component_libnames})
226        list(APPEND static_component_libnames
227          ${static_prefix}${component_libname})
228      endforeach()
229      foreach(component_libname ${component_debug_libnames})
230        list(APPEND static_component_debug_libnames
231          ${static_prefix}${component_libname})
232      endforeach()
233      list(APPEND component_libnames ${static_component_libnames})
234      list(APPEND component_debug_libnames ${static_component_debug_libnames})
235    endif()
236    find_library("${component_cache_release}"
237      NAMES ${component_libnames}
238      HINTS ${icu_roots}
239      PATH_SUFFIXES ${icu_library_suffixes}
240      DOC "ICU ${component} library (release)"
241      NO_PACKAGE_ROOT_PATH
242      )
243    find_library("${component_cache_debug}"
244      NAMES ${component_debug_libnames}
245      HINTS ${icu_roots}
246      PATH_SUFFIXES ${icu_library_suffixes}
247      DOC "ICU ${component} library (debug)"
248      NO_PACKAGE_ROOT_PATH
249      )
250    include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
251    select_library_configurations(ICU_${component_upcase})
252    mark_as_advanced("${component_cache_release}" "${component_cache_debug}")
253    if(${component_cache})
254      set("${component_found}" ON)
255      set("${component_found_compat}" ON)
256      list(APPEND ICU_LIBRARY "${${component_cache}}")
257    endif()
258    mark_as_advanced("${component_found}")
259    mark_as_advanced("${component_found_compat}")
260    set("${component_cache}" "${${component_cache}}" PARENT_SCOPE)
261    set("${component_found}" "${${component_found}}" PARENT_SCOPE)
262    set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE)
263    if(component_found OR component_found_compat)
264      if (ICU_FIND_REQUIRED_${component})
265        list(APPEND ICU_LIBS_FOUND "${component} (required)")
266      else()
267        list(APPEND ICU_LIBS_FOUND "${component} (optional)")
268      endif()
269    else()
270      if (ICU_FIND_REQUIRED_${component})
271        set(ICU_REQUIRED_LIBS_FOUND OFF)
272        list(APPEND ICU_LIBS_NOTFOUND "${component} (required)")
273      else()
274        list(APPEND ICU_LIBS_NOTFOUND "${component} (optional)")
275      endif()
276    endif()
277  endforeach()
278  set(_ICU_REQUIRED_LIBS_FOUND "${ICU_REQUIRED_LIBS_FOUND}" PARENT_SCOPE)
279  set(ICU_LIBRARY "${ICU_LIBRARY}" PARENT_SCOPE)
280
281  # Find all ICU data files
282  if(CMAKE_LIBRARY_ARCHITECTURE)
283    list(APPEND icu_data_suffixes
284      "${_lib64}/${CMAKE_LIBRARY_ARCHITECTURE}/icu/${ICU_VERSION}"
285      "lib/${CMAKE_LIBRARY_ARCHITECTURE}/icu/${ICU_VERSION}"
286      "${_lib64}/${CMAKE_LIBRARY_ARCHITECTURE}/icu"
287      "lib/${CMAKE_LIBRARY_ARCHITECTURE}/icu")
288  endif()
289  list(APPEND icu_data_suffixes
290    "${_lib64}/icu/${ICU_VERSION}"
291    "lib/icu/${ICU_VERSION}"
292    "${_lib64}/icu"
293    "lib/icu")
294  foreach(data ${icu_data})
295    string(TOUPPER "${data}" data_upcase)
296    string(REPLACE "." "_" data_upcase "${data_upcase}")
297    set(cache_var "ICU_${data_upcase}")
298    set(data_var "ICU_${data_upcase}")
299    find_file("${cache_var}"
300      NAMES "${data}"
301      HINTS ${icu_roots}
302      PATH_SUFFIXES ${icu_data_suffixes}
303      DOC "ICU ${data} data file")
304    mark_as_advanced("${cache_var}")
305    set("${data_var}" "${${cache_var}}" PARENT_SCOPE)
306  endforeach()
307
308  if(NOT ICU_FIND_QUIETLY)
309    if(ICU_LIBS_FOUND)
310      message(STATUS "Found the following ICU libraries:")
311      foreach(found ${ICU_LIBS_FOUND})
312        message(STATUS "  ${found}")
313      endforeach()
314    endif()
315    if(ICU_LIBS_NOTFOUND)
316      message(STATUS "The following ICU libraries were not found:")
317      foreach(notfound ${ICU_LIBS_NOTFOUND})
318        message(STATUS "  ${notfound}")
319      endforeach()
320    endif()
321  endif()
322
323  if(ICU_DEBUG)
324    message(STATUS "--------FindICU.cmake search debug--------")
325    message(STATUS "ICU binary path search order: ${icu_roots}")
326    message(STATUS "ICU include path search order: ${icu_roots}")
327    message(STATUS "ICU library path search order: ${icu_roots}")
328    message(STATUS "----------------")
329  endif()
330endfunction()
331
332_ICU_FIND()
333
334include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
335FIND_PACKAGE_HANDLE_STANDARD_ARGS(ICU
336                                  FOUND_VAR ICU_FOUND
337                                  REQUIRED_VARS ICU_INCLUDE_DIR
338                                                ICU_LIBRARY
339                                                _ICU_REQUIRED_LIBS_FOUND
340                                  VERSION_VAR ICU_VERSION
341                                  FAIL_MESSAGE "Failed to find all ICU components")
342
343unset(_ICU_REQUIRED_LIBS_FOUND)
344
345if(ICU_FOUND)
346  set(ICU_INCLUDE_DIRS "${ICU_INCLUDE_DIR}")
347  set(ICU_LIBRARIES "${ICU_LIBRARY}")
348  foreach(_ICU_component ${ICU_FIND_COMPONENTS})
349    string(TOUPPER "${_ICU_component}" _ICU_component_upcase)
350    set(_ICU_component_cache "ICU_${_ICU_component_upcase}_LIBRARY")
351    set(_ICU_component_cache_release "ICU_${_ICU_component_upcase}_LIBRARY_RELEASE")
352    set(_ICU_component_cache_debug "ICU_${_ICU_component_upcase}_LIBRARY_DEBUG")
353    set(_ICU_component_lib "ICU_${_ICU_component_upcase}_LIBRARIES")
354    set(_ICU_component_found "ICU_${_ICU_component_upcase}_FOUND")
355    set(_ICU_imported_target "ICU::${_ICU_component}")
356    if(${_ICU_component_found})
357      set("${_ICU_component_lib}" "${${_ICU_component_cache}}")
358      if(NOT TARGET ${_ICU_imported_target})
359        add_library(${_ICU_imported_target} UNKNOWN IMPORTED)
360        if(ICU_INCLUDE_DIR)
361          set_target_properties(${_ICU_imported_target} PROPERTIES
362            INTERFACE_INCLUDE_DIRECTORIES "${ICU_INCLUDE_DIR}")
363        endif()
364        if(EXISTS "${${_ICU_component_cache}}")
365          set_target_properties(${_ICU_imported_target} PROPERTIES
366            IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
367            IMPORTED_LOCATION "${${_ICU_component_cache}}")
368        endif()
369        if(EXISTS "${${_ICU_component_cache_release}}")
370          set_property(TARGET ${_ICU_imported_target} APPEND PROPERTY
371            IMPORTED_CONFIGURATIONS RELEASE)
372          set_target_properties(${_ICU_imported_target} PROPERTIES
373            IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
374            IMPORTED_LOCATION_RELEASE "${${_ICU_component_cache_release}}")
375        endif()
376        if(EXISTS "${${_ICU_component_cache_debug}}")
377          set_property(TARGET ${_ICU_imported_target} APPEND PROPERTY
378            IMPORTED_CONFIGURATIONS DEBUG)
379          set_target_properties(${_ICU_imported_target} PROPERTIES
380            IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
381            IMPORTED_LOCATION_DEBUG "${${_ICU_component_cache_debug}}")
382        endif()
383        if(CMAKE_DL_LIBS AND _ICU_component STREQUAL "uc")
384          set_target_properties(${_ICU_imported_target} PROPERTIES
385            INTERFACE_LINK_LIBRARIES "${CMAKE_DL_LIBS}")
386        endif()
387      endif()
388    endif()
389    unset(_ICU_component_upcase)
390    unset(_ICU_component_cache)
391    unset(_ICU_component_lib)
392    unset(_ICU_component_found)
393    unset(_ICU_imported_target)
394  endforeach()
395endif()
396
397if(ICU_DEBUG)
398  message(STATUS "--------FindICU.cmake results debug--------")
399  message(STATUS "ICU found: ${ICU_FOUND}")
400  message(STATUS "ICU_VERSION number: ${ICU_VERSION}")
401  message(STATUS "ICU_ROOT directory: ${ICU_ROOT}")
402  message(STATUS "ICU_INCLUDE_DIR directory: ${ICU_INCLUDE_DIR}")
403  message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}")
404
405  foreach(program IN LISTS icu_programs)
406    string(TOUPPER "${program}" program_upcase)
407    set(program_lib "ICU_${program_upcase}_EXECUTABLE")
408    message(STATUS "${program} program: ${program_lib}=${${program_lib}}")
409    unset(program_upcase)
410    unset(program_lib)
411  endforeach()
412
413  foreach(data IN LISTS icu_data)
414    string(TOUPPER "${data}" data_upcase)
415    string(REPLACE "." "_" data_upcase "${data_upcase}")
416    set(data_lib "ICU_${data_upcase}")
417    message(STATUS "${data} data: ${data_lib}=${${data_lib}}")
418    unset(data_upcase)
419    unset(data_lib)
420  endforeach()
421
422  foreach(component IN LISTS ICU_FIND_COMPONENTS)
423    string(TOUPPER "${component}" component_upcase)
424    set(component_lib "ICU_${component_upcase}_LIBRARIES")
425    set(component_found "ICU_${component_upcase}_FOUND")
426    set(component_found_compat "${component_upcase}_FOUND")
427    message(STATUS "${component} library found: ${component_found}=${${component_found}}")
428    message(STATUS "${component} library found (compat name): ${component_found_compat}=${${component_found_compat}}")
429    message(STATUS "${component} library: ${component_lib}=${${component_lib}}")
430    unset(component_upcase)
431    unset(component_lib)
432    unset(component_found)
433    unset(component_found_compat)
434  endforeach()
435  message(STATUS "----------------")
436endif()
437
438unset(icu_programs)
439