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:
5CheckTypeSize
6-------------
7
8Check sizeof a type
9
10.. command:: CHECK_TYPE_SIZE
11
12  .. code-block:: cmake
13
14    CHECK_TYPE_SIZE(TYPE VARIABLE [BUILTIN_TYPES_ONLY]
15                                  [LANGUAGE <language>])
16
17  Check if the type exists and determine its size.  On return,
18  ``HAVE_${VARIABLE}`` holds the existence of the type, and ``${VARIABLE}``
19  holds one of the following:
20
21  ::
22
23     <size> = type has non-zero size <size>
24     "0"    = type has arch-dependent size (see below)
25     ""     = type does not exist
26
27  Both ``HAVE_${VARIABLE}`` and ``${VARIABLE}`` will be created as internal
28  cache variables.
29
30  Furthermore, the variable ``${VARIABLE}_CODE`` holds C preprocessor code
31  to define the macro ``${VARIABLE}`` to the size of the type, or leave
32  the macro undefined if the type does not exist.
33
34  The variable ``${VARIABLE}`` may be ``0`` when
35  :variable:`CMAKE_OSX_ARCHITECTURES` has multiple architectures for building
36  OS X universal binaries.  This indicates that the type size varies across
37  architectures.  In this case ``${VARIABLE}_CODE`` contains C preprocessor
38  tests mapping from each architecture macro to the corresponding type size.
39  The list of architecture macros is stored in ``${VARIABLE}_KEYS``, and the
40  value for each key is stored in ``${VARIABLE}-${KEY}``.
41
42  If the ``BUILTIN_TYPES_ONLY`` option is not given, the macro checks for
43  headers ``<sys/types.h>``, ``<stdint.h>``, and ``<stddef.h>``, and saves
44  results in ``HAVE_SYS_TYPES_H``, ``HAVE_STDINT_H``, and ``HAVE_STDDEF_H``.
45  The type size check automatically includes the available headers, thus
46  supporting checks of types defined in the headers.
47
48  If ``LANGUAGE`` is set, the specified compiler will be used to perform the
49  check. Acceptable values are ``C`` and ``CXX``.
50
51Despite the name of the macro you may use it to check the size of more
52complex expressions, too.  To check e.g.  for the size of a struct
53member you can do something like this:
54
55.. code-block:: cmake
56
57  check_type_size("((struct something*)0)->member" SIZEOF_MEMBER)
58
59
60The following variables may be set before calling this macro to modify
61the way the check is run:
62
63``CMAKE_REQUIRED_FLAGS``
64  string of compile command line flags.
65``CMAKE_REQUIRED_DEFINITIONS``
66  list of macros to define (-DFOO=bar).
67``CMAKE_REQUIRED_INCLUDES``
68  list of include directories.
69``CMAKE_REQUIRED_LINK_OPTIONS``
70  .. versionadded:: 3.14
71    list of options to pass to link command.
72``CMAKE_REQUIRED_LIBRARIES``
73  list of libraries to link.
74``CMAKE_REQUIRED_QUIET``
75  .. versionadded:: 3.1
76    execute quietly without messages.
77``CMAKE_EXTRA_INCLUDE_FILES``
78  list of extra headers to include.
79#]=======================================================================]
80
81include(CheckIncludeFile)
82include(CheckIncludeFileCXX)
83
84get_filename_component(__check_type_size_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
85
86include_guard(GLOBAL)
87
88cmake_policy(PUSH)
89cmake_policy(SET CMP0054 NEW)
90
91#-----------------------------------------------------------------------------
92# Helper function.  DO NOT CALL DIRECTLY.
93function(__check_type_size_impl type var map builtin language)
94  if(NOT CMAKE_REQUIRED_QUIET)
95    message(CHECK_START "Check size of ${type}")
96  endif()
97
98  # Perform language check
99  if(language STREQUAL "C")
100    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.c)
101  elseif(language STREQUAL "CXX")
102    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.cpp)
103  else()
104    message(FATAL_ERROR "Unknown language:\n  ${language}\nSupported languages: C, CXX.\n")
105  endif()
106
107  # Include header files.
108  set(headers)
109  if(builtin)
110    if(language STREQUAL "CXX" AND type MATCHES "^std::")
111      if(HAVE_SYS_TYPES_H)
112        string(APPEND headers "#include <sys/types.h>\n")
113      endif()
114      if(HAVE_CSTDINT)
115        string(APPEND headers "#include <cstdint>\n")
116      endif()
117      if(HAVE_CSTDDEF)
118        string(APPEND headers "#include <cstddef>\n")
119      endif()
120    else()
121      if(HAVE_SYS_TYPES_H)
122        string(APPEND headers "#include <sys/types.h>\n")
123      endif()
124      if(HAVE_STDINT_H)
125        string(APPEND headers "#include <stdint.h>\n")
126      endif()
127      if(HAVE_STDDEF_H)
128        string(APPEND headers "#include <stddef.h>\n")
129      endif()
130    endif()
131  endif()
132  foreach(h ${CMAKE_EXTRA_INCLUDE_FILES})
133    string(APPEND headers "#include \"${h}\"\n")
134  endforeach()
135
136  # Perform the check.
137  set(bin ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.bin)
138  configure_file(${__check_type_size_dir}/CheckTypeSize.c.in ${src} @ONLY)
139  try_compile(HAVE_${var} ${CMAKE_BINARY_DIR} ${src}
140    COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
141    LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}
142    LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}
143    CMAKE_FLAGS
144      "-DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS}"
145      "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}"
146    OUTPUT_VARIABLE output
147    COPY_FILE ${bin}
148    )
149
150  if(HAVE_${var})
151    # The check compiled.  Load information from the binary.
152    file(STRINGS ${bin} strings LIMIT_COUNT 10 REGEX "INFO:size")
153
154    # Parse the information strings.
155    set(regex_size ".*INFO:size\\[0*([^]]*)\\].*")
156    set(regex_key " key\\[([^]]*)\\]")
157    set(keys)
158    set(code)
159    set(mismatch)
160    set(first 1)
161    foreach(info ${strings})
162      if("${info}" MATCHES "${regex_size}")
163        # Get the type size.
164        set(size "${CMAKE_MATCH_1}")
165        if(first)
166          set(${var} ${size})
167        elseif(NOT "${size}" STREQUAL "${${var}}")
168          set(mismatch 1)
169        endif()
170        set(first 0)
171
172        # Get the architecture map key.
173        string(REGEX MATCH   "${regex_key}"       key "${info}")
174        string(REGEX REPLACE "${regex_key}" "\\1" key "${key}")
175        if(key)
176          string(APPEND code "\nset(${var}-${key} \"${size}\")")
177          list(APPEND keys ${key})
178        endif()
179      endif()
180    endforeach()
181
182    # Update the architecture-to-size map.
183    if(mismatch AND keys)
184      configure_file(${__check_type_size_dir}/CheckTypeSizeMap.cmake.in ${map} @ONLY)
185      set(${var} 0)
186    else()
187      file(REMOVE ${map})
188    endif()
189
190    if(mismatch AND NOT keys)
191      message(SEND_ERROR "CHECK_TYPE_SIZE found different results, consider setting CMAKE_OSX_ARCHITECTURES or CMAKE_TRY_COMPILE_OSX_ARCHITECTURES to one or no architecture !")
192    endif()
193
194    if(NOT CMAKE_REQUIRED_QUIET)
195      message(CHECK_PASS "done")
196    endif()
197    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
198      "Determining size of ${type} passed with the following output:\n${output}\n\n")
199    set(${var} "${${var}}" CACHE INTERNAL "CHECK_TYPE_SIZE: sizeof(${type})")
200  else()
201    # The check failed to compile.
202    if(NOT CMAKE_REQUIRED_QUIET)
203      message(CHECK_FAIL "failed")
204    endif()
205    file(READ ${src} content)
206    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
207      "Determining size of ${type} failed with the following output:\n${output}\n${src}:\n${content}\n\n")
208    set(${var} "" CACHE INTERNAL "CHECK_TYPE_SIZE: ${type} unknown")
209    file(REMOVE ${map})
210  endif()
211endfunction()
212
213#-----------------------------------------------------------------------------
214macro(CHECK_TYPE_SIZE TYPE VARIABLE)
215  # parse arguments
216  unset(doing)
217  foreach(arg ${ARGN})
218    if("x${arg}" STREQUAL "xBUILTIN_TYPES_ONLY")
219      set(_CHECK_TYPE_SIZE_${arg} 1)
220      unset(doing)
221    elseif("x${arg}" STREQUAL "xLANGUAGE") # change to MATCHES for more keys
222      set(doing "${arg}")
223      set(_CHECK_TYPE_SIZE_${doing} "")
224    elseif("x${doing}" STREQUAL "xLANGUAGE")
225      set(_CHECK_TYPE_SIZE_${doing} "${arg}")
226      unset(doing)
227    else()
228      message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
229    endif()
230  endforeach()
231  if("x${doing}" MATCHES "^x(LANGUAGE)$")
232    message(FATAL_ERROR "Missing argument:\n  ${doing} arguments requires a value\n")
233  endif()
234  if(DEFINED _CHECK_TYPE_SIZE_LANGUAGE)
235    if(NOT "x${_CHECK_TYPE_SIZE_LANGUAGE}" MATCHES "^x(C|CXX)$")
236      message(FATAL_ERROR "Unknown language:\n  ${_CHECK_TYPE_SIZE_LANGUAGE}.\nSupported languages: C, CXX.\n")
237    endif()
238    set(_language ${_CHECK_TYPE_SIZE_LANGUAGE})
239  else()
240    set(_language C)
241  endif()
242
243  # Optionally check for standard headers.
244  if(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
245    set(_builtin 0)
246  else()
247    set(_builtin 1)
248    if(_language STREQUAL "C")
249      check_include_file(sys/types.h HAVE_SYS_TYPES_H)
250      check_include_file(stdint.h HAVE_STDINT_H)
251      check_include_file(stddef.h HAVE_STDDEF_H)
252    elseif(_language STREQUAL "CXX")
253      check_include_file_cxx(sys/types.h HAVE_SYS_TYPES_H)
254      if("${TYPE}" MATCHES "^std::")
255        check_include_file_cxx(cstdint HAVE_CSTDINT)
256        check_include_file_cxx(cstddef HAVE_CSTDDEF)
257      else()
258        check_include_file_cxx(stdint.h HAVE_STDINT_H)
259        check_include_file_cxx(stddef.h HAVE_STDDEF_H)
260      endif()
261    endif()
262  endif()
263  unset(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
264  unset(_CHECK_TYPE_SIZE_LANGUAGE)
265
266  # Compute or load the size or size map.
267  set(${VARIABLE}_KEYS)
268  set(_map_file ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${VARIABLE}.cmake)
269  if(NOT DEFINED HAVE_${VARIABLE})
270    __check_type_size_impl(${TYPE} ${VARIABLE} ${_map_file} ${_builtin} ${_language})
271  endif()
272  include(${_map_file} OPTIONAL)
273  set(_map_file)
274  set(_builtin)
275
276  # Create preprocessor code.
277  if(${VARIABLE}_KEYS)
278    set(${VARIABLE}_CODE)
279    set(_if if)
280    foreach(key ${${VARIABLE}_KEYS})
281      string(APPEND ${VARIABLE}_CODE "#${_if} defined(${key})\n# define ${VARIABLE} ${${VARIABLE}-${key}}\n")
282      set(_if elif)
283    endforeach()
284    string(APPEND ${VARIABLE}_CODE "#else\n# error ${VARIABLE} unknown\n#endif")
285    set(_if)
286  elseif(${VARIABLE})
287    set(${VARIABLE}_CODE "#define ${VARIABLE} ${${VARIABLE}}")
288  else()
289    set(${VARIABLE}_CODE "/* #undef ${VARIABLE} */")
290  endif()
291endmacro()
292
293#-----------------------------------------------------------------------------
294cmake_policy(POP)
295