CheckTypeSize.cmake 9.09 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 10 11
#.rst:
# CheckTypeSize
# -------------
#
# Check sizeof a type
#
# ::
#
12 13
#   CHECK_TYPE_SIZE(TYPE VARIABLE [BUILTIN_TYPES_ONLY]
#                                 [LANGUAGE <language>])
14 15 16 17 18 19 20 21 22 23 24
#
# 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:
#
# ::
#
#    <size> = type has non-zero size <size>
#    "0"    = type has arch-dependent size (see below)
#    ""     = type does not exist
#
25 26 27
# Both ``HAVE_${VARIABLE}`` and ``${VARIABLE}`` will be created as internal
# cache variables.
#
28 29 30 31 32 33 34 35 36 37 38
# 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.
#
# The variable "${VARIABLE}" may be "0" when 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}".
39 40 41
#
# 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
42 43 44
# 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.
45
#
46 47 48
# If LANGUAGE is set, the specified compiler will be used to perform the
# check. Acceptable values are C and CXX
#
49 50
# 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
51 52
# member you can do something like this:
#
53 54 55 56 57 58 59 60 61 62
# ::
#
#   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:
#
# ::
63
#
64 65 66 67
#   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
#   CMAKE_REQUIRED_LIBRARIES = list of libraries to link
68
#   CMAKE_REQUIRED_QUIET = execute quietly without messages
69
#   CMAKE_EXTRA_INCLUDE_FILES = list of extra headers to include
70 71

include(CheckIncludeFile)
72
include(CheckIncludeFileCXX)
73

74
cmake_policy(PUSH)
75
cmake_policy(SET CMP0054 NEW)
76 77 78 79 80

get_filename_component(__check_type_size_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)

#-----------------------------------------------------------------------------
# Helper function.  DO NOT CALL DIRECTLY.
81
function(__check_type_size_impl type var map builtin language)
82 83 84
  if(NOT CMAKE_REQUIRED_QUIET)
    message(STATUS "Check size of ${type}")
  endif()
85 86 87 88 89

  # Include header files.
  set(headers)
  if(builtin)
    if(HAVE_SYS_TYPES_H)
90
      string(APPEND headers "#include <sys/types.h>\n")
91 92
    endif()
    if(HAVE_STDINT_H)
93
      string(APPEND headers "#include <stdint.h>\n")
94 95
    endif()
    if(HAVE_STDDEF_H)
96
      string(APPEND headers "#include <stddef.h>\n")
97 98 99
    endif()
  endif()
  foreach(h ${CMAKE_EXTRA_INCLUDE_FILES})
100
    string(APPEND headers "#include \"${h}\"\n")
101 102 103
  endforeach()

  # Perform the check.
104

105
  if(language STREQUAL "C")
106
    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.c)
107
  elseif(language STREQUAL "CXX")
108 109 110 111
    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()
112 113 114 115
  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}
116
    LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    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.
138
        set(size "${CMAKE_MATCH_1}")
139 140 141 142 143 144 145 146 147 148 149
        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)
150
          string(APPEND code "\nset(${var}-${key} \"${size}\")")
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
          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()

168 169 170
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Check size of ${type} - done")
    endif()
171 172 173
    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})")
174
  else()
175
    # The check failed to compile.
176 177 178
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Check size of ${type} - failed")
    endif()
179 180 181 182 183
    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})
184
  endif()
185 186 187 188
endfunction()

#-----------------------------------------------------------------------------
macro(CHECK_TYPE_SIZE TYPE VARIABLE)
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
  # 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()

217
  # Optionally check for standard headers.
218
  if(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
219 220 221
    set(_builtin 0)
  else()
    set(_builtin 1)
222
    if(_language STREQUAL "C")
223 224 225
      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)
226
    elseif(_language STREQUAL "CXX")
227 228 229 230
      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()
231
  endif()
232 233
  unset(_CHECK_TYPE_SIZE_BUILTIN_TYPES_ONLY)
  unset(_CHECK_TYPE_SIZE_LANGUAGE)
234 235 236 237 238

  # 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})
239
    __check_type_size_impl(${TYPE} ${VARIABLE} ${_map_file} ${_builtin} ${_language})
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
  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})
      set(${VARIABLE}_CODE "${${VARIABLE}_CODE}#${_if} defined(${key})\n# define ${VARIABLE} ${${VARIABLE}-${key}}\n")
      set(_if elif)
    endforeach()
    set(${VARIABLE}_CODE "${${VARIABLE}_CODE}#else\n# error ${VARIABLE} unknown\n#endif")
    set(_if)
  elseif(${VARIABLE})
    set(${VARIABLE}_CODE "#define ${VARIABLE} ${${VARIABLE}}")
  else()
    set(${VARIABLE}_CODE "/* #undef ${VARIABLE} */")
  endif()
endmacro()

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