CheckTypeSize.cmake 9.44 KB
Newer Older
1 2 3
# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.

4 5 6 7 8 9
#[=======================================================================[.rst:
CheckTypeSize
-------------

Check sizeof a type

10
.. command:: CHECK_TYPE_SIZE
11

12
  .. code-block:: cmake
13

14 15
    CHECK_TYPE_SIZE(TYPE VARIABLE [BUILTIN_TYPES_ONLY]
                                  [LANGUAGE <language>])
16

17 18 19
  Check if the type exists and determine its size.  On return,
  ``HAVE_${VARIABLE}`` holds the existence of the type, and ``${VARIABLE}``
  holds one of the following:
20

21
  ::
22

23 24 25
     <size> = type has non-zero size <size>
     "0"    = type has arch-dependent size (see below)
     ""     = type does not exist
26

27 28
  Both ``HAVE_${VARIABLE}`` and ``${VARIABLE}`` will be created as internal
  cache variables.
29

30 31 32
  Furthermore, the variable ``${VARIABLE}_CODE`` holds C preprocessor code
  to define the macro ``${VARIABLE}`` to the size of the type, or leave
  the macro undefined if the type does not exist.
33

34 35 36 37 38 39 40
  The variable ``${VARIABLE}`` may be ``0`` when
  :variable:`CMAKE_OSX_ARCHITECTURES` has multiple architectures for building
  OS X universal binaries.  This indicates that the type size varies across
  architectures.  In this case ``${VARIABLE}_CODE`` contains C preprocessor
  tests mapping from each architecture macro to the corresponding type size.
  The list of architecture macros is stored in ``${VARIABLE}_KEYS``, and the
  value for each key is stored in ``${VARIABLE}-${KEY}``.
41

42 43 44 45 46 47 48 49
  If the ``BUILTIN_TYPES_ONLY`` option is not given, the macro checks for
  headers ``<sys/types.h>``, ``<stdint.h>``, and ``<stddef.h>``, and saves
  results in ``HAVE_SYS_TYPES_H``, ``HAVE_STDINT_H``, and ``HAVE_STDDEF_H``.
  The type size check automatically includes the available headers, thus
  supporting checks of types defined in the headers.

  If ``LANGUAGE`` is set, the specified compiler will be used to perform the
  check. Acceptable values are ``C`` and ``CXX``.
50 51 52 53 54

Despite the name of the macro you may use it to check the size of more
complex expressions, too.  To check e.g.  for the size of a struct
member you can do something like this:

55
.. code-block:: cmake
56 57 58 59 60 61 62 63 64 65 66 67 68

  check_type_size("((struct something*)0)->member" SIZEOF_MEMBER)



The following variables may be set before calling this macro to modify
the way the check is run:

::

  CMAKE_REQUIRED_FLAGS = string of compile command line flags
  CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
  CMAKE_REQUIRED_INCLUDES = list of include directories
69
  CMAKE_REQUIRED_LINK_OPTIONS  = list of options to pass to link command
70 71 72 73
  CMAKE_REQUIRED_LIBRARIES = list of libraries to link
  CMAKE_REQUIRED_QUIET = execute quietly without messages
  CMAKE_EXTRA_INCLUDE_FILES = list of extra headers to include
#]=======================================================================]
74 75

include(CheckIncludeFile)
76
include(CheckIncludeFileCXX)
77

Cristian Adam's avatar
Cristian Adam committed
78 79 80 81
get_filename_component(__check_type_size_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)

include_guard(GLOBAL)

82
cmake_policy(PUSH)
83
cmake_policy(SET CMP0054 NEW)
84 85 86

#-----------------------------------------------------------------------------
# Helper function.  DO NOT CALL DIRECTLY.
87
function(__check_type_size_impl type var map builtin language)
88 89 90
  if(NOT CMAKE_REQUIRED_QUIET)
    message(STATUS "Check size of ${type}")
  endif()
91 92 93 94 95

  # Include header files.
  set(headers)
  if(builtin)
    if(HAVE_SYS_TYPES_H)
96
      string(APPEND headers "#include <sys/types.h>\n")
97 98
    endif()
    if(HAVE_STDINT_H)
99
      string(APPEND headers "#include <stdint.h>\n")
100 101
    endif()
    if(HAVE_STDDEF_H)
102
      string(APPEND headers "#include <stddef.h>\n")
103 104 105
    endif()
  endif()
  foreach(h ${CMAKE_EXTRA_INCLUDE_FILES})
106
    string(APPEND headers "#include \"${h}\"\n")
107 108 109
  endforeach()

  # Perform the check.
110

111
  if(language STREQUAL "C")
112
    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.c)
113
  elseif(language STREQUAL "CXX")
114 115 116 117
    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.cpp)
  else()
    message(FATAL_ERROR "Unknown language:\n  ${language}\nSupported languages: C, CXX.\n")
  endif()
118 119 120 121
  set(bin ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.bin)
  configure_file(${__check_type_size_dir}/CheckTypeSize.c.in ${src} @ONLY)
  try_compile(HAVE_${var} ${CMAKE_BINARY_DIR} ${src}
    COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
122
    LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}
123
    LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    CMAKE_FLAGS
      "-DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS}"
      "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}"
    OUTPUT_VARIABLE output
    COPY_FILE ${bin}
    )

  if(HAVE_${var})
    # The check compiled.  Load information from the binary.
    file(STRINGS ${bin} strings LIMIT_COUNT 10 REGEX "INFO:size")

    # Parse the information strings.
    set(regex_size ".*INFO:size\\[0*([^]]*)\\].*")
    set(regex_key " key\\[([^]]*)\\]")
    set(keys)
    set(code)
    set(mismatch)
    set(first 1)
    foreach(info ${strings})
      if("${info}" MATCHES "${regex_size}")
        # Get the type size.
145
        set(size "${CMAKE_MATCH_1}")
146 147 148 149 150 151 152 153 154 155 156
        if(first)
          set(${var} ${size})
        elseif(NOT "${size}" STREQUAL "${${var}}")
          set(mismatch 1)
        endif()
        set(first 0)

        # Get the architecture map key.
        string(REGEX MATCH   "${regex_key}"       key "${info}")
        string(REGEX REPLACE "${regex_key}" "\\1" key "${key}")
        if(key)
157
          string(APPEND code "\nset(${var}-${key} \"${size}\")")
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
          list(APPEND keys ${key})
        endif()
      endif()
    endforeach()

    # Update the architecture-to-size map.
    if(mismatch AND keys)
      configure_file(${__check_type_size_dir}/CheckTypeSizeMap.cmake.in ${map} @ONLY)
      set(${var} 0)
    else()
      file(REMOVE ${map})
    endif()

    if(mismatch AND NOT keys)
      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 !")
    endif()

175 176 177
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Check size of ${type} - done")
    endif()
178 179 180
    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
      "Determining size of ${type} passed with the following output:\n${output}\n\n")
    set(${var} "${${var}}" CACHE INTERNAL "CHECK_TYPE_SIZE: sizeof(${type})")
181
  else()
182
    # The check failed to compile.
183 184 185
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Check size of ${type} - failed")
    endif()
186 187 188 189 190
    file(READ ${src} content)
    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
      "Determining size of ${type} failed with the following output:\n${output}\n${src}:\n${content}\n\n")
    set(${var} "" CACHE INTERNAL "CHECK_TYPE_SIZE: ${type} unknown")
    file(REMOVE ${map})
191
  endif()
192 193 194 195
endfunction()

#-----------------------------------------------------------------------------
macro(CHECK_TYPE_SIZE TYPE VARIABLE)
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  # parse arguments
  unset(doing)
  foreach(arg ${ARGN})
    if("x${arg}" STREQUAL "xBUILTIN_TYPES_ONLY")
      set(_CHECK_TYPE_SIZE_${arg} 1)
      unset(doing)
    elseif("x${arg}" STREQUAL "xLANGUAGE") # change to MATCHES for more keys
      set(doing "${arg}")
      set(_CHECK_TYPE_SIZE_${doing} "")
    elseif("x${doing}" STREQUAL "xLANGUAGE")
      set(_CHECK_TYPE_SIZE_${doing} "${arg}")
      unset(doing)
    else()
      message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
    endif()
  endforeach()
  if("x${doing}" MATCHES "^x(LANGUAGE)$")
    message(FATAL_ERROR "Missing argument:\n  ${doing} arguments requires a value\n")
  endif()
  if(DEFINED _CHECK_TYPE_SIZE_LANGUAGE)
    if(NOT "x${_CHECK_TYPE_SIZE_LANGUAGE}" MATCHES "^x(C|CXX)$")
      message(FATAL_ERROR "Unknown language:\n  ${_CHECK_TYPE_SIZE_LANGUAGE}.\nSupported languages: C, CXX.\n")
    endif()
    set(_language ${_CHECK_TYPE_SIZE_LANGUAGE})
  else()
    set(_language C)
  endif()

224
  # Optionally check for standard headers.
225
  if(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
226 227 228
    set(_builtin 0)
  else()
    set(_builtin 1)
229
    if(_language STREQUAL "C")
230 231 232
      check_include_file(sys/types.h HAVE_SYS_TYPES_H)
      check_include_file(stdint.h HAVE_STDINT_H)
      check_include_file(stddef.h HAVE_STDDEF_H)
233
    elseif(_language STREQUAL "CXX")
234 235 236 237
      check_include_file_cxx(sys/types.h HAVE_SYS_TYPES_H)
      check_include_file_cxx(stdint.h HAVE_STDINT_H)
      check_include_file_cxx(stddef.h HAVE_STDDEF_H)
    endif()
238
  endif()
239 240
  unset(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
  unset(_CHECK_TYPE_SIZE_LANGUAGE)
241 242 243 244 245

  # Compute or load the size or size map.
  set(${VARIABLE}_KEYS)
  set(_map_file ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${VARIABLE}.cmake)
  if(NOT DEFINED HAVE_${VARIABLE})
246
    __check_type_size_impl(${TYPE} ${VARIABLE} ${_map_file} ${_builtin} ${_language})
247 248 249 250 251 252 253 254 255 256
  endif()
  include(${_map_file} OPTIONAL)
  set(_map_file)
  set(_builtin)

  # Create preprocessor code.
  if(${VARIABLE}_KEYS)
    set(${VARIABLE}_CODE)
    set(_if if)
    foreach(key ${${VARIABLE}_KEYS})
257
      string(APPEND ${VARIABLE}_CODE "#${_if} defined(${key})\n# define ${VARIABLE} ${${VARIABLE}-${key}}\n")
258 259
      set(_if elif)
    endforeach()
260
    string(APPEND ${VARIABLE}_CODE "#else\n# error ${VARIABLE} unknown\n#endif")
261 262 263 264 265 266 267 268 269 270
    set(_if)
  elseif(${VARIABLE})
    set(${VARIABLE}_CODE "#define ${VARIABLE} ${${VARIABLE}}")
  else()
    set(${VARIABLE}_CODE "/* #undef ${VARIABLE} */")
  endif()
endmacro()

#-----------------------------------------------------------------------------
cmake_policy(POP)