Remove upstream files and directories from vendor/clang/dist that we do
not use. This saves on repository space, and reduces the number of tree conflicts when merging.
This commit is contained in:
parent
676fbe8105
commit
9a83721404
@ -1,4 +0,0 @@
|
||||
{
|
||||
"repository.callsign" : "C",
|
||||
"conduit_uri" : "https://reviews.llvm.org/"
|
||||
}
|
@ -1 +0,0 @@
|
||||
BasedOnStyle: LLVM
|
17
.clang-tidy
17
.clang-tidy
@ -1,17 +0,0 @@
|
||||
Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,readability-identifier-naming'
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: CamelCase
|
||||
|
43
.gitignore
vendored
43
.gitignore
vendored
@ -1,43 +0,0 @@
|
||||
#==============================================================================#
|
||||
# This file specifies intentionally untracked files that git should ignore.
|
||||
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
|
||||
#
|
||||
# This file is intentionally different from the output of `git svn show-ignore`,
|
||||
# as most of those are useless.
|
||||
#==============================================================================#
|
||||
|
||||
#==============================================================================#
|
||||
# File extensions to be ignored anywhere in the tree.
|
||||
#==============================================================================#
|
||||
# Temp files created by most text editors.
|
||||
*~
|
||||
# Merge files created by git.
|
||||
*.orig
|
||||
# Byte compiled python modules.
|
||||
*.pyc
|
||||
# vim swap files
|
||||
.*.sw?
|
||||
.sw?
|
||||
|
||||
#==============================================================================#
|
||||
# Explicit files to ignore (only matches one).
|
||||
#==============================================================================#
|
||||
cscope.files
|
||||
cscope.out
|
||||
/tags
|
||||
|
||||
#==============================================================================#
|
||||
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
|
||||
#==============================================================================#
|
||||
# Clang extra user tools, which is tracked independently (clang-tools-extra).
|
||||
tools/extra
|
||||
# Sphinx build products
|
||||
docs/_build
|
||||
docs/analyzer/_build
|
||||
# debug info testsuite
|
||||
test/debuginfo-tests
|
||||
|
||||
# VS2017 and VSCode config files.
|
||||
.vscode
|
||||
.vs
|
||||
|
816
CMakeLists.txt
816
CMakeLists.txt
@ -1,816 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.4.3)
|
||||
|
||||
if(POLICY CMP0075)
|
||||
cmake_policy(SET CMP0075 NEW)
|
||||
endif()
|
||||
|
||||
# If we are not building as a part of LLVM, build Clang as an
|
||||
# standalone project, using LLVM as an external library:
|
||||
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
|
||||
project(Clang)
|
||||
|
||||
# Rely on llvm-config.
|
||||
set(CONFIG_OUTPUT)
|
||||
if(LLVM_CONFIG)
|
||||
set (LLVM_CONFIG_FOUND 1)
|
||||
message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}")
|
||||
message(DEPRECATION "Using llvm-config to detect the LLVM installation is \
|
||||
deprecated. The installed cmake files should be used \
|
||||
instead. CMake should be able to detect your LLVM install \
|
||||
automatically, but you can also use LLVM_DIR to specify \
|
||||
the path containing LLVMConfig.cmake.")
|
||||
set(CONFIG_COMMAND ${LLVM_CONFIG}
|
||||
"--assertion-mode"
|
||||
"--bindir"
|
||||
"--libdir"
|
||||
"--includedir"
|
||||
"--prefix"
|
||||
"--src-root"
|
||||
"--cmakedir")
|
||||
execute_process(
|
||||
COMMAND ${CONFIG_COMMAND}
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE CONFIG_OUTPUT
|
||||
)
|
||||
if(NOT HAD_ERROR)
|
||||
string(REGEX REPLACE
|
||||
"[ \t]*[\r\n]+[ \t]*" ";"
|
||||
CONFIG_OUTPUT ${CONFIG_OUTPUT})
|
||||
else()
|
||||
string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}")
|
||||
message(STATUS "${CONFIG_COMMAND_STR}")
|
||||
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
|
||||
endif()
|
||||
|
||||
list(GET CONFIG_OUTPUT 0 ENABLE_ASSERTIONS)
|
||||
list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 2 LIBRARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 3 INCLUDE_DIR)
|
||||
list(GET CONFIG_OUTPUT 4 LLVM_OBJ_ROOT)
|
||||
list(GET CONFIG_OUTPUT 5 MAIN_SRC_DIR)
|
||||
list(GET CONFIG_OUTPUT 6 LLVM_CONFIG_CMAKE_PATH)
|
||||
|
||||
# Normalize LLVM_CMAKE_PATH. --cmakedir might contain backslashes.
|
||||
# CMake assumes slashes as PATH.
|
||||
file(TO_CMAKE_PATH ${LLVM_CONFIG_CMAKE_PATH} LLVM_CMAKE_PATH)
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT MSVC_IDE)
|
||||
set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS}
|
||||
CACHE BOOL "Enable assertions")
|
||||
# Assertions should follow llvm-config's.
|
||||
mark_as_advanced(LLVM_ENABLE_ASSERTIONS)
|
||||
endif()
|
||||
|
||||
find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_PATH}")
|
||||
list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR})
|
||||
|
||||
# We can't check LLVM_CONFIG here, because find_package(LLVM ...) also sets
|
||||
# LLVM_CONFIG.
|
||||
if (NOT LLVM_CONFIG_FOUND)
|
||||
# Pull values from LLVMConfig.cmake. We can drop this once the llvm-config
|
||||
# path is removed.
|
||||
set(TOOLS_BINARY_DIR ${LLVM_TOOLS_BINARY_DIR})
|
||||
set(LIBRARY_DIR ${LLVM_LIBRARY_DIR})
|
||||
set(INCLUDE_DIR ${LLVM_INCLUDE_DIR})
|
||||
set(LLVM_OBJ_DIR ${LLVM_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin")
|
||||
set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib")
|
||||
set(LLVM_MAIN_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include")
|
||||
set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree")
|
||||
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
|
||||
|
||||
find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
# They are used as destination of target generators.
|
||||
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
|
||||
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||
if(WIN32 OR CYGWIN)
|
||||
# DLL platform -- put DLLs into bin.
|
||||
set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
|
||||
else()
|
||||
set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_LIBRARY_OUTPUT_INTDIR})
|
||||
endif()
|
||||
|
||||
option(LLVM_ENABLE_WARNINGS "Enable compiler warnings." ON)
|
||||
option(LLVM_INSTALL_TOOLCHAIN_ONLY
|
||||
"Only include toolchain files in the 'install' target." OFF)
|
||||
|
||||
option(LLVM_FORCE_USE_OLD_HOST_TOOLCHAIN
|
||||
"Set to ON to force using an old, unsupported host toolchain." OFF)
|
||||
option(CLANG_ENABLE_BOOTSTRAP "Generate the clang bootstrap target" OFF)
|
||||
option(LLVM_ENABLE_LIBXML2 "Use libxml2 if available." ON)
|
||||
|
||||
include(AddLLVM)
|
||||
include(TableGen)
|
||||
include(HandleLLVMOptions)
|
||||
include(VersionFromVCS)
|
||||
|
||||
set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
|
||||
|
||||
if (NOT DEFINED LLVM_INCLUDE_TESTS)
|
||||
set(LLVM_INCLUDE_TESTS ON)
|
||||
endif()
|
||||
|
||||
include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}")
|
||||
link_directories("${LLVM_LIBRARY_DIR}")
|
||||
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
|
||||
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX} )
|
||||
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX} )
|
||||
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
set(Python_ADDITIONAL_VERSIONS 2.7)
|
||||
include(FindPythonInterp)
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"Unable to find Python interpreter, required for builds and testing.
|
||||
|
||||
Please install Python or specify the PYTHON_EXECUTABLE CMake variable.")
|
||||
endif()
|
||||
|
||||
if( ${PYTHON_VERSION_STRING} VERSION_LESS 2.7 )
|
||||
message(FATAL_ERROR "Python 2.7 or newer is required")
|
||||
endif()
|
||||
|
||||
# Check prebuilt llvm/utils.
|
||||
if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX}
|
||||
AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/count${CMAKE_EXECUTABLE_SUFFIX}
|
||||
AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX})
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
# Note: path not really used, except for checking if lit was found
|
||||
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/llvm-lit utils/llvm-lit)
|
||||
endif()
|
||||
if(NOT LLVM_UTILS_PROVIDED)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/count utils/count)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not)
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
set(CLANG_TEST_DEPS FileCheck count not)
|
||||
endif()
|
||||
set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest)
|
||||
if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h
|
||||
AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt)
|
||||
add_subdirectory(${UNITTEST_DIR} utils/unittest)
|
||||
endif()
|
||||
else()
|
||||
# Seek installed Lit.
|
||||
find_program(LLVM_LIT
|
||||
NAMES llvm-lit lit.py lit
|
||||
PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit"
|
||||
DOC "Path to lit.py")
|
||||
endif()
|
||||
|
||||
if(LLVM_LIT)
|
||||
# Define the default arguments to use with 'lit', and an option for the user
|
||||
# to override.
|
||||
set(LIT_ARGS_DEFAULT "-sv")
|
||||
if (MSVC OR XCODE)
|
||||
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
|
||||
endif()
|
||||
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||
|
||||
# On Win32 hosts, provide an option to specify the path to the GnuWin32 tools.
|
||||
if( WIN32 AND NOT CYGWIN )
|
||||
set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools")
|
||||
endif()
|
||||
else()
|
||||
set(LLVM_INCLUDE_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set( CLANG_BUILT_STANDALONE 1 )
|
||||
set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
else()
|
||||
set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}")
|
||||
endif()
|
||||
|
||||
# Make sure that our source directory is on the current cmake module path so that
|
||||
# we can include cmake files from this directory.
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
if(LLVM_ENABLE_LIBXML2)
|
||||
# Don't look for libxml if we're using MSan, since uninstrumented third party
|
||||
# code may call MSan interceptors like strlen, leading to false positives.
|
||||
if(NOT LLVM_USE_SANITIZER MATCHES "Memory.*")
|
||||
set (LIBXML2_FOUND 0)
|
||||
find_package(LibXml2 2.5.3 QUIET)
|
||||
if (LIBXML2_FOUND)
|
||||
set(CLANG_HAVE_LIBXML 1)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckIncludeFile)
|
||||
check_include_file(sys/resource.h CLANG_HAVE_RLIMITS)
|
||||
|
||||
set(CLANG_RESOURCE_DIR "" CACHE STRING
|
||||
"Relative directory from the Clang binary to its resource files.")
|
||||
|
||||
set(C_INCLUDE_DIRS "" CACHE STRING
|
||||
"Colon separated list of directories clang will search for headers.")
|
||||
|
||||
set(GCC_INSTALL_PREFIX "" CACHE PATH "Directory where gcc is installed." )
|
||||
set(DEFAULT_SYSROOT "" CACHE PATH
|
||||
"Default <path> to all compiler invocations for --sysroot=<path>." )
|
||||
|
||||
set(ENABLE_LINKER_BUILD_ID OFF CACHE BOOL "pass --build-id to ld")
|
||||
|
||||
set(ENABLE_X86_RELAX_RELOCATIONS OFF CACHE BOOL
|
||||
"enable x86 relax relocations by default")
|
||||
|
||||
set(ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER FALSE CACHE BOOL
|
||||
"Enable the experimental new pass manager by default.")
|
||||
|
||||
# TODO: verify the values against LangStandards.def?
|
||||
set(CLANG_DEFAULT_STD_C "" CACHE STRING
|
||||
"Default standard to use for C/ObjC code (IDENT from LangStandards.def, empty for platform default)")
|
||||
set(CLANG_DEFAULT_STD_CXX "" CACHE STRING
|
||||
"Default standard to use for C++/ObjC++ code (IDENT from LangStandards.def, empty for platform default)")
|
||||
|
||||
set(CLANG_DEFAULT_LINKER "" CACHE STRING
|
||||
"Default linker to use (linker name or absolute path, empty for platform default)")
|
||||
|
||||
set(CLANG_DEFAULT_CXX_STDLIB "" CACHE STRING
|
||||
"Default C++ stdlib to use (\"libstdc++\" or \"libc++\", empty for platform default")
|
||||
if (NOT(CLANG_DEFAULT_CXX_STDLIB STREQUAL "" OR
|
||||
CLANG_DEFAULT_CXX_STDLIB STREQUAL "libstdc++" OR
|
||||
CLANG_DEFAULT_CXX_STDLIB STREQUAL "libc++"))
|
||||
message(WARNING "Resetting default C++ stdlib to use platform default")
|
||||
set(CLANG_DEFAULT_CXX_STDLIB "" CACHE STRING
|
||||
"Default C++ stdlib to use (\"libstdc++\" or \"libc++\", empty for platform default" FORCE)
|
||||
endif()
|
||||
|
||||
set(CLANG_DEFAULT_RTLIB "" CACHE STRING
|
||||
"Default runtime library to use (\"libgcc\" or \"compiler-rt\", empty for platform default)")
|
||||
if (NOT(CLANG_DEFAULT_RTLIB STREQUAL "" OR
|
||||
CLANG_DEFAULT_RTLIB STREQUAL "libgcc" OR
|
||||
CLANG_DEFAULT_RTLIB STREQUAL "compiler-rt"))
|
||||
message(WARNING "Resetting default rtlib to use platform default")
|
||||
set(CLANG_DEFAULT_RTLIB "" CACHE STRING
|
||||
"Default runtime library to use (\"libgcc\" or \"compiler-rt\", empty for platform default)" FORCE)
|
||||
endif()
|
||||
|
||||
set(CLANG_DEFAULT_OBJCOPY "objcopy" CACHE STRING
|
||||
"Default objcopy executable to use.")
|
||||
|
||||
set(CLANG_DEFAULT_OPENMP_RUNTIME "libomp" CACHE STRING
|
||||
"Default OpenMP runtime used by -fopenmp.")
|
||||
|
||||
# OpenMP offloading requires at least sm_35 because we use shuffle instructions
|
||||
# to generate efficient code for reductions and the atomicMax instruction on
|
||||
# 64-bit integers in the implementation of conditional lastprivate.
|
||||
set(CLANG_OPENMP_NVPTX_DEFAULT_ARCH "sm_35" CACHE STRING
|
||||
"Default architecture for OpenMP offloading to Nvidia GPUs.")
|
||||
string(REGEX MATCH "^sm_([0-9]+)$" MATCHED_ARCH "${CLANG_OPENMP_NVPTX_DEFAULT_ARCH}")
|
||||
if (NOT DEFINED MATCHED_ARCH OR "${CMAKE_MATCH_1}" LESS 35)
|
||||
message(WARNING "Resetting default architecture for OpenMP offloading to Nvidia GPUs to sm_35")
|
||||
set(CLANG_OPENMP_NVPTX_DEFAULT_ARCH "sm_35" CACHE STRING
|
||||
"Default architecture for OpenMP offloading to Nvidia GPUs." FORCE)
|
||||
endif()
|
||||
|
||||
set(CLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING
|
||||
"Vendor-specific text for showing with version information.")
|
||||
|
||||
if( CLANG_VENDOR )
|
||||
add_definitions( -DCLANG_VENDOR="${CLANG_VENDOR} " )
|
||||
endif()
|
||||
|
||||
set(CLANG_REPOSITORY_STRING "" CACHE STRING
|
||||
"Vendor-specific text for showing the repository the source is taken from.")
|
||||
|
||||
if(CLANG_REPOSITORY_STRING)
|
||||
add_definitions(-DCLANG_REPOSITORY_STRING="${CLANG_REPOSITORY_STRING}")
|
||||
endif()
|
||||
|
||||
set(CLANG_VENDOR_UTI "org.llvm.clang" CACHE STRING
|
||||
"Vendor-specific uti.")
|
||||
|
||||
set(CLANG_PYTHON_BINDINGS_VERSIONS "" CACHE STRING
|
||||
"Python versions to install libclang python bindings for")
|
||||
|
||||
# The libdir suffix must exactly match whatever LLVM's configuration used.
|
||||
set(CLANG_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}")
|
||||
|
||||
set(CLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE )
|
||||
message(FATAL_ERROR "In-source builds are not allowed. "
|
||||
"Please create a directory and run cmake "
|
||||
"from there, passing the path to this source directory as the last argument. "
|
||||
"This process created the file `CMakeCache.txt' and the directory "
|
||||
"`CMakeFiles'. Please delete them.")
|
||||
endif()
|
||||
|
||||
# If CLANG_VERSION_* is specified, use it, if not use LLVM_VERSION_*.
|
||||
if(NOT DEFINED CLANG_VERSION_MAJOR)
|
||||
set(CLANG_VERSION_MAJOR ${LLVM_VERSION_MAJOR})
|
||||
endif()
|
||||
if(NOT DEFINED CLANG_VERSION_MINOR)
|
||||
set(CLANG_VERSION_MINOR ${LLVM_VERSION_MINOR})
|
||||
endif()
|
||||
if(NOT DEFINED CLANG_VERSION_PATCHLEVEL)
|
||||
set(CLANG_VERSION_PATCHLEVEL ${LLVM_VERSION_PATCH})
|
||||
endif()
|
||||
# Unlike PACKAGE_VERSION, CLANG_VERSION does not include LLVM_VERSION_SUFFIX.
|
||||
set(CLANG_VERSION "${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}.${CLANG_VERSION_PATCHLEVEL}")
|
||||
message(STATUS "Clang version: ${CLANG_VERSION}")
|
||||
|
||||
# Configure the Version.inc file.
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/clang/Basic/Version.inc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/clang/Basic/Version.inc)
|
||||
|
||||
# Add appropriate flags for GCC
|
||||
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual")
|
||||
if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
|
||||
endif ()
|
||||
|
||||
# Enable -pedantic for Clang even if it's not enabled for LLVM.
|
||||
if (NOT LLVM_ENABLE_PEDANTIC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-long-long")
|
||||
endif ()
|
||||
|
||||
check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG)
|
||||
if( CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG )
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types" )
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Determine HOST_LINK_VERSION on Darwin.
|
||||
set(HOST_LINK_VERSION)
|
||||
if (APPLE)
|
||||
set(LD_V_OUTPUT)
|
||||
execute_process(
|
||||
COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1"
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE LD_V_OUTPUT
|
||||
)
|
||||
if (NOT HAD_ERROR)
|
||||
if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*")
|
||||
string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT})
|
||||
elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*")
|
||||
string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT})
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CMakeParseArguments)
|
||||
include(AddClang)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
include_directories(BEFORE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
||||
install(DIRECTORY include/clang include/clang-c
|
||||
DESTINATION include
|
||||
FILES_MATCHING
|
||||
PATTERN "*.def"
|
||||
PATTERN "*.h"
|
||||
PATTERN "config.h" EXCLUDE
|
||||
PATTERN ".svn" EXCLUDE
|
||||
)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/clang
|
||||
DESTINATION include
|
||||
FILES_MATCHING
|
||||
PATTERN "CMakeFiles" EXCLUDE
|
||||
PATTERN "*.inc"
|
||||
PATTERN "*.h"
|
||||
)
|
||||
|
||||
install(PROGRAMS utils/bash-autocomplete.sh
|
||||
DESTINATION share/clang
|
||||
)
|
||||
endif()
|
||||
|
||||
add_definitions( -D_GNU_SOURCE )
|
||||
|
||||
option(CLANG_BUILD_TOOLS
|
||||
"Build the Clang tools. If OFF, just generate build targets." ON)
|
||||
|
||||
option(CLANG_ENABLE_ARCMT "Build ARCMT." ON)
|
||||
option(CLANG_ENABLE_STATIC_ANALYZER "Build static analyzer." ON)
|
||||
|
||||
set(CLANG_ANALYZER_Z3_INSTALL_DIR "" CACHE STRING "Install directory of the Z3 solver.")
|
||||
|
||||
find_package(Z3 4.7.1)
|
||||
|
||||
if (CLANG_ANALYZER_Z3_INSTALL_DIR)
|
||||
if (NOT Z3_FOUND)
|
||||
message(FATAL_ERROR "Z3 4.7.1 has not been found in CLANG_ANALYZER_Z3_INSTALL_DIR: ${CLANG_ANALYZER_Z3_INSTALL_DIR}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CLANG_ANALYZER_ENABLE_Z3_SOLVER_DEFAULT "${Z3_FOUND}")
|
||||
|
||||
option(CLANG_ANALYZER_ENABLE_Z3_SOLVER
|
||||
"Enable Support for the Z3 constraint solver in the Clang Static Analyzer."
|
||||
${CLANG_ANALYZER_ENABLE_Z3_SOLVER_DEFAULT}
|
||||
)
|
||||
|
||||
if (CLANG_ANALYZER_ENABLE_Z3_SOLVER)
|
||||
if (NOT Z3_FOUND)
|
||||
message(FATAL_ERROR "CLANG_ANALYZER_ENABLE_Z3_SOLVER cannot be enabled when Z3 is not available.")
|
||||
endif()
|
||||
|
||||
set(CLANG_ANALYZER_WITH_Z3 1)
|
||||
endif()
|
||||
|
||||
option(CLANG_ENABLE_PROTO_FUZZER "Build Clang protobuf fuzzer." OFF)
|
||||
|
||||
if(NOT CLANG_ENABLE_STATIC_ANALYZER AND (CLANG_ENABLE_ARCMT OR CLANG_ANALYZER_ENABLE_Z3_SOLVER))
|
||||
message(FATAL_ERROR "Cannot disable static analyzer while enabling ARCMT or Z3")
|
||||
endif()
|
||||
|
||||
if(CLANG_ENABLE_ARCMT)
|
||||
set(CLANG_ENABLE_OBJC_REWRITER ON)
|
||||
endif()
|
||||
|
||||
# Clang version information
|
||||
set(CLANG_EXECUTABLE_VERSION
|
||||
"${CLANG_VERSION_MAJOR}" CACHE STRING
|
||||
"Major version number that will be appended to the clang executable name")
|
||||
set(LIBCLANG_LIBRARY_VERSION
|
||||
"${CLANG_VERSION_MAJOR}" CACHE STRING
|
||||
"Major version number that will be appended to the libclang library")
|
||||
mark_as_advanced(CLANG_EXECUTABLE_VERSION LIBCLANG_LIBRARY_VERSION)
|
||||
|
||||
option(CLANG_INCLUDE_TESTS
|
||||
"Generate build targets for the Clang unit tests."
|
||||
${LLVM_INCLUDE_TESTS})
|
||||
|
||||
add_subdirectory(utils/TableGen)
|
||||
|
||||
add_subdirectory(include)
|
||||
|
||||
# All targets below may depend on all tablegen'd files.
|
||||
get_property(CLANG_TABLEGEN_TARGETS GLOBAL PROPERTY CLANG_TABLEGEN_TARGETS)
|
||||
add_custom_target(clang-tablegen-targets DEPENDS ${CLANG_TABLEGEN_TARGETS})
|
||||
set_target_properties(clang-tablegen-targets PROPERTIES FOLDER "Misc")
|
||||
list(APPEND LLVM_COMMON_DEPENDS clang-tablegen-targets)
|
||||
|
||||
# Force target to be built as soon as possible. Clang modules builds depend
|
||||
# header-wise on it as they ship all headers from the umbrella folders. Building
|
||||
# an entire module might include header, which depends on intrinsics_gen.
|
||||
if(LLVM_ENABLE_MODULES AND NOT CLANG_BUILT_STANDALONE)
|
||||
list(APPEND LLVM_COMMON_DEPENDS intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(runtime)
|
||||
|
||||
option(CLANG_BUILD_EXAMPLES "Build CLANG example programs by default." OFF)
|
||||
add_subdirectory(examples)
|
||||
|
||||
if(APPLE)
|
||||
# this line is needed as a cleanup to ensure that any CMakeCaches with the old
|
||||
# default value get updated to the new default.
|
||||
if(CLANG_ORDER_FILE STREQUAL "")
|
||||
unset(CLANG_ORDER_FILE CACHE)
|
||||
unset(CLANG_ORDER_FILE)
|
||||
endif()
|
||||
|
||||
|
||||
set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH
|
||||
"Order file to use when compiling clang in order to improve startup time (Darwin Only - requires ld64).")
|
||||
|
||||
if(NOT EXISTS ${CLANG_ORDER_FILE})
|
||||
string(FIND "${CLANG_ORDER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}" PATH_START)
|
||||
if(PATH_START EQUAL 0)
|
||||
file(WRITE ${CLANG_ORDER_FILE} "\n")
|
||||
else()
|
||||
message(FATAL_ERROR "Specified order file '${CLANG_ORDER_FILE}' does not exist.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if( CLANG_INCLUDE_TESTS )
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include/gtest/gtest.h)
|
||||
add_subdirectory(unittests)
|
||||
list(APPEND CLANG_TEST_DEPS ClangUnitTests)
|
||||
list(APPEND CLANG_TEST_PARAMS
|
||||
clang_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/test/Unit/lit.site.cfg
|
||||
)
|
||||
endif()
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(bindings/python/tests)
|
||||
|
||||
if(CLANG_BUILT_STANDALONE)
|
||||
# Add a global check rule now that all subdirectories have been traversed
|
||||
# and we know the total set of lit testsuites.
|
||||
get_property(LLVM_LIT_TESTSUITES GLOBAL PROPERTY LLVM_LIT_TESTSUITES)
|
||||
get_property(LLVM_LIT_PARAMS GLOBAL PROPERTY LLVM_LIT_PARAMS)
|
||||
get_property(LLVM_LIT_DEPENDS GLOBAL PROPERTY LLVM_LIT_DEPENDS)
|
||||
get_property(LLVM_LIT_EXTRA_ARGS GLOBAL PROPERTY LLVM_LIT_EXTRA_ARGS)
|
||||
get_property(LLVM_ADDITIONAL_TEST_TARGETS
|
||||
GLOBAL PROPERTY LLVM_ADDITIONAL_TEST_TARGETS)
|
||||
add_lit_target(check-all
|
||||
"Running all regression tests"
|
||||
${LLVM_LIT_TESTSUITES}
|
||||
PARAMS ${LLVM_LIT_PARAMS}
|
||||
DEPENDS ${LLVM_LIT_DEPENDS} ${LLVM_ADDITIONAL_TEST_TARGETS}
|
||||
ARGS ${LLVM_LIT_EXTRA_ARGS}
|
||||
)
|
||||
endif()
|
||||
add_subdirectory(utils/perf-training)
|
||||
endif()
|
||||
|
||||
option(CLANG_INCLUDE_DOCS "Generate build targets for the Clang docs."
|
||||
${LLVM_INCLUDE_DOCS})
|
||||
if( CLANG_INCLUDE_DOCS )
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
add_subdirectory(cmake/modules)
|
||||
|
||||
if(CLANG_STAGE)
|
||||
message(STATUS "Setting current clang stage to: ${CLANG_STAGE}")
|
||||
endif()
|
||||
|
||||
if (CLANG_ENABLE_BOOTSTRAP)
|
||||
include(ExternalProject)
|
||||
|
||||
add_custom_target(clang-bootstrap-deps DEPENDS clang)
|
||||
|
||||
if(NOT CLANG_STAGE)
|
||||
set(CLANG_STAGE stage1)
|
||||
endif()
|
||||
|
||||
string(REGEX MATCH "stage([0-9]*)" MATCHED_STAGE "${CLANG_STAGE}")
|
||||
if(MATCHED_STAGE)
|
||||
if(NOT LLVM_BUILD_INSTRUMENTED)
|
||||
math(EXPR STAGE_NUM "${CMAKE_MATCH_1} + 1")
|
||||
set(NEXT_CLANG_STAGE stage${STAGE_NUM})
|
||||
else()
|
||||
set(NEXT_CLANG_STAGE stage${CMAKE_MATCH_1})
|
||||
endif()
|
||||
else()
|
||||
set(NEXT_CLANG_STAGE bootstrap)
|
||||
endif()
|
||||
|
||||
if(BOOTSTRAP_LLVM_BUILD_INSTRUMENTED)
|
||||
set(NEXT_CLANG_STAGE ${NEXT_CLANG_STAGE}-instrumented)
|
||||
endif()
|
||||
message(STATUS "Setting next clang stage to: ${NEXT_CLANG_STAGE}")
|
||||
|
||||
|
||||
set(STAMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-stamps/)
|
||||
set(BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-bins/)
|
||||
|
||||
if(BOOTSTRAP_LLVM_ENABLE_LLD)
|
||||
add_dependencies(clang-bootstrap-deps lld)
|
||||
endif()
|
||||
|
||||
# If the next stage is LTO we need to depend on LTO and possibly lld or LLVMgold
|
||||
if(BOOTSTRAP_LLVM_ENABLE_LTO OR LLVM_ENABLE_LTO AND NOT LLVM_BUILD_INSTRUMENTED)
|
||||
if(APPLE)
|
||||
add_dependencies(clang-bootstrap-deps LTO)
|
||||
# on Darwin we need to set DARWIN_LTO_LIBRARY so that -flto will work
|
||||
# using the just-built compiler, and we need to override DYLD_LIBRARY_PATH
|
||||
# so that the host object file tools will use the just-built libLTO.
|
||||
# However if System Integrity Protection is enabled the DYLD variables
|
||||
# will be scrubbed from the environment of any base system commands. This
|
||||
# includes /bin/sh, which ninja uses when executing build commands. To
|
||||
# work around the envar being filtered away we pass it in as a CMake
|
||||
# variable, and have LLVM's CMake append the envar to the archiver calls.
|
||||
set(LTO_LIBRARY -DDARWIN_LTO_LIBRARY=${LLVM_SHLIB_OUTPUT_INTDIR}/libLTO.dylib
|
||||
-DDYLD_LIBRARY_PATH=${LLVM_LIBRARY_OUTPUT_INTDIR})
|
||||
elseif(NOT WIN32)
|
||||
add_dependencies(clang-bootstrap-deps llvm-ar llvm-ranlib)
|
||||
if(NOT BOOTSTRAP_LLVM_ENABLE_LLD AND LLVM_BINUTILS_INCDIR)
|
||||
add_dependencies(clang-bootstrap-deps LLVMgold)
|
||||
endif()
|
||||
set(${CLANG_STAGE}_AR -DCMAKE_AR=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ar)
|
||||
set(${CLANG_STAGE}_RANLIB -DCMAKE_RANLIB=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ranlib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CLANG_BOOTSTRAP_EXTRA_DEPS)
|
||||
add_dependencies(clang-bootstrap-deps ${CLANG_BOOTSTRAP_EXTRA_DEPS})
|
||||
endif()
|
||||
|
||||
add_custom_target(${NEXT_CLANG_STAGE}-clear
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-cleared
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-cleared
|
||||
DEPENDS clang-bootstrap-deps
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${STAMP_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${STAMP_DIR}
|
||||
COMMENT "Clobberring ${NEXT_CLANG_STAGE} build and stamp directories"
|
||||
)
|
||||
|
||||
if(CMAKE_VERBOSE_MAKEFILE)
|
||||
set(verbose -DCMAKE_VERBOSE_MAKEFILE=On)
|
||||
endif()
|
||||
|
||||
set(_BOOTSTRAP_DEFAULT_PASSTHROUGH
|
||||
PACKAGE_VERSION
|
||||
PACKAGE_VENDOR
|
||||
LLVM_VERSION_MAJOR
|
||||
LLVM_VERSION_MINOR
|
||||
LLVM_VERSION_PATCH
|
||||
CLANG_VERSION_MAJOR
|
||||
CLANG_VERSION_MINOR
|
||||
CLANG_VERSION_PATCHLEVEL
|
||||
LLVM_VERSION_SUFFIX
|
||||
LLVM_BINUTILS_INCDIR
|
||||
CLANG_REPOSITORY_STRING
|
||||
CMAKE_MAKE_PROGRAM
|
||||
CMAKE_OSX_ARCHITECTURES
|
||||
LLVM_ENABLE_PROJECTS
|
||||
LLVM_ENABLE_RUNTIMES)
|
||||
|
||||
# We don't need to depend on compiler-rt/libcxx if we're building instrumented
|
||||
# because the next stage will use the same compiler used to build this stage.
|
||||
if(NOT LLVM_BUILD_INSTRUMENTED)
|
||||
if(TARGET compiler-rt)
|
||||
add_dependencies(clang-bootstrap-deps compiler-rt)
|
||||
endif()
|
||||
if(TARGET cxx-headers)
|
||||
add_dependencies(clang-bootstrap-deps cxx-headers)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(C_COMPILER "clang")
|
||||
set(CXX_COMPILER "clang++")
|
||||
if(WIN32)
|
||||
set(C_COMPILER "clang-cl.exe")
|
||||
set(CXX_COMPILER "clang-cl.exe")
|
||||
endif()
|
||||
|
||||
set(COMPILER_OPTIONS
|
||||
-DCMAKE_CXX_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/${CXX_COMPILER}
|
||||
-DCMAKE_C_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/${C_COMPILER}
|
||||
-DCMAKE_ASM_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/${C_COMPILER}
|
||||
-DCMAKE_ASM_COMPILER_ID=Clang)
|
||||
|
||||
if(BOOTSTRAP_CMAKE_SYSTEM_NAME)
|
||||
set(${CLANG_STAGE}_CONFIG -DLLVM_CONFIG_PATH=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-config)
|
||||
set(${CLANG_STAGE}_TABLEGEN
|
||||
-DLLVM_TABLEGEN=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-tblgen
|
||||
-DCLANG_TABLEGEN=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang-tblgen)
|
||||
if(BOOTSTRAP_CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(BOOTSTRAP_LLVM_ENABLE_LLD)
|
||||
set(${CLANG_STAGE}_LINKER -DCMAKE_LINKER=${LLVM_RUNTIME_OUTPUT_INTDIR}/ld.lld)
|
||||
endif()
|
||||
if(NOT BOOTSTRAP_LLVM_ENABLE_LTO)
|
||||
add_dependencies(clang-bootstrap-deps llvm-ar llvm-ranlib)
|
||||
set(${CLANG_STAGE}_AR -DCMAKE_AR=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ar)
|
||||
set(${CLANG_STAGE}_RANLIB -DCMAKE_RANLIB=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ranlib)
|
||||
endif()
|
||||
add_dependencies(clang-bootstrap-deps llvm-objcopy llvm-strip)
|
||||
set(${CLANG_STAGE}_OBJCOPY -DCMAKE_OBJCOPY=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-objcopy)
|
||||
set(${CLANG_STAGE}_STRIP -DCMAKE_STRIP=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-strip)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BOOTSTRAP_LLVM_BUILD_INSTRUMENTED)
|
||||
add_dependencies(clang-bootstrap-deps llvm-profdata)
|
||||
set(PGO_OPT -DLLVM_PROFDATA=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-profdata)
|
||||
endif()
|
||||
|
||||
if(LLVM_BUILD_INSTRUMENTED)
|
||||
add_dependencies(clang-bootstrap-deps generate-profdata)
|
||||
set(PGO_OPT -DLLVM_PROFDATA_FILE=${CMAKE_CURRENT_BINARY_DIR}/utils/perf-training/clang.profdata)
|
||||
# Use the current tools for LTO instead of the instrumented ones
|
||||
list(APPEND _BOOTSTRAP_DEFAULT_PASSTHROUGH
|
||||
CMAKE_CXX_COMPILER
|
||||
CMAKE_C_COMPILER
|
||||
CMAKE_ASM_COMPILER
|
||||
CMAKE_AR
|
||||
CMAKE_RANLIB
|
||||
DARWIN_LTO_LIBRARY
|
||||
DYLD_LIBRARY_PATH)
|
||||
|
||||
set(COMPILER_OPTIONS)
|
||||
set(LTO_LIBRARY)
|
||||
set(LTO_AR)
|
||||
set(LTO_RANLIB)
|
||||
endif()
|
||||
|
||||
# Find all variables that start with BOOTSTRAP_ and populate a variable with
|
||||
# them.
|
||||
get_cmake_property(variableNames VARIABLES)
|
||||
foreach(variableName ${variableNames})
|
||||
if(variableName MATCHES "^BOOTSTRAP_")
|
||||
string(SUBSTRING ${variableName} 10 -1 varName)
|
||||
string(REPLACE ";" "|" value "${${variableName}}")
|
||||
list(APPEND PASSTHROUGH_VARIABLES
|
||||
-D${varName}=${value})
|
||||
endif()
|
||||
if(${variableName} AND variableName MATCHES "LLVM_EXTERNAL_.*_SOURCE_DIR")
|
||||
list(APPEND PASSTHROUGH_VARIABLES
|
||||
-D${variableName}=${${variableName}})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Populate the passthrough variables
|
||||
foreach(variableName ${CLANG_BOOTSTRAP_PASSTHROUGH} ${_BOOTSTRAP_DEFAULT_PASSTHROUGH})
|
||||
if(DEFINED ${variableName})
|
||||
if("${${variableName}}" STREQUAL "")
|
||||
set(value "")
|
||||
else()
|
||||
string(REPLACE ";" "|" value "${${variableName}}")
|
||||
endif()
|
||||
list(APPEND PASSTHROUGH_VARIABLES
|
||||
-D${variableName}=${value})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
ExternalProject_Add(${NEXT_CLANG_STAGE}
|
||||
DEPENDS clang-bootstrap-deps
|
||||
PREFIX ${NEXT_CLANG_STAGE}
|
||||
SOURCE_DIR ${CMAKE_SOURCE_DIR}
|
||||
STAMP_DIR ${STAMP_DIR}
|
||||
BINARY_DIR ${BINARY_DIR}
|
||||
EXCLUDE_FROM_ALL 1
|
||||
CMAKE_ARGS
|
||||
# We shouldn't need to set this here, but INSTALL_DIR doesn't
|
||||
# seem to work, so instead I'm passing this through
|
||||
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
|
||||
${CLANG_BOOTSTRAP_CMAKE_ARGS}
|
||||
${PASSTHROUGH_VARIABLES}
|
||||
-DCLANG_STAGE=${NEXT_CLANG_STAGE}
|
||||
${COMPILER_OPTIONS}
|
||||
${${CLANG_STAGE}_CONFIG}
|
||||
${${CLANG_STAGE}_TABLEGEN}
|
||||
${LTO_LIBRARY} ${verbose} ${PGO_OPT}
|
||||
${${CLANG_STAGE}_LINKER}
|
||||
${${CLANG_STAGE}_AR}
|
||||
${${CLANG_STAGE}_RANLIB}
|
||||
${${CLANG_STAGE}_OBJCOPY}
|
||||
${${CLANG_STAGE}_STRIP}
|
||||
INSTALL_COMMAND ""
|
||||
STEP_TARGETS configure build
|
||||
USES_TERMINAL_CONFIGURE 1
|
||||
USES_TERMINAL_BUILD 1
|
||||
USES_TERMINAL_INSTALL 1
|
||||
LIST_SEPARATOR |
|
||||
)
|
||||
|
||||
# exclude really-install from main target
|
||||
set_target_properties(${NEXT_CLANG_STAGE} PROPERTIES _EP_really-install_EXCLUDE_FROM_MAIN On)
|
||||
ExternalProject_Add_Step(${NEXT_CLANG_STAGE} really-install
|
||||
COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install
|
||||
COMMENT "Performing install step for '${NEXT_CLANG_STAGE}'"
|
||||
DEPENDEES build
|
||||
USES_TERMINAL 1
|
||||
)
|
||||
ExternalProject_Add_StepTargets(${NEXT_CLANG_STAGE} really-install)
|
||||
add_custom_target(${NEXT_CLANG_STAGE}-install DEPENDS ${NEXT_CLANG_STAGE}-really-install)
|
||||
|
||||
if(NOT CLANG_BOOTSTRAP_TARGETS)
|
||||
set(CLANG_BOOTSTRAP_TARGETS check-llvm check-clang check-all)
|
||||
endif()
|
||||
foreach(target ${CLANG_BOOTSTRAP_TARGETS})
|
||||
# exclude from main target
|
||||
set_target_properties(${NEXT_CLANG_STAGE} PROPERTIES _EP_${target}_EXCLUDE_FROM_MAIN On)
|
||||
|
||||
ExternalProject_Add_Step(${NEXT_CLANG_STAGE} ${target}
|
||||
COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target ${target}
|
||||
COMMENT "Performing ${target} for '${NEXT_CLANG_STAGE}'"
|
||||
DEPENDEES configure
|
||||
USES_TERMINAL 1
|
||||
)
|
||||
|
||||
if(target MATCHES "^stage[0-9]*")
|
||||
add_custom_target(${target} DEPENDS ${NEXT_CLANG_STAGE}-${target})
|
||||
endif()
|
||||
|
||||
ExternalProject_Add_StepTargets(${NEXT_CLANG_STAGE} ${target})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (LLVM_ADD_NATIVE_VISUALIZERS_TO_SOLUTION)
|
||||
add_subdirectory(utils/ClangVisualizers)
|
||||
endif()
|
||||
add_subdirectory(utils/hmaptool)
|
||||
|
||||
configure_file(
|
||||
${CLANG_SOURCE_DIR}/include/clang/Config/config.h.cmake
|
||||
${CLANG_BINARY_DIR}/include/clang/Config/config.h)
|
@ -1,62 +0,0 @@
|
||||
This file is a list of the people responsible for ensuring that patches for a
|
||||
particular part of Clang are reviewed, either by themself or by someone else.
|
||||
They are also the gatekeepers for their part of Clang, with the final word on
|
||||
what goes in or not.
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S).
|
||||
|
||||
N: Aaron Ballman
|
||||
E: aaron@aaronballman.com
|
||||
D: Clang attributes
|
||||
|
||||
N: Alexey Bataev
|
||||
E: a.bataev@hotmail.com
|
||||
D: OpenMP support
|
||||
|
||||
N: Chandler Carruth
|
||||
E: chandlerc@gmail.com
|
||||
E: chandlerc@google.com
|
||||
D: CMake, library layering
|
||||
|
||||
N: Eric Christopher
|
||||
E: echristo@gmail.com
|
||||
D: Debug Information, inline assembly
|
||||
|
||||
N: Devin Coughlin
|
||||
E: dcoughlin@apple.com
|
||||
D: Clang Static Analyzer
|
||||
|
||||
N: Doug Gregor
|
||||
E: dgregor@apple.com
|
||||
D: Emeritus owner
|
||||
|
||||
N: Reid Kleckner
|
||||
E: rnk@google.com
|
||||
D: Microsoft C++ ABI compatibility and general Windows support
|
||||
|
||||
N: Manuel Klimek
|
||||
E: klimek@google.com
|
||||
D: AST matchers, LibTooling
|
||||
|
||||
N: Anton Korobeynikov
|
||||
E: anton@korobeynikov.info
|
||||
D: Exception handling, Windows codegen, ARM EABI
|
||||
|
||||
N: John McCall
|
||||
E: rjmccall@apple.com
|
||||
D: Clang LLVM IR generation
|
||||
|
||||
N: Brad Smith
|
||||
E: brad@comstyle.com
|
||||
D: OpenBSD driver
|
||||
|
||||
N: Richard Smith
|
||||
E: richard@metafoo.co.uk
|
||||
D: All parts of Clang not covered by someone else
|
||||
|
||||
N: Anastasia Stulova
|
||||
E: anastasia.stulova@arm.com
|
||||
D: OpenCL support
|
@ -1,2 +0,0 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
@ -1,86 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cfloat>
|
||||
#include <ciso646>
|
||||
#include <climits>
|
||||
#include <clocale>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <csetjmp>
|
||||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iosfwd>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
#if __has_include(<strstream>)
|
||||
#include <strstream>
|
||||
#endif
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <valarray>
|
||||
#include <vector>
|
||||
|
||||
#if __cplusplus >= 201103 || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
#include <array>
|
||||
#if __has_include(<atomic>)
|
||||
#include <atomic>
|
||||
#endif
|
||||
#include <chrono>
|
||||
#if __has_include(<codecvt>)
|
||||
#include <codecvt>
|
||||
#endif
|
||||
#include <condition_variable>
|
||||
#include <forward_list>
|
||||
#if __has_include(<future>)
|
||||
#include <future>
|
||||
#endif
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <ratio>
|
||||
#include <regex>
|
||||
#if __has_include(<scoped_allocator>)
|
||||
#include <scoped_allocator>
|
||||
#endif
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#if __has_include(<typeindex>)
|
||||
#include <typeindex>
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#endif
|
@ -1,639 +0,0 @@
|
||||
/* Test for integer constant types. */
|
||||
|
||||
/* Origin: Joseph Myers <jsm28@cam.ac.uk>. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* Assertion that constant C is of type T. */
|
||||
#define ASSERT_CONST_TYPE(C, T) \
|
||||
do { \
|
||||
typedef T type; \
|
||||
typedef type **typepp; \
|
||||
typedef __typeof__((C)) ctype; \
|
||||
typedef ctype **ctypepp; \
|
||||
typepp x = 0; \
|
||||
ctypepp y = 0; \
|
||||
x = y; \
|
||||
y = x; \
|
||||
} while (0)
|
||||
|
||||
/* (T *) if E is zero, (void *) otherwise. */
|
||||
#define type_if_not(T, E) __typeof__(0 ? (T *)0 : (void *)(E))
|
||||
|
||||
/* (T *) if E is nonzero, (void *) otherwise. */
|
||||
#define type_if(T, E) type_if_not(T, !(E))
|
||||
|
||||
/* Combine pointer types, all but one (void *). */
|
||||
#define type_comb2(T1, T2) __typeof__(0 ? (T1)0 : (T2)0)
|
||||
#define type_comb3(T1, T2, T3) type_comb2(T1, type_comb2(T2, T3))
|
||||
#define type_comb4(T1, T2, T3, T4) \
|
||||
type_comb2(T1, type_comb2(T2, type_comb2(T3, T4)))
|
||||
#define type_comb6(T1, T2, T3, T4, T5, T6) \
|
||||
type_comb2(T1, \
|
||||
type_comb2(T2, \
|
||||
type_comb2(T3, \
|
||||
type_comb2(T4, \
|
||||
type_comb2(T5, T6)))))
|
||||
|
||||
/* (T1 *) if E1, otherwise (T2 *) if E2. */
|
||||
#define first_of2p(T1, E1, T2, E2) type_comb2(type_if(T1, (E1)), \
|
||||
type_if(T2, (!(E1) && (E2))))
|
||||
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3. */
|
||||
#define first_of3p(T1, E1, T2, E2, T3, E3) \
|
||||
type_comb3(type_if(T1, (E1)), \
|
||||
type_if(T2, (!(E1) && (E2))), \
|
||||
type_if(T3, (!(E1) && !(E2) && (E3))))
|
||||
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3, otherwise
|
||||
(T4 *) if E4. */
|
||||
#define first_of4p(T1, E1, T2, E2, T3, E3, T4, E4) \
|
||||
type_comb4(type_if(T1, (E1)), \
|
||||
type_if(T2, (!(E1) && (E2))), \
|
||||
type_if(T3, (!(E1) && !(E2) && (E3))), \
|
||||
type_if(T4, (!(E1) && !(E2) && !(E3) && (E4))))
|
||||
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3, otherwise
|
||||
(T4 *) if E4, otherwise (T5 *) if E5, otherwise (T6 *) if E6. */
|
||||
#define first_of6p(T1, E1, T2, E2, T3, E3, T4, E4, T5, E5, T6, E6) \
|
||||
type_comb6(type_if(T1, (E1)), \
|
||||
type_if(T2, (!(E1) && (E2))), \
|
||||
type_if(T3, (!(E1) && !(E2) && (E3))), \
|
||||
type_if(T4, (!(E1) && !(E2) && !(E3) && (E4))), \
|
||||
type_if(T5, (!(E1) && !(E2) && !(E3) && !(E4) && (E5))), \
|
||||
type_if(T6, (!(E1) && !(E2) && !(E3) \
|
||||
&& !(E4) && !(E5) && (E6))))
|
||||
|
||||
/* Likewise, but return the original type rather than a pointer type. */
|
||||
#define first_of2(T1, E1, T2, E2) \
|
||||
__typeof__(*((first_of2p(T1, (E1), T2, (E2)))0))
|
||||
#define first_of3(T1, E1, T2, E2, T3, E3) \
|
||||
__typeof__(*((first_of3p(T1, (E1), T2, (E2), T3, (E3)))0))
|
||||
#define first_of4(T1, E1, T2, E2, T3, E3, T4, E4) \
|
||||
__typeof__(*((first_of4p(T1, (E1), T2, (E2), T3, (E3), T4, (E4)))0))
|
||||
#define first_of6(T1, E1, T2, E2, T3, E3, T4, E4, T5, E5, T6, E6) \
|
||||
__typeof__(*((first_of6p(T1, (E1), T2, (E2), T3, (E3), \
|
||||
T4, (E4), T5, (E5), T6, (E6)))0))
|
||||
|
||||
/* Types of constants according to the C99 rules. */
|
||||
#define C99_UNSUF_DEC_TYPE(C) \
|
||||
first_of3(int, (C) <= INT_MAX, \
|
||||
long int, (C) <= LONG_MAX, \
|
||||
long long int, (C) <= LLONG_MAX)
|
||||
#define C99_UNSUF_OCTHEX_TYPE(C) \
|
||||
first_of6(int, (C) <= INT_MAX, \
|
||||
unsigned int, (C) <= UINT_MAX, \
|
||||
long int, (C) <= LONG_MAX, \
|
||||
unsigned long int, (C) <= ULONG_MAX, \
|
||||
long long int, (C) <= LLONG_MAX, \
|
||||
unsigned long long int, (C) <= ULLONG_MAX)
|
||||
#define C99_SUFu_TYPE(C) \
|
||||
first_of3(unsigned int, (C) <= UINT_MAX, \
|
||||
unsigned long int, (C) <= ULONG_MAX, \
|
||||
unsigned long long int, (C) <= ULLONG_MAX)
|
||||
#define C99_SUFl_DEC_TYPE(C) \
|
||||
first_of2(long int, (C) <= LONG_MAX, \
|
||||
long long int, (C) <= LLONG_MAX)
|
||||
#define C99_SUFl_OCTHEX_TYPE(C) \
|
||||
first_of4(long int, (C) <= LONG_MAX, \
|
||||
unsigned long int, (C) <= ULONG_MAX, \
|
||||
long long int, (C) <= LLONG_MAX, \
|
||||
unsigned long long int, (C) <= ULLONG_MAX)
|
||||
#define C99_SUFul_TYPE(C) \
|
||||
first_of2(unsigned long int, (C) <= ULONG_MAX, \
|
||||
unsigned long long int, (C) <= ULLONG_MAX)
|
||||
#define C99_SUFll_OCTHEX_TYPE(C) \
|
||||
first_of2(long long int, (C) <= LLONG_MAX, \
|
||||
unsigned long long int, (C) <= ULLONG_MAX)
|
||||
|
||||
/* Checks that constants have correct type. */
|
||||
#define CHECK_UNSUF_DEC_TYPE(C) ASSERT_CONST_TYPE((C), C99_UNSUF_DEC_TYPE((C)))
|
||||
#define CHECK_UNSUF_OCTHEX_TYPE(C) \
|
||||
ASSERT_CONST_TYPE((C), C99_UNSUF_OCTHEX_TYPE((C)))
|
||||
#define CHECK_SUFu_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFu_TYPE((C)))
|
||||
#define CHECK_SUFl_DEC_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFl_DEC_TYPE((C)))
|
||||
#define CHECK_SUFl_OCTHEX_TYPE(C) \
|
||||
ASSERT_CONST_TYPE((C), C99_SUFl_OCTHEX_TYPE((C)))
|
||||
#define CHECK_SUFul_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFul_TYPE((C)))
|
||||
#define CHECK_SUFll_DEC_TYPE(C) ASSERT_CONST_TYPE((C), long long int)
|
||||
#define CHECK_SUFll_OCTHEX_TYPE(C) \
|
||||
ASSERT_CONST_TYPE((C), C99_SUFll_OCTHEX_TYPE((C)))
|
||||
#define CHECK_SUFull_TYPE(C) ASSERT_CONST_TYPE((C), unsigned long long int)
|
||||
|
||||
/* Check a decimal value, with all suffixes. */
|
||||
#define CHECK_DEC_CONST(C) \
|
||||
CHECK_UNSUF_DEC_TYPE(C); \
|
||||
CHECK_SUFu_TYPE(C##u); \
|
||||
CHECK_SUFu_TYPE(C##U); \
|
||||
CHECK_SUFl_DEC_TYPE(C##l); \
|
||||
CHECK_SUFl_DEC_TYPE(C##L); \
|
||||
CHECK_SUFul_TYPE(C##ul); \
|
||||
CHECK_SUFul_TYPE(C##uL); \
|
||||
CHECK_SUFul_TYPE(C##Ul); \
|
||||
CHECK_SUFul_TYPE(C##UL); \
|
||||
CHECK_SUFll_DEC_TYPE(C##ll); \
|
||||
CHECK_SUFll_DEC_TYPE(C##LL); \
|
||||
CHECK_SUFull_TYPE(C##ull); \
|
||||
CHECK_SUFull_TYPE(C##uLL); \
|
||||
CHECK_SUFull_TYPE(C##Ull); \
|
||||
CHECK_SUFull_TYPE(C##ULL);
|
||||
|
||||
/* Check an octal or hexadecimal value, with all suffixes. */
|
||||
#define CHECK_OCTHEX_CONST(C) \
|
||||
CHECK_UNSUF_OCTHEX_TYPE(C); \
|
||||
CHECK_SUFu_TYPE(C##u); \
|
||||
CHECK_SUFu_TYPE(C##U); \
|
||||
CHECK_SUFl_OCTHEX_TYPE(C##l); \
|
||||
CHECK_SUFl_OCTHEX_TYPE(C##L); \
|
||||
CHECK_SUFul_TYPE(C##ul); \
|
||||
CHECK_SUFul_TYPE(C##uL); \
|
||||
CHECK_SUFul_TYPE(C##Ul); \
|
||||
CHECK_SUFul_TYPE(C##UL); \
|
||||
CHECK_SUFll_OCTHEX_TYPE(C##ll); \
|
||||
CHECK_SUFll_OCTHEX_TYPE(C##LL); \
|
||||
CHECK_SUFull_TYPE(C##ull); \
|
||||
CHECK_SUFull_TYPE(C##uLL); \
|
||||
CHECK_SUFull_TYPE(C##Ull); \
|
||||
CHECK_SUFull_TYPE(C##ULL);
|
||||
|
||||
#define CHECK_OCT_CONST(C) CHECK_OCTHEX_CONST(C)
|
||||
#define CHECK_HEX_CONST(C) \
|
||||
CHECK_OCTHEX_CONST(0x##C); \
|
||||
CHECK_OCTHEX_CONST(0X##C);
|
||||
|
||||
/* True iff "long long" is at least B bits. This presumes that (B-2)/3 is at
|
||||
most 63. */
|
||||
#define LLONG_AT_LEAST(B) \
|
||||
(LLONG_MAX >> ((B)-2)/3 >> ((B)-2)/3 \
|
||||
>> ((B)-2 - ((B)-2)/3 - ((B)-2)/3))
|
||||
|
||||
#define LLONG_HAS_BITS(B) (LLONG_AT_LEAST((B)) && !LLONG_AT_LEAST((B) + 1))
|
||||
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
/* Decimal. */
|
||||
/* Check all 2^n and 2^n - 1 up to 2^71 - 1. */
|
||||
CHECK_DEC_CONST(1);
|
||||
CHECK_DEC_CONST(2);
|
||||
CHECK_DEC_CONST(3);
|
||||
CHECK_DEC_CONST(4);
|
||||
CHECK_DEC_CONST(7);
|
||||
CHECK_DEC_CONST(8);
|
||||
CHECK_DEC_CONST(15);
|
||||
CHECK_DEC_CONST(16);
|
||||
CHECK_DEC_CONST(31);
|
||||
CHECK_DEC_CONST(32);
|
||||
CHECK_DEC_CONST(63);
|
||||
CHECK_DEC_CONST(64);
|
||||
CHECK_DEC_CONST(127);
|
||||
CHECK_DEC_CONST(128);
|
||||
CHECK_DEC_CONST(255);
|
||||
CHECK_DEC_CONST(256);
|
||||
CHECK_DEC_CONST(511);
|
||||
CHECK_DEC_CONST(512);
|
||||
CHECK_DEC_CONST(1023);
|
||||
CHECK_DEC_CONST(1024);
|
||||
CHECK_DEC_CONST(2047);
|
||||
CHECK_DEC_CONST(2048);
|
||||
CHECK_DEC_CONST(4095);
|
||||
CHECK_DEC_CONST(4096);
|
||||
CHECK_DEC_CONST(8191);
|
||||
CHECK_DEC_CONST(8192);
|
||||
CHECK_DEC_CONST(16383);
|
||||
CHECK_DEC_CONST(16384);
|
||||
CHECK_DEC_CONST(32767);
|
||||
CHECK_DEC_CONST(32768);
|
||||
CHECK_DEC_CONST(65535);
|
||||
CHECK_DEC_CONST(65536);
|
||||
CHECK_DEC_CONST(131071);
|
||||
CHECK_DEC_CONST(131072);
|
||||
CHECK_DEC_CONST(262143);
|
||||
CHECK_DEC_CONST(262144);
|
||||
CHECK_DEC_CONST(524287);
|
||||
CHECK_DEC_CONST(524288);
|
||||
CHECK_DEC_CONST(1048575);
|
||||
CHECK_DEC_CONST(1048576);
|
||||
CHECK_DEC_CONST(2097151);
|
||||
CHECK_DEC_CONST(2097152);
|
||||
CHECK_DEC_CONST(4194303);
|
||||
CHECK_DEC_CONST(4194304);
|
||||
CHECK_DEC_CONST(8388607);
|
||||
CHECK_DEC_CONST(8388608);
|
||||
CHECK_DEC_CONST(16777215);
|
||||
CHECK_DEC_CONST(16777216);
|
||||
CHECK_DEC_CONST(33554431);
|
||||
CHECK_DEC_CONST(33554432);
|
||||
CHECK_DEC_CONST(67108863);
|
||||
CHECK_DEC_CONST(67108864);
|
||||
CHECK_DEC_CONST(134217727);
|
||||
CHECK_DEC_CONST(134217728);
|
||||
CHECK_DEC_CONST(268435455);
|
||||
CHECK_DEC_CONST(268435456);
|
||||
CHECK_DEC_CONST(536870911);
|
||||
CHECK_DEC_CONST(536870912);
|
||||
CHECK_DEC_CONST(1073741823);
|
||||
CHECK_DEC_CONST(1073741824);
|
||||
CHECK_DEC_CONST(2147483647);
|
||||
CHECK_DEC_CONST(2147483648);
|
||||
CHECK_DEC_CONST(4294967295);
|
||||
CHECK_DEC_CONST(4294967296);
|
||||
CHECK_DEC_CONST(8589934591);
|
||||
CHECK_DEC_CONST(8589934592);
|
||||
CHECK_DEC_CONST(17179869183);
|
||||
CHECK_DEC_CONST(17179869184);
|
||||
CHECK_DEC_CONST(34359738367);
|
||||
CHECK_DEC_CONST(34359738368);
|
||||
CHECK_DEC_CONST(68719476735);
|
||||
CHECK_DEC_CONST(68719476736);
|
||||
CHECK_DEC_CONST(137438953471);
|
||||
CHECK_DEC_CONST(137438953472);
|
||||
CHECK_DEC_CONST(274877906943);
|
||||
CHECK_DEC_CONST(274877906944);
|
||||
CHECK_DEC_CONST(549755813887);
|
||||
CHECK_DEC_CONST(549755813888);
|
||||
CHECK_DEC_CONST(1099511627775);
|
||||
CHECK_DEC_CONST(1099511627776);
|
||||
CHECK_DEC_CONST(2199023255551);
|
||||
CHECK_DEC_CONST(2199023255552);
|
||||
CHECK_DEC_CONST(4398046511103);
|
||||
CHECK_DEC_CONST(4398046511104);
|
||||
CHECK_DEC_CONST(8796093022207);
|
||||
CHECK_DEC_CONST(8796093022208);
|
||||
CHECK_DEC_CONST(17592186044415);
|
||||
CHECK_DEC_CONST(17592186044416);
|
||||
CHECK_DEC_CONST(35184372088831);
|
||||
CHECK_DEC_CONST(35184372088832);
|
||||
CHECK_DEC_CONST(70368744177663);
|
||||
CHECK_DEC_CONST(70368744177664);
|
||||
CHECK_DEC_CONST(140737488355327);
|
||||
CHECK_DEC_CONST(140737488355328);
|
||||
CHECK_DEC_CONST(281474976710655);
|
||||
CHECK_DEC_CONST(281474976710656);
|
||||
CHECK_DEC_CONST(562949953421311);
|
||||
CHECK_DEC_CONST(562949953421312);
|
||||
CHECK_DEC_CONST(1125899906842623);
|
||||
CHECK_DEC_CONST(1125899906842624);
|
||||
CHECK_DEC_CONST(2251799813685247);
|
||||
CHECK_DEC_CONST(2251799813685248);
|
||||
CHECK_DEC_CONST(4503599627370495);
|
||||
CHECK_DEC_CONST(4503599627370496);
|
||||
CHECK_DEC_CONST(9007199254740991);
|
||||
CHECK_DEC_CONST(9007199254740992);
|
||||
CHECK_DEC_CONST(18014398509481983);
|
||||
CHECK_DEC_CONST(18014398509481984);
|
||||
CHECK_DEC_CONST(36028797018963967);
|
||||
CHECK_DEC_CONST(36028797018963968);
|
||||
CHECK_DEC_CONST(72057594037927935);
|
||||
CHECK_DEC_CONST(72057594037927936);
|
||||
CHECK_DEC_CONST(144115188075855871);
|
||||
CHECK_DEC_CONST(144115188075855872);
|
||||
CHECK_DEC_CONST(288230376151711743);
|
||||
CHECK_DEC_CONST(288230376151711744);
|
||||
CHECK_DEC_CONST(576460752303423487);
|
||||
CHECK_DEC_CONST(576460752303423488);
|
||||
CHECK_DEC_CONST(1152921504606846975);
|
||||
CHECK_DEC_CONST(1152921504606846976);
|
||||
CHECK_DEC_CONST(2305843009213693951);
|
||||
CHECK_DEC_CONST(2305843009213693952);
|
||||
CHECK_DEC_CONST(4611686018427387903);
|
||||
CHECK_DEC_CONST(4611686018427387904);
|
||||
CHECK_DEC_CONST(9223372036854775807);
|
||||
#if LLONG_AT_LEAST(65)
|
||||
CHECK_DEC_CONST(9223372036854775808);
|
||||
CHECK_DEC_CONST(18446744073709551615);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(66)
|
||||
CHECK_DEC_CONST(18446744073709551616);
|
||||
CHECK_DEC_CONST(36893488147419103231);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(67)
|
||||
CHECK_DEC_CONST(36893488147419103232);
|
||||
CHECK_DEC_CONST(73786976294838206463);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(68)
|
||||
CHECK_DEC_CONST(73786976294838206464);
|
||||
CHECK_DEC_CONST(147573952589676412927);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(69)
|
||||
CHECK_DEC_CONST(147573952589676412928);
|
||||
CHECK_DEC_CONST(295147905179352825855);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(70)
|
||||
CHECK_DEC_CONST(295147905179352825856);
|
||||
CHECK_DEC_CONST(590295810358705651711);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(71)
|
||||
CHECK_DEC_CONST(590295810358705651712);
|
||||
CHECK_DEC_CONST(1180591620717411303423);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(72)
|
||||
CHECK_DEC_CONST(1180591620717411303424);
|
||||
CHECK_DEC_CONST(2361183241434822606847);
|
||||
#endif
|
||||
/* Octal and hexadecimal. */
|
||||
/* Check all 2^n and 2^n - 1 up to 2^72 - 1. */
|
||||
CHECK_OCT_CONST(0);
|
||||
CHECK_HEX_CONST(0);
|
||||
CHECK_OCT_CONST(01);
|
||||
CHECK_HEX_CONST(1);
|
||||
CHECK_OCT_CONST(02);
|
||||
CHECK_HEX_CONST(2);
|
||||
CHECK_OCT_CONST(03);
|
||||
CHECK_HEX_CONST(3);
|
||||
CHECK_OCT_CONST(04);
|
||||
CHECK_HEX_CONST(4);
|
||||
CHECK_OCT_CONST(07);
|
||||
CHECK_HEX_CONST(7);
|
||||
CHECK_OCT_CONST(010);
|
||||
CHECK_HEX_CONST(8);
|
||||
CHECK_OCT_CONST(017);
|
||||
CHECK_HEX_CONST(f);
|
||||
CHECK_OCT_CONST(020);
|
||||
CHECK_HEX_CONST(10);
|
||||
CHECK_OCT_CONST(037);
|
||||
CHECK_HEX_CONST(1f);
|
||||
CHECK_OCT_CONST(040);
|
||||
CHECK_HEX_CONST(20);
|
||||
CHECK_OCT_CONST(077);
|
||||
CHECK_HEX_CONST(3f);
|
||||
CHECK_OCT_CONST(0100);
|
||||
CHECK_HEX_CONST(40);
|
||||
CHECK_OCT_CONST(0177);
|
||||
CHECK_HEX_CONST(7f);
|
||||
CHECK_OCT_CONST(0200);
|
||||
CHECK_HEX_CONST(80);
|
||||
CHECK_OCT_CONST(0377);
|
||||
CHECK_HEX_CONST(ff);
|
||||
CHECK_OCT_CONST(0400);
|
||||
CHECK_HEX_CONST(100);
|
||||
CHECK_OCT_CONST(0777);
|
||||
CHECK_HEX_CONST(1ff);
|
||||
CHECK_OCT_CONST(01000);
|
||||
CHECK_HEX_CONST(200);
|
||||
CHECK_OCT_CONST(01777);
|
||||
CHECK_HEX_CONST(3ff);
|
||||
CHECK_OCT_CONST(02000);
|
||||
CHECK_HEX_CONST(400);
|
||||
CHECK_OCT_CONST(03777);
|
||||
CHECK_HEX_CONST(7ff);
|
||||
CHECK_OCT_CONST(04000);
|
||||
CHECK_HEX_CONST(800);
|
||||
CHECK_OCT_CONST(07777);
|
||||
CHECK_HEX_CONST(fff);
|
||||
CHECK_OCT_CONST(010000);
|
||||
CHECK_HEX_CONST(1000);
|
||||
CHECK_OCT_CONST(017777);
|
||||
CHECK_HEX_CONST(1fff);
|
||||
CHECK_OCT_CONST(020000);
|
||||
CHECK_HEX_CONST(2000);
|
||||
CHECK_OCT_CONST(037777);
|
||||
CHECK_HEX_CONST(3fff);
|
||||
CHECK_OCT_CONST(040000);
|
||||
CHECK_HEX_CONST(4000);
|
||||
CHECK_OCT_CONST(077777);
|
||||
CHECK_HEX_CONST(7fff);
|
||||
CHECK_OCT_CONST(0100000);
|
||||
CHECK_HEX_CONST(8000);
|
||||
CHECK_OCT_CONST(0177777);
|
||||
CHECK_HEX_CONST(ffff);
|
||||
CHECK_OCT_CONST(0200000);
|
||||
CHECK_HEX_CONST(10000);
|
||||
CHECK_OCT_CONST(0377777);
|
||||
CHECK_HEX_CONST(1ffff);
|
||||
CHECK_OCT_CONST(0400000);
|
||||
CHECK_HEX_CONST(20000);
|
||||
CHECK_OCT_CONST(0777777);
|
||||
CHECK_HEX_CONST(3ffff);
|
||||
CHECK_OCT_CONST(01000000);
|
||||
CHECK_HEX_CONST(40000);
|
||||
CHECK_OCT_CONST(01777777);
|
||||
CHECK_HEX_CONST(7ffff);
|
||||
CHECK_OCT_CONST(02000000);
|
||||
CHECK_HEX_CONST(80000);
|
||||
CHECK_OCT_CONST(03777777);
|
||||
CHECK_HEX_CONST(fffff);
|
||||
CHECK_OCT_CONST(04000000);
|
||||
CHECK_HEX_CONST(100000);
|
||||
CHECK_OCT_CONST(07777777);
|
||||
CHECK_HEX_CONST(1fffff);
|
||||
CHECK_OCT_CONST(010000000);
|
||||
CHECK_HEX_CONST(200000);
|
||||
CHECK_OCT_CONST(017777777);
|
||||
CHECK_HEX_CONST(3fffff);
|
||||
CHECK_OCT_CONST(020000000);
|
||||
CHECK_HEX_CONST(400000);
|
||||
CHECK_OCT_CONST(037777777);
|
||||
CHECK_HEX_CONST(7fffff);
|
||||
CHECK_OCT_CONST(040000000);
|
||||
CHECK_HEX_CONST(800000);
|
||||
CHECK_OCT_CONST(077777777);
|
||||
CHECK_HEX_CONST(ffffff);
|
||||
CHECK_OCT_CONST(0100000000);
|
||||
CHECK_HEX_CONST(1000000);
|
||||
CHECK_OCT_CONST(0177777777);
|
||||
CHECK_HEX_CONST(1ffffff);
|
||||
CHECK_OCT_CONST(0200000000);
|
||||
CHECK_HEX_CONST(2000000);
|
||||
CHECK_OCT_CONST(0377777777);
|
||||
CHECK_HEX_CONST(3ffffff);
|
||||
CHECK_OCT_CONST(0400000000);
|
||||
CHECK_HEX_CONST(4000000);
|
||||
CHECK_OCT_CONST(0777777777);
|
||||
CHECK_HEX_CONST(7ffffff);
|
||||
CHECK_OCT_CONST(01000000000);
|
||||
CHECK_HEX_CONST(8000000);
|
||||
CHECK_OCT_CONST(01777777777);
|
||||
CHECK_HEX_CONST(fffffff);
|
||||
CHECK_OCT_CONST(02000000000);
|
||||
CHECK_HEX_CONST(10000000);
|
||||
CHECK_OCT_CONST(03777777777);
|
||||
CHECK_HEX_CONST(1fffffff);
|
||||
CHECK_OCT_CONST(04000000000);
|
||||
CHECK_HEX_CONST(20000000);
|
||||
CHECK_OCT_CONST(07777777777);
|
||||
CHECK_HEX_CONST(3fffffff);
|
||||
CHECK_OCT_CONST(010000000000);
|
||||
CHECK_HEX_CONST(40000000);
|
||||
CHECK_OCT_CONST(017777777777);
|
||||
CHECK_HEX_CONST(7fffffff);
|
||||
CHECK_OCT_CONST(020000000000);
|
||||
CHECK_HEX_CONST(80000000);
|
||||
CHECK_OCT_CONST(037777777777);
|
||||
CHECK_HEX_CONST(ffffffff);
|
||||
CHECK_OCT_CONST(040000000000);
|
||||
CHECK_HEX_CONST(100000000);
|
||||
CHECK_OCT_CONST(077777777777);
|
||||
CHECK_HEX_CONST(1ffffffff);
|
||||
CHECK_OCT_CONST(0100000000000);
|
||||
CHECK_HEX_CONST(200000000);
|
||||
CHECK_OCT_CONST(0177777777777);
|
||||
CHECK_HEX_CONST(3ffffffff);
|
||||
CHECK_OCT_CONST(0200000000000);
|
||||
CHECK_HEX_CONST(400000000);
|
||||
CHECK_OCT_CONST(0377777777777);
|
||||
CHECK_HEX_CONST(7ffffffff);
|
||||
CHECK_OCT_CONST(0400000000000);
|
||||
CHECK_HEX_CONST(800000000);
|
||||
CHECK_OCT_CONST(0777777777777);
|
||||
CHECK_HEX_CONST(fffffffff);
|
||||
CHECK_OCT_CONST(01000000000000);
|
||||
CHECK_HEX_CONST(1000000000);
|
||||
CHECK_OCT_CONST(01777777777777);
|
||||
CHECK_HEX_CONST(1fffffffff);
|
||||
CHECK_OCT_CONST(02000000000000);
|
||||
CHECK_HEX_CONST(2000000000);
|
||||
CHECK_OCT_CONST(03777777777777);
|
||||
CHECK_HEX_CONST(3fffffffff);
|
||||
CHECK_OCT_CONST(04000000000000);
|
||||
CHECK_HEX_CONST(4000000000);
|
||||
CHECK_OCT_CONST(07777777777777);
|
||||
CHECK_HEX_CONST(7fffffffff);
|
||||
CHECK_OCT_CONST(010000000000000);
|
||||
CHECK_HEX_CONST(8000000000);
|
||||
CHECK_OCT_CONST(017777777777777);
|
||||
CHECK_HEX_CONST(ffffffffff);
|
||||
CHECK_OCT_CONST(020000000000000);
|
||||
CHECK_HEX_CONST(10000000000);
|
||||
CHECK_OCT_CONST(037777777777777);
|
||||
CHECK_HEX_CONST(1ffffffffff);
|
||||
CHECK_OCT_CONST(040000000000000);
|
||||
CHECK_HEX_CONST(20000000000);
|
||||
CHECK_OCT_CONST(077777777777777);
|
||||
CHECK_HEX_CONST(3ffffffffff);
|
||||
CHECK_OCT_CONST(0100000000000000);
|
||||
CHECK_HEX_CONST(40000000000);
|
||||
CHECK_OCT_CONST(0177777777777777);
|
||||
CHECK_HEX_CONST(7ffffffffff);
|
||||
CHECK_OCT_CONST(0200000000000000);
|
||||
CHECK_HEX_CONST(80000000000);
|
||||
CHECK_OCT_CONST(0377777777777777);
|
||||
CHECK_HEX_CONST(fffffffffff);
|
||||
CHECK_OCT_CONST(0400000000000000);
|
||||
CHECK_HEX_CONST(100000000000);
|
||||
CHECK_OCT_CONST(0777777777777777);
|
||||
CHECK_HEX_CONST(1fffffffffff);
|
||||
CHECK_OCT_CONST(01000000000000000);
|
||||
CHECK_HEX_CONST(200000000000);
|
||||
CHECK_OCT_CONST(01777777777777777);
|
||||
CHECK_HEX_CONST(3fffffffffff);
|
||||
CHECK_OCT_CONST(02000000000000000);
|
||||
CHECK_HEX_CONST(400000000000);
|
||||
CHECK_OCT_CONST(03777777777777777);
|
||||
CHECK_HEX_CONST(7fffffffffff);
|
||||
CHECK_OCT_CONST(04000000000000000);
|
||||
CHECK_HEX_CONST(800000000000);
|
||||
CHECK_OCT_CONST(07777777777777777);
|
||||
CHECK_HEX_CONST(ffffffffffff);
|
||||
CHECK_OCT_CONST(010000000000000000);
|
||||
CHECK_HEX_CONST(1000000000000);
|
||||
CHECK_OCT_CONST(017777777777777777);
|
||||
CHECK_HEX_CONST(1ffffffffffff);
|
||||
CHECK_OCT_CONST(020000000000000000);
|
||||
CHECK_HEX_CONST(2000000000000);
|
||||
CHECK_OCT_CONST(037777777777777777);
|
||||
CHECK_HEX_CONST(3ffffffffffff);
|
||||
CHECK_OCT_CONST(040000000000000000);
|
||||
CHECK_HEX_CONST(4000000000000);
|
||||
CHECK_OCT_CONST(077777777777777777);
|
||||
CHECK_HEX_CONST(7ffffffffffff);
|
||||
CHECK_OCT_CONST(0100000000000000000);
|
||||
CHECK_HEX_CONST(8000000000000);
|
||||
CHECK_OCT_CONST(0177777777777777777);
|
||||
CHECK_HEX_CONST(fffffffffffff);
|
||||
CHECK_OCT_CONST(0200000000000000000);
|
||||
CHECK_HEX_CONST(10000000000000);
|
||||
CHECK_OCT_CONST(0377777777777777777);
|
||||
CHECK_HEX_CONST(1fffffffffffff);
|
||||
CHECK_OCT_CONST(0400000000000000000);
|
||||
CHECK_HEX_CONST(20000000000000);
|
||||
CHECK_OCT_CONST(0777777777777777777);
|
||||
CHECK_HEX_CONST(3fffffffffffff);
|
||||
CHECK_OCT_CONST(01000000000000000000);
|
||||
CHECK_HEX_CONST(40000000000000);
|
||||
CHECK_OCT_CONST(01777777777777777777);
|
||||
CHECK_HEX_CONST(7fffffffffffff);
|
||||
CHECK_OCT_CONST(02000000000000000000);
|
||||
CHECK_HEX_CONST(80000000000000);
|
||||
CHECK_OCT_CONST(03777777777777777777);
|
||||
CHECK_HEX_CONST(ffffffffffffff);
|
||||
CHECK_OCT_CONST(04000000000000000000);
|
||||
CHECK_HEX_CONST(100000000000000);
|
||||
CHECK_OCT_CONST(07777777777777777777);
|
||||
CHECK_HEX_CONST(1ffffffffffffff);
|
||||
CHECK_OCT_CONST(010000000000000000000);
|
||||
CHECK_HEX_CONST(200000000000000);
|
||||
CHECK_OCT_CONST(017777777777777777777);
|
||||
CHECK_HEX_CONST(3ffffffffffffff);
|
||||
CHECK_OCT_CONST(020000000000000000000);
|
||||
CHECK_HEX_CONST(400000000000000);
|
||||
CHECK_OCT_CONST(037777777777777777777);
|
||||
CHECK_HEX_CONST(7ffffffffffffff);
|
||||
CHECK_OCT_CONST(040000000000000000000);
|
||||
CHECK_HEX_CONST(800000000000000);
|
||||
CHECK_OCT_CONST(077777777777777777777);
|
||||
CHECK_HEX_CONST(fffffffffffffff);
|
||||
CHECK_OCT_CONST(0100000000000000000000);
|
||||
CHECK_HEX_CONST(1000000000000000);
|
||||
CHECK_OCT_CONST(0177777777777777777777);
|
||||
CHECK_HEX_CONST(1fffffffffffffff);
|
||||
CHECK_OCT_CONST(0200000000000000000000);
|
||||
CHECK_HEX_CONST(2000000000000000);
|
||||
CHECK_OCT_CONST(0377777777777777777777);
|
||||
CHECK_HEX_CONST(3fffffffffffffff);
|
||||
CHECK_OCT_CONST(0400000000000000000000);
|
||||
CHECK_HEX_CONST(4000000000000000);
|
||||
CHECK_OCT_CONST(0777777777777777777777);
|
||||
CHECK_HEX_CONST(7fffffffffffffff);
|
||||
CHECK_OCT_CONST(01000000000000000000000);
|
||||
CHECK_HEX_CONST(8000000000000000);
|
||||
CHECK_OCT_CONST(01777777777777777777777);
|
||||
CHECK_HEX_CONST(ffffffffffffffff);
|
||||
#if LLONG_AT_LEAST(65)
|
||||
CHECK_OCT_CONST(02000000000000000000000);
|
||||
CHECK_HEX_CONST(10000000000000000);
|
||||
CHECK_OCT_CONST(03777777777777777777777);
|
||||
CHECK_HEX_CONST(1ffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(66)
|
||||
CHECK_OCT_CONST(04000000000000000000000);
|
||||
CHECK_HEX_CONST(20000000000000000);
|
||||
CHECK_OCT_CONST(07777777777777777777777);
|
||||
CHECK_HEX_CONST(3ffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(67)
|
||||
CHECK_OCT_CONST(010000000000000000000000);
|
||||
CHECK_HEX_CONST(40000000000000000);
|
||||
CHECK_OCT_CONST(017777777777777777777777);
|
||||
CHECK_HEX_CONST(7ffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(68)
|
||||
CHECK_OCT_CONST(020000000000000000000000);
|
||||
CHECK_HEX_CONST(80000000000000000);
|
||||
CHECK_OCT_CONST(037777777777777777777777);
|
||||
CHECK_HEX_CONST(fffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(69)
|
||||
CHECK_OCT_CONST(040000000000000000000000);
|
||||
CHECK_HEX_CONST(100000000000000000);
|
||||
CHECK_OCT_CONST(077777777777777777777777);
|
||||
CHECK_HEX_CONST(1fffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(70)
|
||||
CHECK_OCT_CONST(0100000000000000000000000);
|
||||
CHECK_HEX_CONST(200000000000000000);
|
||||
CHECK_OCT_CONST(0177777777777777777777777);
|
||||
CHECK_HEX_CONST(3fffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(71)
|
||||
CHECK_OCT_CONST(0200000000000000000000000);
|
||||
CHECK_HEX_CONST(400000000000000000);
|
||||
CHECK_OCT_CONST(0377777777777777777777777);
|
||||
CHECK_HEX_CONST(7fffffffffffffffff);
|
||||
#endif
|
||||
#if LLONG_AT_LEAST(72)
|
||||
CHECK_OCT_CONST(0400000000000000000000000);
|
||||
CHECK_HEX_CONST(800000000000000000);
|
||||
CHECK_OCT_CONST(0777777777777777777777777);
|
||||
CHECK_HEX_CONST(ffffffffffffffffff);
|
||||
#endif
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
//#import<vecLib/vecLib.h>
|
@ -1,27 +0,0 @@
|
||||
#define EXPAND_2_CASES(i, x, y) CASE(i, x, y); CASE(i + 1, x, y);
|
||||
#define EXPAND_4_CASES(i, x, y) EXPAND_2_CASES(i, x, y) EXPAND_2_CASES(i + 2, x, y)
|
||||
#define EXPAND_8_CASES(i, x, y) EXPAND_4_CASES(i, x, y) EXPAND_4_CASES(i + 4, x, y)
|
||||
#define EXPAND_16_CASES(i, x, y) EXPAND_8_CASES(i, x, y) EXPAND_8_CASES(i + 8, x, y)
|
||||
#define EXPAND_32_CASES(i, x, y) EXPAND_16_CASES(i, x, y) EXPAND_16_CASES(i + 16, x, y)
|
||||
#define EXPAND_64_CASES(i, x, y) EXPAND_32_CASES(i, x, y) EXPAND_32_CASES(i + 32, x, y)
|
||||
#define EXPAND_128_CASES(i, x, y) EXPAND_64_CASES(i, x, y) EXPAND_64_CASES(i + 64, x, y)
|
||||
#define EXPAND_256_CASES(i, x, y) EXPAND_128_CASES(i, x, y) EXPAND_128_CASES(i + 128, x, y)
|
||||
#define EXPAND_512_CASES(i, x, y) EXPAND_256_CASES(i, x, y) EXPAND_256_CASES(i + 256, x, y)
|
||||
#define EXPAND_1024_CASES(i, x, y) EXPAND_512_CASES(i, x, y) EXPAND_512_CASES(i + 512, x, y)
|
||||
#define EXPAND_2048_CASES(i, x, y) EXPAND_1024_CASES(i, x, y) EXPAND_1024_CASES(i + 1024, x, y)
|
||||
#define EXPAND_4096_CASES(i, x, y) EXPAND_2048_CASES(i, x, y) EXPAND_2048_CASES(i + 2048, x, y)
|
||||
|
||||
// This has a *monstrous* single fan-out in the CFG, across 8000 blocks inside
|
||||
// the while loop.
|
||||
unsigned cfg_big_switch(int x) {
|
||||
unsigned y = 0;
|
||||
while (x > 0) {
|
||||
switch(x) {
|
||||
#define CASE(i, x, y) \
|
||||
case i: { int case_var = 3*x + i; y += case_var - 1; break; }
|
||||
EXPAND_4096_CASES(0, x, y);
|
||||
}
|
||||
--x;
|
||||
}
|
||||
return y;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
|
||||
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
|
||||
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
|
||||
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
|
||||
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
|
||||
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
|
||||
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
|
||||
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
|
||||
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
|
||||
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
|
||||
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
|
||||
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
|
||||
|
||||
unsigned cfg_long_chain_single_exit(unsigned x) {
|
||||
unsigned y = 0;
|
||||
#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; }
|
||||
EXPAND_4096_BRANCHES(1, x, y);
|
||||
#undef BRANCH
|
||||
return y;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
|
||||
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
|
||||
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
|
||||
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
|
||||
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
|
||||
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
|
||||
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
|
||||
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
|
||||
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
|
||||
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
|
||||
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
|
||||
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
|
||||
|
||||
unsigned cfg_long_chain_multiple_exit(unsigned x) {
|
||||
unsigned y = 0;
|
||||
#define BRANCH(i, x, y) if (((x % 13171) + ++y) < i) { int var = x / 13171 + y; return var; }
|
||||
EXPAND_4096_BRANCHES(1, x, y);
|
||||
#undef BRANCH
|
||||
return 42;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
|
||||
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
|
||||
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
|
||||
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
|
||||
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
|
||||
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
|
||||
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
|
||||
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
|
||||
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
|
||||
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
|
||||
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
|
||||
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
|
||||
|
||||
unsigned cfg_long_chain_many_preds(unsigned x) {
|
||||
unsigned y = 0;
|
||||
#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; } else
|
||||
EXPAND_4096_BRANCHES(1, x, y);
|
||||
#undef BRANCH
|
||||
int var = x / 13171; y^= var;
|
||||
return y;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#define EXPAND_2_INNER_CASES(i, x, y) INNER_CASE(i, x, y); INNER_CASE(i + 1, x, y);
|
||||
#define EXPAND_4_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i + 2, x, y)
|
||||
#define EXPAND_8_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i + 4, x, y)
|
||||
#define EXPAND_16_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i + 8, x, y)
|
||||
#define EXPAND_32_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i + 16, x, y)
|
||||
#define EXPAND_64_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i + 32, x, y)
|
||||
|
||||
#define EXPAND_2_OUTER_CASES(i, x, y) OUTER_CASE(i, x, y); OUTER_CASE(i + 1, x, y);
|
||||
#define EXPAND_4_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i + 2, x, y)
|
||||
#define EXPAND_8_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i + 4, x, y)
|
||||
#define EXPAND_16_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i + 8, x, y)
|
||||
#define EXPAND_32_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i + 16, x, y)
|
||||
#define EXPAND_64_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i + 32, x, y)
|
||||
|
||||
// Rather than a single monstrous fan-out, this fans out in smaller increments,
|
||||
// but to a similar size.
|
||||
unsigned cfg_nested_switch(int x) {
|
||||
unsigned y = 0;
|
||||
while (x > 0) {
|
||||
switch (x) {
|
||||
#define INNER_CASE(i, x, y) \
|
||||
case i: { int case_var = 3*x + i; y += case_var - 1; break; }
|
||||
#define OUTER_CASE(i, x, y) \
|
||||
case i: { \
|
||||
int case_var = y >> 8; \
|
||||
switch (case_var) { \
|
||||
EXPAND_64_INNER_CASES(0, x, y); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
EXPAND_64_OUTER_CASES(0, x, y);
|
||||
}
|
||||
--x;
|
||||
}
|
||||
return y;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// Hammer the CFG with large numbers of overlapping variable scopes, which
|
||||
// implicit destructors triggered at each edge.
|
||||
|
||||
#define EXPAND_BASIC_STRUCT(i) struct X##i { X##i(int); ~X##i(); };
|
||||
#define EXPAND_NORET_STRUCT(i) struct X##i { X##i(int); ~X##i() __attribute__((noreturn)); };
|
||||
EXPAND_BASIC_STRUCT(0000); EXPAND_NORET_STRUCT(0001);
|
||||
EXPAND_BASIC_STRUCT(0010); EXPAND_BASIC_STRUCT(0011);
|
||||
EXPAND_BASIC_STRUCT(0100); EXPAND_NORET_STRUCT(0101);
|
||||
EXPAND_NORET_STRUCT(0110); EXPAND_BASIC_STRUCT(0111);
|
||||
EXPAND_BASIC_STRUCT(1000); EXPAND_NORET_STRUCT(1001);
|
||||
EXPAND_BASIC_STRUCT(1010); EXPAND_BASIC_STRUCT(1011);
|
||||
EXPAND_NORET_STRUCT(1100); EXPAND_NORET_STRUCT(1101);
|
||||
EXPAND_BASIC_STRUCT(1110); EXPAND_BASIC_STRUCT(1111);
|
||||
|
||||
#define EXPAND_2_VARS(c, i, x) const X##i var_##c##_##i##0(x), &var_##c##_##i##1 = X##i(x)
|
||||
#define EXPAND_4_VARS(c, i, x) EXPAND_2_VARS(c, i##0, x); EXPAND_2_VARS(c, i##1, x)
|
||||
#define EXPAND_8_VARS(c, i, x) EXPAND_4_VARS(c, i##0, x); EXPAND_4_VARS(c, i##1, x)
|
||||
#define EXPAND_16_VARS(c, i, x) EXPAND_8_VARS(c, i##0, x); EXPAND_8_VARS(c, i##1, x)
|
||||
#define EXPAND_32_VARS(c, x) EXPAND_16_VARS(c, 0, x); EXPAND_16_VARS(c, 1, x)
|
||||
|
||||
#define EXPAND_2_INNER_CASES(i, x, y) INNER_CASE(i, x, y); INNER_CASE(i + 1, x, y);
|
||||
#define EXPAND_4_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i + 2, x, y)
|
||||
#define EXPAND_8_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i + 4, x, y)
|
||||
#define EXPAND_16_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i + 8, x, y)
|
||||
#define EXPAND_32_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i + 16, x, y)
|
||||
|
||||
#define EXPAND_2_OUTER_CASES(i, x, y) OUTER_CASE(i, x, y); OUTER_CASE(i + 1, x, y);
|
||||
#define EXPAND_4_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i + 2, x, y)
|
||||
#define EXPAND_8_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i + 4, x, y)
|
||||
#define EXPAND_16_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i + 8, x, y)
|
||||
#define EXPAND_32_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i + 16, x, y)
|
||||
|
||||
unsigned cfg_nested_vars(int x) {
|
||||
int y = 0;
|
||||
while (x > 0) {
|
||||
EXPAND_32_VARS(a, x);
|
||||
switch (x) {
|
||||
#define INNER_CASE(i, x, y) \
|
||||
case i: { \
|
||||
int case_var = 3*x + i; \
|
||||
EXPAND_32_VARS(c, case_var); \
|
||||
y += case_var - 1; \
|
||||
break; \
|
||||
}
|
||||
#define OUTER_CASE(i, x, y) \
|
||||
case i: { \
|
||||
int case_var = y >> 8; \
|
||||
EXPAND_32_VARS(b, y); \
|
||||
switch (case_var) { \
|
||||
EXPAND_32_INNER_CASES(0, x, y); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
EXPAND_32_OUTER_CASES(0, x, y);
|
||||
}
|
||||
--x;
|
||||
}
|
||||
return y;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// clang -I/usr/include/c++/4.0.0 -I/usr/include/c++/4.0.0/powerpc-apple-darwin8 -I/usr/include/c++/4.0.0/backward INPUTS/iostream.cc -Eonly
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <stdint.h>
|
@ -1,17 +0,0 @@
|
||||
|
||||
// This pounds on macro expansion for performance reasons. This is currently
|
||||
// heavily constrained by darwin's malloc.
|
||||
|
||||
// Function-like macros.
|
||||
#define A0(A, B) A B
|
||||
#define A1(A, B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B)
|
||||
#define A2(A, B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B)
|
||||
#define A3(A, B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B)
|
||||
#define A4(A, B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B)
|
||||
#define A5(A, B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B)
|
||||
#define A6(A, B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B)
|
||||
#define A7(A, B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B)
|
||||
#define A8(A, B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B)
|
||||
|
||||
A8(a, b)
|
||||
|
@ -1,16 +0,0 @@
|
||||
|
||||
// This pounds on macro expansion for performance reasons. This is currently
|
||||
// heavily constrained by darwin's malloc.
|
||||
|
||||
// Object-like expansions
|
||||
#define A0 a b
|
||||
#define A1 A0 A0 A0 A0 A0 A0
|
||||
#define A2 A1 A1 A1 A1 A1 A1
|
||||
#define A3 A2 A2 A2 A2 A2 A2
|
||||
#define A4 A3 A3 A3 A3 A3 A3
|
||||
#define A5 A4 A4 A4 A4 A4 A4
|
||||
#define A6 A5 A5 A5 A5 A5 A5
|
||||
#define A7 A6 A6 A6 A6 A6 A6
|
||||
#define A8 A7 A7 A7 A7 A7 A7
|
||||
|
||||
A8
|
@ -1,47 +0,0 @@
|
||||
#define __extension__
|
||||
|
||||
#define __stpcpy(dest, src) (__extension__ (__builtin_constant_p (src) ? (__string2_1bptr_p (src) && strlen (src) + 1 <= 8 ? __stpcpy_small (dest, __stpcpy_args (src), strlen (src) + 1) : ((char *) __mempcpy (dest, src, strlen (src) + 1) - 1)) : __stpcpy (dest, src)))
|
||||
#define stpcpy(dest, src) __stpcpy (dest, src)
|
||||
#define __stpcpy_args(src) __extension__ __STRING2_SMALL_GET16 (src, 0), __extension__ __STRING2_SMALL_GET16 (src, 4), __extension__ __STRING2_SMALL_GET32 (src, 0), __extension__ __STRING2_SMALL_GET32 (src, 4)
|
||||
|
||||
#define __mempcpy(dest, src, n) (__extension__ (__builtin_constant_p (src) && __builtin_constant_p (n) && __string2_1bptr_p (src) && n <= 8 ? __mempcpy_small (dest, __mempcpy_args (src), n) : __mempcpy (dest, src, n)))
|
||||
#define mempcpy(dest, src, n) __mempcpy (dest, src, n)
|
||||
#define __mempcpy_args(src) ((char *) (src))[0], ((char *) (src))[2], ((char *) (src))[4], ((char *) (src))[6], __extension__ __STRING2_SMALL_GET16 (src, 0), __extension__ __STRING2_SMALL_GET16 (src, 4), __extension__ __STRING2_SMALL_GET32 (src, 0), __extension__ __STRING2_SMALL_GET32 (src, 4)
|
||||
|
||||
#define __STRING2_SMALL_GET16(src, idx) (((__const unsigned char *) (__const char *) (src))[idx + 1] << 8 | ((__const unsigned char *) (__const char *) (src))[idx])
|
||||
|
||||
#define __STRING2_SMALL_GET32(src, idx) (((((__const unsigned char *) (__const char *) (src))[idx + 3] << 8 | ((__const unsigned char *) (__const char *) (src))[idx + 2]) << 8 | ((__const unsigned char *) (__const char *) (src))[idx + 1]) << 8 | ((__const unsigned char *) (__const char *) (src))[idx])
|
||||
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
||||
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
|
48
INSTALL.txt
48
INSTALL.txt
@ -1,48 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Clang Installation Instructions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
These instructions describe how to build and install Clang.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Step 1: Organization
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Clang is designed to be built as part of an LLVM build. Assuming that the LLVM
|
||||
source code is located at $LLVM_SRC_ROOT, then the clang source code should be
|
||||
installed as:
|
||||
|
||||
$LLVM_SRC_ROOT/tools/clang
|
||||
|
||||
The directory is not required to be called clang, but doing so will allow the
|
||||
LLVM build system to automatically recognize it and build it along with LLVM.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Step 2: Configure and Build LLVM
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Configure and build your copy of LLVM (see $LLVM_SRC_ROOT/GettingStarted.html
|
||||
for more information).
|
||||
|
||||
Assuming you installed clang at $LLVM_SRC_ROOT/tools/clang then Clang will
|
||||
automatically be built with LLVM. Otherwise, run 'make' in the Clang source
|
||||
directory to build Clang.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Step 3: (Optional) Verify Your Build
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
It is a good idea to run the Clang tests to make sure your build works
|
||||
correctly. From inside the Clang build directory, run 'make test' to run the
|
||||
tests.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Step 4: Install Clang
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
From inside the Clang build directory, run 'make install' to install the Clang
|
||||
compiler and header files into the prefix directory selected when LLVM was
|
||||
configured.
|
||||
|
||||
The Clang compiler is available as 'clang' and 'clang++'. It supports a gcc like
|
||||
command line interface. See the man page for clang for more information.
|
@ -1,5 +0,0 @@
|
||||
# This file provides information for llvm-top
|
||||
DepModule: llvm
|
||||
ConfigCmd:
|
||||
ConfigTest:
|
||||
BuildCmd:
|
104
NOTES.txt
104
NOTES.txt
@ -1,104 +0,0 @@
|
||||
//===---------------------------------------------------------------------===//
|
||||
// Random Notes
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
To time GCC preprocessing speed without output, use:
|
||||
"time gcc -MM file"
|
||||
This is similar to -Eonly.
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
C++ Template Instantiation benchmark:
|
||||
http://users.rcn.com/abrahams/instantiation_speed/index.html
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
TODO: File Manager Speedup:
|
||||
|
||||
We currently do a lot of stat'ing for files that don't exist, particularly
|
||||
when lots of -I paths exist (e.g. see the <iostream> example, check for
|
||||
failures in stat in FileManager::getFile). It would be far better to make
|
||||
the following changes:
|
||||
1. FileEntry contains a sys::Path instead of a std::string for Name.
|
||||
2. sys::Path contains timestamp and size, lazily computed. Eliminate from
|
||||
FileEntry.
|
||||
3. File UIDs are created on request, not when files are opened.
|
||||
These changes make it possible to efficiently have FileEntry objects for
|
||||
files that exist on the file system, but have not been used yet.
|
||||
|
||||
Once this is done:
|
||||
1. DirectoryEntry gets a boolean value "has read entries". When false, not
|
||||
all entries in the directory are in the file mgr, when true, they are.
|
||||
2. Instead of stat'ing the file in FileManager::getFile, check to see if
|
||||
the dir has been read. If so, fail immediately, if not, read the dir,
|
||||
then retry.
|
||||
3. Reading the dir uses the getdirentries syscall, creating a FileEntry
|
||||
for all files found.
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
// Specifying targets: -triple and -arch
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
The clang supports "-triple" and "-arch" options. At most one -triple and one
|
||||
-arch option may be specified. Both are optional.
|
||||
|
||||
The "selection of target" behavior is defined as follows:
|
||||
|
||||
(1) If the user does not specify -triple, we default to the host triple.
|
||||
(2) If the user specifies a -arch, that overrides the arch in the host or
|
||||
specified triple.
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
|
||||
verifyInputConstraint and verifyOutputConstraint should not return bool.
|
||||
|
||||
Instead we should return something like:
|
||||
|
||||
enum VerifyConstraintResult {
|
||||
Valid,
|
||||
|
||||
// Output only
|
||||
OutputOperandConstraintLacksEqualsCharacter,
|
||||
MatchingConstraintNotValidInOutputOperand,
|
||||
|
||||
// Input only
|
||||
InputOperandConstraintContainsEqualsCharacter,
|
||||
MatchingConstraintReferencesInvalidOperandNumber,
|
||||
|
||||
// Both
|
||||
PercentConstraintUsedWithLastOperand
|
||||
};
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
Blocks should not capture variables that are only used in dead code.
|
||||
|
||||
The rule that we came up with is that blocks are required to capture
|
||||
variables if they're referenced in evaluated code, even if that code
|
||||
doesn't actually rely on the value of the captured variable.
|
||||
|
||||
For example, this requires a capture:
|
||||
(void) var;
|
||||
But this does not:
|
||||
if (false) puts(var);
|
||||
|
||||
Summary of <rdar://problem/9851835>: if we implement this, we should
|
||||
warn about non-POD variables that are referenced but not captured, but
|
||||
only if the non-reachability is not due to macro or template
|
||||
metaprogramming.
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
We can still apply a modified version of the constructor/destructor
|
||||
delegation optimization in cases of virtual inheritance where:
|
||||
- there is no function-try-block,
|
||||
- the constructor signature is not variadic, and
|
||||
- the parameter variables can safely be copied and repassed
|
||||
to the base constructor because either
|
||||
- they have not had their addresses taken by the vbase initializers or
|
||||
- they were passed indirectly.
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
27
README.txt
27
README.txt
@ -1,27 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// C Language Family Front-end
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Welcome to Clang. This is a compiler front-end for the C family of languages
|
||||
(C, C++, Objective-C, and Objective-C++) which is built as part of the LLVM
|
||||
compiler infrastructure project.
|
||||
|
||||
Unlike many other compiler frontends, Clang is useful for a number of things
|
||||
beyond just compiling code: we intend for Clang to be host to a number of
|
||||
different source-level tools. One example of this is the Clang Static Analyzer.
|
||||
|
||||
If you're interested in more (including how to build Clang) it is best to read
|
||||
the relevant web sites. Here are some pointers:
|
||||
|
||||
Information on Clang: http://clang.llvm.org/
|
||||
Building and using Clang: http://clang.llvm.org/get_started.html
|
||||
Clang Static Analyzer: http://clang-analyzer.llvm.org/
|
||||
Information on the LLVM project: http://llvm.org/
|
||||
|
||||
If you have questions or comments about Clang, a great place to discuss them is
|
||||
on the Clang development mailing list:
|
||||
http://lists.llvm.org/mailman/listinfo/cfe-dev
|
||||
|
||||
If you find a bug in Clang, please file it in the LLVM bug tracker:
|
||||
http://llvm.org/bugs/
|
||||
|
@ -1,18 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Clang Python Bindings
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
This directory implements Python bindings for Clang.
|
||||
|
||||
You may need to set CLANG_LIBRARY_PATH so that the Clang library can be
|
||||
found. The unit tests are designed to be run with any standard test
|
||||
runner. For example:
|
||||
--
|
||||
$ env PYTHONPATH=$(echo ~/llvm/tools/clang/bindings/python/) \
|
||||
CLANG_LIBRARY_PATH=$(llvm-config --libdir) \
|
||||
python -m unittest discover -v
|
||||
tests.cindex.test_index.test_create ... ok
|
||||
...
|
||||
|
||||
OK
|
||||
--
|
@ -1,24 +0,0 @@
|
||||
#===- __init__.py - Clang Python Bindings --------------------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
r"""
|
||||
Clang Library Bindings
|
||||
======================
|
||||
|
||||
This package provides access to the Clang compiler and libraries.
|
||||
|
||||
The available modules are:
|
||||
|
||||
cindex
|
||||
|
||||
Bindings for the Clang indexing library.
|
||||
"""
|
||||
|
||||
__all__ = ['cindex']
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,34 +0,0 @@
|
||||
#===- enumerations.py - Python Enumerations ------------------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
Clang Enumerations
|
||||
==================
|
||||
|
||||
This module provides static definitions of enumerations that exist in libclang.
|
||||
|
||||
Enumerations are typically defined as a list of tuples. The exported values are
|
||||
typically munged into other types or classes at module load time.
|
||||
|
||||
All enumerations are centrally defined in this file so they are all grouped
|
||||
together and easier to audit. And, maybe even one day this file will be
|
||||
automatically generated by scanning the libclang headers!
|
||||
"""
|
||||
|
||||
# Maps to CXTokenKind. Note that libclang maintains a separate set of token
|
||||
# enumerations from the C++ API.
|
||||
TokenKinds = [
|
||||
('PUNCTUATION', 0),
|
||||
('KEYWORD', 1),
|
||||
('IDENTIFIER', 2),
|
||||
('LITERAL', 3),
|
||||
('COMMENT', 4),
|
||||
]
|
||||
|
||||
__all__ = ['TokenKinds']
|
@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#===- cindex-dump.py - cindex/Python Source Dump -------------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
A simple command line tool for dumping a source file using the Clang Index
|
||||
Library.
|
||||
"""
|
||||
|
||||
def get_diag_info(diag):
|
||||
return { 'severity' : diag.severity,
|
||||
'location' : diag.location,
|
||||
'spelling' : diag.spelling,
|
||||
'ranges' : diag.ranges,
|
||||
'fixits' : diag.fixits }
|
||||
|
||||
def get_cursor_id(cursor, cursor_list = []):
|
||||
if not opts.showIDs:
|
||||
return None
|
||||
|
||||
if cursor is None:
|
||||
return None
|
||||
|
||||
# FIXME: This is really slow. It would be nice if the index API exposed
|
||||
# something that let us hash cursors.
|
||||
for i,c in enumerate(cursor_list):
|
||||
if cursor == c:
|
||||
return i
|
||||
cursor_list.append(cursor)
|
||||
return len(cursor_list) - 1
|
||||
|
||||
def get_info(node, depth=0):
|
||||
if opts.maxDepth is not None and depth >= opts.maxDepth:
|
||||
children = None
|
||||
else:
|
||||
children = [get_info(c, depth+1)
|
||||
for c in node.get_children()]
|
||||
return { 'id' : get_cursor_id(node),
|
||||
'kind' : node.kind,
|
||||
'usr' : node.get_usr(),
|
||||
'spelling' : node.spelling,
|
||||
'location' : node.location,
|
||||
'extent.start' : node.extent.start,
|
||||
'extent.end' : node.extent.end,
|
||||
'is_definition' : node.is_definition(),
|
||||
'definition id' : get_cursor_id(node.get_definition()),
|
||||
'children' : children }
|
||||
|
||||
def main():
|
||||
from clang.cindex import Index
|
||||
from pprint import pprint
|
||||
|
||||
from optparse import OptionParser, OptionGroup
|
||||
|
||||
global opts
|
||||
|
||||
parser = OptionParser("usage: %prog [options] {filename} [clang-args*]")
|
||||
parser.add_option("", "--show-ids", dest="showIDs",
|
||||
help="Compute cursor IDs (very slow)",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--max-depth", dest="maxDepth",
|
||||
help="Limit cursor expansion to depth N",
|
||||
metavar="N", type=int, default=None)
|
||||
parser.disable_interspersed_args()
|
||||
(opts, args) = parser.parse_args()
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error('invalid number arguments')
|
||||
|
||||
index = Index.create()
|
||||
tu = index.parse(None, args)
|
||||
if not tu:
|
||||
parser.error("unable to load input")
|
||||
|
||||
pprint(('diags', [get_diag_info(d) for d in tu.diagnostics]))
|
||||
pprint(('nodes', get_info(tu.cursor)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#===- cindex-includes.py - cindex/Python Inclusion Graph -----*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
A simple command line tool for dumping a Graphviz description (dot) that
|
||||
describes include dependencies.
|
||||
"""
|
||||
|
||||
def main():
|
||||
import sys
|
||||
from clang.cindex import Index
|
||||
|
||||
from optparse import OptionParser, OptionGroup
|
||||
|
||||
parser = OptionParser("usage: %prog [options] {filename} [clang-args*]")
|
||||
parser.disable_interspersed_args()
|
||||
(opts, args) = parser.parse_args()
|
||||
if len(args) == 0:
|
||||
parser.error('invalid number arguments')
|
||||
|
||||
# FIXME: Add an output file option
|
||||
out = sys.stdout
|
||||
|
||||
index = Index.create()
|
||||
tu = index.parse(None, args)
|
||||
if not tu:
|
||||
parser.error("unable to load input")
|
||||
|
||||
# A helper function for generating the node name.
|
||||
def name(f):
|
||||
if f:
|
||||
return "\"" + f.name + "\""
|
||||
|
||||
# Generate the include graph
|
||||
out.write("digraph G {\n")
|
||||
for i in tu.get_includes():
|
||||
line = " ";
|
||||
if i.is_input_file:
|
||||
# Always write the input file as a node just in case it doesn't
|
||||
# actually include anything. This would generate a 1 node graph.
|
||||
line += name(i.include)
|
||||
else:
|
||||
line += '%s->%s' % (name(i.source), name(i.include))
|
||||
line += "\n";
|
||||
out.write(line)
|
||||
out.write("}\n")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,46 +0,0 @@
|
||||
# Test target to run Python test suite from main build.
|
||||
|
||||
add_custom_target(check-clang-python
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
CLANG_LIBRARY_PATH=$<TARGET_FILE_DIR:libclang>
|
||||
${PYTHON_EXECUTABLE} -m unittest discover
|
||||
DEPENDS libclang
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
set(RUN_PYTHON_TESTS TRUE)
|
||||
set_target_properties(check-clang-python PROPERTIES FOLDER "Clang tests")
|
||||
|
||||
# Tests require libclang.so which is only built with LLVM_ENABLE_PIC=ON
|
||||
if(NOT LLVM_ENABLE_PIC)
|
||||
set(RUN_PYTHON_TESTS FALSE)
|
||||
endif()
|
||||
|
||||
# Do not try to run if libclang was built with ASan because
|
||||
# the sanitizer library will likely be loaded too late to perform
|
||||
# interception and will then fail.
|
||||
# We could use LD_PRELOAD/DYLD_INSERT_LIBRARIES but this isn't
|
||||
# portable so its easier just to not run the tests when building
|
||||
# with ASan.
|
||||
list(FIND LLVM_USE_SANITIZER "Address" LLVM_USE_ASAN_INDEX)
|
||||
if(NOT LLVM_USE_ASAN_INDEX EQUAL -1)
|
||||
set(RUN_PYTHON_TESTS FALSE)
|
||||
endif()
|
||||
|
||||
# Tests fail on Windows, and need someone knowledgeable to fix.
|
||||
# It's not clear whether it's a test or a valid binding problem.
|
||||
if(WIN32)
|
||||
set(RUN_PYTHON_TESTS FALSE)
|
||||
endif()
|
||||
|
||||
# AArch64 and Hexagon have known test failures that need to be
|
||||
# addressed.
|
||||
# SystemZ has broken Python/FFI interface:
|
||||
# https://reviews.llvm.org/D52840#1265716
|
||||
if(${LLVM_NATIVE_ARCH} MATCHES "^(AArch64|Hexagon|SystemZ)$")
|
||||
set(RUN_PYTHON_TESTS FALSE)
|
||||
endif()
|
||||
|
||||
if(RUN_PYTHON_TESTS)
|
||||
set_property(GLOBAL APPEND PROPERTY
|
||||
LLVM_ADDITIONAL_TEST_TARGETS check-clang-python)
|
||||
endif()
|
@ -1,17 +0,0 @@
|
||||
[
|
||||
{
|
||||
"directory": "/home/john.doe/MyProject",
|
||||
"command": "clang++ -o project.o -c /home/john.doe/MyProject/project.cpp",
|
||||
"file": "/home/john.doe/MyProject/project.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/john.doe/MyProjectA",
|
||||
"command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp",
|
||||
"file": "/home/john.doe/MyProject/project2.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/john.doe/MyProjectB",
|
||||
"command": "clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp",
|
||||
"file": "/home/john.doe/MyProject/project2.cpp"
|
||||
}
|
||||
]
|
@ -1,6 +0,0 @@
|
||||
#ifndef HEADER1
|
||||
#define HEADER1
|
||||
|
||||
#include "header3.h"
|
||||
|
||||
#endif
|
@ -1,6 +0,0 @@
|
||||
#ifndef HEADER2
|
||||
#define HEADER2
|
||||
|
||||
#include "header3.h"
|
||||
|
||||
#endif
|
@ -1,3 +0,0 @@
|
||||
// Not a guarded header!
|
||||
|
||||
void f();
|
@ -1,6 +0,0 @@
|
||||
#include "stdio.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("hello world\n");
|
||||
return 0;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#include "header1.h"
|
||||
#include "header2.h"
|
||||
#include "header1.h"
|
||||
|
||||
int main() { }
|
@ -1,2 +0,0 @@
|
||||
int DECL_ONE = 1;
|
||||
int DECL_TWO = 2;
|
@ -1,41 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import AccessSpecifier
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestAccessSpecifiers(unittest.TestCase):
|
||||
def test_access_specifiers(self):
|
||||
"""Ensure that C++ access specifiers are available on cursors"""
|
||||
|
||||
tu = get_tu("""
|
||||
class test_class {
|
||||
public:
|
||||
void public_member_function();
|
||||
protected:
|
||||
void protected_member_function();
|
||||
private:
|
||||
void private_member_function();
|
||||
};
|
||||
""", lang = 'cpp')
|
||||
|
||||
test_class = get_cursor(tu, "test_class")
|
||||
self.assertEqual(test_class.access_specifier, AccessSpecifier.INVALID)
|
||||
|
||||
public = get_cursor(tu.cursor, "public_member_function")
|
||||
self.assertEqual(public.access_specifier, AccessSpecifier.PUBLIC)
|
||||
|
||||
protected = get_cursor(tu.cursor, "protected_member_function")
|
||||
self.assertEqual(protected.access_specifier, AccessSpecifier.PROTECTED)
|
||||
|
||||
private = get_cursor(tu.cursor, "private_member_function")
|
||||
self.assertEqual(private.access_specifier, AccessSpecifier.PRIVATE)
|
@ -1,130 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import CompilationDatabase
|
||||
from clang.cindex import CompilationDatabaseError
|
||||
from clang.cindex import CompileCommands
|
||||
from clang.cindex import CompileCommand
|
||||
import os
|
||||
import gc
|
||||
import unittest
|
||||
import sys
|
||||
from .util import skip_if_no_fspath
|
||||
from .util import str_to_path
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform == 'win32', "TODO: Fix these tests on Windows")
|
||||
class TestCDB(unittest.TestCase):
|
||||
def test_create_fail(self):
|
||||
"""Check we fail loading a database with an assertion"""
|
||||
path = os.path.dirname(__file__)
|
||||
with self.assertRaises(CompilationDatabaseError) as cm:
|
||||
cdb = CompilationDatabase.fromDirectory(path)
|
||||
e = cm.exception
|
||||
self.assertEqual(e.cdb_error,
|
||||
CompilationDatabaseError.ERROR_CANNOTLOADDATABASE)
|
||||
|
||||
def test_create(self):
|
||||
"""Check we can load a compilation database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
|
||||
def test_lookup_succeed(self):
|
||||
"""Check we get some results if the file exists in the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
self.assertNotEqual(len(cmds), 0)
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_lookup_succeed_pathlike(self):
|
||||
"""Same as test_lookup_succeed, but with PathLikes"""
|
||||
cdb = CompilationDatabase.fromDirectory(str_to_path(kInputsDir))
|
||||
cmds = cdb.getCompileCommands(str_to_path('/home/john.doe/MyProject/project.cpp'))
|
||||
self.assertNotEqual(len(cmds), 0)
|
||||
|
||||
def test_all_compilecommand(self):
|
||||
"""Check we get all results from the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getAllCompileCommands()
|
||||
self.assertEqual(len(cmds), 3)
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProject',
|
||||
'file': '/home/john.doe/MyProject/project.cpp',
|
||||
'line': ['clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
self.assertEqual(cmds[i].directory, expected[i]['wd'])
|
||||
self.assertEqual(cmds[i].filename, expected[i]['file'])
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_1_compilecommand(self):
|
||||
"""Check file with single compile command"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
file = '/home/john.doe/MyProject/project.cpp'
|
||||
cmds = cdb.getCompileCommands(file)
|
||||
self.assertEqual(len(cmds), 1)
|
||||
self.assertEqual(cmds[0].directory, os.path.dirname(file))
|
||||
self.assertEqual(cmds[0].filename, file)
|
||||
expected = [ 'clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']
|
||||
for arg, exp in zip(cmds[0].arguments, expected):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_2_compilecommand(self):
|
||||
"""Check file with 2 compile commands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
|
||||
self.assertEqual(len(cmds), 2)
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']}
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
self.assertEqual(cmds[i].directory, expected[i]['wd'])
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_compilecommand_iterator_stops(self):
|
||||
"""Check that iterator stops after the correct number of elements"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
count = 0
|
||||
for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'):
|
||||
count += 1
|
||||
self.assertLessEqual(count, 2)
|
||||
|
||||
def test_compilationDB_references(self):
|
||||
"""Ensure CompilationsCommands are independent of the database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
gc.collect()
|
||||
workingdir = cmds[0].directory
|
||||
|
||||
def test_compilationCommands_references(self):
|
||||
"""Ensure CompilationsCommand keeps a reference to CompilationCommands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
cmd0 = cmds[0]
|
||||
del cmds
|
||||
gc.collect()
|
||||
workingdir = cmd0.directory
|
@ -1,112 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
import unittest
|
||||
from .util import skip_if_no_fspath
|
||||
from .util import str_to_path
|
||||
|
||||
|
||||
class TestCodeCompletion(unittest.TestCase):
|
||||
def check_completion_results(self, cr, expected):
|
||||
self.assertIsNotNone(cr)
|
||||
self.assertEqual(len(cr.diagnostics), 0)
|
||||
|
||||
completions = [str(c) for c in cr.results]
|
||||
|
||||
for c in expected:
|
||||
self.assertIn(c, completions)
|
||||
|
||||
def test_code_complete(self):
|
||||
files = [('fake.c', """
|
||||
/// Aaa.
|
||||
int test1;
|
||||
|
||||
/// Bbb.
|
||||
void test2(void);
|
||||
|
||||
void f() {
|
||||
|
||||
}
|
||||
""")]
|
||||
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
|
||||
cr = tu.codeComplete('fake.c', 9, 1, unsaved_files=files, include_brief_comments=True)
|
||||
|
||||
expected = [
|
||||
"{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
|
||||
"{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
|
||||
"{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_code_complete_pathlike(self):
|
||||
files = [(str_to_path('fake.c'), """
|
||||
/// Aaa.
|
||||
int test1;
|
||||
|
||||
/// Bbb.
|
||||
void test2(void);
|
||||
|
||||
void f() {
|
||||
|
||||
}
|
||||
""")]
|
||||
|
||||
tu = TranslationUnit.from_source(str_to_path('fake.c'), ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
|
||||
cr = tu.codeComplete(str_to_path('fake.c'), 9, 1, unsaved_files=files, include_brief_comments=True)
|
||||
|
||||
expected = [
|
||||
"{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
|
||||
"{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
|
||||
"{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
||||
def test_code_complete_availability(self):
|
||||
files = [('fake.cpp', """
|
||||
class P {
|
||||
protected:
|
||||
int member;
|
||||
};
|
||||
|
||||
class Q : public P {
|
||||
public:
|
||||
using P::member;
|
||||
};
|
||||
|
||||
void f(P x, Q y) {
|
||||
x.; // member is inaccessible
|
||||
y.; // member is accessible
|
||||
}
|
||||
""")]
|
||||
|
||||
tu = TranslationUnit.from_source('fake.cpp', ['-std=c++98'], unsaved_files=files)
|
||||
|
||||
cr = tu.codeComplete('fake.cpp', 12, 5, unsaved_files=files)
|
||||
|
||||
expected = [
|
||||
"{'const', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
|
||||
"{'volatile', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
|
||||
"{'operator', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'P', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
|
||||
"{'Q', TypedText} || Priority: 50 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
||||
cr = tu.codeComplete('fake.cpp', 13, 5, unsaved_files=files)
|
||||
expected = [
|
||||
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
|
||||
"{'P &', ResultType} | {'operator=', TypedText} | {'(', LeftParen} | {'const P &', Placeholder} | {')', RightParen} || Priority: 79 || Availability: Available || Brief comment: None",
|
||||
"{'int', ResultType} | {'member', TypedText} || Priority: 35 || Availability: NotAccessible || Brief comment: None",
|
||||
"{'void', ResultType} | {'~P', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 79 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
@ -1,47 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import TranslationUnit
|
||||
from tests.cindex.util import get_cursor
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestComment(unittest.TestCase):
|
||||
def test_comment(self):
|
||||
files = [('fake.c', """
|
||||
/// Aaa.
|
||||
int test1;
|
||||
|
||||
/// Bbb.
|
||||
/// x
|
||||
void test2(void);
|
||||
|
||||
void f() {
|
||||
|
||||
}
|
||||
""")]
|
||||
# make a comment-aware TU
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
test1 = get_cursor(tu, 'test1')
|
||||
self.assertIsNotNone(test1, "Could not find test1.")
|
||||
self.assertTrue(test1.type.is_pod())
|
||||
raw = test1.raw_comment
|
||||
brief = test1.brief_comment
|
||||
self.assertEqual(raw, """/// Aaa.""")
|
||||
self.assertEqual(brief, """Aaa.""")
|
||||
|
||||
test2 = get_cursor(tu, 'test2')
|
||||
raw = test2.raw_comment
|
||||
brief = test2.brief_comment
|
||||
self.assertEqual(raw, """/// Bbb.\n/// x""")
|
||||
self.assertEqual(brief, """Bbb. x""")
|
||||
|
||||
f = get_cursor(tu, 'f')
|
||||
raw = f.raw_comment
|
||||
brief = f.brief_comment
|
||||
self.assertIsNone(raw)
|
||||
self.assertIsNone(brief)
|
@ -1,569 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
import ctypes
|
||||
import gc
|
||||
import unittest
|
||||
|
||||
from clang.cindex import AvailabilityKind
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import TemplateArgumentKind
|
||||
from clang.cindex import TranslationUnit
|
||||
from clang.cindex import TypeKind
|
||||
from .util import get_cursor
|
||||
from .util import get_cursors
|
||||
from .util import get_tu
|
||||
|
||||
|
||||
kInput = """\
|
||||
struct s0 {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
struct s1;
|
||||
|
||||
void f0(int a0, int a1) {
|
||||
int l0, l1;
|
||||
|
||||
if (a0)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
kParentTest = """\
|
||||
class C {
|
||||
void f();
|
||||
}
|
||||
|
||||
void C::f() { }
|
||||
"""
|
||||
|
||||
kTemplateArgTest = """\
|
||||
template <int kInt, typename T, bool kBool>
|
||||
void foo();
|
||||
|
||||
template<>
|
||||
void foo<-7, float, true>();
|
||||
"""
|
||||
|
||||
class TestCursor(unittest.TestCase):
|
||||
def test_get_children(self):
|
||||
tu = get_tu(kInput)
|
||||
|
||||
it = tu.cursor.get_children()
|
||||
tu_nodes = list(it)
|
||||
|
||||
self.assertEqual(len(tu_nodes), 3)
|
||||
for cursor in tu_nodes:
|
||||
self.assertIsNotNone(cursor.translation_unit)
|
||||
|
||||
self.assertNotEqual(tu_nodes[0], tu_nodes[1])
|
||||
self.assertEqual(tu_nodes[0].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertEqual(tu_nodes[0].spelling, 's0')
|
||||
self.assertEqual(tu_nodes[0].is_definition(), True)
|
||||
self.assertEqual(tu_nodes[0].location.file.name, 't.c')
|
||||
self.assertEqual(tu_nodes[0].location.line, 1)
|
||||
self.assertEqual(tu_nodes[0].location.column, 8)
|
||||
self.assertGreater(tu_nodes[0].hash, 0)
|
||||
self.assertIsNotNone(tu_nodes[0].translation_unit)
|
||||
|
||||
s0_nodes = list(tu_nodes[0].get_children())
|
||||
self.assertEqual(len(s0_nodes), 2)
|
||||
self.assertEqual(s0_nodes[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(s0_nodes[0].spelling, 'a')
|
||||
self.assertEqual(s0_nodes[0].type.kind, TypeKind.INT)
|
||||
self.assertEqual(s0_nodes[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(s0_nodes[1].spelling, 'b')
|
||||
self.assertEqual(s0_nodes[1].type.kind, TypeKind.INT)
|
||||
|
||||
self.assertEqual(tu_nodes[1].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertEqual(tu_nodes[1].spelling, 's1')
|
||||
self.assertEqual(tu_nodes[1].displayname, 's1')
|
||||
self.assertEqual(tu_nodes[1].is_definition(), False)
|
||||
|
||||
self.assertEqual(tu_nodes[2].kind, CursorKind.FUNCTION_DECL)
|
||||
self.assertEqual(tu_nodes[2].spelling, 'f0')
|
||||
self.assertEqual(tu_nodes[2].displayname, 'f0(int, int)')
|
||||
self.assertEqual(tu_nodes[2].is_definition(), True)
|
||||
|
||||
def test_references(self):
|
||||
"""Ensure that references to TranslationUnit are kept."""
|
||||
tu = get_tu('int x;')
|
||||
cursors = list(tu.cursor.get_children())
|
||||
self.assertGreater(len(cursors), 0)
|
||||
|
||||
cursor = cursors[0]
|
||||
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
# Delete reference to TU and perform a full GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
parent = cursor.semantic_parent
|
||||
|
||||
def test_canonical(self):
|
||||
source = 'struct X; struct X; struct X { int member; };'
|
||||
tu = get_tu(source)
|
||||
|
||||
cursors = []
|
||||
for cursor in tu.cursor.get_children():
|
||||
if cursor.spelling == 'X':
|
||||
cursors.append(cursor)
|
||||
|
||||
self.assertEqual(len(cursors), 3)
|
||||
self.assertEqual(cursors[1].canonical, cursors[2].canonical)
|
||||
|
||||
def test_is_const_method(self):
|
||||
"""Ensure Cursor.is_const_method works."""
|
||||
source = 'class X { void foo() const; void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_const_method())
|
||||
self.assertFalse(bar.is_const_method())
|
||||
|
||||
def test_is_converting_constructor(self):
|
||||
"""Ensure Cursor.is_converting_constructor works."""
|
||||
source = 'class X { explicit X(int); X(double); X(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
|
||||
self.assertEqual(len(xs), 4)
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_converting_constructor())
|
||||
self.assertTrue(cs[1].is_converting_constructor())
|
||||
self.assertFalse(cs[2].is_converting_constructor())
|
||||
|
||||
|
||||
def test_is_copy_constructor(self):
|
||||
"""Ensure Cursor.is_copy_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_copy_constructor())
|
||||
self.assertTrue(cs[1].is_copy_constructor())
|
||||
self.assertFalse(cs[2].is_copy_constructor())
|
||||
|
||||
def test_is_default_constructor(self):
|
||||
"""Ensure Cursor.is_default_constructor works."""
|
||||
source = 'class X { X(); X(int); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertTrue(cs[0].is_default_constructor())
|
||||
self.assertFalse(cs[1].is_default_constructor())
|
||||
|
||||
def test_is_move_constructor(self):
|
||||
"""Ensure Cursor.is_move_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_move_constructor())
|
||||
self.assertFalse(cs[1].is_move_constructor())
|
||||
self.assertTrue(cs[2].is_move_constructor())
|
||||
|
||||
def test_is_default_method(self):
|
||||
"""Ensure Cursor.is_default_method works."""
|
||||
source = 'class X { X() = default; }; class Y { Y(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
ys = get_cursors(tu, 'Y')
|
||||
|
||||
self.assertEqual(len(xs), 2)
|
||||
self.assertEqual(len(ys), 2)
|
||||
|
||||
xc = xs[1]
|
||||
yc = ys[1]
|
||||
|
||||
self.assertTrue(xc.is_default_method())
|
||||
self.assertFalse(yc.is_default_method())
|
||||
|
||||
def test_is_mutable_field(self):
|
||||
"""Ensure Cursor.is_mutable_field works."""
|
||||
source = 'class X { int x_; mutable int y_; };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
x_ = get_cursor(tu, 'x_')
|
||||
y_ = get_cursor(tu, 'y_')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(x_)
|
||||
self.assertIsNotNone(y_)
|
||||
|
||||
self.assertFalse(x_.is_mutable_field())
|
||||
self.assertTrue(y_.is_mutable_field())
|
||||
|
||||
def test_is_static_method(self):
|
||||
"""Ensure Cursor.is_static_method works."""
|
||||
|
||||
source = 'class X { static void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_static_method())
|
||||
self.assertFalse(bar.is_static_method())
|
||||
|
||||
def test_is_pure_virtual_method(self):
|
||||
"""Ensure Cursor.is_pure_virtual_method works."""
|
||||
source = 'class X { virtual void foo() = 0; virtual void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_pure_virtual_method())
|
||||
self.assertFalse(bar.is_pure_virtual_method())
|
||||
|
||||
def test_is_virtual_method(self):
|
||||
"""Ensure Cursor.is_virtual_method works."""
|
||||
source = 'class X { virtual void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_virtual_method())
|
||||
self.assertFalse(bar.is_virtual_method())
|
||||
|
||||
def test_is_abstract_record(self):
|
||||
"""Ensure Cursor.is_abstract_record works."""
|
||||
source = 'struct X { virtual void x() = 0; }; struct Y : X { void x(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
self.assertTrue(cls.is_abstract_record())
|
||||
|
||||
cls = get_cursor(tu, 'Y')
|
||||
self.assertFalse(cls.is_abstract_record())
|
||||
|
||||
def test_is_scoped_enum(self):
|
||||
"""Ensure Cursor.is_scoped_enum works."""
|
||||
source = 'class X {}; enum RegularEnum {}; enum class ScopedEnum {};'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
regular_enum = get_cursor(tu, 'RegularEnum')
|
||||
scoped_enum = get_cursor(tu, 'ScopedEnum')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(regular_enum)
|
||||
self.assertIsNotNone(scoped_enum)
|
||||
|
||||
self.assertFalse(cls.is_scoped_enum())
|
||||
self.assertFalse(regular_enum.is_scoped_enum())
|
||||
self.assertTrue(scoped_enum.is_scoped_enum())
|
||||
|
||||
def test_underlying_type(self):
|
||||
tu = get_tu('typedef int foo;')
|
||||
typedef = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(typedef)
|
||||
|
||||
self.assertTrue(typedef.kind.is_declaration())
|
||||
underlying = typedef.underlying_typedef_type
|
||||
self.assertEqual(underlying.kind, TypeKind.INT)
|
||||
|
||||
def test_semantic_parent(self):
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
self.assertEqual(len(curs), 2)
|
||||
self.assertEqual(curs[0].semantic_parent, curs[1].semantic_parent)
|
||||
self.assertEqual(curs[0].semantic_parent, decl)
|
||||
|
||||
def test_lexical_parent(self):
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
self.assertEqual(len(curs), 2)
|
||||
self.assertNotEqual(curs[0].lexical_parent, curs[1].lexical_parent)
|
||||
self.assertEqual(curs[0].lexical_parent, decl)
|
||||
self.assertEqual(curs[1].lexical_parent, tu.cursor)
|
||||
|
||||
def test_enum_type(self):
|
||||
tu = get_tu('enum TEST { FOO=1, BAR=2 };')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
enum_type = enum.enum_type
|
||||
self.assertIn(enum_type.kind, (TypeKind.UINT, TypeKind.INT))
|
||||
|
||||
def test_enum_type_cpp(self):
|
||||
tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
self.assertEqual(enum.enum_type.kind, TypeKind.LONGLONG)
|
||||
|
||||
def test_objc_type_encoding(self):
|
||||
tu = get_tu('int i;', lang='objc')
|
||||
i = get_cursor(tu, 'i')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertEqual(i.objc_type_encoding, 'i')
|
||||
|
||||
def test_enum_values(self):
|
||||
tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
self.assertEqual(len(enum_constants), 3)
|
||||
|
||||
spam, egg, ham = enum_constants
|
||||
|
||||
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(spam.enum_value, 1)
|
||||
self.assertEqual(egg.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(egg.enum_value, 2)
|
||||
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(ham.enum_value, 40)
|
||||
|
||||
def test_enum_values_cpp(self):
|
||||
tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
self.assertEqual(len(enum_constants), 2)
|
||||
|
||||
spam, ham = enum_constants
|
||||
|
||||
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(spam.enum_value, -1)
|
||||
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(ham.enum_value, 0x10000000000)
|
||||
|
||||
def test_annotation_attribute(self):
|
||||
tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));')
|
||||
|
||||
foo = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
|
||||
for c in foo.get_children():
|
||||
if c.kind == CursorKind.ANNOTATE_ATTR:
|
||||
self.assertEqual(c.displayname, "here be annotation attribute")
|
||||
break
|
||||
else:
|
||||
self.fail("Couldn't find annotation")
|
||||
|
||||
def test_annotation_template(self):
|
||||
annotation = '__attribute__ ((annotate("annotation")))'
|
||||
for source, kind in [
|
||||
('int foo (T value) %s;', CursorKind.FUNCTION_TEMPLATE),
|
||||
('class %s foo {};', CursorKind.CLASS_TEMPLATE),
|
||||
]:
|
||||
source = 'template<typename T> ' + (source % annotation)
|
||||
tu = get_tu(source, lang="cpp")
|
||||
|
||||
foo = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertEqual(foo.kind, kind)
|
||||
|
||||
for c in foo.get_children():
|
||||
if c.kind == CursorKind.ANNOTATE_ATTR:
|
||||
self.assertEqual(c.displayname, "annotation")
|
||||
break
|
||||
else:
|
||||
self.fail("Couldn't find annotation for {}".format(kind))
|
||||
|
||||
def test_result_type(self):
|
||||
tu = get_tu('int foo();')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
self.assertIsNotNone(foo)
|
||||
t = foo.result_type
|
||||
self.assertEqual(t.kind, TypeKind.INT)
|
||||
|
||||
def test_result_type_objc_method_decl(self):
|
||||
code = """\
|
||||
@interface Interface : NSObject
|
||||
-(void)voidMethod;
|
||||
@end
|
||||
"""
|
||||
tu = get_tu(code, lang='objc')
|
||||
cursor = get_cursor(tu, 'voidMethod')
|
||||
result_type = cursor.result_type
|
||||
self.assertEqual(cursor.kind, CursorKind.OBJC_INSTANCE_METHOD_DECL)
|
||||
self.assertEqual(result_type.kind, TypeKind.VOID)
|
||||
|
||||
def test_availability(self):
|
||||
tu = get_tu('class A { A(A const&) = delete; };', lang='cpp')
|
||||
|
||||
# AvailabilityKind.AVAILABLE
|
||||
cursor = get_cursor(tu, 'A')
|
||||
self.assertEqual(cursor.kind, CursorKind.CLASS_DECL)
|
||||
self.assertEqual(cursor.availability, AvailabilityKind.AVAILABLE)
|
||||
|
||||
# AvailabilityKind.NOT_AVAILABLE
|
||||
cursors = get_cursors(tu, 'A')
|
||||
for c in cursors:
|
||||
if c.kind == CursorKind.CONSTRUCTOR:
|
||||
self.assertEqual(c.availability, AvailabilityKind.NOT_AVAILABLE)
|
||||
break
|
||||
else:
|
||||
self.fail("Could not find cursor for deleted constructor")
|
||||
|
||||
# AvailabilityKind.DEPRECATED
|
||||
tu = get_tu('void test() __attribute__((deprecated));', lang='cpp')
|
||||
cursor = get_cursor(tu, 'test')
|
||||
self.assertEqual(cursor.availability, AvailabilityKind.DEPRECATED)
|
||||
|
||||
# AvailabilityKind.NOT_ACCESSIBLE is only used in the code completion results
|
||||
|
||||
def test_get_tokens(self):
|
||||
"""Ensure we can map cursors back to tokens."""
|
||||
tu = get_tu('int foo(int i);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
tokens = list(foo.get_tokens())
|
||||
self.assertEqual(len(tokens), 6)
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
self.assertEqual(tokens[1].spelling, 'foo')
|
||||
|
||||
def test_get_token_cursor(self):
|
||||
"""Ensure we can map tokens to cursors."""
|
||||
tu = get_tu('class A {}; int foo(A var = A());', lang='cpp')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
for cursor in foo.walk_preorder():
|
||||
if cursor.kind.is_expression() and not cursor.kind.is_statement():
|
||||
break
|
||||
else:
|
||||
self.fail("Could not find default value expression")
|
||||
|
||||
tokens = list(cursor.get_tokens())
|
||||
self.assertEqual(len(tokens), 4, [t.spelling for t in tokens])
|
||||
self.assertEqual(tokens[0].spelling, '=')
|
||||
self.assertEqual(tokens[1].spelling, 'A')
|
||||
self.assertEqual(tokens[2].spelling, '(')
|
||||
self.assertEqual(tokens[3].spelling, ')')
|
||||
t_cursor = tokens[1].cursor
|
||||
self.assertEqual(t_cursor.kind, CursorKind.TYPE_REF)
|
||||
r_cursor = t_cursor.referenced # should not raise an exception
|
||||
self.assertEqual(r_cursor.kind, CursorKind.CLASS_DECL)
|
||||
|
||||
def test_get_arguments(self):
|
||||
tu = get_tu('void foo(int i, int j);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
arguments = list(foo.get_arguments())
|
||||
|
||||
self.assertEqual(len(arguments), 2)
|
||||
self.assertEqual(arguments[0].spelling, "i")
|
||||
self.assertEqual(arguments[1].spelling, "j")
|
||||
|
||||
def test_get_num_template_arguments(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_num_template_arguments(), 3)
|
||||
|
||||
def test_get_template_argument_kind(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_kind(0), TemplateArgumentKind.INTEGRAL)
|
||||
self.assertEqual(foos[1].get_template_argument_kind(1), TemplateArgumentKind.TYPE)
|
||||
self.assertEqual(foos[1].get_template_argument_kind(2), TemplateArgumentKind.INTEGRAL)
|
||||
|
||||
def test_get_template_argument_type(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_type(1).kind, TypeKind.FLOAT)
|
||||
|
||||
def test_get_template_argument_value(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_value(0), -7)
|
||||
self.assertEqual(foos[1].get_template_argument_value(2), True)
|
||||
|
||||
def test_get_template_argument_unsigned_value(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_unsigned_value(0), 2 ** 32 - 7)
|
||||
self.assertEqual(foos[1].get_template_argument_unsigned_value(2), True)
|
||||
|
||||
def test_referenced(self):
|
||||
tu = get_tu('void foo(); void bar() { foo(); }')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
for c in bar.get_children():
|
||||
if c.kind == CursorKind.CALL_EXPR:
|
||||
self.assertEqual(c.referenced.spelling, foo.spelling)
|
||||
break
|
||||
|
||||
def test_mangled_name(self):
|
||||
kInputForMangling = """\
|
||||
int foo(int, int);
|
||||
"""
|
||||
tu = get_tu(kInputForMangling, lang='cpp')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
# Since libclang does not link in targets, we cannot pass a triple to it
|
||||
# and force the target. To enable this test to pass on all platforms, accept
|
||||
# all valid manglings.
|
||||
# [c-index-test handles this by running the source through clang, emitting
|
||||
# an AST file and running libclang on that AST file]
|
||||
self.assertIn(foo.mangled_name, ('_Z3fooii', '__Z3fooii', '?foo@@YAHHH', '?foo@@YAHHH@Z'))
|
@ -1,58 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestCursorKind(unittest.TestCase):
|
||||
def test_name(self):
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.name is 'UNEXPOSED_DECL')
|
||||
|
||||
def test_get_all_kinds(self):
|
||||
kinds = CursorKind.get_all_kinds()
|
||||
self.assertIn(CursorKind.UNEXPOSED_DECL, kinds)
|
||||
self.assertIn(CursorKind.TRANSLATION_UNIT, kinds)
|
||||
self.assertIn(CursorKind.VARIABLE_REF, kinds)
|
||||
self.assertIn(CursorKind.LAMBDA_EXPR, kinds)
|
||||
self.assertIn(CursorKind.OBJ_BOOL_LITERAL_EXPR, kinds)
|
||||
self.assertIn(CursorKind.OBJ_SELF_EXPR, kinds)
|
||||
self.assertIn(CursorKind.MS_ASM_STMT, kinds)
|
||||
self.assertIn(CursorKind.MODULE_IMPORT_DECL, kinds)
|
||||
self.assertIn(CursorKind.TYPE_ALIAS_TEMPLATE_DECL, kinds)
|
||||
|
||||
def test_kind_groups(self):
|
||||
"""Check that every kind classifies to exactly one group."""
|
||||
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.is_declaration())
|
||||
self.assertTrue(CursorKind.TYPE_REF.is_reference())
|
||||
self.assertTrue(CursorKind.DECL_REF_EXPR.is_expression())
|
||||
self.assertTrue(CursorKind.UNEXPOSED_STMT.is_statement())
|
||||
self.assertTrue(CursorKind.INVALID_FILE.is_invalid())
|
||||
|
||||
self.assertTrue(CursorKind.TRANSLATION_UNIT.is_translation_unit())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_translation_unit())
|
||||
|
||||
self.assertTrue(CursorKind.PREPROCESSING_DIRECTIVE.is_preprocessing())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_preprocessing())
|
||||
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.is_unexposed())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_unexposed())
|
||||
|
||||
for k in CursorKind.get_all_kinds():
|
||||
group = [n for n in ('is_declaration', 'is_reference', 'is_expression',
|
||||
'is_statement', 'is_invalid', 'is_attribute')
|
||||
if getattr(k, n)()]
|
||||
|
||||
if k in ( CursorKind.TRANSLATION_UNIT,
|
||||
CursorKind.MACRO_DEFINITION,
|
||||
CursorKind.MACRO_INSTANTIATION,
|
||||
CursorKind.INCLUSION_DIRECTIVE,
|
||||
CursorKind.PREPROCESSING_DIRECTIVE,
|
||||
CursorKind.OVERLOAD_CANDIDATE):
|
||||
self.assertEqual(len(group), 0)
|
||||
else:
|
||||
self.assertEqual(len(group), 1)
|
@ -1,110 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import *
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
# FIXME: We need support for invalid translation units to test better.
|
||||
|
||||
|
||||
class TestDiagnostics(unittest.TestCase):
|
||||
def test_diagnostic_warning(self):
|
||||
tu = get_tu('int f0() {}\n')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 11)
|
||||
self.assertEqual(tu.diagnostics[0].spelling,
|
||||
'control reaches end of non-void function')
|
||||
|
||||
def test_diagnostic_note(self):
|
||||
# FIXME: We aren't getting notes here for some reason.
|
||||
tu = get_tu('#define A x\nvoid *A = 1;\n')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 2)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 7)
|
||||
self.assertIn('incompatible', tu.diagnostics[0].spelling)
|
||||
# self.assertEqual(tu.diagnostics[1].severity, Diagnostic.Note)
|
||||
# self.assertEqual(tu.diagnostics[1].location.line, 1)
|
||||
# self.assertEqual(tu.diagnostics[1].location.column, 11)
|
||||
# self.assertEqual(tu.diagnostics[1].spelling, 'instantiated from')
|
||||
|
||||
def test_diagnostic_fixit(self):
|
||||
tu = get_tu('struct { int f0; } x = { f0 : 1 };')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 26)
|
||||
self.assertRegexpMatches(tu.diagnostics[0].spelling,
|
||||
'use of GNU old-style.*')
|
||||
self.assertEqual(len(tu.diagnostics[0].fixits), 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.column, 26)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.column, 30)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].value, '.f0 = ')
|
||||
|
||||
def test_diagnostic_range(self):
|
||||
tu = get_tu('void f() { int i = "a"; }')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 16)
|
||||
self.assertRegexpMatches(tu.diagnostics[0].spelling,
|
||||
'incompatible pointer to.*')
|
||||
self.assertEqual(len(tu.diagnostics[0].fixits), 0)
|
||||
self.assertEqual(len(tu.diagnostics[0].ranges), 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].start.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].start.column, 20)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].end.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].end.column, 23)
|
||||
with self.assertRaises(IndexError):
|
||||
tu.diagnostics[0].ranges[1].start.line
|
||||
|
||||
def test_diagnostic_category(self):
|
||||
"""Ensure that category properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
self.assertEqual(d.severity, Diagnostic.Warning)
|
||||
self.assertEqual(d.location.line, 1)
|
||||
self.assertEqual(d.location.column, 11)
|
||||
|
||||
self.assertEqual(d.category_number, 2)
|
||||
self.assertEqual(d.category_name, 'Semantic Issue')
|
||||
|
||||
def test_diagnostic_option(self):
|
||||
"""Ensure that category option properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
self.assertEqual(d.option, '-Wunused-parameter')
|
||||
self.assertEqual(d.disable_option, '-Wno-unused-parameter')
|
||||
|
||||
def test_diagnostic_children(self):
|
||||
tu = get_tu('void f(int x) {} void g() { f(); }')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
children = d.children
|
||||
self.assertEqual(len(children), 1)
|
||||
self.assertEqual(children[0].severity, Diagnostic.Note)
|
||||
self.assertRegexpMatches(children[0].spelling,
|
||||
'.*declared here')
|
||||
self.assertEqual(children[0].location.line, 1)
|
||||
self.assertEqual(children[0].location.column, 1)
|
||||
|
||||
def test_diagnostic_string_repr(self):
|
||||
tu = get_tu('struct MissingSemicolon{}')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
self.assertEqual(repr(d), '<Diagnostic severity 3, location <SourceLocation file \'t.c\', line 1, column 26>, spelling "expected \';\' after struct">')
|
@ -1,35 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import ExceptionSpecificationKind
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
def find_function_declarations(node, declarations=[]):
|
||||
if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
|
||||
declarations.append((node.spelling, node.exception_specification_kind))
|
||||
for child in node.get_children():
|
||||
declarations = find_function_declarations(child, declarations)
|
||||
return declarations
|
||||
|
||||
|
||||
class TestExceptionSpecificationKind(unittest.TestCase):
|
||||
def test_exception_specification_kind(self):
|
||||
source = """int square1(int x);
|
||||
int square2(int x) noexcept;
|
||||
int square3(int x) noexcept(noexcept(x * x));"""
|
||||
|
||||
tu = get_tu(source, lang='cpp', flags=['-std=c++14'])
|
||||
|
||||
declarations = find_function_declarations(tu.cursor)
|
||||
expected = [
|
||||
('square1', ExceptionSpecificationKind.NONE),
|
||||
('square2', ExceptionSpecificationKind.BASIC_NOEXCEPT),
|
||||
('square3', ExceptionSpecificationKind.COMPUTED_NOEXCEPT)
|
||||
]
|
||||
self.assertListEqual(declarations, expected)
|
@ -1,18 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import Index, File
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestFile(unittest.TestCase):
|
||||
def test_file(self):
|
||||
index = Index.create()
|
||||
tu = index.parse('t.c', unsaved_files = [('t.c', "")])
|
||||
file = File.from_name(tu, "t.c")
|
||||
self.assertEqual(str(file), "t.c")
|
||||
self.assertEqual(file.name, "t.c")
|
||||
self.assertEqual(repr(file), "<File: t.c>")
|
@ -1,26 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import *
|
||||
import os
|
||||
import unittest
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
|
||||
class TestIndex(unittest.TestCase):
|
||||
def test_create(self):
|
||||
index = Index.create()
|
||||
|
||||
# FIXME: test Index.read
|
||||
|
||||
def test_parse(self):
|
||||
index = Index.create()
|
||||
self.assertIsInstance(index, Index)
|
||||
tu = index.parse(os.path.join(kInputsDir, 'hello.cpp'))
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
||||
tu = index.parse(None, ['-c', os.path.join(kInputsDir, 'hello.cpp')])
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
@ -1,38 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import LinkageKind
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestLinkage(unittest.TestCase):
|
||||
def test_linkage(self):
|
||||
"""Ensure that linkage specifers are available on cursors"""
|
||||
|
||||
tu = get_tu("""
|
||||
void foo() { int no_linkage; }
|
||||
static int internal;
|
||||
namespace { struct unique_external_type {} }
|
||||
unique_external_type unique_external;
|
||||
extern int external;
|
||||
""", lang = 'cpp')
|
||||
|
||||
no_linkage = get_cursor(tu.cursor, 'no_linkage')
|
||||
self.assertEqual(no_linkage.linkage, LinkageKind.NO_LINKAGE)
|
||||
|
||||
internal = get_cursor(tu.cursor, 'internal')
|
||||
self.assertEqual(internal.linkage, LinkageKind.INTERNAL)
|
||||
|
||||
unique_external = get_cursor(tu.cursor, 'unique_external')
|
||||
self.assertEqual(unique_external.linkage, LinkageKind.UNIQUE_EXTERNAL)
|
||||
|
||||
external = get_cursor(tu.cursor, 'external')
|
||||
self.assertEqual(external.linkage, LinkageKind.EXTERNAL)
|
@ -1,105 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import File
|
||||
from clang.cindex import SourceLocation
|
||||
from clang.cindex import SourceRange
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
baseInput="int one;\nint two;\n"
|
||||
|
||||
|
||||
class TestLocation(unittest.TestCase):
|
||||
def assert_location(self, loc, line, column, offset):
|
||||
self.assertEqual(loc.line, line)
|
||||
self.assertEqual(loc.column, column)
|
||||
self.assertEqual(loc.offset, offset)
|
||||
|
||||
def test_location(self):
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
self.assertIsNotNone(one)
|
||||
self.assertIsNotNone(two)
|
||||
|
||||
self.assert_location(one.location,line=1,column=5,offset=4)
|
||||
self.assert_location(two.location,line=2,column=5,offset=13)
|
||||
|
||||
# adding a linebreak at top should keep columns same
|
||||
tu = get_tu('\n' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
self.assertIsNotNone(one)
|
||||
self.assertIsNotNone(two)
|
||||
|
||||
self.assert_location(one.location,line=2,column=5,offset=5)
|
||||
self.assert_location(two.location,line=3,column=5,offset=14)
|
||||
|
||||
# adding a space should affect column on first line only
|
||||
tu = get_tu(' ' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
self.assert_location(one.location,line=1,column=6,offset=5)
|
||||
self.assert_location(two.location,line=2,column=5,offset=14)
|
||||
|
||||
# define the expected location ourselves and see if it matches
|
||||
# the returned location
|
||||
tu = get_tu(baseInput)
|
||||
|
||||
file = File.from_name(tu, 't.c')
|
||||
location = SourceLocation.from_position(tu, file, 1, 5)
|
||||
cursor = Cursor.from_location(tu, location)
|
||||
|
||||
one = get_cursor(tu, 'one')
|
||||
self.assertIsNotNone(one)
|
||||
self.assertEqual(one, cursor)
|
||||
|
||||
# Ensure locations referring to the same entity are equivalent.
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 5)
|
||||
self.assertEqual(location, location2)
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 4)
|
||||
self.assertNotEqual(location2, location3)
|
||||
|
||||
offset_location = SourceLocation.from_offset(tu, file, 5)
|
||||
cursor = Cursor.from_location(tu, offset_location)
|
||||
verified = False
|
||||
for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']:
|
||||
self.assertEqual(n, cursor)
|
||||
verified = True
|
||||
|
||||
self.assertTrue(verified)
|
||||
|
||||
def test_extent(self):
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
self.assert_location(one.extent.start,line=1,column=1,offset=0)
|
||||
self.assert_location(one.extent.end,line=1,column=8,offset=7)
|
||||
self.assertEqual(baseInput[one.extent.start.offset:one.extent.end.offset], "int one")
|
||||
|
||||
self.assert_location(two.extent.start,line=2,column=1,offset=9)
|
||||
self.assert_location(two.extent.end,line=2,column=8,offset=16)
|
||||
self.assertEqual(baseInput[two.extent.start.offset:two.extent.end.offset], "int two")
|
||||
|
||||
file = File.from_name(tu, 't.c')
|
||||
location1 = SourceLocation.from_position(tu, file, 1, 1)
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 8)
|
||||
|
||||
range1 = SourceRange.from_locations(location1, location2)
|
||||
range2 = SourceRange.from_locations(location1, location2)
|
||||
self.assertEqual(range1, range2)
|
||||
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 6)
|
||||
range3 = SourceRange.from_locations(location1, location3)
|
||||
self.assertNotEqual(range1, range3)
|
@ -1,54 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import TLSKind
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestTLSKind(unittest.TestCase):
|
||||
def test_tls_kind(self):
|
||||
"""Ensure that thread-local storage kinds are available on cursors."""
|
||||
|
||||
tu = get_tu("""
|
||||
int tls_none;
|
||||
thread_local int tls_dynamic;
|
||||
_Thread_local int tls_static;
|
||||
""", lang = 'cpp')
|
||||
|
||||
tls_none = get_cursor(tu.cursor, 'tls_none')
|
||||
self.assertEqual(tls_none.tls_kind, TLSKind.NONE)
|
||||
|
||||
tls_dynamic = get_cursor(tu.cursor, 'tls_dynamic')
|
||||
self.assertEqual(tls_dynamic.tls_kind, TLSKind.DYNAMIC)
|
||||
|
||||
tls_static = get_cursor(tu.cursor, 'tls_static')
|
||||
self.assertEqual(tls_static.tls_kind, TLSKind.STATIC)
|
||||
|
||||
# The following case tests '__declspec(thread)'. Since it is a Microsoft
|
||||
# specific extension, specific flags are required for the parser to pick
|
||||
# these up.
|
||||
flags = ['-fms-extensions', '-target', 'x86_64-unknown-windows-win32',
|
||||
'-fms-compatibility-version=18']
|
||||
tu = get_tu("""
|
||||
__declspec(thread) int tls_declspec_msvc18;
|
||||
""", lang = 'cpp', flags=flags)
|
||||
|
||||
tls_declspec_msvc18 = get_cursor(tu.cursor, 'tls_declspec_msvc18')
|
||||
self.assertEqual(tls_declspec_msvc18.tls_kind, TLSKind.STATIC)
|
||||
|
||||
flags = ['-fms-extensions', '-target', 'x86_64-unknown-windows-win32',
|
||||
'-fms-compatibility-version=19']
|
||||
tu = get_tu("""
|
||||
__declspec(thread) int tls_declspec_msvc19;
|
||||
""", lang = 'cpp', flags=flags)
|
||||
|
||||
tls_declspec_msvc19 = get_cursor(tu.cursor, 'tls_declspec_msvc19')
|
||||
self.assertEqual(tls_declspec_msvc19.tls_kind, TLSKind.DYNAMIC)
|
@ -1,49 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import TokenKind
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestTokenKind(unittest.TestCase):
|
||||
def test_constructor(self):
|
||||
"""Ensure TokenKind constructor works as expected."""
|
||||
|
||||
t = TokenKind(5, 'foo')
|
||||
|
||||
self.assertEqual(t.value, 5)
|
||||
self.assertEqual(t.name, 'foo')
|
||||
|
||||
def test_bad_register(self):
|
||||
"""Ensure a duplicate value is rejected for registration."""
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
TokenKind.register(2, 'foo')
|
||||
|
||||
def test_unknown_value(self):
|
||||
"""Ensure trying to fetch an unknown value raises."""
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
TokenKind.from_value(-1)
|
||||
|
||||
def test_registration(self):
|
||||
"""Ensure that items registered appear as class attributes."""
|
||||
self.assertTrue(hasattr(TokenKind, 'LITERAL'))
|
||||
literal = TokenKind.LITERAL
|
||||
|
||||
self.assertIsInstance(literal, TokenKind)
|
||||
|
||||
def test_from_value(self):
|
||||
"""Ensure registered values can be obtained from from_value()."""
|
||||
t = TokenKind.from_value(3)
|
||||
self.assertIsInstance(t, TokenKind)
|
||||
self.assertEqual(t, TokenKind.LITERAL)
|
||||
|
||||
def test_repr(self):
|
||||
"""Ensure repr() works."""
|
||||
|
||||
r = repr(TokenKind.LITERAL)
|
||||
self.assertEqual(r, 'TokenKind.LITERAL')
|
@ -1,59 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import Index
|
||||
from clang.cindex import SourceLocation
|
||||
from clang.cindex import SourceRange
|
||||
from clang.cindex import TokenKind
|
||||
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestTokens(unittest.TestCase):
|
||||
def test_token_to_cursor(self):
|
||||
"""Ensure we can obtain a Cursor from a Token instance."""
|
||||
tu = get_tu('int i = 5;')
|
||||
r = tu.get_extent('t.c', (0, 9))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
|
||||
self.assertEqual(len(tokens), 4)
|
||||
self.assertEqual(tokens[1].spelling, 'i')
|
||||
self.assertEqual(tokens[1].kind, TokenKind.IDENTIFIER)
|
||||
|
||||
cursor = tokens[1].cursor
|
||||
self.assertEqual(cursor.kind, CursorKind.VAR_DECL)
|
||||
self.assertEqual(tokens[1].cursor, tokens[2].cursor)
|
||||
|
||||
def test_token_location(self):
|
||||
"""Ensure Token.location works."""
|
||||
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
self.assertEqual(len(tokens), 4)
|
||||
|
||||
loc = tokens[1].location
|
||||
self.assertIsInstance(loc, SourceLocation)
|
||||
self.assertEqual(loc.line, 1)
|
||||
self.assertEqual(loc.column, 5)
|
||||
self.assertEqual(loc.offset, 4)
|
||||
|
||||
def test_token_extent(self):
|
||||
"""Ensure Token.extent works."""
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
self.assertEqual(len(tokens), 4)
|
||||
|
||||
extent = tokens[1].extent
|
||||
self.assertIsInstance(extent, SourceRange)
|
||||
|
||||
self.assertEqual(extent.start.offset, 4)
|
||||
self.assertEqual(extent.end.offset, 7)
|
@ -1,338 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
from contextlib import contextmanager
|
||||
import gc
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import File
|
||||
from clang.cindex import Index
|
||||
from clang.cindex import SourceLocation
|
||||
from clang.cindex import SourceRange
|
||||
from clang.cindex import TranslationUnitSaveError
|
||||
from clang.cindex import TranslationUnitLoadError
|
||||
from clang.cindex import TranslationUnit
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
from .util import skip_if_no_fspath
|
||||
from .util import str_to_path
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
|
||||
@contextmanager
|
||||
def save_tu(tu):
|
||||
"""Convenience API to save a TranslationUnit to a file.
|
||||
|
||||
Returns the filename it was saved to.
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile() as t:
|
||||
tu.save(t.name)
|
||||
yield t.name
|
||||
|
||||
|
||||
@contextmanager
|
||||
def save_tu_pathlike(tu):
|
||||
"""Convenience API to save a TranslationUnit to a file.
|
||||
|
||||
Returns the filename it was saved to.
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile() as t:
|
||||
tu.save(str_to_path(t.name))
|
||||
yield t.name
|
||||
|
||||
|
||||
class TestTranslationUnit(unittest.TestCase):
|
||||
def test_spelling(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = TranslationUnit.from_source(path)
|
||||
self.assertEqual(tu.spelling, path)
|
||||
|
||||
def test_cursor(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = get_tu(path)
|
||||
c = tu.cursor
|
||||
self.assertIsInstance(c, Cursor)
|
||||
self.assertIs(c.kind, CursorKind.TRANSLATION_UNIT)
|
||||
|
||||
def test_parse_arguments(self):
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'hello')
|
||||
self.assertEqual(spellings[-1], 'hi')
|
||||
|
||||
def test_reparse_arguments(self):
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
tu.reparse()
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'hello')
|
||||
self.assertEqual(spellings[-1], 'hi')
|
||||
|
||||
def test_unsaved_files(self):
|
||||
tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
|
||||
('fake.c', """
|
||||
#include "fake.h"
|
||||
int x;
|
||||
int SOME_DEFINE;
|
||||
"""),
|
||||
('./fake.h', """
|
||||
#define SOME_DEFINE y
|
||||
""")
|
||||
])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'x')
|
||||
self.assertEqual(spellings[-1], 'y')
|
||||
|
||||
def test_unsaved_files_2(self):
|
||||
if sys.version_info.major >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
tu = TranslationUnit.from_source('fake.c', unsaved_files = [
|
||||
('fake.c', StringIO('int x;'))])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-1], 'x')
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_from_source_accepts_pathlike(self):
|
||||
tu = TranslationUnit.from_source(str_to_path('fake.c'), ['-Iincludes'], unsaved_files = [
|
||||
(str_to_path('fake.c'), """
|
||||
#include "fake.h"
|
||||
int x;
|
||||
int SOME_DEFINE;
|
||||
"""),
|
||||
(str_to_path('includes/fake.h'), """
|
||||
#define SOME_DEFINE y
|
||||
""")
|
||||
])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'x')
|
||||
self.assertEqual(spellings[-1], 'y')
|
||||
|
||||
def assert_normpaths_equal(self, path1, path2):
|
||||
""" Compares two paths for equality after normalizing them with
|
||||
os.path.normpath
|
||||
"""
|
||||
self.assertEqual(os.path.normpath(path1),
|
||||
os.path.normpath(path2))
|
||||
|
||||
def test_includes(self):
|
||||
def eq(expected, actual):
|
||||
if not actual.is_input_file:
|
||||
self.assert_normpaths_equal(expected[0], actual.source.name)
|
||||
self.assert_normpaths_equal(expected[1], actual.include.name)
|
||||
else:
|
||||
self.assert_normpaths_equal(expected[1], actual.include.name)
|
||||
|
||||
src = os.path.join(kInputsDir, 'include.cpp')
|
||||
h1 = os.path.join(kInputsDir, "header1.h")
|
||||
h2 = os.path.join(kInputsDir, "header2.h")
|
||||
h3 = os.path.join(kInputsDir, "header3.h")
|
||||
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
|
||||
|
||||
tu = TranslationUnit.from_source(src)
|
||||
for i in zip(inc, tu.get_includes()):
|
||||
eq(i[0], i[1])
|
||||
|
||||
def test_inclusion_directive(self):
|
||||
src = os.path.join(kInputsDir, 'include.cpp')
|
||||
h1 = os.path.join(kInputsDir, "header1.h")
|
||||
h2 = os.path.join(kInputsDir, "header2.h")
|
||||
h3 = os.path.join(kInputsDir, "header3.h")
|
||||
inc = [h1, h3, h2, h3, h1]
|
||||
|
||||
tu = TranslationUnit.from_source(src, options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD)
|
||||
inclusion_directive_files = [c.get_included_file().name for c in tu.cursor.get_children() if c.kind == CursorKind.INCLUSION_DIRECTIVE]
|
||||
for i in zip(inc, inclusion_directive_files):
|
||||
self.assert_normpaths_equal(i[0], i[1])
|
||||
|
||||
def test_save(self):
|
||||
"""Ensure TranslationUnit.save() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
with save_tu(tu) as path:
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.assertGreater(os.path.getsize(path), 0)
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_save_pathlike(self):
|
||||
"""Ensure TranslationUnit.save() works with PathLike filename."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
with save_tu_pathlike(tu) as path:
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.assertGreater(os.path.getsize(path), 0)
|
||||
|
||||
def test_save_translation_errors(self):
|
||||
"""Ensure that saving to an invalid directory raises."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
path = '/does/not/exist/llvm-test.ast'
|
||||
self.assertFalse(os.path.exists(os.path.dirname(path)))
|
||||
|
||||
with self.assertRaises(TranslationUnitSaveError) as cm:
|
||||
tu.save(path)
|
||||
ex = cm.exception
|
||||
expected = TranslationUnitSaveError.ERROR_UNKNOWN
|
||||
self.assertEqual(ex.save_error, expected)
|
||||
|
||||
def test_load(self):
|
||||
"""Ensure TranslationUnits can be constructed from saved files."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
self.assertEqual(len(tu.diagnostics), 0)
|
||||
with save_tu(tu) as path:
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.assertGreater(os.path.getsize(path), 0)
|
||||
|
||||
tu2 = TranslationUnit.from_ast_file(filename=path)
|
||||
self.assertEqual(len(tu2.diagnostics), 0)
|
||||
|
||||
foo = get_cursor(tu2, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
|
||||
# Just in case there is an open file descriptor somewhere.
|
||||
del tu2
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_load_pathlike(self):
|
||||
"""Ensure TranslationUnits can be constructed from saved files -
|
||||
PathLike variant."""
|
||||
tu = get_tu('int foo();')
|
||||
self.assertEqual(len(tu.diagnostics), 0)
|
||||
with save_tu(tu) as path:
|
||||
tu2 = TranslationUnit.from_ast_file(filename=str_to_path(path))
|
||||
self.assertEqual(len(tu2.diagnostics), 0)
|
||||
|
||||
foo = get_cursor(tu2, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
|
||||
# Just in case there is an open file descriptor somewhere.
|
||||
del tu2
|
||||
|
||||
def test_index_parse(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
index = Index.create()
|
||||
tu = index.parse(path)
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
||||
|
||||
def test_get_file(self):
|
||||
"""Ensure tu.get_file() works appropriately."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
f = tu.get_file('t.c')
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertEqual(f.name, 't.c')
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
f = tu.get_file('foobar.cpp')
|
||||
|
||||
@skip_if_no_fspath
|
||||
def test_get_file_pathlike(self):
|
||||
"""Ensure tu.get_file() works appropriately with PathLike filenames."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
f = tu.get_file(str_to_path('t.c'))
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertEqual(f.name, 't.c')
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
f = tu.get_file(str_to_path('foobar.cpp'))
|
||||
|
||||
def test_get_source_location(self):
|
||||
"""Ensure tu.get_source_location() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
location = tu.get_location('t.c', 2)
|
||||
self.assertIsInstance(location, SourceLocation)
|
||||
self.assertEqual(location.offset, 2)
|
||||
self.assertEqual(location.file.name, 't.c')
|
||||
|
||||
location = tu.get_location('t.c', (1, 3))
|
||||
self.assertIsInstance(location, SourceLocation)
|
||||
self.assertEqual(location.line, 1)
|
||||
self.assertEqual(location.column, 3)
|
||||
self.assertEqual(location.file.name, 't.c')
|
||||
|
||||
def test_get_source_range(self):
|
||||
"""Ensure tu.get_source_range() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
r = tu.get_extent('t.c', (1,4))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.offset, 1)
|
||||
self.assertEqual(r.end.offset, 4)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
r = tu.get_extent('t.c', ((1,2), (1,3)))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.line, 1)
|
||||
self.assertEqual(r.start.column, 2)
|
||||
self.assertEqual(r.end.line, 1)
|
||||
self.assertEqual(r.end.column, 3)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
start = tu.get_location('t.c', 0)
|
||||
end = tu.get_location('t.c', 5)
|
||||
|
||||
r = tu.get_extent('t.c', (start, end))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.offset, 0)
|
||||
self.assertEqual(r.end.offset, 5)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
def test_get_tokens_gc(self):
|
||||
"""Ensures get_tokens() works properly with garbage collection."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
r = tu.get_extent('t.c', (0, 10))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
gc.collect()
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
|
||||
del tokens[1]
|
||||
gc.collect()
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
|
||||
# May trigger segfault if we don't do our job properly.
|
||||
del tokens
|
||||
gc.collect()
|
||||
gc.collect() # Just in case.
|
||||
|
||||
def test_fail_from_source(self):
|
||||
path = os.path.join(kInputsDir, 'non-existent.cpp')
|
||||
try:
|
||||
tu = TranslationUnit.from_source(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
self.assertEqual(tu, None)
|
||||
|
||||
def test_fail_from_ast_file(self):
|
||||
path = os.path.join(kInputsDir, 'non-existent.ast')
|
||||
try:
|
||||
tu = TranslationUnit.from_ast_file(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
self.assertEqual(tu, None)
|
@ -1,468 +0,0 @@
|
||||
import os
|
||||
from clang.cindex import Config
|
||||
if 'CLANG_LIBRARY_PATH' in os.environ:
|
||||
Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
|
||||
|
||||
import gc
|
||||
import unittest
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import TranslationUnit
|
||||
from clang.cindex import TypeKind
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
|
||||
kInput = """\
|
||||
|
||||
typedef int I;
|
||||
|
||||
struct teststruct {
|
||||
int a;
|
||||
I b;
|
||||
long c;
|
||||
unsigned long d;
|
||||
signed long e;
|
||||
const int f;
|
||||
int *g;
|
||||
int ***h;
|
||||
};
|
||||
|
||||
"""
|
||||
|
||||
|
||||
constarrayInput="""
|
||||
struct teststruct {
|
||||
void *A[2];
|
||||
};
|
||||
"""
|
||||
|
||||
|
||||
class TestType(unittest.TestCase):
|
||||
def test_a_struct(self):
|
||||
tu = get_tu(kInput)
|
||||
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
self.assertIsNotNone(teststruct, "Could not find teststruct.")
|
||||
fields = list(teststruct.get_children())
|
||||
|
||||
self.assertEqual(fields[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[0].translation_unit)
|
||||
self.assertEqual(fields[0].spelling, 'a')
|
||||
self.assertFalse(fields[0].type.is_const_qualified())
|
||||
self.assertEqual(fields[0].type.kind, TypeKind.INT)
|
||||
self.assertEqual(fields[0].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[0].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[1].translation_unit)
|
||||
self.assertEqual(fields[1].spelling, 'b')
|
||||
self.assertFalse(fields[1].type.is_const_qualified())
|
||||
self.assertEqual(fields[1].type.kind, TypeKind.TYPEDEF)
|
||||
self.assertEqual(fields[1].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[1].type.get_declaration().spelling, 'I')
|
||||
self.assertEqual(fields[1].type.get_typedef_name(), 'I')
|
||||
|
||||
self.assertEqual(fields[2].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[2].translation_unit)
|
||||
self.assertEqual(fields[2].spelling, 'c')
|
||||
self.assertFalse(fields[2].type.is_const_qualified())
|
||||
self.assertEqual(fields[2].type.kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[2].type.get_canonical().kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[2].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[3].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[3].translation_unit)
|
||||
self.assertEqual(fields[3].spelling, 'd')
|
||||
self.assertFalse(fields[3].type.is_const_qualified())
|
||||
self.assertEqual(fields[3].type.kind, TypeKind.ULONG)
|
||||
self.assertEqual(fields[3].type.get_canonical().kind, TypeKind.ULONG)
|
||||
self.assertEqual(fields[3].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[4].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[4].translation_unit)
|
||||
self.assertEqual(fields[4].spelling, 'e')
|
||||
self.assertFalse(fields[4].type.is_const_qualified())
|
||||
self.assertEqual(fields[4].type.kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[4].type.get_canonical().kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[4].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[5].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[5].translation_unit)
|
||||
self.assertEqual(fields[5].spelling, 'f')
|
||||
self.assertTrue(fields[5].type.is_const_qualified())
|
||||
self.assertEqual(fields[5].type.kind, TypeKind.INT)
|
||||
self.assertEqual(fields[5].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[5].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[6].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[6].translation_unit)
|
||||
self.assertEqual(fields[6].spelling, 'g')
|
||||
self.assertFalse(fields[6].type.is_const_qualified())
|
||||
self.assertEqual(fields[6].type.kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[6].type.get_pointee().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[6].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[7].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[7].translation_unit)
|
||||
self.assertEqual(fields[7].spelling, 'h')
|
||||
self.assertFalse(fields[7].type.is_const_qualified())
|
||||
self.assertEqual(fields[7].type.kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().get_pointee().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().get_pointee().get_pointee().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[7].type.get_typedef_name(), '')
|
||||
|
||||
def test_references(self):
|
||||
"""Ensure that a Type maintains a reference to a TranslationUnit."""
|
||||
|
||||
tu = get_tu('int x;')
|
||||
children = list(tu.cursor.get_children())
|
||||
self.assertGreater(len(children), 0)
|
||||
|
||||
cursor = children[0]
|
||||
t = cursor.type
|
||||
|
||||
self.assertIsInstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# Delete main TranslationUnit reference and force a GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
self.assertIsInstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
decl = t.get_declaration()
|
||||
|
||||
def testConstantArray(self):
|
||||
tu = get_tu(constarrayInput)
|
||||
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
self.assertIsNotNone(teststruct, "Didn't find teststruct??")
|
||||
fields = list(teststruct.get_children())
|
||||
self.assertEqual(fields[0].spelling, 'A')
|
||||
self.assertEqual(fields[0].type.kind, TypeKind.CONSTANTARRAY)
|
||||
self.assertIsNotNone(fields[0].type.get_array_element_type())
|
||||
self.assertEqual(fields[0].type.get_array_element_type().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[0].type.get_array_size(), 2)
|
||||
|
||||
def test_equal(self):
|
||||
"""Ensure equivalence operators work on Type."""
|
||||
source = 'int a; int b; void *v;'
|
||||
tu = get_tu(source)
|
||||
|
||||
a = get_cursor(tu, 'a')
|
||||
b = get_cursor(tu, 'b')
|
||||
v = get_cursor(tu, 'v')
|
||||
|
||||
self.assertIsNotNone(a)
|
||||
self.assertIsNotNone(b)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
self.assertEqual(a.type, b.type)
|
||||
self.assertNotEqual(a.type, v.type)
|
||||
|
||||
self.assertNotEqual(a.type, None)
|
||||
self.assertNotEqual(a.type, 'foo')
|
||||
|
||||
def test_type_spelling(self):
|
||||
"""Ensure Type.spelling works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
x = get_cursor(tu, 'x')
|
||||
v = get_cursor(tu, 'v')
|
||||
self.assertIsNotNone(c)
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(x)
|
||||
self.assertIsNotNone(v)
|
||||
self.assertEqual(c.type.spelling, "int [5]")
|
||||
self.assertEqual(i.type.spelling, "int []")
|
||||
self.assertEqual(x.type.spelling, "int")
|
||||
self.assertEqual(v.type.spelling, "int [x]")
|
||||
|
||||
def test_typekind_spelling(self):
|
||||
"""Ensure TypeKind.spelling works."""
|
||||
tu = get_tu('int a;')
|
||||
a = get_cursor(tu, 'a')
|
||||
|
||||
self.assertIsNotNone(a)
|
||||
self.assertEqual(a.type.kind.spelling, 'Int')
|
||||
|
||||
def test_function_argument_types(self):
|
||||
"""Ensure that Type.argument_types() works as expected."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
args = f.type.argument_types()
|
||||
self.assertIsNotNone(args)
|
||||
self.assertEqual(len(args), 2)
|
||||
|
||||
t0 = args[0]
|
||||
self.assertIsNotNone(t0)
|
||||
self.assertEqual(t0.kind, TypeKind.INT)
|
||||
|
||||
t1 = args[1]
|
||||
self.assertIsNotNone(t1)
|
||||
self.assertEqual(t1.kind, TypeKind.INT)
|
||||
|
||||
args2 = list(args)
|
||||
self.assertEqual(len(args2), 2)
|
||||
self.assertEqual(t0, args2[0])
|
||||
self.assertEqual(t1, args2[1])
|
||||
|
||||
def test_argument_types_string_key(self):
|
||||
"""Ensure that non-int keys raise a TypeError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
args = f.type.argument_types()
|
||||
self.assertEqual(len(args), 2)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
args['foo']
|
||||
|
||||
def test_argument_types_negative_index(self):
|
||||
"""Ensure that negative indexes on argument_types Raises an IndexError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
args[-1]
|
||||
|
||||
def test_argument_types_overflow_index(self):
|
||||
"""Ensure that indexes beyond the length of Type.argument_types() raise."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
args[2]
|
||||
|
||||
def test_argument_types_invalid_type(self):
|
||||
"""Ensure that obtaining argument_types on a Type without them raises."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
self.assertIsNotNone(i)
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
i.type.argument_types()
|
||||
|
||||
def test_is_pod(self):
|
||||
"""Ensure Type.is_pod() works."""
|
||||
tu = get_tu('int i; void f();')
|
||||
i = get_cursor(tu, 'i')
|
||||
f = get_cursor(tu, 'f')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
self.assertTrue(i.type.is_pod())
|
||||
self.assertFalse(f.type.is_pod())
|
||||
|
||||
def test_function_variadic(self):
|
||||
"""Ensure Type.is_function_variadic works."""
|
||||
|
||||
source ="""
|
||||
#include <stdarg.h>
|
||||
|
||||
void foo(int a, ...);
|
||||
void bar(int a, int b);
|
||||
"""
|
||||
|
||||
tu = get_tu(source)
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertIsInstance(foo.type.is_function_variadic(), bool)
|
||||
self.assertTrue(foo.type.is_function_variadic())
|
||||
self.assertFalse(bar.type.is_function_variadic())
|
||||
|
||||
def test_element_type(self):
|
||||
"""Ensure Type.element_type works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
v = get_cursor(tu, 'v')
|
||||
self.assertIsNotNone(c)
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
self.assertEqual(c.type.kind, TypeKind.CONSTANTARRAY)
|
||||
self.assertEqual(c.type.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(i.type.kind, TypeKind.INCOMPLETEARRAY)
|
||||
self.assertEqual(i.type.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(v.type.kind, TypeKind.VARIABLEARRAY)
|
||||
self.assertEqual(v.type.element_type.kind, TypeKind.INT)
|
||||
|
||||
def test_invalid_element_type(self):
|
||||
"""Ensure Type.element_type raises if type doesn't have elements."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
self.assertIsNotNone(i)
|
||||
with self.assertRaises(Exception):
|
||||
i.element_type
|
||||
|
||||
def test_element_count(self):
|
||||
"""Ensure Type.element_count works."""
|
||||
tu = get_tu('int i[5]; int j;')
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertEqual(i.type.element_count, 5)
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
j.type.element_count
|
||||
|
||||
def test_is_volatile_qualified(self):
|
||||
"""Ensure Type.is_volatile_qualified works."""
|
||||
|
||||
tu = get_tu('volatile int i = 4; int j = 2;')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertIsInstance(i.type.is_volatile_qualified(), bool)
|
||||
self.assertTrue(i.type.is_volatile_qualified())
|
||||
self.assertFalse(j.type.is_volatile_qualified())
|
||||
|
||||
def test_is_restrict_qualified(self):
|
||||
"""Ensure Type.is_restrict_qualified works."""
|
||||
|
||||
tu = get_tu('struct s { void * restrict i; void * j; };')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertIsInstance(i.type.is_restrict_qualified(), bool)
|
||||
self.assertTrue(i.type.is_restrict_qualified())
|
||||
self.assertFalse(j.type.is_restrict_qualified())
|
||||
|
||||
def test_record_layout(self):
|
||||
"""Ensure Cursor.type.get_size, Cursor.type.get_align and
|
||||
Cursor.type.get_offset works."""
|
||||
|
||||
source ="""
|
||||
struct a {
|
||||
long a1;
|
||||
long a2:3;
|
||||
long a3:4;
|
||||
long long a4;
|
||||
};
|
||||
"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,35,64)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,64,67,128)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,35,64)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,35,48))]
|
||||
for flags, values in tries:
|
||||
align,total,a1,a2,a3,a4 = values
|
||||
|
||||
tu = get_tu(source, flags=flags)
|
||||
teststruct = get_cursor(tu, 'a')
|
||||
fields = list(teststruct.get_children())
|
||||
|
||||
self.assertEqual(teststruct.type.get_align(), align)
|
||||
self.assertEqual(teststruct.type.get_size(), total)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[0].spelling), a1)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[1].spelling), a2)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[2].spelling), a3)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[3].spelling), a4)
|
||||
self.assertEqual(fields[0].is_bitfield(), False)
|
||||
self.assertEqual(fields[1].is_bitfield(), True)
|
||||
self.assertEqual(fields[1].get_bitfield_width(), 3)
|
||||
self.assertEqual(fields[2].is_bitfield(), True)
|
||||
self.assertEqual(fields[2].get_bitfield_width(), 4)
|
||||
self.assertEqual(fields[3].is_bitfield(), False)
|
||||
|
||||
def test_offset(self):
|
||||
"""Ensure Cursor.get_record_field_offset works in anonymous records"""
|
||||
source="""
|
||||
struct Test {
|
||||
struct {int a;} typeanon;
|
||||
struct {
|
||||
int bariton;
|
||||
union {
|
||||
int foo;
|
||||
};
|
||||
};
|
||||
int bar;
|
||||
};"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64,96)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,32,64,96)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,64,96)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,64,96))]
|
||||
for flags, values in tries:
|
||||
align,total,f1,bariton,foo,bar = values
|
||||
tu = get_tu(source)
|
||||
teststruct = get_cursor(tu, 'Test')
|
||||
children = list(teststruct.get_children())
|
||||
fields = list(teststruct.type.get_fields())
|
||||
self.assertEqual(children[0].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertNotEqual(children[0].spelling, "typeanon")
|
||||
self.assertEqual(children[1].spelling, "typeanon")
|
||||
self.assertEqual(fields[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(fields[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertTrue(fields[1].is_anonymous())
|
||||
self.assertEqual(teststruct.type.get_offset("typeanon"), f1)
|
||||
self.assertEqual(teststruct.type.get_offset("bariton"), bariton)
|
||||
self.assertEqual(teststruct.type.get_offset("foo"), foo)
|
||||
self.assertEqual(teststruct.type.get_offset("bar"), bar)
|
||||
|
||||
def test_decay(self):
|
||||
"""Ensure decayed types are handled as the original type"""
|
||||
|
||||
tu = get_tu("void foo(int a[]);")
|
||||
foo = get_cursor(tu, 'foo')
|
||||
a = foo.type.argument_types()[0]
|
||||
|
||||
self.assertEqual(a.kind, TypeKind.INCOMPLETEARRAY)
|
||||
self.assertEqual(a.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(a.get_canonical().kind, TypeKind.INCOMPLETEARRAY)
|
||||
|
||||
def test_addrspace(self):
|
||||
"""Ensure the address space can be queried"""
|
||||
tu = get_tu('__attribute__((address_space(2))) int testInteger = 3;', 'c')
|
||||
|
||||
testInteger = get_cursor(tu, 'testInteger')
|
||||
|
||||
self.assertIsNotNone(testInteger, "Could not find testInteger.")
|
||||
self.assertEqual(testInteger.type.get_address_space(), 2)
|
||||
|
||||
def test_template_arguments(self):
|
||||
source = """
|
||||
class Foo {
|
||||
};
|
||||
template <typename T>
|
||||
class Template {
|
||||
};
|
||||
Template<Foo> instance;
|
||||
int bar;
|
||||
"""
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
# Varible with a template argument.
|
||||
cursor = get_cursor(tu, 'instance')
|
||||
cursor_type = cursor.type
|
||||
self.assertEqual(cursor.kind, CursorKind.VAR_DECL)
|
||||
self.assertEqual(cursor_type.spelling, 'Template<Foo>')
|
||||
self.assertEqual(cursor_type.get_num_template_arguments(), 1)
|
||||
template_type = cursor_type.get_template_argument_type(0)
|
||||
self.assertEqual(template_type.spelling, 'Foo')
|
||||
|
||||
# Variable without a template argument.
|
||||
cursor = get_cursor(tu, 'bar')
|
||||
self.assertEqual(cursor.get_num_template_arguments(), -1)
|
@ -1,90 +0,0 @@
|
||||
# This file provides common utility functions for the test suite.
|
||||
|
||||
import os
|
||||
HAS_FSPATH = hasattr(os, 'fspath')
|
||||
|
||||
if HAS_FSPATH:
|
||||
from pathlib import Path as str_to_path
|
||||
else:
|
||||
str_to_path = None
|
||||
|
||||
import unittest
|
||||
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
def get_tu(source, lang='c', all_warnings=False, flags=[]):
|
||||
"""Obtain a translation unit from source and language.
|
||||
|
||||
By default, the translation unit is created from source file "t.<ext>"
|
||||
where <ext> is the default file extension for the specified language. By
|
||||
default it is C, so "t.c" is the default file name.
|
||||
|
||||
Supported languages are {c, cpp, objc}.
|
||||
|
||||
all_warnings is a convenience argument to enable all compiler warnings.
|
||||
"""
|
||||
args = list(flags)
|
||||
name = 't.c'
|
||||
if lang == 'cpp':
|
||||
name = 't.cpp'
|
||||
args.append('-std=c++11')
|
||||
elif lang == 'objc':
|
||||
name = 't.m'
|
||||
elif lang != 'c':
|
||||
raise Exception('Unknown language: %s' % lang)
|
||||
|
||||
if all_warnings:
|
||||
args += ['-Wall', '-Wextra']
|
||||
|
||||
return TranslationUnit.from_source(name, args, unsaved_files=[(name,
|
||||
source)])
|
||||
|
||||
def get_cursor(source, spelling):
|
||||
"""Obtain a cursor from a source object.
|
||||
|
||||
This provides a convenient search mechanism to find a cursor with specific
|
||||
spelling within a source. The first argument can be either a
|
||||
TranslationUnit or Cursor instance.
|
||||
|
||||
If the cursor is not found, None is returned.
|
||||
"""
|
||||
# Convenience for calling on a TU.
|
||||
root_cursor = source if isinstance(source, Cursor) else source.cursor
|
||||
|
||||
for cursor in root_cursor.walk_preorder():
|
||||
if cursor.spelling == spelling:
|
||||
return cursor
|
||||
|
||||
return None
|
||||
|
||||
def get_cursors(source, spelling):
|
||||
"""Obtain all cursors from a source object with a specific spelling.
|
||||
|
||||
This provides a convenient search mechanism to find all cursors with
|
||||
specific spelling within a source. The first argument can be either a
|
||||
TranslationUnit or Cursor instance.
|
||||
|
||||
If no cursors are found, an empty list is returned.
|
||||
"""
|
||||
# Convenience for calling on a TU.
|
||||
root_cursor = source if isinstance(source, Cursor) else source.cursor
|
||||
|
||||
cursors = []
|
||||
for cursor in root_cursor.walk_preorder():
|
||||
if cursor.spelling == spelling:
|
||||
cursors.append(cursor)
|
||||
|
||||
return cursors
|
||||
|
||||
|
||||
skip_if_no_fspath = unittest.skipUnless(HAS_FSPATH,
|
||||
"Requires file system path protocol / Python 3.6+")
|
||||
|
||||
__all__ = [
|
||||
'get_cursor',
|
||||
'get_cursors',
|
||||
'get_tu',
|
||||
'skip_if_no_fspath',
|
||||
'str_to_path',
|
||||
]
|
@ -1,600 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
|
||||
<start>
|
||||
<choice>
|
||||
<!-- Everything else not explicitly mentioned below. -->
|
||||
<ref name="Other" />
|
||||
|
||||
<ref name="Function" />
|
||||
<ref name="Class" />
|
||||
<ref name="Variable" />
|
||||
<ref name="Namespace" />
|
||||
<ref name="Typedef" />
|
||||
<ref name="Enum" />
|
||||
</choice>
|
||||
</start>
|
||||
|
||||
<define name="Other">
|
||||
<element name="Other">
|
||||
<ref name="attrSourceLocation" />
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Function">
|
||||
<element name="Function">
|
||||
<optional>
|
||||
<attribute name="templateKind">
|
||||
<choice>
|
||||
<value>template</value>
|
||||
<value>specialization</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</optional>
|
||||
<ref name="attrSourceLocation" />
|
||||
|
||||
<optional>
|
||||
<attribute name="isInstanceMethod">
|
||||
<data type="boolean" />
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="isClassMethod">
|
||||
<data type="boolean" />
|
||||
</attribute>
|
||||
</optional>
|
||||
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Exceptions" />
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<ref name="Availability" />
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<ref name="Deprecated" />
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<ref name="Unavailable" />
|
||||
</zeroOrMore>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Class">
|
||||
<element name="Class">
|
||||
<optional>
|
||||
<attribute name="templateKind">
|
||||
<choice>
|
||||
<value>template</value>
|
||||
<value>specialization</value>
|
||||
<value>partialSpecialization</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</optional>
|
||||
<ref name="attrSourceLocation" />
|
||||
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
|
||||
<!-- Parameters and results don't make sense for classes, but the user
|
||||
can specify \param or \returns in a comment anyway. -->
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Variable">
|
||||
<element name="Variable">
|
||||
<ref name="attrSourceLocation" />
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
|
||||
<!-- Template parameters, parameters and results don't make sense for
|
||||
variables, but the user can specify \tparam \param or \returns
|
||||
in a comment anyway. -->
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Namespace">
|
||||
<element name="Namespace">
|
||||
<ref name="attrSourceLocation" />
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
|
||||
<!-- Template parameters, parameters and results don't make sense for
|
||||
namespaces, but the user can specify \tparam, \param or \returns
|
||||
in a comment anyway. -->
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Typedef">
|
||||
<element name="Typedef">
|
||||
<ref name="attrSourceLocation" />
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
|
||||
<!-- Parameters and results might make sense for typedefs if the type is
|
||||
a function pointer type. -->
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Enum">
|
||||
<element name="Enum">
|
||||
<ref name="attrSourceLocation" />
|
||||
<ref name="Name" />
|
||||
<optional>
|
||||
<ref name="USR" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Headerfile" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Declaration" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Abstract" />
|
||||
</optional>
|
||||
|
||||
<!-- Template parameters, parameters and results don't make sense for
|
||||
enums, but the user can specify \tparam \param or \returns in a
|
||||
comment anyway. -->
|
||||
<optional>
|
||||
<ref name="TemplateParameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Parameters" />
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ResultDiscussion" />
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<ref name="Discussion" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="attrSourceLocation">
|
||||
<optional>
|
||||
<attribute name="file">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="line">
|
||||
<data type="positiveInteger" />
|
||||
</attribute>
|
||||
<attribute name="column">
|
||||
<data type="positiveInteger" />
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
<define name="Name">
|
||||
<element name="Name">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="USR">
|
||||
<element name="USR">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Abstract">
|
||||
<element name="Abstract">
|
||||
<zeroOrMore>
|
||||
<ref name="TextBlockContent" />
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Declaration">
|
||||
<element name="Declaration">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string"/>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Headerfile">
|
||||
<element name="Headerfile">
|
||||
<oneOrMore>
|
||||
<ref name="TextBlockContent" />
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Discussion">
|
||||
<element name="Discussion">
|
||||
<zeroOrMore>
|
||||
<ref name="TextBlockContent" />
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="TemplateParameters">
|
||||
<element name="TemplateParameters">
|
||||
<!-- Parameter elements should be sorted according to position. -->
|
||||
<oneOrMore>
|
||||
<element name="Parameter">
|
||||
<element name="Name">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
<optional>
|
||||
<!-- This is index at depth 0. libclang API can return more
|
||||
information about position, but we expose only essential
|
||||
information here, since "Parameter" elements are already
|
||||
sorted.
|
||||
|
||||
"Position" element could be added in future if needed. -->
|
||||
<element name="Index">
|
||||
<data type="nonNegativeInteger" />
|
||||
</element>
|
||||
</optional>
|
||||
<!-- In general, template parameters with whitespace discussion
|
||||
should not be emitted. Schema might be more strict here. -->
|
||||
<element name="Discussion">
|
||||
<ref name="TextBlockContent" />
|
||||
</element>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Parameters">
|
||||
<element name="Parameters">
|
||||
<!-- Parameter elements should be sorted according to index. -->
|
||||
<oneOrMore>
|
||||
<element name="Parameter">
|
||||
<element name="Name">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="Index">
|
||||
<data type="nonNegativeInteger" />
|
||||
</element>
|
||||
<element name="IsVarArg">
|
||||
<empty />
|
||||
</element>
|
||||
</choice>
|
||||
</optional>
|
||||
<element name="Direction">
|
||||
<attribute name="isExplicit">
|
||||
<data type="boolean" />
|
||||
</attribute>
|
||||
<choice>
|
||||
<value>in</value>
|
||||
<value>out</value>
|
||||
<value>in,out</value>
|
||||
</choice>
|
||||
</element>
|
||||
<!-- In general, template parameters with whitespace discussion
|
||||
should not be emitted, unless direction is explicitly specified.
|
||||
Schema might be more strict here. -->
|
||||
<element name="Discussion">
|
||||
<ref name="TextBlockContent" />
|
||||
</element>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Exceptions">
|
||||
<element name="Exceptions">
|
||||
<oneOrMore>
|
||||
<ref name="TextBlockContent" />
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Availability">
|
||||
<element name="Availability">
|
||||
<attribute name="distribution">
|
||||
<data type="string" />
|
||||
</attribute>
|
||||
<optional>
|
||||
<element name="IntroducedInVersion">
|
||||
<data type="string">
|
||||
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
|
||||
</data>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="DeprecatedInVersion">
|
||||
<data type="string">
|
||||
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
|
||||
</data>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="RemovedAfterVersion">
|
||||
<data type="string">
|
||||
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
|
||||
</data>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="DeprecationSummary">
|
||||
<data type="string" />
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="Unavailable" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Deprecated">
|
||||
<element name="Deprecated">
|
||||
<optional>
|
||||
<data type="string" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="Unavailable">
|
||||
<element name="Unavailable">
|
||||
<optional>
|
||||
<data type="string" />
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="ResultDiscussion">
|
||||
<element name="ResultDiscussion">
|
||||
<zeroOrMore>
|
||||
<ref name="TextBlockContent" />
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="TextBlockContent">
|
||||
<choice>
|
||||
<element name="Para">
|
||||
<optional>
|
||||
<attribute name="kind">
|
||||
<choice>
|
||||
<value>attention</value>
|
||||
<value>author</value>
|
||||
<value>authors</value>
|
||||
<value>bug</value>
|
||||
<value>copyright</value>
|
||||
<value>date</value>
|
||||
<value>invariant</value>
|
||||
<value>note</value>
|
||||
<value>post</value>
|
||||
<value>pre</value>
|
||||
<value>remark</value>
|
||||
<value>remarks</value>
|
||||
<value>sa</value>
|
||||
<value>see</value>
|
||||
<value>since</value>
|
||||
<value>todo</value>
|
||||
<value>version</value>
|
||||
<value>warning</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<ref name="TextInlineContent" />
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
<element name="Verbatim">
|
||||
<attribute name="xml:space">
|
||||
<value>preserve</value>
|
||||
</attribute>
|
||||
<attribute name="kind">
|
||||
<!-- TODO: add all Doxygen verbatim kinds -->
|
||||
<choice>
|
||||
<value>code</value>
|
||||
<value>verbatim</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<text />
|
||||
</element>
|
||||
</choice>
|
||||
</define>
|
||||
|
||||
<define name="TextInlineContent">
|
||||
<choice>
|
||||
<text />
|
||||
<element name="bold">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
<element name="monospaced">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
<element name="emphasized">
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
<element name="rawHTML">
|
||||
<optional>
|
||||
<!-- If not specified, the default value is 'false'. -->
|
||||
<!-- The value 'false' or absence of the attribute does not imply
|
||||
that the HTML is actually well-formed. -->
|
||||
<attribute name="isMalformed">
|
||||
<data type="boolean" />
|
||||
</attribute>
|
||||
</optional>
|
||||
<!-- Non-empty text content. -->
|
||||
<data type="string">
|
||||
<param name="pattern">.*\S.*</param>
|
||||
</data>
|
||||
</element>
|
||||
</choice>
|
||||
</define>
|
||||
|
||||
</grammar>
|
||||
|
@ -1,15 +0,0 @@
|
||||
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(LLVM_BUILD_EXTERNAL_COMPILER_RT ON CACHE BOOL "")
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
clang
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
test-suite CACHE STRING "")
|
||||
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/3-stage-base.cmake
|
||||
CACHE STRING "")
|
@ -1,16 +0,0 @@
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
clang
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
test-suite
|
||||
stage3
|
||||
stage3-clang
|
||||
stage3-check-all
|
||||
stage3-check-llvm
|
||||
stage3-check-clang
|
||||
stage3-test-suite CACHE STRING "")
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "")
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/3-stage-base.cmake)
|
@ -1,52 +0,0 @@
|
||||
set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING "")
|
||||
|
||||
set(CLANG_VENDOR Android CACHE STRING "")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(LLVM_LIBDIR_SUFFIX 64 CACHE STRING "")
|
||||
set(LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
|
||||
set(ANDROID_RUNTIMES_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(ANDROID_RUNTIMES_BUILD_TYPE Release CACHE STRING "")
|
||||
set(ANDROID_BUILTINS_BUILD_TYPE Release CACHE STRING "")
|
||||
|
||||
set(LLVM_BUILTIN_TARGETS "i686-linux-android;x86_64-linux-android;aarch64-linux-android;armv7-linux-android" CACHE STRING "")
|
||||
foreach(target i686;x86_64;aarch64;armv7)
|
||||
set(BUILTINS_${target}-linux-android_ANDROID 1 CACHE STRING "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_BUILD_TYPE ${ANDROID_BUILTINS_BUILD_TYPE} CACHE STRING "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_ASM_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_C_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_SYSROOT ${ANDROID_${target}_SYSROOT} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_EXE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_SHARED_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_MOUDLE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
endforeach()
|
||||
|
||||
|
||||
set(LLVM_RUNTIME_TARGETS "i686-linux-android;x86_64-linux-android;aarch64-linux-android;armv7-linux-android" CACHE STRING "")
|
||||
foreach(target i686;x86_64;aarch64;armv7)
|
||||
set(RUNTIMES_${target}-linux-android_ANDROID 1 CACHE STRING "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_ASM_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_BUILD_TYPE ${ANDROID_RUNTIMES_BUILD_TYPE} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_C_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_CXX_FLAGS ${ANDROID_${target}_CXX_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_SYSROOT ${ANDROID_${target}_SYSROOT} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_EXE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_SHARED_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_MODULE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_ENABLE_WERROR ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_TEST_COMPILER_CFLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_ASSERTIONS ${ANDROID_RUNTIMES_ENABLE_ASSERTIONS} CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBUNWIND_HAS_NO_EXCEPTIONS_FLAG ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBUNWIND_HAS_FUNWIND_TABLES ON CACHE BOOL "")
|
||||
endforeach()
|
||||
|
||||
set(RUNTIMES_armv7-linux-android_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
|
@ -1,43 +0,0 @@
|
||||
# This file sets up a CMakeCache for an Android toolchain build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86 CACHE STRING "")
|
||||
|
||||
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
|
||||
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
|
||||
set(CLANG_VENDOR Android CACHE STRING "")
|
||||
|
||||
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
|
||||
|
||||
set(HAVE_LIBCXXABI ON CACHE BOOL "")
|
||||
set(LLVM_BUILD_TOOLS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(LLVM_LIBDIR_SUFFIX 64 CACHE STRING "")
|
||||
set(LLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD OFF CACHE BOOL "")
|
||||
set(LLVM_TOOL_OPENMP_BUILD OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
|
||||
if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
|
||||
list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=${LIBCXX_ENABLE_ABI_LINKER_SCRIPT})
|
||||
endif()
|
||||
|
||||
if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
|
||||
list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=${LIBCXX_ENABLE_STATIC_ABI_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (LLVM_BUILD_EXTERNAL_COMPILER_RT)
|
||||
set(APPEND EXTRA_ARGS -DLLVM_BUILD_EXTERNAL_COMPILER_RT=${LLVM_BUILD_EXTERNAL_COMPILER_RT})
|
||||
endif()
|
||||
|
||||
get_cmake_property(variableNames VARIABLES)
|
||||
foreach(variableName ${variableNames})
|
||||
if(variableName MATCHES "^STAGE2_")
|
||||
string(REPLACE "STAGE2_" "" new_name ${variableName})
|
||||
list(APPEND EXTRA_ARGS "-D${new_name}=${${variableName}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
${EXTRA_ARGS}
|
||||
-C${CMAKE_CURRENT_LIST_DIR}/Android-stage2.cmake CACHE STRING "")
|
@ -1,56 +0,0 @@
|
||||
# This file sets up a CMakeCache for Apple-style bootstrap builds. It can be
|
||||
# used on any Darwin system to approximate Apple Clang builds.
|
||||
|
||||
if($ENV{DT_TOOLCHAIN_DIR})
|
||||
set(CMAKE_INSTALL_PREFIX $ENV{DT_TOOLCHAIN_DIR}/usr/)
|
||||
else()
|
||||
set(CMAKE_INSTALL_PREFIX /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.toolchain/usr/)
|
||||
endif()
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86 CACHE STRING "")
|
||||
set(CLANG_VENDOR Apple CACHE STRING "")
|
||||
set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_UTILS OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
set(CLANG_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "")
|
||||
set(CMAKE_MACOSX_RPATH ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_PASSTHROUGH
|
||||
CMAKE_OSX_ARCHITECTURES
|
||||
CACHE STRING "")
|
||||
|
||||
# Disabling embedded darwin compiler-rt on stage1 builds is required because we
|
||||
# don't build stage1 to support arm code generation.
|
||||
set(COMPILER_RT_ENABLE_IOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_WATCHOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_TVOS OFF CACHE BOOL "")
|
||||
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
generate-order-file
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
llvm-config
|
||||
test-suite
|
||||
test-depends
|
||||
llvm-test-depends
|
||||
clang-test-depends
|
||||
distribution
|
||||
install-distribution
|
||||
install-xcode-toolchain
|
||||
install-distribution-toolchain
|
||||
clang CACHE STRING "")
|
||||
|
||||
#bootstrap
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/Apple-stage2.cmake
|
||||
CACHE STRING "")
|
@ -1,6 +0,0 @@
|
||||
# This file sets up a CMakeCache for Apple-style stage2 ThinLTO bootstrap. It is
|
||||
# specified by the stage1 build.
|
||||
|
||||
|
||||
set(LLVM_ENABLE_LTO THIN CACHE BOOL "")
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Apple-stage2.cmake)
|
@ -1,70 +0,0 @@
|
||||
# This file sets up a CMakeCache for Apple-style stage2 bootstrap. It is
|
||||
# specified by the stage1 build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86 ARM AArch64 CACHE STRING "")
|
||||
set(PACKAGE_VENDOR Apple CACHE STRING "")
|
||||
set(CLANG_VENDOR_UTI com.apple.clang CACHE STRING "")
|
||||
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
set(LLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD OFF CACHE BOOL "")
|
||||
set(CLANG_TOOL_SCAN_BUILD_BUILD OFF CACHE BOOL "")
|
||||
set(CLANG_TOOL_SCAN_VIEW_BUILD OFF CACHE BOOL "")
|
||||
set(CLANG_LINKS_TO_CREATE clang++ cc c++ CACHE STRING "")
|
||||
set(CMAKE_MACOSX_RPATH ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
|
||||
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "")
|
||||
|
||||
set(LLVM_BUILD_EXTERNAL_COMPILER_RT ON CACHE BOOL "Build Compiler-RT with just-built clang")
|
||||
set(COMPILER_RT_ENABLE_IOS ON CACHE BOOL "Build iOS Compiler-RT libraries")
|
||||
|
||||
set(LLVM_CREATE_XCODE_TOOLCHAIN ON CACHE BOOL "Generate targets to create and install an Xcode compatible toolchain")
|
||||
|
||||
# Make unit tests (if present) part of the ALL target
|
||||
set(LLVM_BUILD_TESTS ON CACHE BOOL "")
|
||||
|
||||
set(LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
set(CMAKE_C_FLAGS "-fno-stack-protector -fno-common -Wno-profile-instr-unprofiled" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS "-fno-stack-protector -fno-common -Wno-profile-instr-unprofiled" CACHE STRING "")
|
||||
if(LLVM_ENABLE_LTO AND NOT LLVM_ENABLE_LTO STREQUAL "THIN")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
endif()
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
|
||||
set(LIBCXX_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(LIBCXX_INSTALL_HEADERS ON CACHE BOOL "")
|
||||
set(LIBCXX_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(LLVM_LTO_VERSION_OFFSET 3000 CACHE STRING "")
|
||||
|
||||
# Generating Xcode toolchains is useful for developers wanting to build and use
|
||||
# clang without installing over existing tools.
|
||||
set(LLVM_CREATE_XCODE_TOOLCHAIN ON CACHE BOOL "")
|
||||
|
||||
# setup toolchain
|
||||
set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "")
|
||||
set(LLVM_TOOLCHAIN_TOOLS
|
||||
dsymutil
|
||||
llvm-cov
|
||||
llvm-dwarfdump
|
||||
llvm-profdata
|
||||
llvm-objdump
|
||||
llvm-nm
|
||||
llvm-size
|
||||
CACHE STRING "")
|
||||
|
||||
set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
clang
|
||||
LTO
|
||||
clang-format
|
||||
clang-headers
|
||||
cxx-headers
|
||||
${LLVM_TOOLCHAIN_TOOLS}
|
||||
CACHE STRING "")
|
||||
|
||||
# test args
|
||||
|
||||
set(LLVM_LIT_ARGS "--xunit-xml-output=testresults.xunit.xml -v" CACHE STRING "")
|
@ -1,50 +0,0 @@
|
||||
set(LLVM_TARGETS_TO_BUILD ARM;X86 CACHE STRING "")
|
||||
|
||||
# Builtins
|
||||
set(LLVM_BUILTIN_TARGETS "armv7m-none-eabi;armv6m-none-eabi;armv7em-none-eabi" CACHE STRING "Builtin Targets")
|
||||
|
||||
set(BUILTINS_armv6m-none-eabi_CMAKE_SYSROOT ${BAREMETAL_ARMV6M_SYSROOT} CACHE STRING "armv6m-none-eabi Sysroot")
|
||||
set(BUILTINS_armv6m-none-eabi_CMAKE_SYSTEM_NAME Generic CACHE STRING "armv6m-none-eabi System Name")
|
||||
set(BUILTINS_armv6m-none-eabi_COMPILER_RT_BAREMETAL_BUILD ON CACHE BOOL "armv6m-none-eabi Baremetal build")
|
||||
set(BUILTINS_armv6m-none-eabi_COMPILER_RT_OS_DIR "baremetal" CACHE STRING "armv6m-none-eabi os dir")
|
||||
|
||||
set(BUILTINS_armv7m-none-eabi_CMAKE_SYSROOT ${BAREMETAL_ARMV7M_SYSROOT} CACHE STRING "armv7m-none-eabi Sysroot")
|
||||
set(BUILTINS_armv7m-none-eabi_CMAKE_SYSTEM_NAME Generic CACHE STRING "armv7m-none-eabi System Name")
|
||||
set(BUILTINS_armv7m-none-eabi_COMPILER_RT_BAREMETAL_BUILD ON CACHE BOOL "armv7m-none-eabi Baremetal build")
|
||||
set(BUILTINS_armv7m-none-eabi_CMAKE_C_FLAGS "-mfpu=fp-armv8" CACHE STRING "armv7m-none-eabi C Flags")
|
||||
set(BUILTINS_armv7m-none-eabi_CMAKE_ASM_FLAGS "-mfpu=fp-armv8" CACHE STRING "armv7m-none-eabi ASM Flags")
|
||||
set(BUILTINS_armv7m-none-eabi_COMPILER_RT_OS_DIR "baremetal" CACHE STRING "armv7m-none-eabi os dir")
|
||||
|
||||
set(BUILTINS_armv7em-none-eabi_CMAKE_SYSROOT ${BAREMETAL_ARMV7EM_SYSROOT} CACHE STRING "armv7em-none-eabi Sysroot")
|
||||
set(BUILTINS_armv7em-none-eabi_CMAKE_SYSTEM_NAME Generic CACHE STRING "armv7em-none-eabi System Name")
|
||||
set(BUILTINS_armv7em-none-eabi_COMPILER_RT_BAREMETAL_BUILD ON CACHE BOOL "armv7em-none-eabi Baremetal build")
|
||||
set(BUILTINS_armv7em-none-eabi_CMAKE_C_FLAGS "-mfpu=fp-armv8" CACHE STRING "armv7em-none-eabi C Flags")
|
||||
set(BUILTINS_armv7em-none-eabi_CMAKE_ASM_FLAGS "-mfpu=fp-armv8" CACHE STRING "armv7em-none-eabi ASM Flags")
|
||||
set(BUILTINS_armv7em-none-eabi_COMPILER_RT_OS_DIR "baremetal" CACHE STRING "armv7em-none-eabi os dir")
|
||||
|
||||
set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "")
|
||||
set(LLVM_TOOLCHAIN_TOOLS
|
||||
dsymutil
|
||||
llc
|
||||
llvm-ar
|
||||
llvm-cxxfilt
|
||||
llvm-dwarfdump
|
||||
llvm-nm
|
||||
llvm-objdump
|
||||
llvm-ranlib
|
||||
llvm-readobj
|
||||
llvm-size
|
||||
llvm-symbolizer
|
||||
opt
|
||||
CACHE STRING "")
|
||||
|
||||
set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
clang
|
||||
lld
|
||||
clang-headers
|
||||
builtins-armv6m-none-eabi
|
||||
builtins-armv7m-none-eabi
|
||||
builtins-armv7em-none-eabi
|
||||
runtimes
|
||||
${LLVM_TOOLCHAIN_TOOLS}
|
||||
CACHE STRING "")
|
@ -1,30 +0,0 @@
|
||||
# This file sets up a CMakeCache for the second stage of a simple distribution
|
||||
# bootstrap build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING "")
|
||||
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
|
||||
# setup toolchain
|
||||
set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "")
|
||||
set(LLVM_TOOLCHAIN_TOOLS
|
||||
dsymutil
|
||||
llvm-cov
|
||||
llvm-dwarfdump
|
||||
llvm-profdata
|
||||
llvm-objdump
|
||||
llvm-nm
|
||||
llvm-size
|
||||
CACHE STRING "")
|
||||
|
||||
set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
clang
|
||||
LTO
|
||||
clang-format
|
||||
clang-headers
|
||||
builtins
|
||||
runtimes
|
||||
${LLVM_TOOLCHAIN_TOOLS}
|
||||
CACHE STRING "")
|
@ -1,41 +0,0 @@
|
||||
# This file sets up a CMakeCache for a simple distribution bootstrap build.
|
||||
|
||||
# Only build the native target in stage1 since it is a throwaway build.
|
||||
set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "")
|
||||
|
||||
# Optimize the stage1 compiler, but don't LTO it because that wastes time.
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
|
||||
# Setup vendor-specific settings.
|
||||
set(PACKAGE_VENDOR LLVM.org CACHE STRING "")
|
||||
|
||||
# Setting up the stage2 LTO option needs to be done on the stage1 build so that
|
||||
# the proper LTO library dependencies can be connected.
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
|
||||
# Expose stage2 targets through the stage1 build configuration.
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
llvm-config
|
||||
test-suite
|
||||
test-depends
|
||||
llvm-test-depends
|
||||
clang-test-depends
|
||||
distribution
|
||||
install-distribution
|
||||
clang CACHE STRING "")
|
||||
|
||||
# Setup the bootstrap build.
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
|
||||
if(STAGE2_CACHE_FILE)
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${STAGE2_CACHE_FILE}
|
||||
CACHE STRING "")
|
||||
else()
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/DistributionExample-stage2.cmake
|
||||
CACHE STRING "")
|
||||
endif()
|
@ -1,179 +0,0 @@
|
||||
# This file sets up a CMakeCache for the second stage of a Fuchsia toolchain build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING "")
|
||||
|
||||
set(PACKAGE_VENDOR Fuchsia CACHE STRING "")
|
||||
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
if(NOT APPLE)
|
||||
set(LLVM_ENABLE_LLD ON CACHE BOOL "")
|
||||
endif()
|
||||
set(LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
|
||||
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
|
||||
set(CLANG_DEFAULT_CXX_STDLIB libc++ CACHE STRING "")
|
||||
if(NOT APPLE)
|
||||
set(CLANG_DEFAULT_LINKER lld CACHE STRING "")
|
||||
set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "")
|
||||
endif()
|
||||
set(CLANG_DEFAULT_RTLIB compiler-rt CACHE STRING "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
|
||||
set(ENABLE_LINKER_BUILD_ID ON CACHE BOOL "")
|
||||
set(ENABLE_X86_RELAX_RELOCATIONS ON CACHE BOOL "")
|
||||
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND BUILTIN_TARGETS "default")
|
||||
list(APPEND RUNTIME_TARGETS "default")
|
||||
|
||||
set(COMPILER_RT_ENABLE_IOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_TVOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_WATCHOS OFF CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
foreach(target aarch64-linux-gnu;armv7-linux-gnueabihf;i386-linux-gnu;x86_64-linux-gnu)
|
||||
if(LINUX_${target}_SYSROOT)
|
||||
# Set the per-target builtins options.
|
||||
list(APPEND BUILTIN_TARGETS "${target}")
|
||||
set(BUILTINS_${target}_CMAKE_SYSTEM_NAME Linux CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_SYSROOT ${LINUX_${target}_SYSROOT} CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_MODULE_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_EXE_LINKER_FLAG "-fuse-ld=lld" CACHE STRING "")
|
||||
|
||||
# Set the per-target runtimes options.
|
||||
list(APPEND RUNTIME_TARGETS "${target}")
|
||||
set(RUNTIMES_${target}_CMAKE_SYSTEM_NAME Linux CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_SYSROOT ${LINUX_${target}_SYSROOT} CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_MODULE_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_EXE_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
|
||||
set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
|
||||
set(RUNTIMES_${target}_SANITIZER_CXX_ABI "libc++" CACHE STRING "")
|
||||
set(RUNTIMES_${target}_SANITIZER_CXX_ABI_INTREE ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_COMPILER_RT_USE_BUILTINS_LIBRARY ON CACHE BOOL "")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(FUCHSIA_SDK)
|
||||
set(FUCHSIA_aarch64_NAME arm64)
|
||||
set(FUCHSIA_x86_64_NAME x64)
|
||||
foreach(target x86_64;aarch64)
|
||||
set(FUCHSIA_${target}_COMPILER_FLAGS "-I${FUCHSIA_SDK}/pkg/fdio/include")
|
||||
set(FUCHSIA_${target}_LINKER_FLAGS "-L${FUCHSIA_SDK}/arch/${FUCHSIA_${target}_NAME}/lib")
|
||||
set(FUCHSIA_${target}_SYSROOT "${FUCHSIA_SDK}/arch/${FUCHSIA_${target}_NAME}/sysroot")
|
||||
endforeach()
|
||||
|
||||
foreach(target x86_64;aarch64)
|
||||
# Set the per-target builtins options.
|
||||
list(APPEND BUILTIN_TARGETS "${target}-fuchsia")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_SYSTEM_NAME Fuchsia CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_ASM_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_C_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_CXX_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_SHARED_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_MODULE_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_EXE_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_SYSROOT ${FUCHSIA_${target}_SYSROOT} CACHE PATH "")
|
||||
|
||||
# Set the per-target runtimes options.
|
||||
list(APPEND RUNTIME_TARGETS "${target}-fuchsia")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_SYSTEM_NAME Fuchsia CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_ASM_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_C_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_CXX_FLAGS ${FUCHSIA_${target}_COMPILER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_SHARED_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_MODULE_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_EXE_LINKER_FLAGS ${FUCHSIA_${target}_LINKER_FLAGS} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_SYSROOT ${FUCHSIA_${target}_SYSROOT} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-fuchsia_LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBUNWIND_INSTALL_STATIC_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_INSTALL_STATIC_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_HERMETIC_STATIC_LIBRARY ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_ABI_VERSION 2 CACHE STRING "")
|
||||
endforeach()
|
||||
|
||||
set(LLVM_RUNTIME_SANITIZERS "Address" CACHE STRING "")
|
||||
set(LLVM_RUNTIME_SANITIZER_Address_TARGETS "x86_64-fuchsia;aarch64-fuchsia" CACHE STRING "")
|
||||
endif()
|
||||
|
||||
set(LLVM_BUILTIN_TARGETS "${BUILTIN_TARGETS}" CACHE STRING "")
|
||||
set(LLVM_RUNTIME_TARGETS "${RUNTIME_TARGETS}" CACHE STRING "")
|
||||
|
||||
# Setup toolchain.
|
||||
set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "")
|
||||
set(LLVM_TOOLCHAIN_TOOLS
|
||||
dsymutil
|
||||
llc
|
||||
llvm-ar
|
||||
llvm-cov
|
||||
llvm-cxxfilt
|
||||
llvm-dwarfdump
|
||||
llvm-dwp
|
||||
llvm-lib
|
||||
llvm-nm
|
||||
llvm-objcopy
|
||||
llvm-objdump
|
||||
llvm-profdata
|
||||
llvm-ranlib
|
||||
llvm-readelf
|
||||
llvm-readobj
|
||||
llvm-size
|
||||
llvm-strip
|
||||
llvm-symbolizer
|
||||
llvm-xray
|
||||
opt
|
||||
sancov
|
||||
CACHE STRING "")
|
||||
|
||||
set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
clang
|
||||
libclang
|
||||
lld
|
||||
LTO
|
||||
clang-apply-replacements
|
||||
clang-format
|
||||
clang-headers
|
||||
clang-include-fixer
|
||||
clang-refactor
|
||||
clang-tidy
|
||||
clangd
|
||||
builtins
|
||||
runtimes
|
||||
${LLVM_TOOLCHAIN_TOOLS}
|
||||
CACHE STRING "")
|
@ -1,116 +0,0 @@
|
||||
# This file sets up a CMakeCache for a Fuchsia toolchain build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING "")
|
||||
|
||||
set(PACKAGE_VENDOR Fuchsia CACHE STRING "")
|
||||
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
|
||||
set(CLANG_DEFAULT_CXX_STDLIB libc++ CACHE STRING "")
|
||||
if(NOT APPLE)
|
||||
set(CLANG_DEFAULT_LINKER lld CACHE STRING "")
|
||||
set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "")
|
||||
endif()
|
||||
set(CLANG_DEFAULT_RTLIB compiler-rt CACHE STRING "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
|
||||
set(ENABLE_LINKER_BUILD_ID ON CACHE BOOL "")
|
||||
set(ENABLE_X86_RELAX_RELOCATIONS ON CACHE BOOL "")
|
||||
|
||||
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
|
||||
if(APPLE)
|
||||
set(COMPILER_RT_ENABLE_IOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_TVOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_WATCHOS OFF CACHE BOOL "")
|
||||
elseif(UNIX)
|
||||
set(LIBUNWIND_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(LIBUNWIND_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
set(LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
|
||||
set(LIBCXXABI_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
if(BOOTSTRAP_CMAKE_SYSTEM_NAME)
|
||||
set(target "${BOOTSTRAP_CMAKE_CXX_COMPILER_TARGET}")
|
||||
if(STAGE2_LINUX_${target}_SYSROOT)
|
||||
set(BUILTINS_${target}_CMAKE_SYSTEM_NAME Linux CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(BUILTINS_${target}_CMAKE_SYSROOT ${STAGE2_LINUX_${target}_SYSROOT} CACHE STRING "")
|
||||
set(LLVM_BUILTIN_TARGETS "${target}" CACHE STRING "")
|
||||
|
||||
set(RUNTIMES_${target}_CMAKE_SYSTEM_NAME Linux CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(RUNTIMES_${target}_CMAKE_SYSROOT ${STAGE2_LINUX_${target}_SYSROOT} CACHE STRING "")
|
||||
set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBUNWIND_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXXABI_INSTALL_LIBRARY OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
|
||||
set(RUNTIMES_${target}_SANITIZER_CXX_ABI "libc++" CACHE STRING "")
|
||||
set(RUNTIMES_${target}_SANITIZER_CXX_ABI_INTREE ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}_COMPILER_RT_USE_BUILTINS_LIBRARY ON CACHE BOOL "")
|
||||
set(LLVM_RUNTIME_TARGETS "${target}" CACHE STRING "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
if(NOT APPLE)
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LLD ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
check-lld
|
||||
llvm-config
|
||||
test-suite
|
||||
test-depends
|
||||
llvm-test-depends
|
||||
clang-test-depends
|
||||
lld-test-depends
|
||||
distribution
|
||||
install-distribution
|
||||
install-distribution-stripped
|
||||
install-distribution-toolchain
|
||||
clang CACHE STRING "")
|
||||
|
||||
get_cmake_property(variableNames VARIABLES)
|
||||
foreach(variableName ${variableNames})
|
||||
if(variableName MATCHES "^STAGE2_")
|
||||
string(REPLACE "STAGE2_" "" new_name ${variableName})
|
||||
list(APPEND EXTRA_ARGS "-D${new_name}=${${variableName}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Setup the bootstrap build.
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_EXTRA_DEPS
|
||||
builtins
|
||||
runtimes
|
||||
CACHE STRING "")
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
${EXTRA_ARGS}
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/Fuchsia-stage2.cmake
|
||||
CACHE STRING "")
|
@ -1,22 +0,0 @@
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
distribution
|
||||
install-distribution
|
||||
install-distribution-toolchain
|
||||
check-all
|
||||
check-llvm
|
||||
check-clang
|
||||
test-suite CACHE STRING "")
|
||||
|
||||
if(PGO_BUILD_CONFIGURATION)
|
||||
include(${PGO_BUILD_CONFIGURATION})
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${PGO_BUILD_CONFIGURATION}
|
||||
CACHE STRING "")
|
||||
else()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/PGO-stage2.cmake)
|
||||
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/PGO-stage2.cmake
|
||||
CACHE STRING "")
|
||||
endif()
|
@ -1,2 +0,0 @@
|
||||
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
|
||||
set(LLVM_BUILD_EXTERNAL_COMPILER_RT ON CACHE BOOL "")
|
@ -1,30 +0,0 @@
|
||||
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(LLVM_BUILD_EXTERNAL_COMPILER_RT ON CACHE BOOL "")
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86 CACHE STRING "")
|
||||
set(BOOTSTRAP_LLVM_BUILD_INSTRUMENTED ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_TARGETS
|
||||
generate-profdata
|
||||
stage2
|
||||
stage2-distribution
|
||||
stage2-install-distribution
|
||||
stage2-install-distribution-toolchain
|
||||
stage2-check-all
|
||||
stage2-check-llvm
|
||||
stage2-check-clang
|
||||
stage2-test-suite CACHE STRING "")
|
||||
|
||||
if(PGO_INSTRUMENT_LTO)
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ${PGO_INSTRUMENT_LTO} CACHE BOOL "")
|
||||
set(BOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LTO ${PGO_INSTRUMENT_LTO} CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
if(PGO_BUILD_CONFIGURATION)
|
||||
set(EXTRA_ARGS -DPGO_BUILD_CONFIGURATION=${PGO_BUILD_CONFIGURATION})
|
||||
endif()
|
||||
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
${EXTRA_ARGS}
|
||||
-C ${CMAKE_CURRENT_LIST_DIR}/PGO-stage2-instrumented.cmake
|
||||
CACHE STRING "")
|
@ -1,74 +0,0 @@
|
||||
CMake Caches
|
||||
============
|
||||
|
||||
This directory contains CMake cache scripts that pre-populate the CMakeCache in
|
||||
a build directory with commonly used settings.
|
||||
|
||||
You can use the caches files with the following CMake invocation:
|
||||
|
||||
cmake -G <build system>
|
||||
-C <path to cache file>
|
||||
[additional CMake options (i.e. -DCMAKE_INSTALL_PREFIX=<install path>)]
|
||||
<path to llvm>
|
||||
|
||||
Options specified on the command line will override options in the cache files.
|
||||
|
||||
The following cache files exist.
|
||||
|
||||
Apple-stage1
|
||||
------------
|
||||
|
||||
The Apple stage1 cache configures a two stage build similar to how Apple builds
|
||||
the clang shipped with Xcode. The build files generated from this invocation has
|
||||
a target named "stage2" which performs an LTO build of clang.
|
||||
|
||||
The Apple-stage2 cache can be used directly to match the build settings Apple
|
||||
uses in shipping builds without doing a full bootstrap build.
|
||||
|
||||
PGO
|
||||
---
|
||||
|
||||
The PGO CMake cache can be used to generate a multi-stage instrumented compiler.
|
||||
You can configure your build directory with the following invocation of CMake:
|
||||
|
||||
cmake -G <generator> -C <path_to_clang>/cmake/caches/PGO.cmake <source dir>
|
||||
|
||||
After configuration the following additional targets will be generated:
|
||||
|
||||
stage2-instrumented:
|
||||
Builds a stage1 x86 compiler, runtime, and required tools (llvm-config,
|
||||
llvm-profdata) then uses that compiler to build an instrumented stage2 compiler.
|
||||
|
||||
stage2-instrumented-generate-profdata:
|
||||
Depends on "stage2-instrumented" and will use the instrumented compiler to
|
||||
generate profdata based on the training files in <clang>/utils/perf-training
|
||||
|
||||
stage2:
|
||||
Depends on "stage2-instrumented-generate-profdata" and will use the stage1
|
||||
compiler with the stage2 profdata to build a PGO-optimized compiler.
|
||||
|
||||
stage2-check-llvm:
|
||||
Depends on stage2 and runs check-llvm using the stage3 compiler.
|
||||
|
||||
stage2-check-clang:
|
||||
Depends on stage2 and runs check-clang using the stage3 compiler.
|
||||
|
||||
stage2-check-all:
|
||||
Depends on stage2 and runs check-all using the stage3 compiler.
|
||||
|
||||
stage2-test-suite:
|
||||
Depends on stage2 and runs the test-suite using the stage3 compiler (requires
|
||||
in-tree test-suite).
|
||||
|
||||
3-stage
|
||||
-------
|
||||
|
||||
This cache file can be used to generate a 3-stage clang build. You can configure
|
||||
using the following CMake command:
|
||||
|
||||
cmake -C <path to clang>/cmake/caches/3-stage.cmake -G Ninja <path to llvm>
|
||||
|
||||
You can then run "ninja stage3-clang" to build stage1, stage2 and stage3 clangs.
|
||||
|
||||
This is useful for finding non-determinism the compiler by verifying that stage2
|
||||
and stage3 are identical.
|
@ -1,161 +0,0 @@
|
||||
function(clang_tablegen)
|
||||
# Syntax:
|
||||
# clang_tablegen output-file [tablegen-arg ...] SOURCE source-file
|
||||
# [[TARGET cmake-target-name] [DEPENDS extra-dependency ...]]
|
||||
#
|
||||
# Generates a custom command for invoking tblgen as
|
||||
#
|
||||
# tblgen source-file -o=output-file tablegen-arg ...
|
||||
#
|
||||
# and, if cmake-target-name is provided, creates a custom target for
|
||||
# executing the custom command depending on output-file. It is
|
||||
# possible to list more files to depend after DEPENDS.
|
||||
|
||||
cmake_parse_arguments(CTG "" "SOURCE;TARGET" "" ${ARGN})
|
||||
|
||||
if( NOT CTG_SOURCE )
|
||||
message(FATAL_ERROR "SOURCE source-file required by clang_tablegen")
|
||||
endif()
|
||||
|
||||
set( LLVM_TARGET_DEFINITIONS ${CTG_SOURCE} )
|
||||
tablegen(CLANG ${CTG_UNPARSED_ARGUMENTS})
|
||||
|
||||
if(CTG_TARGET)
|
||||
add_public_tablegen_target(${CTG_TARGET})
|
||||
set_target_properties( ${CTG_TARGET} PROPERTIES FOLDER "Clang tablegenning")
|
||||
set_property(GLOBAL APPEND PROPERTY CLANG_TABLEGEN_TARGETS ${CTG_TARGET})
|
||||
endif()
|
||||
endfunction(clang_tablegen)
|
||||
|
||||
macro(set_clang_windows_version_resource_properties name)
|
||||
if(DEFINED windows_resource_file)
|
||||
set_windows_version_resource_properties(${name} ${windows_resource_file}
|
||||
VERSION_MAJOR ${CLANG_VERSION_MAJOR}
|
||||
VERSION_MINOR ${CLANG_VERSION_MINOR}
|
||||
VERSION_PATCHLEVEL ${CLANG_VERSION_PATCHLEVEL}
|
||||
VERSION_STRING "${CLANG_VERSION} (${BACKEND_PACKAGE_STRING})"
|
||||
PRODUCT_NAME "clang")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(add_clang_subdirectory name)
|
||||
add_llvm_subdirectory(CLANG TOOL ${name})
|
||||
endmacro()
|
||||
|
||||
macro(add_clang_library name)
|
||||
cmake_parse_arguments(ARG
|
||||
"SHARED"
|
||||
""
|
||||
"ADDITIONAL_HEADERS"
|
||||
${ARGN})
|
||||
set(srcs)
|
||||
if(MSVC_IDE OR XCODE)
|
||||
# Add public headers
|
||||
file(RELATIVE_PATH lib_path
|
||||
${CLANG_SOURCE_DIR}/lib/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
if(NOT lib_path MATCHES "^[.][.]")
|
||||
file( GLOB_RECURSE headers
|
||||
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.h
|
||||
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.def
|
||||
)
|
||||
set_source_files_properties(${headers} PROPERTIES HEADER_FILE_ONLY ON)
|
||||
|
||||
file( GLOB_RECURSE tds
|
||||
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.td
|
||||
)
|
||||
source_group("TableGen descriptions" FILES ${tds})
|
||||
set_source_files_properties(${tds}} PROPERTIES HEADER_FILE_ONLY ON)
|
||||
|
||||
if(headers OR tds)
|
||||
set(srcs ${headers} ${tds})
|
||||
endif()
|
||||
endif()
|
||||
endif(MSVC_IDE OR XCODE)
|
||||
if(srcs OR ARG_ADDITIONAL_HEADERS)
|
||||
set(srcs
|
||||
ADDITIONAL_HEADERS
|
||||
${srcs}
|
||||
${ARG_ADDITIONAL_HEADERS} # It may contain unparsed unknown args.
|
||||
)
|
||||
endif()
|
||||
if(ARG_SHARED)
|
||||
set(ARG_ENABLE_SHARED SHARED)
|
||||
endif()
|
||||
llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS} ${srcs})
|
||||
|
||||
if(TARGET ${name})
|
||||
target_link_libraries(${name} INTERFACE ${LLVM_COMMON_LIBS})
|
||||
|
||||
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libclang")
|
||||
|
||||
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
|
||||
NOT LLVM_DISTRIBUTION_COMPONENTS)
|
||||
set(export_to_clangtargets EXPORT ClangTargets)
|
||||
set_property(GLOBAL PROPERTY CLANG_HAS_EXPORTS True)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${name}
|
||||
COMPONENT ${name}
|
||||
${export_to_clangtargets}
|
||||
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
|
||||
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
add_llvm_install_targets(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMPONENT ${name})
|
||||
endif()
|
||||
endif()
|
||||
set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name})
|
||||
else()
|
||||
# Add empty "phony" target
|
||||
add_custom_target(${name})
|
||||
endif()
|
||||
|
||||
set_target_properties(${name} PROPERTIES FOLDER "Clang libraries")
|
||||
set_clang_windows_version_resource_properties(${name})
|
||||
endmacro(add_clang_library)
|
||||
|
||||
macro(add_clang_executable name)
|
||||
add_llvm_executable( ${name} ${ARGN} )
|
||||
set_target_properties(${name} PROPERTIES FOLDER "Clang executables")
|
||||
set_clang_windows_version_resource_properties(${name})
|
||||
endmacro(add_clang_executable)
|
||||
|
||||
macro(add_clang_tool name)
|
||||
if (NOT CLANG_BUILD_TOOLS)
|
||||
set(EXCLUDE_FROM_ALL ON)
|
||||
endif()
|
||||
|
||||
add_clang_executable(${name} ${ARGN})
|
||||
add_dependencies(${name} clang-headers)
|
||||
|
||||
if (CLANG_BUILD_TOOLS)
|
||||
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
|
||||
NOT LLVM_DISTRIBUTION_COMPONENTS)
|
||||
set(export_to_clangtargets EXPORT ClangTargets)
|
||||
set_property(GLOBAL PROPERTY CLANG_HAS_EXPORTS True)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${name}
|
||||
${export_to_clangtargets}
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT ${name})
|
||||
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES)
|
||||
add_llvm_install_targets(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMPONENT ${name})
|
||||
endif()
|
||||
set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(add_clang_symlink name dest)
|
||||
add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE)
|
||||
# Always generate install targets
|
||||
llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE)
|
||||
endmacro()
|
@ -1,64 +0,0 @@
|
||||
# Generate a list of CMake library targets so that other CMake projects can
|
||||
# link against them. LLVM calls its version of this file LLVMExports.cmake, but
|
||||
# the usual CMake convention seems to be ${Project}Targets.cmake.
|
||||
set(CLANG_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/clang)
|
||||
set(clang_cmake_builddir "${CMAKE_BINARY_DIR}/${CLANG_INSTALL_PACKAGE_DIR}")
|
||||
|
||||
# Keep this in sync with llvm/cmake/CMakeLists.txt!
|
||||
set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
|
||||
set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
|
||||
|
||||
get_property(CLANG_EXPORTS GLOBAL PROPERTY CLANG_EXPORTS)
|
||||
export(TARGETS ${CLANG_EXPORTS} FILE ${clang_cmake_builddir}/ClangTargets.cmake)
|
||||
|
||||
# Generate ClangConfig.cmake for the build tree.
|
||||
set(CLANG_CONFIG_CMAKE_DIR "${clang_cmake_builddir}")
|
||||
set(CLANG_CONFIG_LLVM_CMAKE_DIR "${llvm_cmake_builddir}")
|
||||
set(CLANG_CONFIG_EXPORTS_FILE "${clang_cmake_builddir}/ClangTargets.cmake")
|
||||
set(CLANG_CONFIG_INCLUDE_DIRS
|
||||
"${CLANG_SOURCE_DIR}/include"
|
||||
"${CLANG_BINARY_DIR}/include"
|
||||
)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ClangConfig.cmake.in
|
||||
${clang_cmake_builddir}/ClangConfig.cmake
|
||||
@ONLY)
|
||||
set(CLANG_CONFIG_CMAKE_DIR)
|
||||
set(CLANG_CONFIG_LLVM_CMAKE_DIR)
|
||||
set(CLANG_CONFIG_EXPORTS_FILE)
|
||||
|
||||
# Generate ClangConfig.cmake for the install tree.
|
||||
set(CLANG_CONFIG_CODE "
|
||||
# Compute the installation prefix from this LLVMConfig.cmake file location.
|
||||
get_filename_component(CLANG_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)")
|
||||
# Construct the proper number of get_filename_component(... PATH)
|
||||
# calls to compute the installation prefix.
|
||||
string(REGEX REPLACE "/" ";" _count "${CLANG_INSTALL_PACKAGE_DIR}")
|
||||
foreach(p ${_count})
|
||||
set(CLANG_CONFIG_CODE "${CLANG_CONFIG_CODE}
|
||||
get_filename_component(CLANG_INSTALL_PREFIX \"\${CLANG_INSTALL_PREFIX}\" PATH)")
|
||||
endforeach(p)
|
||||
set(CLANG_CONFIG_CMAKE_DIR "\${CLANG_INSTALL_PREFIX}/${CLANG_INSTALL_PACKAGE_DIR}")
|
||||
set(CLANG_CONFIG_LLVM_CMAKE_DIR "\${CLANG_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
|
||||
set(CLANG_CONFIG_EXPORTS_FILE "\${CLANG_CMAKE_DIR}/ClangTargets.cmake")
|
||||
set(CLANG_CONFIG_INCLUDE_DIRS
|
||||
"\${CLANG_INSTALL_PREFIX}/include"
|
||||
)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ClangConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/ClangConfig.cmake
|
||||
@ONLY)
|
||||
set(CLANG_CONFIG_CODE)
|
||||
set(CLANG_CONFIG_CMAKE_DIR)
|
||||
set(CLANG_CONFIG_EXPORTS_FILE)
|
||||
|
||||
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
||||
get_property(clang_has_exports GLOBAL PROPERTY CLANG_HAS_EXPORTS)
|
||||
if(clang_has_exports)
|
||||
install(EXPORT ClangTargets DESTINATION ${CLANG_INSTALL_PACKAGE_DIR})
|
||||
endif()
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/ClangConfig.cmake
|
||||
DESTINATION ${CLANG_INSTALL_PACKAGE_DIR})
|
||||
endif()
|
@ -1,20 +0,0 @@
|
||||
# This file allows users to call find_package(Clang) and pick up our targets.
|
||||
|
||||
@CLANG_CONFIG_CODE@
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG
|
||||
HINTS "@CLANG_CONFIG_LLVM_CMAKE_DIR@")
|
||||
|
||||
set(CLANG_EXPORTED_TARGETS "@CLANG_EXPORTS@")
|
||||
set(CLANG_CMAKE_DIR "@CLANG_CONFIG_CMAKE_DIR@")
|
||||
set(CLANG_INCLUDE_DIRS "@CLANG_CONFIG_INCLUDE_DIRS@")
|
||||
|
||||
# Provide all our library targets to users.
|
||||
include("@CLANG_CONFIG_EXPORTS_FILE@")
|
||||
|
||||
# By creating clang-tablegen-targets here, subprojects that depend on Clang's
|
||||
# tablegen-generated headers can always depend on this target whether building
|
||||
# in-tree with Clang or not.
|
||||
if(NOT TARGET clang-tablegen-targets)
|
||||
add_custom_target(clang-tablegen-targets)
|
||||
endif()
|
@ -1,51 +0,0 @@
|
||||
# Looking for Z3 in CLANG_ANALYZER_Z3_INSTALL_DIR
|
||||
find_path(Z3_INCLUDE_DIR NAMES z3.h
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CLANG_ANALYZER_Z3_INSTALL_DIR}/include
|
||||
PATH_SUFFIXES libz3 z3
|
||||
)
|
||||
|
||||
find_library(Z3_LIBRARIES NAMES z3 libz3
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CLANG_ANALYZER_Z3_INSTALL_DIR}
|
||||
PATH_SUFFIXES lib bin
|
||||
)
|
||||
|
||||
find_program(Z3_EXECUTABLE z3
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CLANG_ANALYZER_Z3_INSTALL_DIR}
|
||||
PATH_SUFFIXES bin
|
||||
)
|
||||
|
||||
# If Z3 has not been found in CLANG_ANALYZER_Z3_INSTALL_DIR look in the default directories
|
||||
find_path(Z3_INCLUDE_DIR NAMES z3.h
|
||||
PATH_SUFFIXES libz3 z3
|
||||
)
|
||||
|
||||
find_library(Z3_LIBRARIES NAMES z3 libz3
|
||||
PATH_SUFFIXES lib bin
|
||||
)
|
||||
|
||||
find_program(Z3_EXECUTABLE z3
|
||||
PATH_SUFFIXES bin
|
||||
)
|
||||
|
||||
if(Z3_INCLUDE_DIR AND Z3_LIBRARIES AND Z3_EXECUTABLE)
|
||||
execute_process (COMMAND ${Z3_EXECUTABLE} -version
|
||||
OUTPUT_VARIABLE libz3_version_str
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
string(REGEX REPLACE "^Z3 version ([0-9.]+)" "\\1"
|
||||
Z3_VERSION_STRING "${libz3_version_str}")
|
||||
unset(libz3_version_str)
|
||||
endif()
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set Z3_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Z3
|
||||
REQUIRED_VARS Z3_LIBRARIES Z3_INCLUDE_DIR
|
||||
VERSION_VAR Z3_VERSION_STRING)
|
||||
|
||||
mark_as_advanced(Z3_INCLUDE_DIR Z3_LIBRARIES)
|
@ -1,19 +0,0 @@
|
||||
set(PBM_PREFIX protobuf_mutator)
|
||||
set(PBM_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PBM_PREFIX}/src/${PBM_PREFIX})
|
||||
set(PBM_LIB_PATH ${PBM_PATH}-build/src/libprotobuf-mutator.a)
|
||||
set(PBM_FUZZ_LIB_PATH ${PBM_PATH}-build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a)
|
||||
|
||||
ExternalProject_Add(${PBM_PREFIX}
|
||||
PREFIX ${PBM_PREFIX}
|
||||
GIT_REPOSITORY https://github.com/google/libprotobuf-mutator.git
|
||||
GIT_TAG master
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
|
||||
BUILD_BYPRODUCTS ${PBM_LIB_PATH} ${PBM_FUZZ_LIB_PATH}
|
||||
UPDATE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
set(ProtobufMutator_INCLUDE_DIRS ${PBM_PATH})
|
||||
set(ProtobufMutator_LIBRARIES ${PBM_FUZZ_LIB_PATH} ${PBM_LIB_PATH})
|
@ -1,298 +0,0 @@
|
||||
================
|
||||
AddressSanitizer
|
||||
================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
AddressSanitizer is a fast memory error detector. It consists of a compiler
|
||||
instrumentation module and a run-time library. The tool can detect the
|
||||
following types of bugs:
|
||||
|
||||
* Out-of-bounds accesses to heap, stack and globals
|
||||
* Use-after-free
|
||||
* Use-after-return (runtime flag `ASAN_OPTIONS=detect_stack_use_after_return=1`)
|
||||
* Use-after-scope (clang flag `-fsanitize-address-use-after-scope`)
|
||||
* Double-free, invalid free
|
||||
* Memory leaks (experimental)
|
||||
|
||||
Typical slowdown introduced by AddressSanitizer is **2x**.
|
||||
|
||||
How to build
|
||||
============
|
||||
|
||||
Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>`_.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Simply compile and link your program with ``-fsanitize=address`` flag. The
|
||||
AddressSanitizer run-time library should be linked to the final executable, so
|
||||
make sure to use ``clang`` (not ``ld``) for the final link step. When linking
|
||||
shared libraries, the AddressSanitizer run-time is not linked, so
|
||||
``-Wl,-z,defs`` may cause link errors (don't use it with AddressSanitizer). To
|
||||
get a reasonable performance add ``-O1`` or higher. To get nicer stack traces
|
||||
in error messages add ``-fno-omit-frame-pointer``. To get perfect stack traces
|
||||
you may need to disable inlining (just use ``-O1``) and tail call elimination
|
||||
(``-fno-optimize-sibling-calls``).
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
% cat example_UseAfterFree.cc
|
||||
int main(int argc, char **argv) {
|
||||
int *array = new int[100];
|
||||
delete [] array;
|
||||
return array[argc]; // BOOM
|
||||
}
|
||||
|
||||
# Compile and link
|
||||
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.cc
|
||||
|
||||
or:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Compile
|
||||
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer -c example_UseAfterFree.cc
|
||||
# Link
|
||||
% clang++ -g -fsanitize=address example_UseAfterFree.o
|
||||
|
||||
If a bug is detected, the program will print an error message to stderr and
|
||||
exit with a non-zero exit code. AddressSanitizer exits on the first detected error.
|
||||
This is by design:
|
||||
|
||||
* This approach allows AddressSanitizer to produce faster and smaller generated code
|
||||
(both by ~5%).
|
||||
* Fixing bugs becomes unavoidable. AddressSanitizer does not produce
|
||||
false alarms. Once a memory corruption occurs, the program is in an inconsistent
|
||||
state, which could lead to confusing results and potentially misleading
|
||||
subsequent reports.
|
||||
|
||||
If your process is sandboxed and you are running on OS X 10.10 or earlier, you
|
||||
will need to set ``DYLD_INSERT_LIBRARIES`` environment variable and point it to
|
||||
the ASan library that is packaged with the compiler used to build the
|
||||
executable. (You can find the library by searching for dynamic libraries with
|
||||
``asan`` in their name.) If the environment variable is not set, the process will
|
||||
try to re-exec. Also keep in mind that when moving the executable to another machine,
|
||||
the ASan library will also need to be copied over.
|
||||
|
||||
Symbolizing the Reports
|
||||
=========================
|
||||
|
||||
To make AddressSanitizer symbolize its output
|
||||
you need to set the ``ASAN_SYMBOLIZER_PATH`` environment variable to point to
|
||||
the ``llvm-symbolizer`` binary (or make sure ``llvm-symbolizer`` is in your
|
||||
``$PATH``):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
% ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer ./a.out
|
||||
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
|
||||
READ of size 4 at 0x7f7ddab8c084 thread T0
|
||||
#0 0x403c8c in main example_UseAfterFree.cc:4
|
||||
#1 0x7f7ddabcac4d in __libc_start_main ??:0
|
||||
0x7f7ddab8c084 is located 4 bytes inside of 400-byte region [0x7f7ddab8c080,0x7f7ddab8c210)
|
||||
freed by thread T0 here:
|
||||
#0 0x404704 in operator delete[](void*) ??:0
|
||||
#1 0x403c53 in main example_UseAfterFree.cc:4
|
||||
#2 0x7f7ddabcac4d in __libc_start_main ??:0
|
||||
previously allocated by thread T0 here:
|
||||
#0 0x404544 in operator new[](unsigned long) ??:0
|
||||
#1 0x403c43 in main example_UseAfterFree.cc:2
|
||||
#2 0x7f7ddabcac4d in __libc_start_main ??:0
|
||||
==9442== ABORTING
|
||||
|
||||
If that does not work for you (e.g. your process is sandboxed), you can use a
|
||||
separate script to symbolize the result offline (online symbolization can be
|
||||
force disabled by setting ``ASAN_OPTIONS=symbolize=0``):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
% ASAN_OPTIONS=symbolize=0 ./a.out 2> log
|
||||
% projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt
|
||||
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
|
||||
READ of size 4 at 0x7f7ddab8c084 thread T0
|
||||
#0 0x403c8c in main example_UseAfterFree.cc:4
|
||||
#1 0x7f7ddabcac4d in __libc_start_main ??:0
|
||||
...
|
||||
|
||||
Note that on OS X you may need to run ``dsymutil`` on your binary to have the
|
||||
file\:line info in the AddressSanitizer reports.
|
||||
|
||||
Additional Checks
|
||||
=================
|
||||
|
||||
Initialization order checking
|
||||
-----------------------------
|
||||
|
||||
AddressSanitizer can optionally detect dynamic initialization order problems,
|
||||
when initialization of globals defined in one translation unit uses
|
||||
globals defined in another translation unit. To enable this check at runtime,
|
||||
you should set environment variable
|
||||
``ASAN_OPTIONS=check_initialization_order=1``.
|
||||
|
||||
Note that this option is not supported on OS X.
|
||||
|
||||
Memory leak detection
|
||||
---------------------
|
||||
|
||||
For more information on leak detector in AddressSanitizer, see
|
||||
:doc:`LeakSanitizer`. The leak detection is turned on by default on Linux,
|
||||
and can be enabled using ``ASAN_OPTIONS=detect_leaks=1`` on OS X;
|
||||
however, it is not yet supported on other platforms.
|
||||
|
||||
Issue Suppression
|
||||
=================
|
||||
|
||||
AddressSanitizer is not expected to produce false positives. If you see one,
|
||||
look again; most likely it is a true positive!
|
||||
|
||||
Suppressing Reports in External Libraries
|
||||
-----------------------------------------
|
||||
Runtime interposition allows AddressSanitizer to find bugs in code that is
|
||||
not being recompiled. If you run into an issue in external libraries, we
|
||||
recommend immediately reporting it to the library maintainer so that it
|
||||
gets addressed. However, you can use the following suppression mechanism
|
||||
to unblock yourself and continue on with the testing. This suppression
|
||||
mechanism should only be used for suppressing issues in external code; it
|
||||
does not work on code recompiled with AddressSanitizer. To suppress errors
|
||||
in external libraries, set the ``ASAN_OPTIONS`` environment variable to point
|
||||
to a suppression file. You can either specify the full path to the file or the
|
||||
path of the file relative to the location of your executable.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ASAN_OPTIONS=suppressions=MyASan.supp
|
||||
|
||||
Use the following format to specify the names of the functions or libraries
|
||||
you want to suppress. You can see these in the error report. Remember that
|
||||
the narrower the scope of the suppression, the more bugs you will be able to
|
||||
catch.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
interceptor_via_fun:NameOfCFunctionToSuppress
|
||||
interceptor_via_fun:-[ClassName objCMethodToSuppress:]
|
||||
interceptor_via_lib:NameOfTheLibraryToSuppress
|
||||
|
||||
Conditional Compilation with ``__has_feature(address_sanitizer)``
|
||||
-----------------------------------------------------------------
|
||||
|
||||
In some cases one may need to execute different code depending on whether
|
||||
AddressSanitizer is enabled.
|
||||
:ref:`\_\_has\_feature <langext-__has_feature-__has_extension>` can be used for
|
||||
this purpose.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
// code that builds only under AddressSanitizer
|
||||
# endif
|
||||
#endif
|
||||
|
||||
Disabling Instrumentation with ``__attribute__((no_sanitize("address")))``
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Some code should not be instrumented by AddressSanitizer. One may use
|
||||
the attribute ``__attribute__((no_sanitize("address")))`` (which has
|
||||
deprecated synonyms `no_sanitize_address` and
|
||||
`no_address_safety_analysis`) to disable instrumentation of a
|
||||
particular function. This attribute may not be supported by other
|
||||
compilers, so we suggest to use it together with
|
||||
``__has_feature(address_sanitizer)``.
|
||||
|
||||
The same attribute used on a global variable prevents AddressSanitizer
|
||||
from adding redzones around it and detecting out of bounds accesses.
|
||||
|
||||
Suppressing Errors in Recompiled Code (Blacklist)
|
||||
-------------------------------------------------
|
||||
|
||||
AddressSanitizer supports ``src`` and ``fun`` entity types in
|
||||
:doc:`SanitizerSpecialCaseList`, that can be used to suppress error reports
|
||||
in the specified source files or functions. Additionally, AddressSanitizer
|
||||
introduces ``global`` and ``type`` entity types that can be used to
|
||||
suppress error reports for out-of-bound access to globals with certain
|
||||
names and types (you may only specify class or struct types).
|
||||
|
||||
You may use an ``init`` category to suppress reports about initialization-order
|
||||
problems happening in certain source files or with certain global variables.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Suppress error reports for code in a file or in a function:
|
||||
src:bad_file.cpp
|
||||
# Ignore all functions with names containing MyFooBar:
|
||||
fun:*MyFooBar*
|
||||
# Disable out-of-bound checks for global:
|
||||
global:bad_array
|
||||
# Disable out-of-bound checks for global instances of a given class ...
|
||||
type:Namespace::BadClassName
|
||||
# ... or a given struct. Use wildcard to deal with anonymous namespace.
|
||||
type:Namespace2::*::BadStructName
|
||||
# Disable initialization-order checks for globals:
|
||||
global:bad_init_global=init
|
||||
type:*BadInitClassSubstring*=init
|
||||
src:bad/init/files/*=init
|
||||
|
||||
Suppressing memory leaks
|
||||
------------------------
|
||||
|
||||
Memory leak reports produced by :doc:`LeakSanitizer` (if it is run as a part
|
||||
of AddressSanitizer) can be suppressed by a separate file passed as
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
LSAN_OPTIONS=suppressions=MyLSan.supp
|
||||
|
||||
which contains lines of the form `leak:<pattern>`. Memory leak will be
|
||||
suppressed if pattern matches any function name, source file name, or
|
||||
library name in the symbolized stack trace of the leak report. See
|
||||
`full documentation
|
||||
<https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions>`_
|
||||
for more details.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
* AddressSanitizer uses more real memory than a native run. Exact overhead
|
||||
depends on the allocations sizes. The smaller the allocations you make the
|
||||
bigger the overhead is.
|
||||
* AddressSanitizer uses more stack memory. We have seen up to 3x increase.
|
||||
* On 64-bit platforms AddressSanitizer maps (but not reserves) 16+ Terabytes of
|
||||
virtual address space. This means that tools like ``ulimit`` may not work as
|
||||
usually expected.
|
||||
* Static linking of executables is not supported.
|
||||
|
||||
Supported Platforms
|
||||
===================
|
||||
|
||||
AddressSanitizer is supported on:
|
||||
|
||||
* Linux i386/x86\_64 (tested on Ubuntu 12.04)
|
||||
* OS X 10.7 - 10.11 (i386/x86\_64)
|
||||
* iOS Simulator
|
||||
* Android ARM
|
||||
* NetBSD i386/x86\_64
|
||||
* FreeBSD i386/x86\_64 (tested on FreeBSD 11-current)
|
||||
* Windows 8.1+ (i386/x86\_64)
|
||||
|
||||
Ports to various other platforms are in progress.
|
||||
|
||||
Current Status
|
||||
==============
|
||||
|
||||
AddressSanitizer is fully functional on supported platforms starting from LLVM
|
||||
3.1. The test suite is integrated into CMake build and can be run with ``make
|
||||
check-asan`` command.
|
||||
|
||||
The Windows port is functional and is used by Chrome and Firefox, but it is not
|
||||
as well supported as the other ports.
|
||||
|
||||
More Information
|
||||
================
|
||||
|
||||
`<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_
|
@ -1,13 +0,0 @@
|
||||
..
|
||||
-------------------------------------------------------------------
|
||||
NOTE: This file is automatically generated by running clang-tblgen
|
||||
-gen-attr-docs. Do not edit this file by hand!! The contents for
|
||||
this file are automatically generated by a server-side process.
|
||||
|
||||
Please do not commit this file. The file exists for local testing
|
||||
purposes only.
|
||||
-------------------------------------------------------------------
|
||||
|
||||
===================
|
||||
Attributes in Clang
|
||||
===================
|
File diff suppressed because it is too large
Load Diff
@ -1,943 +0,0 @@
|
||||
==================================
|
||||
Block Implementation Specification
|
||||
==================================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
History
|
||||
=======
|
||||
|
||||
* 2008/7/14 - created.
|
||||
* 2008/8/21 - revised, C++.
|
||||
* 2008/9/24 - add ``NULL`` ``isa`` field to ``__block`` storage.
|
||||
* 2008/10/1 - revise block layout to use a ``static`` descriptor structure.
|
||||
* 2008/10/6 - revise block layout to use an unsigned long int flags.
|
||||
* 2008/10/28 - specify use of ``_Block_object_assign`` and
|
||||
``_Block_object_dispose`` for all "Object" types in helper functions.
|
||||
* 2008/10/30 - revise new layout to have invoke function in same place.
|
||||
* 2008/10/30 - add ``__weak`` support.
|
||||
* 2010/3/16 - rev for stret return, signature field.
|
||||
* 2010/4/6 - improved wording.
|
||||
* 2013/1/6 - improved wording and converted to rst.
|
||||
|
||||
This document describes the Apple ABI implementation specification of Blocks.
|
||||
|
||||
The first shipping version of this ABI is found in Mac OS X 10.6, and shall be
|
||||
referred to as 10.6.ABI. As of 2010/3/16, the following describes the ABI
|
||||
contract with the runtime and the compiler, and, as necessary, will be referred
|
||||
to as ABI.2010.3.16.
|
||||
|
||||
Since the Apple ABI references symbols from other elements of the system, any
|
||||
attempt to use this ABI on systems prior to SnowLeopard is undefined.
|
||||
|
||||
High Level
|
||||
==========
|
||||
|
||||
The ABI of ``Blocks`` consist of their layout and the runtime functions required
|
||||
by the compiler. A ``Block`` consists of a structure of the following form:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct Block_literal_1 {
|
||||
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(void *, ...);
|
||||
struct Block_descriptor_1 {
|
||||
unsigned long int reserved; // NULL
|
||||
unsigned long int size; // sizeof(struct Block_literal_1)
|
||||
// optional helper functions
|
||||
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
|
||||
void (*dispose_helper)(void *src); // IFF (1<<25)
|
||||
// required ABI.2010.3.16
|
||||
const char *signature; // IFF (1<<30)
|
||||
} *descriptor;
|
||||
// imported variables
|
||||
};
|
||||
|
||||
The following flags bits are in use thusly for a possible ABI.2010.3.16:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
enum {
|
||||
// Set to true on blocks that have captures (and thus are not true
|
||||
// global blocks) but are known not to escape for various other
|
||||
// reasons. For backward compatiblity with old runtimes, whenever
|
||||
// BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
|
||||
// non-escaping block returns the original block and releasing such a
|
||||
// block is a no-op, which is exactly how global blocks are handled.
|
||||
BLOCK_IS_NOESCAPE = (1 << 23),
|
||||
|
||||
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
|
||||
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
|
||||
BLOCK_IS_GLOBAL = (1 << 28),
|
||||
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
|
||||
BLOCK_HAS_SIGNATURE = (1 << 30),
|
||||
};
|
||||
|
||||
In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime -
|
||||
it had been a transitional marker that did not get deleted after the
|
||||
transition. This bit is now paired with (1<<30), and represented as the pair
|
||||
(3<<30), for the following combinations of valid bit settings, and their
|
||||
meanings:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
switch (flags & (3<<29)) {
|
||||
case (0<<29): 10.6.ABI, no signature field available
|
||||
case (1<<29): 10.6.ABI, no signature field available
|
||||
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
|
||||
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
|
||||
}
|
||||
|
||||
The signature field is not always populated.
|
||||
|
||||
The following discussions are presented as 10.6.ABI otherwise.
|
||||
|
||||
``Block`` literals may occur within functions where the structure is created in
|
||||
stack local memory. They may also appear as initialization expressions for
|
||||
``Block`` variables of global or ``static`` local variables.
|
||||
|
||||
When a ``Block`` literal expression is evaluated the stack based structure is
|
||||
initialized as follows:
|
||||
|
||||
1. A ``static`` descriptor structure is declared and initialized as follows:
|
||||
|
||||
a. The ``invoke`` function pointer is set to a function that takes the
|
||||
``Block`` structure as its first argument and the rest of the arguments (if
|
||||
any) to the ``Block`` and executes the ``Block`` compound statement.
|
||||
|
||||
b. The ``size`` field is set to the size of the following ``Block`` literal
|
||||
structure.
|
||||
|
||||
c. The ``copy_helper`` and ``dispose_helper`` function pointers are set to
|
||||
respective helper functions if they are required by the ``Block`` literal.
|
||||
|
||||
2. A stack (or global) ``Block`` literal data structure is created and
|
||||
initialized as follows:
|
||||
|
||||
a. The ``isa`` field is set to the address of the external
|
||||
``_NSConcreteStackBlock``, which is a block of uninitialized memory supplied
|
||||
in ``libSystem``, or ``_NSConcreteGlobalBlock`` if this is a static or file
|
||||
level ``Block`` literal.
|
||||
|
||||
b. The ``flags`` field is set to zero unless there are variables imported
|
||||
into the ``Block`` that need helper functions for program level
|
||||
``Block_copy()`` and ``Block_release()`` operations, in which case the
|
||||
(1<<25) flags bit is set.
|
||||
|
||||
As an example, the ``Block`` literal expression:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^ { printf("hello world\n"); }
|
||||
|
||||
would cause the following to be created on a 32-bit system:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_1 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_1 *);
|
||||
struct __block_descriptor_1 *descriptor;
|
||||
};
|
||||
|
||||
void __block_invoke_1(struct __block_literal_1 *_block) {
|
||||
printf("hello world\n");
|
||||
}
|
||||
|
||||
static struct __block_descriptor_1 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1), __block_invoke_1 };
|
||||
|
||||
and where the ``Block`` literal itself appears:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_1 _block_literal = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<29), <uninitialized>,
|
||||
__block_invoke_1,
|
||||
&__block_descriptor_1
|
||||
};
|
||||
|
||||
A ``Block`` imports other ``Block`` references, ``const`` copies of other
|
||||
variables, and variables marked ``__block``. In Objective-C, variables may
|
||||
additionally be objects.
|
||||
|
||||
When a ``Block`` literal expression is used as the initial value of a global
|
||||
or ``static`` local variable, it is initialized as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_1 __block_literal_1 = {
|
||||
&_NSConcreteGlobalBlock,
|
||||
(1<<28)|(1<<29), <uninitialized>,
|
||||
__block_invoke_1,
|
||||
&__block_descriptor_1
|
||||
};
|
||||
|
||||
that is, a different address is provided as the first value and a particular
|
||||
(1<<28) bit is set in the ``flags`` field, and otherwise it is the same as for
|
||||
stack based ``Block`` literals. This is an optimization that can be used for
|
||||
any ``Block`` literal that imports no ``const`` or ``__block`` storage
|
||||
variables.
|
||||
|
||||
Imported Variables
|
||||
==================
|
||||
|
||||
Variables of ``auto`` storage class are imported as ``const`` copies. Variables
|
||||
of ``__block`` storage class are imported as a pointer to an enclosing data
|
||||
structure. Global variables are simply referenced and not considered as
|
||||
imported.
|
||||
|
||||
Imported ``const`` copy variables
|
||||
---------------------------------
|
||||
|
||||
Automatic storage variables not marked with ``__block`` are imported as
|
||||
``const`` copies.
|
||||
|
||||
The simplest example is that of importing a variable of type ``int``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int x = 10;
|
||||
void (^vv)(void) = ^{ printf("x is %d\n", x); }
|
||||
x = 11;
|
||||
vv();
|
||||
|
||||
which would be compiled to:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_2 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_2 *);
|
||||
struct __block_descriptor_2 *descriptor;
|
||||
const int x;
|
||||
};
|
||||
|
||||
void __block_invoke_2(struct __block_literal_2 *_block) {
|
||||
printf("x is %d\n", _block->x);
|
||||
}
|
||||
|
||||
static struct __block_descriptor_2 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_2 __block_literal_2 = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<29), <uninitialized>,
|
||||
__block_invoke_2,
|
||||
&__block_descriptor_2,
|
||||
x
|
||||
};
|
||||
|
||||
In summary, scalars, structures, unions, and function pointers are generally
|
||||
imported as ``const`` copies with no need for helper functions.
|
||||
|
||||
Imported ``const`` copy of ``Block`` reference
|
||||
----------------------------------------------
|
||||
|
||||
The first case where copy and dispose helper functions are required is for the
|
||||
case of when a ``Block`` itself is imported. In this case both a
|
||||
``copy_helper`` function and a ``dispose_helper`` function are needed. The
|
||||
``copy_helper`` function is passed both the existing stack based pointer and the
|
||||
pointer to the new heap version and should call back into the runtime to
|
||||
actually do the copy operation on the imported fields within the ``Block``. The
|
||||
runtime functions are all described in :ref:`RuntimeHelperFunctions`.
|
||||
|
||||
A quick example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void (^existingBlock)(void) = ...;
|
||||
void (^vv)(void) = ^{ existingBlock(); }
|
||||
vv();
|
||||
|
||||
struct __block_literal_3 {
|
||||
...; // existing block
|
||||
};
|
||||
|
||||
struct __block_literal_4 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_4 *);
|
||||
struct __block_literal_3 *const existingBlock;
|
||||
};
|
||||
|
||||
void __block_invoke_4(struct __block_literal_2 *_block) {
|
||||
__block->existingBlock->invoke(__block->existingBlock);
|
||||
}
|
||||
|
||||
void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
|
||||
//_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
|
||||
_Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
|
||||
}
|
||||
|
||||
void __block_dispose_4(struct __block_literal_4 *src) {
|
||||
// was _Block_destroy
|
||||
_Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
|
||||
}
|
||||
|
||||
static struct __block_descriptor_4 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
|
||||
void (*dispose_helper)(struct __block_literal_4 *);
|
||||
} __block_descriptor_4 = {
|
||||
0,
|
||||
sizeof(struct __block_literal_4),
|
||||
__block_copy_4,
|
||||
__block_dispose_4,
|
||||
};
|
||||
|
||||
and where said ``Block`` is used:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_4 _block_literal = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<25)|(1<<29), <uninitialized>
|
||||
__block_invoke_4,
|
||||
& __block_descriptor_4
|
||||
existingBlock,
|
||||
};
|
||||
|
||||
Importing ``__attribute__((NSObject))`` variables
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
GCC introduces ``__attribute__((NSObject))`` on structure pointers to mean "this
|
||||
is an object". This is useful because many low level data structures are
|
||||
declared as opaque structure pointers, e.g. ``CFStringRef``, ``CFArrayRef``,
|
||||
etc. When used from C, however, these are still really objects and are the
|
||||
second case where that requires copy and dispose helper functions to be
|
||||
generated. The copy helper functions generated by the compiler should use the
|
||||
``_Block_object_assign`` runtime helper function and in the dispose helper the
|
||||
``_Block_object_dispose`` runtime helper function should be called.
|
||||
|
||||
For example, ``Block`` foo in the following:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
|
||||
...
|
||||
void (^foo)(void) = ^{ CFPrint(objectPointer); };
|
||||
|
||||
would have the following helper functions generated:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void __block_copy_foo(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||||
_Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
|
||||
}
|
||||
|
||||
void __block_dispose_foo(struct __block_literal_5 *src) {
|
||||
_Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
|
||||
}
|
||||
|
||||
Imported ``__block`` marked variables
|
||||
-------------------------------------
|
||||
|
||||
Layout of ``__block`` marked variables
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The compiler must embed variables that are marked ``__block`` in a specialized
|
||||
structure of the form:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_foo {
|
||||
void *isa;
|
||||
struct Block_byref *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
typeof(marked_variable) marked_variable;
|
||||
};
|
||||
|
||||
Variables of certain types require helper functions for when ``Block_copy()``
|
||||
and ``Block_release()`` are performed upon a referencing ``Block``. At the "C"
|
||||
level only variables that are of type ``Block`` or ones that have
|
||||
``__attribute__((NSObject))`` marked require helper functions. In Objective-C
|
||||
objects require helper functions and in C++ stack based objects require helper
|
||||
functions. Variables that require helper functions use the form:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_foo {
|
||||
void *isa;
|
||||
struct _block_byref_foo *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
// helper functions called via Block_copy() and Block_release()
|
||||
void (*byref_keep)(void *dst, void *src);
|
||||
void (*byref_dispose)(void *);
|
||||
typeof(marked_variable) marked_variable;
|
||||
};
|
||||
|
||||
The structure is initialized such that:
|
||||
|
||||
a. The ``forwarding`` pointer is set to the beginning of its enclosing
|
||||
structure.
|
||||
|
||||
b. The ``size`` field is initialized to the total size of the enclosing
|
||||
structure.
|
||||
|
||||
c. The ``flags`` field is set to either 0 if no helper functions are needed
|
||||
or (1<<25) if they are.
|
||||
|
||||
d. The helper functions are initialized (if present).
|
||||
|
||||
e. The variable itself is set to its initial value.
|
||||
|
||||
f. The ``isa`` field is set to ``NULL``.
|
||||
|
||||
Access to ``__block`` variables from within its lexical scope
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to "move" the variable to the heap upon a ``copy_helper`` operation the
|
||||
compiler must rewrite access to such a variable to be indirect through the
|
||||
structures ``forwarding`` pointer. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int __block i = 10;
|
||||
i = 11;
|
||||
|
||||
would be rewritten to be:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_i {
|
||||
void *isa;
|
||||
struct _block_byref_i *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
int captured_i;
|
||||
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };
|
||||
|
||||
i.forwarding->captured_i = 11;
|
||||
|
||||
In the case of a ``Block`` reference variable being marked ``__block`` the
|
||||
helper code generated must use the ``_Block_object_assign`` and
|
||||
``_Block_object_dispose`` routines supplied by the runtime to make the
|
||||
copies. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
__block void (voidBlock)(void) = blockA;
|
||||
voidBlock = blockB;
|
||||
|
||||
would translate into:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_voidBlock {
|
||||
void *isa;
|
||||
struct _block_byref_voidBlock *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
|
||||
void (*byref_dispose)(struct _block_byref_voidBlock *);
|
||||
void (^captured_voidBlock)(void);
|
||||
};
|
||||
|
||||
void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
|
||||
//_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
|
||||
_Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
|
||||
}
|
||||
|
||||
void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
|
||||
//_Block_destroy(param->captured_voidBlock, 0);
|
||||
_Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
|
||||
.byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
|
||||
.captured_voidBlock=blockA )};
|
||||
|
||||
voidBlock.forwarding->captured_voidBlock = blockB;
|
||||
|
||||
Importing ``__block`` variables into ``Blocks``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A ``Block`` that uses a ``__block`` variable in its compound statement body must
|
||||
import the variable and emit ``copy_helper`` and ``dispose_helper`` helper
|
||||
functions that, in turn, call back into the runtime to actually copy or release
|
||||
the ``byref`` data block using the functions ``_Block_object_assign`` and
|
||||
``_Block_object_dispose``.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int __block i = 2;
|
||||
functioncall(^{ i = 10; });
|
||||
|
||||
would translate to:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_i {
|
||||
void *isa; // set to NULL
|
||||
struct _block_byref_voidBlock *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
|
||||
void (*byref_dispose)(struct _block_byref_i *);
|
||||
int captured_i;
|
||||
};
|
||||
|
||||
|
||||
struct __block_literal_5 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_5 *);
|
||||
struct __block_descriptor_5 *descriptor;
|
||||
struct _block_byref_i *i_holder;
|
||||
};
|
||||
|
||||
void __block_invoke_5(struct __block_literal_5 *_block) {
|
||||
_block->forwarding->captured_i = 10;
|
||||
}
|
||||
|
||||
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||||
//_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
|
||||
_Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
|
||||
}
|
||||
|
||||
void __block_dispose_5(struct __block_literal_5 *src) {
|
||||
//_Block_byref_release(src->captured_i);
|
||||
_Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
|
||||
}
|
||||
|
||||
static struct __block_descriptor_5 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
|
||||
void (*dispose_helper)(struct __block_literal_5 *);
|
||||
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_i i = {( .isa=NULL, .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i), .captured_i=2 )};
|
||||
struct __block_literal_5 _block_literal = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<25)|(1<<29), <uninitialized>,
|
||||
__block_invoke_5,
|
||||
&__block_descriptor_5,
|
||||
&i,
|
||||
};
|
||||
|
||||
Importing ``__attribute__((NSObject))`` ``__block`` variables
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A ``__block`` variable that is also marked ``__attribute__((NSObject))`` should
|
||||
have ``byref_keep`` and ``byref_dispose`` helper functions that use
|
||||
``_Block_object_assign`` and ``_Block_object_dispose``.
|
||||
|
||||
``__block`` escapes
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Because ``Blocks`` referencing ``__block`` variables may have ``Block_copy()``
|
||||
performed upon them the underlying storage for the variables may move to the
|
||||
heap. In Objective-C Garbage Collection Only compilation environments the heap
|
||||
used is the garbage collected one and no further action is required. Otherwise
|
||||
the compiler must issue a call to potentially release any heap storage for
|
||||
``__block`` variables at all escapes or terminations of their scope. The call
|
||||
should be:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);
|
||||
|
||||
Nesting
|
||||
^^^^^^^
|
||||
|
||||
``Blocks`` may contain ``Block`` literal expressions. Any variables used within
|
||||
inner blocks are imported into all enclosing ``Block`` scopes even if the
|
||||
variables are not used. This includes ``const`` imports as well as ``__block``
|
||||
variables.
|
||||
|
||||
Objective C Extensions to ``Blocks``
|
||||
====================================
|
||||
|
||||
Importing Objects
|
||||
-----------------
|
||||
|
||||
Objects should be treated as ``__attribute__((NSObject))`` variables; all
|
||||
``copy_helper``, ``dispose_helper``, ``byref_keep``, and ``byref_dispose``
|
||||
helper functions should use ``_Block_object_assign`` and
|
||||
``_Block_object_dispose``. There should be no code generated that uses
|
||||
``*-retain`` or ``*-release`` methods.
|
||||
|
||||
``Blocks`` as Objects
|
||||
---------------------
|
||||
|
||||
The compiler will treat ``Blocks`` as objects when synthesizing property setters
|
||||
and getters, will characterize them as objects when generating garbage
|
||||
collection strong and weak layout information in the same manner as objects, and
|
||||
will issue strong and weak write-barrier assignments in the same manner as
|
||||
objects.
|
||||
|
||||
``__weak __block`` Support
|
||||
--------------------------
|
||||
|
||||
Objective-C (and Objective-C++) support the ``__weak`` attribute on ``__block``
|
||||
variables. Under normal circumstances the compiler uses the Objective-C runtime
|
||||
helper support functions ``objc_assign_weak`` and ``objc_read_weak``. Both
|
||||
should continue to be used for all reads and writes of ``__weak __block``
|
||||
variables:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
objc_read_weak(&block->byref_i->forwarding->i)
|
||||
|
||||
The ``__weak`` variable is stored in a ``_block_byref_foo`` structure and the
|
||||
``Block`` has copy and dispose helpers for this structure that call:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
|
||||
|
||||
In turn, the ``block_byref`` copy support helpers distinguish between whether
|
||||
the ``__block`` variable is a ``Block`` or not and should either call:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);
|
||||
|
||||
for something declared as an object or:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
|
||||
|
||||
for something declared as a ``Block``.
|
||||
|
||||
A full example follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
__block __weak id obj = <initialization expression>;
|
||||
functioncall(^{ [obj somemessage]; });
|
||||
|
||||
would translate to:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct _block_byref_obj {
|
||||
void *isa; // uninitialized
|
||||
struct _block_byref_obj *forwarding;
|
||||
int flags; //refcount;
|
||||
int size;
|
||||
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
|
||||
void (*byref_dispose)(struct _block_byref_i *);
|
||||
id captured_obj;
|
||||
};
|
||||
|
||||
void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
|
||||
//_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
|
||||
_Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
|
||||
}
|
||||
|
||||
void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
|
||||
//_Block_destroy(param->captured_obj, 0);
|
||||
_Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
|
||||
};
|
||||
|
||||
for the block ``byref`` part and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct __block_literal_5 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_5 *);
|
||||
struct __block_descriptor_5 *descriptor;
|
||||
struct _block_byref_obj *byref_obj;
|
||||
};
|
||||
|
||||
void __block_invoke_5(struct __block_literal_5 *_block) {
|
||||
[objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
|
||||
}
|
||||
|
||||
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||||
//_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
|
||||
_Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
|
||||
}
|
||||
|
||||
void __block_dispose_5(struct __block_literal_5 *src) {
|
||||
//_Block_byref_release(src->byref_obj);
|
||||
_Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
|
||||
}
|
||||
|
||||
static struct __block_descriptor_5 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
|
||||
void (*dispose_helper)(struct __block_literal_5 *);
|
||||
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };
|
||||
|
||||
and within the compound statement:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
truct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
|
||||
.byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
|
||||
.captured_obj = <initialization expression> )};
|
||||
|
||||
truct __block_literal_5 _block_literal = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<25)|(1<<29), <uninitialized>,
|
||||
__block_invoke_5,
|
||||
&__block_descriptor_5,
|
||||
&obj, // a reference to the on-stack structure containing "captured_obj"
|
||||
};
|
||||
|
||||
|
||||
functioncall(_block_literal->invoke(&_block_literal));
|
||||
|
||||
C++ Support
|
||||
===========
|
||||
|
||||
Within a block stack based C++ objects are copied into ``const`` copies using
|
||||
the copy constructor. It is an error if a stack based C++ object is used within
|
||||
a block if it does not have a copy constructor. In addition both copy and
|
||||
destroy helper routines must be synthesized for the block to support the
|
||||
``Block_copy()`` operation, and the flags work marked with the (1<<26) bit in
|
||||
addition to the (1<<25) bit. The copy helper should call the constructor using
|
||||
appropriate offsets of the variable within the supplied stack based block source
|
||||
and heap based destination for all ``const`` constructed copies, and similarly
|
||||
should call the destructor in the destroy routine.
|
||||
|
||||
As an example, suppose a C++ class ``FOO`` existed with a copy constructor.
|
||||
Within a code block a stack version of a ``FOO`` object is declared and used
|
||||
within a ``Block`` literal expression:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
{
|
||||
FOO foo;
|
||||
void (^block)(void) = ^{ printf("%d\n", foo.value()); };
|
||||
}
|
||||
|
||||
The compiler would synthesize:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
struct __block_literal_10 {
|
||||
void *isa;
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(struct __block_literal_10 *);
|
||||
struct __block_descriptor_10 *descriptor;
|
||||
const FOO foo;
|
||||
};
|
||||
|
||||
void __block_invoke_10(struct __block_literal_10 *_block) {
|
||||
printf("%d\n", _block->foo.value());
|
||||
}
|
||||
|
||||
void __block_literal_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
|
||||
FOO_ctor(&dst->foo, &src->foo);
|
||||
}
|
||||
|
||||
void __block_dispose_10(struct __block_literal_10 *src) {
|
||||
FOO_dtor(&src->foo);
|
||||
}
|
||||
|
||||
static struct __block_descriptor_10 {
|
||||
unsigned long int reserved;
|
||||
unsigned long int Block_size;
|
||||
void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
|
||||
void (*dispose_helper)(struct __block_literal_10 *);
|
||||
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };
|
||||
|
||||
and the code would be:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
{
|
||||
FOO foo;
|
||||
comp_ctor(&foo); // default constructor
|
||||
struct __block_literal_10 _block_literal = {
|
||||
&_NSConcreteStackBlock,
|
||||
(1<<25)|(1<<26)|(1<<29), <uninitialized>,
|
||||
__block_invoke_10,
|
||||
&__block_descriptor_10,
|
||||
};
|
||||
comp_ctor(&_block_literal->foo, &foo); // const copy into stack version
|
||||
struct __block_literal_10 &block = &_block_literal; // assign literal to block variable
|
||||
block->invoke(block); // invoke block
|
||||
comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
|
||||
comp_dtor(&foo); // destroy original version
|
||||
}
|
||||
|
||||
|
||||
C++ objects stored in ``__block`` storage start out on the stack in a
|
||||
``block_byref`` data structure as do other variables. Such objects (if not
|
||||
``const`` objects) must support a regular copy constructor. The ``block_byref``
|
||||
data structure will have copy and destroy helper routines synthesized by the
|
||||
compiler. The copy helper will have code created to perform the copy
|
||||
constructor based on the initial stack ``block_byref`` data structure, and will
|
||||
also set the (1<<26) bit in addition to the (1<<25) bit. The destroy helper
|
||||
will have code to do the destructor on the object stored within the supplied
|
||||
``block_byref`` heap data structure. For example,
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
__block FOO blockStorageFoo;
|
||||
|
||||
requires the normal constructor for the embedded ``blockStorageFoo`` object:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);
|
||||
|
||||
and at scope termination the destructor:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);
|
||||
|
||||
Note that the forwarding indirection is *NOT* used.
|
||||
|
||||
The compiler would need to generate (if used from a block literal) the following
|
||||
copy/dispose helpers:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
|
||||
FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
|
||||
}
|
||||
|
||||
void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
|
||||
FOO_dtor(&src->blockStorageFoo);
|
||||
}
|
||||
|
||||
for the appropriately named constructor and destructor for the class/struct
|
||||
``FOO``.
|
||||
|
||||
To support member variable and function access the compiler will synthesize a
|
||||
``const`` pointer to a block version of the ``this`` pointer.
|
||||
|
||||
.. _RuntimeHelperFunctions:
|
||||
|
||||
Runtime Helper Functions
|
||||
========================
|
||||
|
||||
The runtime helper functions are described in
|
||||
``/usr/local/include/Block_private.h``. To summarize their use, a ``Block``
|
||||
requires copy/dispose helpers if it imports any block variables, ``__block``
|
||||
storage variables, ``__attribute__((NSObject))`` variables, or C++ ``const``
|
||||
copied objects with constructor/destructors. The (1<<26) bit is set and
|
||||
functions are generated.
|
||||
|
||||
The block copy helper function should, for each of the variables of the type
|
||||
mentioned above, call:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);
|
||||
|
||||
in the copy helper and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);
|
||||
|
||||
in the dispose helper where ``<apropos>`` is:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
enum {
|
||||
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
|
||||
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
|
||||
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
|
||||
|
||||
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
|
||||
|
||||
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
|
||||
};
|
||||
|
||||
and of course the constructors/destructors for ``const`` copied C++ objects.
|
||||
|
||||
The ``block_byref`` data structure similarly requires copy/dispose helpers for
|
||||
block variables, ``__attribute__((NSObject))`` variables, or C++ ``const``
|
||||
copied objects with constructor/destructors, and again the (1<<26) bit is set
|
||||
and functions are generated in the same manner.
|
||||
|
||||
Under ObjC we allow ``__weak`` as an attribute on ``__block`` variables, and
|
||||
this causes the addition of ``BLOCK_FIELD_IS_WEAK`` orred onto the
|
||||
``BLOCK_FIELD_IS_BYREF`` flag when copying the ``block_byref`` structure in the
|
||||
``Block`` copy helper, and onto the ``BLOCK_FIELD_<apropos>`` field within the
|
||||
``block_byref`` copy/dispose helper calls.
|
||||
|
||||
The prototypes, and summary, of the helper functions are:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Certain field types require runtime assistance when being copied to the
|
||||
heap. The following function is used to copy fields of types: blocks,
|
||||
pointers to byref structures, and objects (including
|
||||
__attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to
|
||||
the other choices which are mutually exclusive. Only in a Block copy
|
||||
helper will one see BLOCK_FIELD_IS_BYREF.
|
||||
*/
|
||||
void _Block_object_assign(void *destAddr, const void *object, const int flags);
|
||||
|
||||
/* Similarly a compiler generated dispose helper needs to call back for each
|
||||
field of the byref data structure. (Currently the implementation only
|
||||
packs one field into the byref structure but in principle there could be
|
||||
more). The same flags used in the copy helper should be used for each
|
||||
call generated to this function:
|
||||
*/
|
||||
void _Block_object_dispose(const void *object, const int flags);
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright 2008-2010 Apple, Inc.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1 +0,0 @@
|
||||
*NOTE* This document has moved to https://clang.llvm.org/docs/Block-ABI-Apple.html.
|
@ -1,361 +0,0 @@
|
||||
|
||||
.. role:: block-term
|
||||
|
||||
=================================
|
||||
Language Specification for Blocks
|
||||
=================================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Revisions
|
||||
=========
|
||||
|
||||
- 2008/2/25 --- created
|
||||
- 2008/7/28 --- revised, ``__block`` syntax
|
||||
- 2008/8/13 --- revised, Block globals
|
||||
- 2008/8/21 --- revised, C++ elaboration
|
||||
- 2008/11/1 --- revised, ``__weak`` support
|
||||
- 2009/1/12 --- revised, explicit return types
|
||||
- 2009/2/10 --- revised, ``__block`` objects need retain
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
A new derived type is introduced to C and, by extension, Objective-C,
|
||||
C++, and Objective-C++
|
||||
|
||||
The Block Type
|
||||
==============
|
||||
|
||||
Like function types, the :block-term:`Block type` is a pair consisting
|
||||
of a result value type and a list of parameter types very similar to a
|
||||
function type. Blocks are intended to be used much like functions with
|
||||
the key distinction being that in addition to executable code they
|
||||
also contain various variable bindings to automatic (stack) or managed
|
||||
(heap) memory.
|
||||
|
||||
The abstract declarator,
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int (^)(char, float)
|
||||
|
||||
describes a reference to a Block that, when invoked, takes two
|
||||
parameters, the first of type char and the second of type float, and
|
||||
returns a value of type int. The Block referenced is of opaque data
|
||||
that may reside in automatic (stack) memory, global memory, or heap
|
||||
memory.
|
||||
|
||||
Block Variable Declarations
|
||||
===========================
|
||||
|
||||
A :block-term:`variable with Block type` is declared using function
|
||||
pointer style notation substituting ``^`` for ``*``. The following are
|
||||
valid Block variable declarations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void (^blockReturningVoidWithVoidArgument)(void);
|
||||
int (^blockReturningIntWithIntAndCharArguments)(int, char);
|
||||
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
|
||||
|
||||
Variadic ``...`` arguments are supported. [variadic.c] A Block that
|
||||
takes no arguments must specify void in the argument list [voidarg.c].
|
||||
An empty parameter list does not represent, as K&R provide, an
|
||||
unspecified argument list. Note: both gcc and clang support K&R style
|
||||
as a convenience.
|
||||
|
||||
A Block reference may be cast to a pointer of arbitrary type and vice
|
||||
versa. [cast.c] A Block reference may not be dereferenced via the
|
||||
pointer dereference operator ``*``, and thus a Block's size may not be
|
||||
computed at compile time. [sizeof.c]
|
||||
|
||||
Block Literal Expressions
|
||||
=========================
|
||||
|
||||
A :block-term:`Block literal expression` produces a reference to a
|
||||
Block. It is introduced by the use of the ``^`` token as a unary
|
||||
operator.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
Block_literal_expression ::= ^ block_decl compound_statement_body
|
||||
block_decl ::=
|
||||
block_decl ::= parameter_list
|
||||
block_decl ::= type_expression
|
||||
|
||||
where type expression is extended to allow ``^`` as a Block reference
|
||||
(pointer) where ``*`` is allowed as a function reference (pointer).
|
||||
|
||||
The following Block literal:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^ void (void) { printf("hello world\n"); }
|
||||
|
||||
produces a reference to a Block with no arguments with no return value.
|
||||
|
||||
The return type is optional and is inferred from the return
|
||||
statements. If the return statements return a value, they all must
|
||||
return a value of the same type. If there is no value returned the
|
||||
inferred type of the Block is void; otherwise it is the type of the
|
||||
return statement value.
|
||||
|
||||
If the return type is omitted and the argument list is ``( void )``,
|
||||
the ``( void )`` argument list may also be omitted.
|
||||
|
||||
So:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^ ( void ) { printf("hello world\n"); }
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^ { printf("hello world\n"); }
|
||||
|
||||
are exactly equivalent constructs for the same expression.
|
||||
|
||||
The type_expression extends C expression parsing to accommodate Block
|
||||
reference declarations as it accommodates function pointer
|
||||
declarations.
|
||||
|
||||
Given:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef int (*pointerToFunctionThatReturnsIntWithCharArg)(char);
|
||||
pointerToFunctionThatReturnsIntWithCharArg functionPointer;
|
||||
^ pointerToFunctionThatReturnsIntWithCharArg (float x) { return functionPointer; }
|
||||
|
||||
and:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^ int ((*)(float x))(char) { return functionPointer; }
|
||||
|
||||
are equivalent expressions, as is:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
^(float x) { return functionPointer; }
|
||||
|
||||
[returnfunctionptr.c]
|
||||
|
||||
The compound statement body establishes a new lexical scope within
|
||||
that of its parent. Variables used within the scope of the compound
|
||||
statement are bound to the Block in the normal manner with the
|
||||
exception of those in automatic (stack) storage. Thus one may access
|
||||
functions and global variables as one would expect, as well as static
|
||||
local variables. [testme]
|
||||
|
||||
Local automatic (stack) variables referenced within the compound
|
||||
statement of a Block are imported and captured by the Block as const
|
||||
copies. The capture (binding) is performed at the time of the Block
|
||||
literal expression evaluation.
|
||||
|
||||
The compiler is not required to capture a variable if it can prove
|
||||
that no references to the variable will actually be evaluated.
|
||||
Programmers can force a variable to be captured by referencing it in a
|
||||
statement at the beginning of the Block, like so:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
(void) foo;
|
||||
|
||||
This matters when capturing the variable has side-effects, as it can
|
||||
in Objective-C or C++.
|
||||
|
||||
The lifetime of variables declared in a Block is that of a function;
|
||||
each activation frame contains a new copy of variables declared within
|
||||
the local scope of the Block. Such variable declarations should be
|
||||
allowed anywhere [testme] rather than only when C99 parsing is
|
||||
requested, including for statements. [testme]
|
||||
|
||||
Block literal expressions may occur within Block literal expressions
|
||||
(nest) and all variables captured by any nested blocks are implicitly
|
||||
also captured in the scopes of their enclosing Blocks.
|
||||
|
||||
A Block literal expression may be used as the initialization value for
|
||||
Block variables at global or local static scope.
|
||||
|
||||
The Invoke Operator
|
||||
===================
|
||||
|
||||
Blocks are :block-term:`invoked` using function call syntax with a
|
||||
list of expression parameters of types corresponding to the
|
||||
declaration and returning a result type also according to the
|
||||
declaration. Given:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int (^x)(char);
|
||||
void (^z)(void);
|
||||
int (^(*y))(char) = &x;
|
||||
|
||||
the following are all legal Block invocations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
x('a');
|
||||
(*y)('a');
|
||||
(true ? x : *y)('a')
|
||||
|
||||
The Copy and Release Operations
|
||||
===============================
|
||||
|
||||
The compiler and runtime provide :block-term:`copy` and
|
||||
:block-term:`release` operations for Block references that create and,
|
||||
in matched use, release allocated storage for referenced Blocks.
|
||||
|
||||
The copy operation ``Block_copy()`` is styled as a function that takes
|
||||
an arbitrary Block reference and returns a Block reference of the same
|
||||
type. The release operation, ``Block_release()``, is styled as a
|
||||
function that takes an arbitrary Block reference and, if dynamically
|
||||
matched to a Block copy operation, allows recovery of the referenced
|
||||
allocated memory.
|
||||
|
||||
|
||||
The ``__block`` Storage Qualifier
|
||||
=================================
|
||||
|
||||
In addition to the new Block type we also introduce a new storage
|
||||
qualifier, :block-term:`__block`, for local variables. [testme: a
|
||||
__block declaration within a block literal] The ``__block`` storage
|
||||
qualifier is mutually exclusive to the existing local storage
|
||||
qualifiers auto, register, and static. [testme] Variables qualified by
|
||||
``__block`` act as if they were in allocated storage and this storage
|
||||
is automatically recovered after last use of said variable. An
|
||||
implementation may choose an optimization where the storage is
|
||||
initially automatic and only "moved" to allocated (heap) storage upon
|
||||
a Block_copy of a referencing Block. Such variables may be mutated as
|
||||
normal variables are.
|
||||
|
||||
In the case where a ``__block`` variable is a Block one must assume
|
||||
that the ``__block`` variable resides in allocated storage and as such
|
||||
is assumed to reference a Block that is also in allocated storage
|
||||
(that it is the result of a ``Block_copy`` operation). Despite this
|
||||
there is no provision to do a ``Block_copy`` or a ``Block_release`` if
|
||||
an implementation provides initial automatic storage for Blocks. This
|
||||
is due to the inherent race condition of potentially several threads
|
||||
trying to update the shared variable and the need for synchronization
|
||||
around disposing of older values and copying new ones. Such
|
||||
synchronization is beyond the scope of this language specification.
|
||||
|
||||
|
||||
Control Flow
|
||||
============
|
||||
|
||||
The compound statement of a Block is treated much like a function body
|
||||
with respect to control flow in that goto, break, and continue do not
|
||||
escape the Block. Exceptions are treated *normally* in that when
|
||||
thrown they pop stack frames until a catch clause is found.
|
||||
|
||||
|
||||
Objective-C Extensions
|
||||
======================
|
||||
|
||||
Objective-C extends the definition of a Block reference type to be
|
||||
that also of id. A variable or expression of Block type may be
|
||||
messaged or used as a parameter wherever an id may be. The converse is
|
||||
also true. Block references may thus appear as properties and are
|
||||
subject to the assign, retain, and copy attribute logic that is
|
||||
reserved for objects.
|
||||
|
||||
All Blocks are constructed to be Objective-C objects regardless of
|
||||
whether the Objective-C runtime is operational in the program or
|
||||
not. Blocks using automatic (stack) memory are objects and may be
|
||||
messaged, although they may not be assigned into ``__weak`` locations
|
||||
if garbage collection is enabled.
|
||||
|
||||
Within a Block literal expression within a method definition
|
||||
references to instance variables are also imported into the lexical
|
||||
scope of the compound statement. These variables are implicitly
|
||||
qualified as references from self, and so self is imported as a const
|
||||
copy. The net effect is that instance variables can be mutated.
|
||||
|
||||
The :block-term:`Block_copy` operator retains all objects held in
|
||||
variables of automatic storage referenced within the Block expression
|
||||
(or form strong references if running under garbage collection).
|
||||
Object variables of ``__block`` storage type are assumed to hold
|
||||
normal pointers with no provision for retain and release messages.
|
||||
|
||||
Foundation defines (and supplies) ``-copy`` and ``-release`` methods for
|
||||
Blocks.
|
||||
|
||||
In the Objective-C and Objective-C++ languages, we allow the
|
||||
``__weak`` specifier for ``__block`` variables of object type. If
|
||||
garbage collection is not enabled, this qualifier causes these
|
||||
variables to be kept without retain messages being sent. This
|
||||
knowingly leads to dangling pointers if the Block (or a copy) outlives
|
||||
the lifetime of this object.
|
||||
|
||||
In garbage collected environments, the ``__weak`` variable is set to
|
||||
nil when the object it references is collected, as long as the
|
||||
``__block`` variable resides in the heap (either by default or via
|
||||
``Block_copy()``). The initial Apple implementation does in fact
|
||||
start ``__block`` variables on the stack and migrate them to the heap
|
||||
only as a result of a ``Block_copy()`` operation.
|
||||
|
||||
It is a runtime error to attempt to assign a reference to a
|
||||
stack-based Block into any storage marked ``__weak``, including
|
||||
``__weak`` ``__block`` variables.
|
||||
|
||||
|
||||
C++ Extensions
|
||||
==============
|
||||
|
||||
Block literal expressions within functions are extended to allow const
|
||||
use of C++ objects, pointers, or references held in automatic storage.
|
||||
|
||||
As usual, within the block, references to captured variables become
|
||||
const-qualified, as if they were references to members of a const
|
||||
object. Note that this does not change the type of a variable of
|
||||
reference type.
|
||||
|
||||
For example, given a class Foo:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
Foo foo;
|
||||
Foo &fooRef = foo;
|
||||
Foo *fooPtr = &foo;
|
||||
|
||||
A Block that referenced these variables would import the variables as
|
||||
const variations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const Foo block_foo = foo;
|
||||
Foo &block_fooRef = fooRef;
|
||||
Foo *const block_fooPtr = fooPtr;
|
||||
|
||||
Captured variables are copied into the Block at the instant of
|
||||
evaluating the Block literal expression. They are also copied when
|
||||
calling ``Block_copy()`` on a Block allocated on the stack. In both
|
||||
cases, they are copied as if the variable were const-qualified, and
|
||||
it's an error if there's no such constructor.
|
||||
|
||||
Captured variables in Blocks on the stack are destroyed when control
|
||||
leaves the compound statement that contains the Block literal
|
||||
expression. Captured variables in Blocks on the heap are destroyed
|
||||
when the reference count of the Block drops to zero.
|
||||
|
||||
Variables declared as residing in ``__block`` storage may be initially
|
||||
allocated in the heap or may first appear on the stack and be copied
|
||||
to the heap as a result of a ``Block_copy()`` operation. When copied
|
||||
from the stack, ``__block`` variables are copied using their normal
|
||||
qualification (i.e. without adding const). In C++11, ``__block``
|
||||
variables are copied as x-values if that is possible, then as l-values
|
||||
if not; if both fail, it's an error. The destructor for any initial
|
||||
stack-based version is called at the variable's normal end of scope.
|
||||
|
||||
References to ``this``, as well as references to non-static members of
|
||||
any enclosing class, are evaluated by capturing ``this`` just like a
|
||||
normal variable of C pointer type.
|
||||
|
||||
Member variables that are Blocks may not be overloaded by the types of
|
||||
their arguments.
|
@ -1,107 +0,0 @@
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
if (LLVM_ENABLE_DOXYGEN)
|
||||
set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (HAVE_DOT)
|
||||
set(DOT ${LLVM_PATH_DOT})
|
||||
endif()
|
||||
|
||||
if (LLVM_DOXYGEN_EXTERNAL_SEARCH)
|
||||
set(enable_searchengine "YES")
|
||||
set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}")
|
||||
set(enable_server_based_search "YES")
|
||||
set(enable_external_search "YES")
|
||||
set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}")
|
||||
else()
|
||||
set(enable_searchengine "NO")
|
||||
set(searchengine_url "")
|
||||
set(enable_server_based_search "NO")
|
||||
set(enable_external_search "NO")
|
||||
set(extra_search_mappings "")
|
||||
endif()
|
||||
|
||||
# If asked, configure doxygen for the creation of a Qt Compressed Help file.
|
||||
if (LLVM_ENABLE_DOXYGEN_QT_HELP)
|
||||
set(CLANG_DOXYGEN_QCH_FILENAME "org.llvm.clang.qch" CACHE STRING
|
||||
"Filename of the Qt Compressed help file")
|
||||
set(CLANG_DOXYGEN_QHP_NAMESPACE "org.llvm.clang" CACHE STRING
|
||||
"Namespace under which the intermediate Qt Help Project file lives")
|
||||
set(CLANG_DOXYGEN_QHP_CUST_FILTER_NAME "Clang ${CLANG_VERSION}" CACHE STRING
|
||||
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters")
|
||||
set(CLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS "Clang,${CLANG_VERSION}" CACHE STRING
|
||||
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes")
|
||||
set(clang_doxygen_generate_qhp "YES")
|
||||
set(clang_doxygen_qch_filename "${CLANG_DOXYGEN_QCH_FILENAME}")
|
||||
set(clang_doxygen_qhp_namespace "${CLANG_DOXYGEN_QHP_NAMESPACE}")
|
||||
set(clang_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}")
|
||||
set(clang_doxygen_qhp_cust_filter_name "${CLANG_DOXYGEN_QHP_CUST_FILTER_NAME}")
|
||||
set(clang_doxygen_qhp_cust_filter_attrs "${CLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS}")
|
||||
else()
|
||||
set(clang_doxygen_generate_qhp "NO")
|
||||
set(clang_doxygen_qch_filename "")
|
||||
set(clang_doxygen_qhp_namespace "")
|
||||
set(clang_doxygen_qhelpgenerator_path "")
|
||||
set(clang_doxygen_qhp_cust_filter_name "")
|
||||
set(clang_doxygen_qhp_cust_filter_attrs "")
|
||||
endif()
|
||||
|
||||
option(LLVM_DOXYGEN_SVG
|
||||
"Use svg instead of png files for doxygen graphs." OFF)
|
||||
if (LLVM_DOXYGEN_SVG)
|
||||
set(DOT_IMAGE_FORMAT "svg")
|
||||
else()
|
||||
set(DOT_IMAGE_FORMAT "png")
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY)
|
||||
|
||||
set(abs_top_srcdir)
|
||||
set(abs_top_builddir)
|
||||
set(DOT)
|
||||
set(enable_searchengine)
|
||||
set(searchengine_url)
|
||||
set(enable_server_based_search)
|
||||
set(enable_external_search)
|
||||
set(extra_search_mappings)
|
||||
set(clang_doxygen_generate_qhp)
|
||||
set(clang_doxygen_qch_filename)
|
||||
set(clang_doxygen_qhp_namespace)
|
||||
set(clang_doxygen_qhelpgenerator_path)
|
||||
set(clang_doxygen_qhp_cust_filter_name)
|
||||
set(clang_doxygen_qhp_cust_filter_attrs)
|
||||
set(DOT_IMAGE_FORMAT)
|
||||
|
||||
add_custom_target(doxygen-clang
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating clang doxygen documentation." VERBATIM)
|
||||
|
||||
if (LLVM_BUILD_DOCS)
|
||||
add_dependencies(doxygen doxygen-clang)
|
||||
endif()
|
||||
|
||||
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
|
||||
DESTINATION docs/html)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LLVM_ENABLE_SPHINX)
|
||||
include(AddSphinxTarget)
|
||||
if (SPHINX_FOUND)
|
||||
if (${SPHINX_OUTPUT_HTML})
|
||||
add_sphinx_target(html clang)
|
||||
add_custom_command(TARGET docs-clang-html POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/LibASTMatchersReference.html"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/html/LibASTMatchersReference.html")
|
||||
endif()
|
||||
if (${SPHINX_OUTPUT_MAN})
|
||||
add_sphinx_target(man clang)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
@ -1,36 +0,0 @@
|
||||
==========
|
||||
ClangCheck
|
||||
==========
|
||||
|
||||
`ClangCheck` is a small wrapper around :doc:`LibTooling` which can be used to
|
||||
do basic error checking and AST dumping.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat <<EOF > snippet.cc
|
||||
> void f() {
|
||||
> int a = 0
|
||||
> }
|
||||
> EOF
|
||||
$ ~/clang/build/bin/clang-check snippet.cc -ast-dump --
|
||||
Processing: /Users/danieljasper/clang/llvm/tools/clang/docs/snippet.cc.
|
||||
/Users/danieljasper/clang/llvm/tools/clang/docs/snippet.cc:2:12: error: expected ';' at end of
|
||||
declaration
|
||||
int a = 0
|
||||
^
|
||||
;
|
||||
(TranslationUnitDecl 0x7ff3a3029ed0 <<invalid sloc>>
|
||||
(TypedefDecl 0x7ff3a302a410 <<invalid sloc>> __int128_t '__int128')
|
||||
(TypedefDecl 0x7ff3a302a470 <<invalid sloc>> __uint128_t 'unsigned __int128')
|
||||
(TypedefDecl 0x7ff3a302a830 <<invalid sloc>> __builtin_va_list '__va_list_tag [1]')
|
||||
(FunctionDecl 0x7ff3a302a8d0 </Users/danieljasper/clang/llvm/tools/clang/docs/snippet.cc:1:1, line:3:1> f 'void (void)'
|
||||
(CompoundStmt 0x7ff3a302aa10 <line:1:10, line:3:1>
|
||||
(DeclStmt 0x7ff3a302a9f8 <line:2:3, line:3:1>
|
||||
(VarDecl 0x7ff3a302a980 <line:2:3, col:11> a 'int'
|
||||
(IntegerLiteral 0x7ff3a302a9d8 <col:11> 'int' 0))))))
|
||||
1 error generated.
|
||||
Error while processing snippet.cc.
|
||||
|
||||
The '--' at the end is important as it prevents :program:`clang-check` from
|
||||
searching for a compilation database. For more information on how to setup and
|
||||
use :program:`clang-check` in a project, see :doc:`HowToSetupToolingForLLVM`.
|
File diff suppressed because it is too large
Load Diff
@ -1,216 +0,0 @@
|
||||
===========
|
||||
ClangFormat
|
||||
===========
|
||||
|
||||
`ClangFormat` describes a set of tools that are built on top of
|
||||
:doc:`LibFormat`. It can support your workflow in a variety of ways including a
|
||||
standalone tool and editor integrations.
|
||||
|
||||
|
||||
Standalone Tool
|
||||
===============
|
||||
|
||||
:program:`clang-format` is located in `clang/tools/clang-format` and can be used
|
||||
to format C/C++/Java/JavaScript/Objective-C/Protobuf code.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ clang-format -help
|
||||
OVERVIEW: A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf code.
|
||||
|
||||
If no arguments are specified, it formats the code from standard input
|
||||
and writes the result to the standard output.
|
||||
If <file>s are given, it reformats the files. If -i is specified
|
||||
together with <file>s, the files are edited in-place. Otherwise, the
|
||||
result is written to the standard output.
|
||||
|
||||
USAGE: clang-format [options] [<file> ...]
|
||||
|
||||
OPTIONS:
|
||||
|
||||
Clang-format options:
|
||||
|
||||
-assume-filename=<string> - When reading from stdin, clang-format assumes this
|
||||
filename to look for a style config file (with
|
||||
-style=file) and to determine the language.
|
||||
-cursor=<uint> - The position of the cursor when invoking
|
||||
clang-format from an editor integration
|
||||
-dump-config - Dump configuration options to stdout and exit.
|
||||
Can be used with -style option.
|
||||
-fallback-style=<string> - The name of the predefined style used as a
|
||||
fallback in case clang-format is invoked with
|
||||
-style=file, but can not find the .clang-format
|
||||
file to use.
|
||||
Use -fallback-style=none to skip formatting.
|
||||
-i - Inplace edit <file>s, if specified.
|
||||
-length=<uint> - Format a range of this length (in bytes).
|
||||
Multiple ranges can be formatted by specifying
|
||||
several -offset and -length pairs.
|
||||
When only a single -offset is specified without
|
||||
-length, clang-format will format up to the end
|
||||
of the file.
|
||||
Can only be used with one input file.
|
||||
-lines=<string> - <start line>:<end line> - format a range of
|
||||
lines (both 1-based).
|
||||
Multiple ranges can be formatted by specifying
|
||||
several -lines arguments.
|
||||
Can't be used with -offset and -length.
|
||||
Can only be used with one input file.
|
||||
-offset=<uint> - Format a range starting at this byte offset.
|
||||
Multiple ranges can be formatted by specifying
|
||||
several -offset and -length pairs.
|
||||
Can only be used with one input file.
|
||||
-output-replacements-xml - Output replacements as XML.
|
||||
-sort-includes - Sort touched include lines
|
||||
-style=<string> - Coding style, currently supports:
|
||||
LLVM, Google, Chromium, Mozilla, WebKit.
|
||||
Use -style=file to load style configuration from
|
||||
.clang-format file located in one of the parent
|
||||
directories of the source file (or current
|
||||
directory for stdin).
|
||||
Use -style="{key: value, ...}" to set specific
|
||||
parameters, e.g.:
|
||||
-style="{BasedOnStyle: llvm, IndentWidth: 8}"
|
||||
-verbose - If set, shows the list of processed files
|
||||
|
||||
Generic Options:
|
||||
|
||||
-help - Display available options (-help-hidden for more)
|
||||
-help-list - Display list of available options (-help-list-hidden for more)
|
||||
-version - Display the version of this program
|
||||
|
||||
|
||||
When the desired code formatting style is different from the available options,
|
||||
the style can be customized using the ``-style="{key: value, ...}"`` option or
|
||||
by putting your style configuration in the ``.clang-format`` or ``_clang-format``
|
||||
file in your project's directory and using ``clang-format -style=file``.
|
||||
|
||||
An easy way to create the ``.clang-format`` file is:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-format -style=llvm -dump-config > .clang-format
|
||||
|
||||
Available style options are described in :doc:`ClangFormatStyleOptions`.
|
||||
|
||||
|
||||
Vim Integration
|
||||
===============
|
||||
|
||||
There is an integration for :program:`vim` which lets you run the
|
||||
:program:`clang-format` standalone tool on your current buffer, optionally
|
||||
selecting regions to reformat. The integration has the form of a `python`-file
|
||||
which can be found under `clang/tools/clang-format/clang-format.py`.
|
||||
|
||||
This can be integrated by adding the following to your `.vimrc`:
|
||||
|
||||
.. code-block:: vim
|
||||
|
||||
map <C-K> :pyf <path-to-this-file>/clang-format.py<cr>
|
||||
imap <C-K> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
|
||||
|
||||
The first line enables :program:`clang-format` for NORMAL and VISUAL mode, the
|
||||
second line adds support for INSERT mode. Change "C-K" to another binding if
|
||||
you need :program:`clang-format` on a different key (C-K stands for Ctrl+k).
|
||||
|
||||
With this integration you can press the bound key and clang-format will
|
||||
format the current line in NORMAL and INSERT mode or the selected region in
|
||||
VISUAL mode. The line or region is extended to the next bigger syntactic
|
||||
entity.
|
||||
|
||||
It operates on the current, potentially unsaved buffer and does not create
|
||||
or save any files. To revert a formatting, just undo.
|
||||
|
||||
An alternative option is to format changes when saving a file and thus to
|
||||
have a zero-effort integration into the coding workflow. To do this, add this to
|
||||
your `.vimrc`:
|
||||
|
||||
.. code-block:: vim
|
||||
|
||||
function! Formatonsave()
|
||||
let l:formatdiff = 1
|
||||
pyf ~/llvm/tools/clang/tools/clang-format/clang-format.py
|
||||
endfunction
|
||||
autocmd BufWritePre *.h,*.cc,*.cpp call Formatonsave()
|
||||
|
||||
|
||||
Emacs Integration
|
||||
=================
|
||||
|
||||
Similar to the integration for :program:`vim`, there is an integration for
|
||||
:program:`emacs`. It can be found at `clang/tools/clang-format/clang-format.el`
|
||||
and used by adding this to your `.emacs`:
|
||||
|
||||
.. code-block:: common-lisp
|
||||
|
||||
(load "<path-to-clang>/tools/clang-format/clang-format.el")
|
||||
(global-set-key [C-M-tab] 'clang-format-region)
|
||||
|
||||
This binds the function `clang-format-region` to C-M-tab, which then formats the
|
||||
current line or selected region.
|
||||
|
||||
|
||||
BBEdit Integration
|
||||
==================
|
||||
|
||||
:program:`clang-format` cannot be used as a text filter with BBEdit, but works
|
||||
well via a script. The AppleScript to do this integration can be found at
|
||||
`clang/tools/clang-format/clang-format-bbedit.applescript`; place a copy in
|
||||
`~/Library/Application Support/BBEdit/Scripts`, and edit the path within it to
|
||||
point to your local copy of :program:`clang-format`.
|
||||
|
||||
With this integration you can select the script from the Script menu and
|
||||
:program:`clang-format` will format the selection. Note that you can rename the
|
||||
menu item by renaming the script, and can assign the menu item a keyboard
|
||||
shortcut in the BBEdit preferences, under Menus & Shortcuts.
|
||||
|
||||
|
||||
Visual Studio Integration
|
||||
=========================
|
||||
|
||||
Download the latest Visual Studio extension from the `alpha build site
|
||||
<https://llvm.org/builds/>`_. The default key-binding is Ctrl-R,Ctrl-F.
|
||||
|
||||
|
||||
Script for patch reformatting
|
||||
=============================
|
||||
|
||||
The python script `clang/tools/clang-format/clang-format-diff.py` parses the
|
||||
output of a unified diff and reformats all contained lines with
|
||||
:program:`clang-format`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
usage: clang-format-diff.py [-h] [-i] [-p NUM] [-regex PATTERN] [-style STYLE]
|
||||
|
||||
Reformat changed lines in diff. Without -i option just output the diff that
|
||||
would be introduced.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-i apply edits to files instead of displaying a diff
|
||||
-p NUM strip the smallest prefix containing P slashes
|
||||
-regex PATTERN custom pattern selecting file paths to reformat
|
||||
-style STYLE formatting style to apply (LLVM, Google, Chromium, Mozilla,
|
||||
WebKit)
|
||||
|
||||
So to reformat all the lines in the latest :program:`git` commit, just do:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git diff -U0 --no-color HEAD^ | clang-format-diff.py -i -p1
|
||||
|
||||
With Mercurial/:program:`hg`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
hg diff -U0 --color=never | clang-format-diff.py -i -p1
|
||||
|
||||
In an SVN client, you can do:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
svn diff --diff-cmd=diff -x -U0 | clang-format-diff.py -i
|
||||
|
||||
The option `-U0` will create a diff without context lines (the script would format
|
||||
those as well).
|
File diff suppressed because it is too large
Load Diff
@ -1,130 +0,0 @@
|
||||
=============
|
||||
Clang Plugins
|
||||
=============
|
||||
|
||||
Clang Plugins make it possible to run extra user defined actions during a
|
||||
compilation. This document will provide a basic walkthrough of how to write and
|
||||
run a Clang Plugin.
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Clang Plugins run FrontendActions over code. See the :doc:`FrontendAction
|
||||
tutorial <RAVFrontendAction>` on how to write a ``FrontendAction`` using the
|
||||
``RecursiveASTVisitor``. In this tutorial, we'll demonstrate how to write a
|
||||
simple clang plugin.
|
||||
|
||||
Writing a ``PluginASTAction``
|
||||
=============================
|
||||
|
||||
The main difference from writing normal ``FrontendActions`` is that you can
|
||||
handle plugin command line options. The ``PluginASTAction`` base class declares
|
||||
a ``ParseArgs`` method which you have to implement in your plugin.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool ParseArgs(const CompilerInstance &CI,
|
||||
const std::vector<std::string>& args) {
|
||||
for (unsigned i = 0, e = args.size(); i != e; ++i) {
|
||||
if (args[i] == "-some-arg") {
|
||||
// Handle the command line argument.
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Registering a plugin
|
||||
====================
|
||||
|
||||
A plugin is loaded from a dynamic library at runtime by the compiler. To
|
||||
register a plugin in a library, use ``FrontendPluginRegistry::Add<>``:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
static FrontendPluginRegistry::Add<MyPlugin> X("my-plugin-name", "my plugin description");
|
||||
|
||||
Defining pragmas
|
||||
================
|
||||
|
||||
Plugins can also define pragmas by declaring a ``PragmaHandler`` and
|
||||
registering it using ``PragmaHandlerRegistry::Add<>``:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// Define a pragma handler for #pragma example_pragma
|
||||
class ExamplePragmaHandler : public PragmaHandler {
|
||||
public:
|
||||
ExamplePragmaHandler() : PragmaHandler("example_pragma") { }
|
||||
void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
|
||||
Token &PragmaTok) {
|
||||
// Handle the pragma
|
||||
}
|
||||
};
|
||||
|
||||
static PragmaHandlerRegistry::Add<ExamplePragmaHandler> Y("example_pragma","example pragma description");
|
||||
|
||||
Putting it all together
|
||||
=======================
|
||||
|
||||
Let's look at an example plugin that prints top-level function names. This
|
||||
example is checked into the clang repository; please take a look at
|
||||
the `latest version of PrintFunctionNames.cpp
|
||||
<https://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/PrintFunctionNames.cpp?view=markup>`_.
|
||||
|
||||
Running the plugin
|
||||
==================
|
||||
|
||||
|
||||
Using the cc1 command line
|
||||
--------------------------
|
||||
|
||||
To run a plugin, the dynamic library containing the plugin registry must be
|
||||
loaded via the `-load` command line option. This will load all plugins
|
||||
that are registered, and you can select the plugins to run by specifying the
|
||||
`-plugin` option. Additional parameters for the plugins can be passed with
|
||||
`-plugin-arg-<plugin-name>`.
|
||||
|
||||
Note that those options must reach clang's cc1 process. There are two
|
||||
ways to do so:
|
||||
|
||||
* Directly call the parsing process by using the `-cc1` option; this
|
||||
has the downside of not configuring the default header search paths, so
|
||||
you'll need to specify the full system path configuration on the command
|
||||
line.
|
||||
* Use clang as usual, but prefix all arguments to the cc1 process with
|
||||
`-Xclang`.
|
||||
|
||||
For example, to run the ``print-function-names`` plugin over a source file in
|
||||
clang, first build the plugin, and then call clang with the plugin from the
|
||||
source tree:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ export BD=/path/to/build/directory
|
||||
$ (cd $BD && make PrintFunctionNames )
|
||||
$ clang++ -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
|
||||
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE \
|
||||
-I$BD/tools/clang/include -Itools/clang/include -I$BD/include -Iinclude \
|
||||
tools/clang/tools/clang-check/ClangCheck.cpp -fsyntax-only \
|
||||
-Xclang -load -Xclang $BD/lib/PrintFunctionNames.so -Xclang \
|
||||
-plugin -Xclang print-fns
|
||||
|
||||
Also see the print-function-name plugin example's
|
||||
`README <https://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup>`_
|
||||
|
||||
|
||||
Using the clang command line
|
||||
----------------------------
|
||||
|
||||
Using `-fplugin=plugin` on the clang command line passes the plugin
|
||||
through as an argument to `-load` on the cc1 command line. If the plugin
|
||||
class implements the ``getActionType`` method then the plugin is run
|
||||
automatically. For example, to run the plugin automatically after the main AST
|
||||
action (i.e. the same as using `-add-plugin`):
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// Automatically run the plugin after the main AST action
|
||||
PluginASTAction::ActionType getActionType() override {
|
||||
return AddAfterMainAction;
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
========
|
||||
Overview
|
||||
========
|
||||
|
||||
Clang Tools are standalone command line (and potentially GUI) tools
|
||||
designed for use by C++ developers who are already using and enjoying
|
||||
Clang as their compiler. These tools provide developer-oriented
|
||||
functionality such as fast syntax checking, automatic formatting,
|
||||
refactoring, etc.
|
||||
|
||||
Only a couple of the most basic and fundamental tools are kept in the
|
||||
primary Clang Subversion project. The rest of the tools are kept in a
|
||||
side-project so that developers who don't want or need to build them
|
||||
don't. If you want to get access to the extra Clang Tools repository,
|
||||
simply check it out into the tools tree of your Clang checkout and
|
||||
follow the usual process for building and working with a combined
|
||||
LLVM/Clang checkout:
|
||||
|
||||
- With Subversion:
|
||||
|
||||
- ``cd llvm/tools/clang/tools``
|
||||
- ``svn co https://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra``
|
||||
|
||||
- Or with Git:
|
||||
|
||||
- ``cd llvm/tools/clang/tools``
|
||||
- ``git clone https://llvm.org/git/clang-tools-extra.git extra``
|
||||
|
||||
This document describes a high-level overview of the organization of
|
||||
Clang Tools within the project as well as giving an introduction to some
|
||||
of the more important tools. However, it should be noted that this
|
||||
document is currently focused on Clang and Clang Tool developers, not on
|
||||
end users of these tools.
|
||||
|
||||
Clang Tools Organization
|
||||
========================
|
||||
|
||||
Clang Tools are CLI or GUI programs that are intended to be directly
|
||||
used by C++ developers. That is they are *not* primarily for use by
|
||||
Clang developers, although they are hopefully useful to C++ developers
|
||||
who happen to work on Clang, and we try to actively dogfood their
|
||||
functionality. They are developed in three components: the underlying
|
||||
infrastructure for building a standalone tool based on Clang, core
|
||||
shared logic used by many different tools in the form of refactoring and
|
||||
rewriting libraries, and the tools themselves.
|
||||
|
||||
The underlying infrastructure for Clang Tools is the
|
||||
:doc:`LibTooling <LibTooling>` platform. See its documentation for much
|
||||
more detailed information about how this infrastructure works. The
|
||||
common refactoring and rewriting toolkit-style library is also part of
|
||||
LibTooling organizationally.
|
||||
|
||||
A few Clang Tools are developed along side the core Clang libraries as
|
||||
examples and test cases of fundamental functionality. However, most of
|
||||
the tools are developed in a side repository to provide easy separation
|
||||
from the core libraries. We intentionally do not support public
|
||||
libraries in the side repository, as we want to carefully review and
|
||||
find good APIs for libraries as they are lifted out of a few tools and
|
||||
into the core Clang library set.
|
||||
|
||||
Regardless of which repository Clang Tools' code resides in, the
|
||||
development process and practices for all Clang Tools are exactly those
|
||||
of Clang itself. They are entirely within the Clang *project*,
|
||||
regardless of the version control scheme.
|
||||
|
||||
Core Clang Tools
|
||||
================
|
||||
|
||||
The core set of Clang tools that are within the main repository are
|
||||
tools that very specifically complement, and allow use and testing of
|
||||
*Clang* specific functionality.
|
||||
|
||||
``clang-check``
|
||||
---------------
|
||||
|
||||
:doc:`ClangCheck` combines the LibTooling framework for running a
|
||||
Clang tool with the basic Clang diagnostics by syntax checking specific files
|
||||
in a fast, command line interface. It can also accept flags to re-display the
|
||||
diagnostics in different formats with different flags, suitable for use driving
|
||||
an IDE or editor. Furthermore, it can be used in fixit-mode to directly apply
|
||||
fixit-hints offered by clang. See :doc:`HowToSetupToolingForLLVM` for
|
||||
instructions on how to setup and used `clang-check`.
|
||||
|
||||
``clang-format``
|
||||
----------------
|
||||
|
||||
Clang-format is both a :doc:`library <LibFormat>` and a :doc:`stand-alone tool
|
||||
<ClangFormat>` with the goal of automatically reformatting C++ sources files
|
||||
according to configurable style guides. To do so, clang-format uses Clang's
|
||||
``Lexer`` to transform an input file into a token stream and then changes all
|
||||
the whitespace around those tokens. The goal is for clang-format to serve both
|
||||
as a user tool (ideally with powerful IDE integrations) and as part of other
|
||||
refactoring tools, e.g. to do a reformatting of all the lines changed during a
|
||||
renaming.
|
||||
|
||||
|
||||
Extra Clang Tools
|
||||
=================
|
||||
|
||||
As various categories of Clang Tools are added to the extra repository,
|
||||
they'll be tracked here. The focus of this documentation is on the scope
|
||||
and features of the tools for other tool developers; each tool should
|
||||
provide its own user-focused documentation.
|
||||
|
||||
``clang-tidy``
|
||||
--------------
|
||||
|
||||
`clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`_ is a clang-based C++
|
||||
linter tool. It provides an extensible framework for building compiler-based
|
||||
static analyses detecting and fixing bug-prone patterns, performance,
|
||||
portability and maintainability issues.
|
||||
|
||||
|
||||
Ideas for new Tools
|
||||
===================
|
||||
|
||||
* C++ cast conversion tool. Will convert C-style casts (``(type) value``) to
|
||||
appropriate C++ cast (``static_cast``, ``const_cast`` or
|
||||
``reinterpret_cast``).
|
||||
* Non-member ``begin()`` and ``end()`` conversion tool. Will convert
|
||||
``foo.begin()`` into ``begin(foo)`` and similarly for ``end()``, where
|
||||
``foo`` is a standard container. We could also detect similar patterns for
|
||||
arrays.
|
||||
* ``tr1`` removal tool. Will migrate source code from using TR1 library
|
||||
features to C++11 library. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <tr1/unordered_map>
|
||||
int main()
|
||||
{
|
||||
std::tr1::unordered_map <int, int> ma;
|
||||
std::cout << ma.size () << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
should be rewritten to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <unordered_map>
|
||||
int main()
|
||||
{
|
||||
std::unordered_map <int, int> ma;
|
||||
std::cout << ma.size () << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
* A tool to remove ``auto``. Will convert ``auto`` to an explicit type or add
|
||||
comments with deduced types. The motivation is that there are developers
|
||||
that don't want to use ``auto`` because they are afraid that they might lose
|
||||
control over their code.
|
||||
|
||||
* C++14: less verbose operator function objects (`N3421
|
||||
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421.htm>`_).
|
||||
For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
sort(v.begin(), v.end(), greater<ValueType>());
|
||||
|
||||
should be rewritten to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
sort(v.begin(), v.end(), greater<>());
|
||||
|
@ -1,635 +0,0 @@
|
||||
clang - the Clang C, C++, and Objective-C compiler
|
||||
==================================================
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
:program:`clang` [*options*] *filename ...*
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
:program:`clang` is a C, C++, and Objective-C compiler which encompasses
|
||||
preprocessing, parsing, optimization, code generation, assembly, and linking.
|
||||
Depending on which high-level mode setting is passed, Clang will stop before
|
||||
doing a full link. While Clang is highly integrated, it is important to
|
||||
understand the stages of compilation, to understand how to invoke it. These
|
||||
stages are:
|
||||
|
||||
Driver
|
||||
The clang executable is actually a small driver which controls the overall
|
||||
execution of other tools such as the compiler, assembler and linker.
|
||||
Typically you do not need to interact with the driver, but you
|
||||
transparently use it to run the other tools.
|
||||
|
||||
Preprocessing
|
||||
This stage handles tokenization of the input source file, macro expansion,
|
||||
#include expansion and handling of other preprocessor directives. The
|
||||
output of this stage is typically called a ".i" (for C), ".ii" (for C++),
|
||||
".mi" (for Objective-C), or ".mii" (for Objective-C++) file.
|
||||
|
||||
Parsing and Semantic Analysis
|
||||
This stage parses the input file, translating preprocessor tokens into a
|
||||
parse tree. Once in the form of a parse tree, it applies semantic
|
||||
analysis to compute types for expressions as well and determine whether
|
||||
the code is well formed. This stage is responsible for generating most of
|
||||
the compiler warnings as well as parse errors. The output of this stage is
|
||||
an "Abstract Syntax Tree" (AST).
|
||||
|
||||
Code Generation and Optimization
|
||||
This stage translates an AST into low-level intermediate code (known as
|
||||
"LLVM IR") and ultimately to machine code. This phase is responsible for
|
||||
optimizing the generated code and handling target-specific code generation.
|
||||
The output of this stage is typically called a ".s" file or "assembly" file.
|
||||
|
||||
Clang also supports the use of an integrated assembler, in which the code
|
||||
generator produces object files directly. This avoids the overhead of
|
||||
generating the ".s" file and of calling the target assembler.
|
||||
|
||||
Assembler
|
||||
This stage runs the target assembler to translate the output of the
|
||||
compiler into a target object file. The output of this stage is typically
|
||||
called a ".o" file or "object" file.
|
||||
|
||||
Linker
|
||||
This stage runs the target linker to merge multiple object files into an
|
||||
executable or dynamic library. The output of this stage is typically called
|
||||
an "a.out", ".dylib" or ".so" file.
|
||||
|
||||
:program:`Clang Static Analyzer`
|
||||
|
||||
The Clang Static Analyzer is a tool that scans source code to try to find bugs
|
||||
through code analysis. This tool uses many parts of Clang and is built into
|
||||
the same driver. Please see <https://clang-analyzer.llvm.org> for more details
|
||||
on how to use the static analyzer.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
Stage Selection Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -E
|
||||
|
||||
Run the preprocessor stage.
|
||||
|
||||
.. option:: -fsyntax-only
|
||||
|
||||
Run the preprocessor, parser and type checking stages.
|
||||
|
||||
.. option:: -S
|
||||
|
||||
Run the previous stages as well as LLVM generation and optimization stages
|
||||
and target-specific code generation, producing an assembly file.
|
||||
|
||||
.. option:: -c
|
||||
|
||||
Run all of the above, plus the assembler, generating a target ".o" object file.
|
||||
|
||||
.. option:: no stage selection option
|
||||
|
||||
If no stage selection option is specified, all stages above are run, and the
|
||||
linker is run to combine the results into an executable or shared library.
|
||||
|
||||
Language Selection and Mode Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -x <language>
|
||||
|
||||
Treat subsequent input files as having type language.
|
||||
|
||||
.. option:: -std=<standard>
|
||||
|
||||
Specify the language standard to compile for.
|
||||
|
||||
Supported values for the C language are:
|
||||
|
||||
| ``c89``
|
||||
| ``c90``
|
||||
| ``iso9899:1990``
|
||||
|
||||
ISO C 1990
|
||||
|
||||
| ``iso9899:199409``
|
||||
|
||||
ISO C 1990 with amendment 1
|
||||
|
||||
| ``gnu89``
|
||||
| ``gnu90``
|
||||
|
||||
ISO C 1990 with GNU extensions
|
||||
|
||||
| ``c99``
|
||||
| ``iso9899:1999``
|
||||
|
||||
ISO C 1999
|
||||
|
||||
| ``gnu99``
|
||||
|
||||
ISO C 1999 with GNU extensions
|
||||
|
||||
| ``c11``
|
||||
| ``iso9899:2011``
|
||||
|
||||
ISO C 2011
|
||||
|
||||
| ``gnu11``
|
||||
|
||||
ISO C 2011 with GNU extensions
|
||||
|
||||
| ``c17``
|
||||
| ``iso9899:2017``
|
||||
|
||||
ISO C 2017
|
||||
|
||||
| ``gnu17``
|
||||
|
||||
ISO C 2017 with GNU extensions
|
||||
|
||||
The default C language standard is ``gnu11``, except on PS4, where it is
|
||||
``gnu99``.
|
||||
|
||||
Supported values for the C++ language are:
|
||||
|
||||
| ``c++98``
|
||||
| ``c++03``
|
||||
|
||||
ISO C++ 1998 with amendments
|
||||
|
||||
| ``gnu++98``
|
||||
| ``gnu++03``
|
||||
|
||||
ISO C++ 1998 with amendments and GNU extensions
|
||||
|
||||
| ``c++11``
|
||||
|
||||
ISO C++ 2011 with amendments
|
||||
|
||||
| ``gnu++11``
|
||||
|
||||
ISO C++ 2011 with amendments and GNU extensions
|
||||
|
||||
| ``c++14``
|
||||
|
||||
ISO C++ 2014 with amendments
|
||||
|
||||
| ``gnu++14``
|
||||
|
||||
ISO C++ 2014 with amendments and GNU extensions
|
||||
|
||||
| ``c++17``
|
||||
|
||||
ISO C++ 2017 with amendments
|
||||
|
||||
| ``gnu++17``
|
||||
|
||||
ISO C++ 2017 with amendments and GNU extensions
|
||||
|
||||
| ``c++2a``
|
||||
|
||||
Working draft for ISO C++ 2020
|
||||
|
||||
| ``gnu++2a``
|
||||
|
||||
Working draft for ISO C++ 2020 with GNU extensions
|
||||
|
||||
The default C++ language standard is ``gnu++14``.
|
||||
|
||||
Supported values for the OpenCL language are:
|
||||
|
||||
| ``cl1.0``
|
||||
|
||||
OpenCL 1.0
|
||||
|
||||
| ``cl1.1``
|
||||
|
||||
OpenCL 1.1
|
||||
|
||||
| ``cl1.2``
|
||||
|
||||
OpenCL 1.2
|
||||
|
||||
| ``cl2.0``
|
||||
|
||||
OpenCL 2.0
|
||||
|
||||
The default OpenCL language standard is ``cl1.0``.
|
||||
|
||||
Supported values for the CUDA language are:
|
||||
|
||||
| ``cuda``
|
||||
|
||||
NVIDIA CUDA(tm)
|
||||
|
||||
.. option:: -stdlib=<library>
|
||||
|
||||
Specify the C++ standard library to use; supported options are libstdc++ and
|
||||
libc++. If not specified, platform default will be used.
|
||||
|
||||
.. option:: -rtlib=<library>
|
||||
|
||||
Specify the compiler runtime library to use; supported options are libgcc and
|
||||
compiler-rt. If not specified, platform default will be used.
|
||||
|
||||
.. option:: -ansi
|
||||
|
||||
Same as -std=c89.
|
||||
|
||||
.. option:: -ObjC, -ObjC++
|
||||
|
||||
Treat source input files as Objective-C and Object-C++ inputs respectively.
|
||||
|
||||
.. option:: -trigraphs
|
||||
|
||||
Enable trigraphs.
|
||||
|
||||
.. option:: -ffreestanding
|
||||
|
||||
Indicate that the file should be compiled for a freestanding, not a hosted,
|
||||
environment.
|
||||
|
||||
.. option:: -fno-builtin
|
||||
|
||||
Disable special handling and optimizations of builtin functions like
|
||||
:c:func:`strlen` and :c:func:`malloc`.
|
||||
|
||||
.. option:: -fmath-errno
|
||||
|
||||
Indicate that math functions should be treated as updating :c:data:`errno`.
|
||||
|
||||
.. option:: -fpascal-strings
|
||||
|
||||
Enable support for Pascal-style strings with "\\pfoo".
|
||||
|
||||
.. option:: -fms-extensions
|
||||
|
||||
Enable support for Microsoft extensions.
|
||||
|
||||
.. option:: -fmsc-version=
|
||||
|
||||
Set _MSC_VER. Defaults to 1300 on Windows. Not set otherwise.
|
||||
|
||||
.. option:: -fborland-extensions
|
||||
|
||||
Enable support for Borland extensions.
|
||||
|
||||
.. option:: -fwritable-strings
|
||||
|
||||
Make all string literals default to writable. This disables uniquing of
|
||||
strings and other optimizations.
|
||||
|
||||
.. option:: -flax-vector-conversions
|
||||
|
||||
Allow loose type checking rules for implicit vector conversions.
|
||||
|
||||
.. option:: -fblocks
|
||||
|
||||
Enable the "Blocks" language feature.
|
||||
|
||||
.. option:: -fobjc-abi-version=version
|
||||
|
||||
Select the Objective-C ABI version to use. Available versions are 1 (legacy
|
||||
"fragile" ABI), 2 (non-fragile ABI 1), and 3 (non-fragile ABI 2).
|
||||
|
||||
.. option:: -fobjc-nonfragile-abi-version=<version>
|
||||
|
||||
Select the Objective-C non-fragile ABI version to use by default. This will
|
||||
only be used as the Objective-C ABI when the non-fragile ABI is enabled
|
||||
(either via :option:`-fobjc-nonfragile-abi`, or because it is the platform
|
||||
default).
|
||||
|
||||
.. option:: -fobjc-nonfragile-abi, -fno-objc-nonfragile-abi
|
||||
|
||||
Enable use of the Objective-C non-fragile ABI. On platforms for which this is
|
||||
the default ABI, it can be disabled with :option:`-fno-objc-nonfragile-abi`.
|
||||
|
||||
Target Selection Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clang fully supports cross compilation as an inherent part of its design.
|
||||
Depending on how your version of Clang is configured, it may have support for a
|
||||
number of cross compilers, or may only support a native target.
|
||||
|
||||
.. option:: -arch <architecture>
|
||||
|
||||
Specify the architecture to build for.
|
||||
|
||||
.. option:: -mmacosx-version-min=<version>
|
||||
|
||||
When building for Mac OS X, specify the minimum version supported by your
|
||||
application.
|
||||
|
||||
.. option:: -miphoneos-version-min
|
||||
|
||||
When building for iPhone OS, specify the minimum version supported by your
|
||||
application.
|
||||
|
||||
.. option:: -march=<cpu>
|
||||
|
||||
Specify that Clang should generate code for a specific processor family
|
||||
member and later. For example, if you specify -march=i486, the compiler is
|
||||
allowed to generate instructions that are valid on i486 and later processors,
|
||||
but which may not exist on earlier ones.
|
||||
|
||||
|
||||
Code Generation Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4
|
||||
|
||||
Specify which optimization level to use:
|
||||
|
||||
:option:`-O0` Means "no optimization": this level compiles the fastest and
|
||||
generates the most debuggable code.
|
||||
|
||||
:option:`-O1` Somewhere between :option:`-O0` and :option:`-O2`.
|
||||
|
||||
:option:`-O2` Moderate level of optimization which enables most
|
||||
optimizations.
|
||||
|
||||
:option:`-O3` Like :option:`-O2`, except that it enables optimizations that
|
||||
take longer to perform or that may generate larger code (in an attempt to
|
||||
make the program run faster).
|
||||
|
||||
:option:`-Ofast` Enables all the optimizations from :option:`-O3` along
|
||||
with other aggressive optimizations that may violate strict compliance with
|
||||
language standards.
|
||||
|
||||
:option:`-Os` Like :option:`-O2` with extra optimizations to reduce code
|
||||
size.
|
||||
|
||||
:option:`-Oz` Like :option:`-Os` (and thus :option:`-O2`), but reduces code
|
||||
size further.
|
||||
|
||||
:option:`-Og` Like :option:`-O1`. In future versions, this option might
|
||||
disable different optimizations in order to improve debuggability.
|
||||
|
||||
:option:`-O` Equivalent to :option:`-O2`.
|
||||
|
||||
:option:`-O4` and higher
|
||||
|
||||
Currently equivalent to :option:`-O3`
|
||||
|
||||
.. option:: -g, -gline-tables-only, -gmodules
|
||||
|
||||
Control debug information output. Note that Clang debug information works
|
||||
best at :option:`-O0`. When more than one option starting with `-g` is
|
||||
specified, the last one wins:
|
||||
|
||||
:option:`-g` Generate debug information.
|
||||
|
||||
:option:`-gline-tables-only` Generate only line table debug information. This
|
||||
allows for symbolicated backtraces with inlining information, but does not
|
||||
include any information about variables, their locations or types.
|
||||
|
||||
:option:`-gmodules` Generate debug information that contains external
|
||||
references to types defined in Clang modules or precompiled headers instead
|
||||
of emitting redundant debug type information into every object file. This
|
||||
option transparently switches the Clang module format to object file
|
||||
containers that hold the Clang module together with the debug information.
|
||||
When compiling a program that uses Clang modules or precompiled headers,
|
||||
this option produces complete debug information with faster compile
|
||||
times and much smaller object files.
|
||||
|
||||
This option should not be used when building static libraries for
|
||||
distribution to other machines because the debug info will contain
|
||||
references to the module cache on the machine the object files in the
|
||||
library were built on.
|
||||
|
||||
.. option:: -fstandalone-debug -fno-standalone-debug
|
||||
|
||||
Clang supports a number of optimizations to reduce the size of debug
|
||||
information in the binary. They work based on the assumption that the
|
||||
debug type information can be spread out over multiple compilation units.
|
||||
For instance, Clang will not emit type definitions for types that are not
|
||||
needed by a module and could be replaced with a forward declaration.
|
||||
Further, Clang will only emit type info for a dynamic C++ class in the
|
||||
module that contains the vtable for the class.
|
||||
|
||||
The :option:`-fstandalone-debug` option turns off these optimizations.
|
||||
This is useful when working with 3rd-party libraries that don't come with
|
||||
debug information. This is the default on Darwin. Note that Clang will
|
||||
never emit type information for types that are not referenced at all by the
|
||||
program.
|
||||
|
||||
.. option:: -fexceptions
|
||||
|
||||
Enable generation of unwind information. This allows exceptions to be thrown
|
||||
through Clang compiled stack frames. This is on by default in x86-64.
|
||||
|
||||
.. option:: -ftrapv
|
||||
|
||||
Generate code to catch integer overflow errors. Signed integer overflow is
|
||||
undefined in C. With this flag, extra code is generated to detect this and
|
||||
abort when it happens.
|
||||
|
||||
.. option:: -fvisibility
|
||||
|
||||
This flag sets the default visibility level.
|
||||
|
||||
.. option:: -fcommon, -fno-common
|
||||
|
||||
This flag specifies that variables without initializers get common linkage.
|
||||
It can be disabled with :option:`-fno-common`.
|
||||
|
||||
.. option:: -ftls-model=<model>
|
||||
|
||||
Set the default thread-local storage (TLS) model to use for thread-local
|
||||
variables. Valid values are: "global-dynamic", "local-dynamic",
|
||||
"initial-exec" and "local-exec". The default is "global-dynamic". The default
|
||||
model can be overridden with the tls_model attribute. The compiler will try
|
||||
to choose a more efficient model if possible.
|
||||
|
||||
.. option:: -flto, -flto=full, -flto=thin, -emit-llvm
|
||||
|
||||
Generate output files in LLVM formats, suitable for link time optimization.
|
||||
When used with :option:`-S` this generates LLVM intermediate language
|
||||
assembly files, otherwise this generates LLVM bitcode format object files
|
||||
(which may be passed to the linker depending on the stage selection options).
|
||||
|
||||
The default for :option:`-flto` is "full", in which the
|
||||
LLVM bitcode is suitable for monolithic Link Time Optimization (LTO), where
|
||||
the linker merges all such modules into a single combined module for
|
||||
optimization. With "thin", :doc:`ThinLTO <../ThinLTO>`
|
||||
compilation is invoked instead.
|
||||
|
||||
Driver Options
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -###
|
||||
|
||||
Print (but do not run) the commands to run for this compilation.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Display available options.
|
||||
|
||||
.. option:: -Qunused-arguments
|
||||
|
||||
Do not emit any warnings for unused driver arguments.
|
||||
|
||||
.. option:: -Wa,<args>
|
||||
|
||||
Pass the comma separated arguments in args to the assembler.
|
||||
|
||||
.. option:: -Wl,<args>
|
||||
|
||||
Pass the comma separated arguments in args to the linker.
|
||||
|
||||
.. option:: -Wp,<args>
|
||||
|
||||
Pass the comma separated arguments in args to the preprocessor.
|
||||
|
||||
.. option:: -Xanalyzer <arg>
|
||||
|
||||
Pass arg to the static analyzer.
|
||||
|
||||
.. option:: -Xassembler <arg>
|
||||
|
||||
Pass arg to the assembler.
|
||||
|
||||
.. option:: -Xlinker <arg>
|
||||
|
||||
Pass arg to the linker.
|
||||
|
||||
.. option:: -Xpreprocessor <arg>
|
||||
|
||||
Pass arg to the preprocessor.
|
||||
|
||||
.. option:: -o <file>
|
||||
|
||||
Write output to file.
|
||||
|
||||
.. option:: -print-file-name=<file>
|
||||
|
||||
Print the full library path of file.
|
||||
|
||||
.. option:: -print-libgcc-file-name
|
||||
|
||||
Print the library path for the currently used compiler runtime library
|
||||
("libgcc.a" or "libclang_rt.builtins.*.a").
|
||||
|
||||
.. option:: -print-prog-name=<name>
|
||||
|
||||
Print the full program path of name.
|
||||
|
||||
.. option:: -print-search-dirs
|
||||
|
||||
Print the paths used for finding libraries and programs.
|
||||
|
||||
.. option:: -save-temps
|
||||
|
||||
Save intermediate compilation results.
|
||||
|
||||
.. option:: -save-stats, -save-stats=cwd, -save-stats=obj
|
||||
|
||||
Save internal code generation (LLVM) statistics to a file in the current
|
||||
directory (:option:`-save-stats`/"-save-stats=cwd") or the directory
|
||||
of the output file ("-save-state=obj").
|
||||
|
||||
.. option:: -integrated-as, -no-integrated-as
|
||||
|
||||
Used to enable and disable, respectively, the use of the integrated
|
||||
assembler. Whether the integrated assembler is on by default is target
|
||||
dependent.
|
||||
|
||||
.. option:: -time
|
||||
|
||||
Time individual commands.
|
||||
|
||||
.. option:: -ftime-report
|
||||
|
||||
Print timing summary of each stage of compilation.
|
||||
|
||||
.. option:: -v
|
||||
|
||||
Show commands to run and use verbose output.
|
||||
|
||||
|
||||
Diagnostics Options
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -fshow-column, -fshow-source-location, -fcaret-diagnostics, -fdiagnostics-fixit-info, -fdiagnostics-parseable-fixits, -fdiagnostics-print-source-range-info, -fprint-source-range-info, -fdiagnostics-show-option, -fmessage-length
|
||||
|
||||
These options control how Clang prints out information about diagnostics
|
||||
(errors and warnings). Please see the Clang User's Manual for more information.
|
||||
|
||||
Preprocessor Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -D<macroname>=<value>
|
||||
|
||||
Adds an implicit #define into the predefines buffer which is read before the
|
||||
source file is preprocessed.
|
||||
|
||||
.. option:: -U<macroname>
|
||||
|
||||
Adds an implicit #undef into the predefines buffer which is read before the
|
||||
source file is preprocessed.
|
||||
|
||||
.. option:: -include <filename>
|
||||
|
||||
Adds an implicit #include into the predefines buffer which is read before the
|
||||
source file is preprocessed.
|
||||
|
||||
.. option:: -I<directory>
|
||||
|
||||
Add the specified directory to the search path for include files.
|
||||
|
||||
.. option:: -F<directory>
|
||||
|
||||
Add the specified directory to the search path for framework include files.
|
||||
|
||||
.. option:: -nostdinc
|
||||
|
||||
Do not search the standard system directories or compiler builtin directories
|
||||
for include files.
|
||||
|
||||
.. option:: -nostdlibinc
|
||||
|
||||
Do not search the standard system directories for include files, but do
|
||||
search compiler builtin include directories.
|
||||
|
||||
.. option:: -nobuiltininc
|
||||
|
||||
Do not search clang's builtin directory for include files.
|
||||
|
||||
|
||||
ENVIRONMENT
|
||||
-----------
|
||||
|
||||
.. envvar:: TMPDIR, TEMP, TMP
|
||||
|
||||
These environment variables are checked, in order, for the location to write
|
||||
temporary files used during the compilation process.
|
||||
|
||||
.. envvar:: CPATH
|
||||
|
||||
If this environment variable is present, it is treated as a delimited list of
|
||||
paths to be added to the default system include path list. The delimiter is
|
||||
the platform dependent delimiter, as used in the PATH environment variable.
|
||||
|
||||
Empty components in the environment variable are ignored.
|
||||
|
||||
.. envvar:: C_INCLUDE_PATH, OBJC_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJCPLUS_INCLUDE_PATH
|
||||
|
||||
These environment variables specify additional paths, as for :envvar:`CPATH`, which are
|
||||
only used when processing the appropriate language.
|
||||
|
||||
.. envvar:: MACOSX_DEPLOYMENT_TARGET
|
||||
|
||||
If :option:`-mmacosx-version-min` is unspecified, the default deployment
|
||||
target is read from this environment variable. This option only affects
|
||||
Darwin targets.
|
||||
|
||||
BUGS
|
||||
----
|
||||
|
||||
To report bugs, please visit <https://bugs.llvm.org/>. Most bug reports should
|
||||
include preprocessed source files (use the :option:`-E` option) and the full
|
||||
output of the compiler, along with information to reproduce.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`as(1)`, :manpage:`ld(1)`
|
@ -1,52 +0,0 @@
|
||||
diagtool - clang diagnostics tool
|
||||
=================================
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
:program:`diagtool` *command* [*args*]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
:program:`diagtool` is a combination of four tool for dealing with diagnostics in :program:`clang`.
|
||||
|
||||
SUBCOMMANDS
|
||||
-----------
|
||||
|
||||
:program:`diagtool` is separated into several subcommands each tailored to a
|
||||
different purpose. A brief summary of each command follows, with more detail in
|
||||
the sections that follow.
|
||||
|
||||
* :ref:`find_diagnostic_id` - Print the id of the given diagnostic.
|
||||
* :ref:`list_warnings` - List warnings and their corresponding flags.
|
||||
* :ref:`show_enabled` - Show which warnings are enabled for a given command line.
|
||||
* :ref:`tree` - Show warning flags in a tree view.
|
||||
|
||||
.. _find_diagnostic_id:
|
||||
|
||||
find-diagnostic-id
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:program:`diagtool` find-diagnostic-id *diagnostic-name*
|
||||
|
||||
.. _list_warnings:
|
||||
|
||||
list-warnings
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
:program:`diagtool` list-warnings
|
||||
|
||||
.. _show_enabled:
|
||||
|
||||
show-enabled
|
||||
~~~~~~~~~~~~
|
||||
|
||||
:program:`diagtool` show-enabled [*options*] *filename ...*
|
||||
|
||||
.. _tree:
|
||||
|
||||
tree
|
||||
~~~~
|
||||
|
||||
:program:`diagtool` tree [*diagnostic-group*]
|
@ -1,18 +0,0 @@
|
||||
Clang "man" pages
|
||||
-----------------
|
||||
|
||||
The following documents are command descriptions for all of the Clang tools.
|
||||
These pages describe how to use the Clang commands and what their options are.
|
||||
Note that these pages do not describe all of the options available for all
|
||||
tools. To get a complete listing, pass the ``--help`` (general options) or
|
||||
``--help-hidden`` (general and debugging options) arguments to the tool you are
|
||||
interested in.
|
||||
|
||||
Basic Commands
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
clang
|
||||
diagtool
|
@ -1,343 +0,0 @@
|
||||
======================
|
||||
Control Flow Integrity
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
ControlFlowIntegrityDesign
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Clang includes an implementation of a number of control flow integrity (CFI)
|
||||
schemes, which are designed to abort the program upon detecting certain forms
|
||||
of undefined behavior that can potentially allow attackers to subvert the
|
||||
program's control flow. These schemes have been optimized for performance,
|
||||
allowing developers to enable them in release builds.
|
||||
|
||||
To enable Clang's available CFI schemes, use the flag ``-fsanitize=cfi``.
|
||||
You can also enable a subset of available :ref:`schemes <cfi-schemes>`.
|
||||
As currently implemented, all schemes rely on link-time optimization (LTO);
|
||||
so it is required to specify ``-flto``, and the linker used must support LTO,
|
||||
for example via the `gold plugin`_.
|
||||
|
||||
To allow the checks to be implemented efficiently, the program must
|
||||
be structured such that certain object files are compiled with CFI
|
||||
enabled, and are statically linked into the program. This may preclude
|
||||
the use of shared libraries in some cases.
|
||||
|
||||
The compiler will only produce CFI checks for a class if it can infer hidden
|
||||
LTO visibility for that class. LTO visibility is a property of a class that
|
||||
is inferred from flags and attributes. For more details, see the documentation
|
||||
for :doc:`LTO visibility <LTOVisibility>`.
|
||||
|
||||
The ``-fsanitize=cfi-{vcall,nvcall,derived-cast,unrelated-cast}`` flags
|
||||
require that a ``-fvisibility=`` flag also be specified. This is because the
|
||||
default visibility setting is ``-fvisibility=default``, which would disable
|
||||
CFI checks for classes without visibility attributes. Most users will want
|
||||
to specify ``-fvisibility=hidden``, which enables CFI checks for such classes.
|
||||
|
||||
Experimental support for :ref:`cross-DSO control flow integrity
|
||||
<cfi-cross-dso>` exists that does not require classes to have hidden LTO
|
||||
visibility. This cross-DSO support has unstable ABI at this time.
|
||||
|
||||
.. _gold plugin: https://llvm.org/docs/GoldPlugin.html
|
||||
|
||||
.. _cfi-schemes:
|
||||
|
||||
Available schemes
|
||||
=================
|
||||
|
||||
Available schemes are:
|
||||
|
||||
- ``-fsanitize=cfi-cast-strict``: Enables :ref:`strict cast checks
|
||||
<cfi-strictness>`.
|
||||
- ``-fsanitize=cfi-derived-cast``: Base-to-derived cast to the wrong
|
||||
dynamic type.
|
||||
- ``-fsanitize=cfi-unrelated-cast``: Cast from ``void*`` or another
|
||||
unrelated type to the wrong dynamic type.
|
||||
- ``-fsanitize=cfi-nvcall``: Non-virtual call via an object whose vptr is of
|
||||
the wrong dynamic type.
|
||||
- ``-fsanitize=cfi-vcall``: Virtual call via an object whose vptr is of the
|
||||
wrong dynamic type.
|
||||
- ``-fsanitize=cfi-icall``: Indirect call of a function with wrong dynamic
|
||||
type.
|
||||
- ``-fsanitize=cfi-mfcall``: Indirect call via a member function pointer with
|
||||
wrong dynamic type.
|
||||
|
||||
You can use ``-fsanitize=cfi`` to enable all the schemes and use
|
||||
``-fno-sanitize`` flag to narrow down the set of schemes as desired.
|
||||
For example, you can build your program with
|
||||
``-fsanitize=cfi -fno-sanitize=cfi-nvcall,cfi-icall``
|
||||
to use all schemes except for non-virtual member function call and indirect call
|
||||
checking.
|
||||
|
||||
Remember that you have to provide ``-flto`` if at least one CFI scheme is
|
||||
enabled.
|
||||
|
||||
Trapping and Diagnostics
|
||||
========================
|
||||
|
||||
By default, CFI will abort the program immediately upon detecting a control
|
||||
flow integrity violation. You can use the :ref:`-fno-sanitize-trap=
|
||||
<controlling-code-generation>` flag to cause CFI to print a diagnostic
|
||||
similar to the one below before the program aborts.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
bad-cast.cpp:109:7: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast (vtable address 0x000000425a50)
|
||||
0x000000425a50: note: vtable is of type 'A'
|
||||
00 00 00 00 f0 f1 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 5a 42 00
|
||||
^
|
||||
|
||||
If diagnostics are enabled, you can also configure CFI to continue program
|
||||
execution instead of aborting by using the :ref:`-fsanitize-recover=
|
||||
<controlling-code-generation>` flag.
|
||||
|
||||
Forward-Edge CFI for Virtual Calls
|
||||
==================================
|
||||
|
||||
This scheme checks that virtual calls take place using a vptr of the correct
|
||||
dynamic type; that is, the dynamic type of the called object must be a
|
||||
derived class of the static type of the object used to make the call.
|
||||
This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vcall``.
|
||||
|
||||
For this scheme to work, all translation units containing the definition
|
||||
of a virtual member function (whether inline or not), other than members
|
||||
of :ref:`blacklisted <cfi-blacklist>` types or types with public :doc:`LTO
|
||||
visibility <LTOVisibility>`, must be compiled with ``-flto`` or ``-flto=thin``
|
||||
enabled and be statically linked into the program.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
A performance overhead of less than 1% has been measured by running the
|
||||
Dromaeo benchmark suite against an instrumented version of the Chromium
|
||||
web browser. Another good performance benchmark for this mechanism is the
|
||||
virtual-call-heavy SPEC 2006 xalancbmk.
|
||||
|
||||
Note that this scheme has not yet been optimized for binary size; an increase
|
||||
of up to 15% has been observed for Chromium.
|
||||
|
||||
Bad Cast Checking
|
||||
=================
|
||||
|
||||
This scheme checks that pointer casts are made to an object of the correct
|
||||
dynamic type; that is, the dynamic type of the object must be a derived class
|
||||
of the pointee type of the cast. The checks are currently only introduced
|
||||
where the class being casted to is a polymorphic class.
|
||||
|
||||
Bad casts are not in themselves control flow integrity violations, but they
|
||||
can also create security vulnerabilities, and the implementation uses many
|
||||
of the same mechanisms.
|
||||
|
||||
There are two types of bad cast that may be forbidden: bad casts
|
||||
from a base class to a derived class (which can be checked with
|
||||
``-fsanitize=cfi-derived-cast``), and bad casts from a pointer of
|
||||
type ``void*`` or another unrelated type (which can be checked with
|
||||
``-fsanitize=cfi-unrelated-cast``).
|
||||
|
||||
The difference between these two types of casts is that the first is defined
|
||||
by the C++ standard to produce an undefined value, while the second is not
|
||||
in itself undefined behavior (it is well defined to cast the pointer back
|
||||
to its original type) unless the object is uninitialized and the cast is a
|
||||
``static_cast`` (see C++14 [basic.life]p5).
|
||||
|
||||
If a program as a matter of policy forbids the second type of cast, that
|
||||
restriction can normally be enforced. However it may in some cases be necessary
|
||||
for a function to perform a forbidden cast to conform with an external API
|
||||
(e.g. the ``allocate`` member function of a standard library allocator). Such
|
||||
functions may be :ref:`blacklisted <cfi-blacklist>`.
|
||||
|
||||
For this scheme to work, all translation units containing the definition
|
||||
of a virtual member function (whether inline or not), other than members
|
||||
of :ref:`blacklisted <cfi-blacklist>` types or types with public :doc:`LTO
|
||||
visibility <LTOVisibility>`, must be compiled with ``-flto`` or ``-flto=thin``
|
||||
enabled and be statically linked into the program.
|
||||
|
||||
Non-Virtual Member Function Call Checking
|
||||
=========================================
|
||||
|
||||
This scheme checks that non-virtual calls take place using an object of
|
||||
the correct dynamic type; that is, the dynamic type of the called object
|
||||
must be a derived class of the static type of the object used to make the
|
||||
call. The checks are currently only introduced where the object is of a
|
||||
polymorphic class type. This CFI scheme can be enabled on its own using
|
||||
``-fsanitize=cfi-nvcall``.
|
||||
|
||||
For this scheme to work, all translation units containing the definition
|
||||
of a virtual member function (whether inline or not), other than members
|
||||
of :ref:`blacklisted <cfi-blacklist>` types or types with public :doc:`LTO
|
||||
visibility <LTOVisibility>`, must be compiled with ``-flto`` or ``-flto=thin``
|
||||
enabled and be statically linked into the program.
|
||||
|
||||
.. _cfi-strictness:
|
||||
|
||||
Strictness
|
||||
----------
|
||||
|
||||
If a class has a single non-virtual base and does not introduce or override
|
||||
virtual member functions or fields other than an implicitly defined virtual
|
||||
destructor, it will have the same layout and virtual function semantics as
|
||||
its base. By default, casts to such classes are checked as if they were made
|
||||
to the least derived such class.
|
||||
|
||||
Casting an instance of a base class to such a derived class is technically
|
||||
undefined behavior, but it is a relatively common hack for introducing
|
||||
member functions on class instances with specific properties that works under
|
||||
most compilers and should not have security implications, so we allow it by
|
||||
default. It can be disabled with ``-fsanitize=cfi-cast-strict``.
|
||||
|
||||
Indirect Function Call Checking
|
||||
===============================
|
||||
|
||||
This scheme checks that function calls take place using a function of the
|
||||
correct dynamic type; that is, the dynamic type of the function must match
|
||||
the static type used at the call. This CFI scheme can be enabled on its own
|
||||
using ``-fsanitize=cfi-icall``.
|
||||
|
||||
For this scheme to work, each indirect function call in the program, other
|
||||
than calls in :ref:`blacklisted <cfi-blacklist>` functions, must call a
|
||||
function which was either compiled with ``-fsanitize=cfi-icall`` enabled,
|
||||
or whose address was taken by a function in a translation unit compiled with
|
||||
``-fsanitize=cfi-icall``.
|
||||
|
||||
If a function in a translation unit compiled with ``-fsanitize=cfi-icall``
|
||||
takes the address of a function not compiled with ``-fsanitize=cfi-icall``,
|
||||
that address may differ from the address taken by a function in a translation
|
||||
unit not compiled with ``-fsanitize=cfi-icall``. This is technically a
|
||||
violation of the C and C++ standards, but it should not affect most programs.
|
||||
|
||||
Each translation unit compiled with ``-fsanitize=cfi-icall`` must be
|
||||
statically linked into the program or shared library, and calls across
|
||||
shared library boundaries are handled as if the callee was not compiled with
|
||||
``-fsanitize=cfi-icall``.
|
||||
|
||||
This scheme is currently only supported on the x86 and x86_64 architectures.
|
||||
|
||||
``-fsanitize-cfi-icall-generalize-pointers``
|
||||
--------------------------------------------
|
||||
|
||||
Mismatched pointer types are a common cause of cfi-icall check failures.
|
||||
Translation units compiled with the ``-fsanitize-cfi-icall-generalize-pointers``
|
||||
flag relax pointer type checking for call sites in that translation unit,
|
||||
applied across all functions compiled with ``-fsanitize=cfi-icall``.
|
||||
|
||||
Specifically, pointers in return and argument types are treated as equivalent as
|
||||
long as the qualifiers for the type they point to match. For example, ``char*``,
|
||||
``char**``, and ``int*`` are considered equivalent types. However, ``char*`` and
|
||||
``const char*`` are considered separate types.
|
||||
|
||||
``-fsanitize-cfi-icall-generalize-pointers`` is not compatible with
|
||||
``-fsanitize-cfi-cross-dso``.
|
||||
|
||||
|
||||
``-fsanitize=cfi-icall`` and ``-fsanitize=function``
|
||||
----------------------------------------------------
|
||||
|
||||
This tool is similar to ``-fsanitize=function`` in that both tools check
|
||||
the types of function calls. However, the two tools occupy different points
|
||||
on the design space; ``-fsanitize=function`` is a developer tool designed
|
||||
to find bugs in local development builds, whereas ``-fsanitize=cfi-icall``
|
||||
is a security hardening mechanism designed to be deployed in release builds.
|
||||
|
||||
``-fsanitize=function`` has a higher space and time overhead due to a more
|
||||
complex type check at indirect call sites, as well as a need for run-time
|
||||
type information (RTTI), which may make it unsuitable for deployment. Because
|
||||
of the need for RTTI, ``-fsanitize=function`` can only be used with C++
|
||||
programs, whereas ``-fsanitize=cfi-icall`` can protect both C and C++ programs.
|
||||
|
||||
On the other hand, ``-fsanitize=function`` conforms more closely with the C++
|
||||
standard and user expectations around interaction with shared libraries;
|
||||
the identity of function pointers is maintained, and calls across shared
|
||||
library boundaries are no different from calls within a single program or
|
||||
shared library.
|
||||
|
||||
Member Function Pointer Call Checking
|
||||
=====================================
|
||||
|
||||
This scheme checks that indirect calls via a member function pointer
|
||||
take place using an object of the correct dynamic type. Specifically, we
|
||||
check that the dynamic type of the member function referenced by the member
|
||||
function pointer matches the "function pointer" part of the member function
|
||||
pointer, and that the member function's class type is related to the base
|
||||
type of the member function. This CFI scheme can be enabled on its own using
|
||||
``-fsanitize=cfi-mfcall``.
|
||||
|
||||
The compiler will only emit a full CFI check if the member function pointer's
|
||||
base type is complete. This is because the complete definition of the base
|
||||
type contains information that is necessary to correctly compile the CFI
|
||||
check. To ensure that the compiler always emits a full CFI check, it is
|
||||
recommended to also pass the flag ``-fcomplete-member-pointers``, which
|
||||
enables a non-conforming language extension that requires member pointer
|
||||
base types to be complete if they may be used for a call.
|
||||
|
||||
For this scheme to work, all translation units containing the definition
|
||||
of a virtual member function (whether inline or not), other than members
|
||||
of :ref:`blacklisted <cfi-blacklist>` types or types with public :doc:`LTO
|
||||
visibility <LTOVisibility>`, must be compiled with ``-flto`` or ``-flto=thin``
|
||||
enabled and be statically linked into the program.
|
||||
|
||||
This scheme is currently not compatible with cross-DSO CFI or the
|
||||
Microsoft ABI.
|
||||
|
||||
.. _cfi-blacklist:
|
||||
|
||||
Blacklist
|
||||
=========
|
||||
|
||||
A :doc:`SanitizerSpecialCaseList` can be used to relax CFI checks for certain
|
||||
source files, functions and types using the ``src``, ``fun`` and ``type``
|
||||
entity types. Specific CFI modes can be be specified using ``[section]``
|
||||
headers.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Suppress all CFI checking for code in a file.
|
||||
src:bad_file.cpp
|
||||
src:bad_header.h
|
||||
# Ignore all functions with names containing MyFooBar.
|
||||
fun:*MyFooBar*
|
||||
# Ignore all types in the standard library.
|
||||
type:std::*
|
||||
# Disable only unrelated cast checks for this function
|
||||
[cfi-unrelated-cast]
|
||||
fun:*UnrelatedCast*
|
||||
# Disable CFI call checks for this function without affecting cast checks
|
||||
[cfi-vcall|cfi-nvcall|cfi-icall]
|
||||
fun:*BadCall*
|
||||
|
||||
|
||||
.. _cfi-cross-dso:
|
||||
|
||||
Shared library support
|
||||
======================
|
||||
|
||||
Use **-f[no-]sanitize-cfi-cross-dso** to enable the cross-DSO control
|
||||
flow integrity mode, which allows all CFI schemes listed above to
|
||||
apply across DSO boundaries. As in the regular CFI, each DSO must be
|
||||
built with ``-flto``.
|
||||
|
||||
Normally, CFI checks will only be performed for classes that have hidden LTO
|
||||
visibility. With this flag enabled, the compiler will emit cross-DSO CFI
|
||||
checks for all classes, except for those which appear in the CFI blacklist
|
||||
or which use a ``no_sanitize`` attribute.
|
||||
|
||||
Design
|
||||
======
|
||||
|
||||
Please refer to the :doc:`design document<ControlFlowIntegrityDesign>`.
|
||||
|
||||
Publications
|
||||
============
|
||||
|
||||
`Control-Flow Integrity: Principles, Implementations, and Applications <http://research.microsoft.com/pubs/64250/ccs05.pdf>`_.
|
||||
Martin Abadi, Mihai Budiu, Úlfar Erlingsson, Jay Ligatti.
|
||||
|
||||
`Enforcing Forward-Edge Control-Flow Integrity in GCC & LLVM <http://www.pcc.me.uk/~peter/acad/usenix14.pdf>`_.
|
||||
Caroline Tice, Tom Roeder, Peter Collingbourne, Stephen Checkoway,
|
||||
Úlfar Erlingsson, Luis Lozano, Geoff Pike.
|
@ -1,803 +0,0 @@
|
||||
===========================================
|
||||
Control Flow Integrity Design Documentation
|
||||
===========================================
|
||||
|
||||
This page documents the design of the :doc:`ControlFlowIntegrity` schemes
|
||||
supported by Clang.
|
||||
|
||||
Forward-Edge CFI for Virtual Calls
|
||||
==================================
|
||||
|
||||
This scheme works by allocating, for each static type used to make a virtual
|
||||
call, a region of read-only storage in the object file holding a bit vector
|
||||
that maps onto to the region of storage used for those virtual tables. Each
|
||||
set bit in the bit vector corresponds to the `address point`_ for a virtual
|
||||
table compatible with the static type for which the bit vector is being built.
|
||||
|
||||
For example, consider the following three C++ classes:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
struct A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
virtual void f3();
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
virtual void f3();
|
||||
};
|
||||
|
||||
struct C : A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
virtual void f3();
|
||||
};
|
||||
|
||||
The scheme will cause the virtual tables for A, B and C to be laid out
|
||||
consecutively:
|
||||
|
||||
.. csv-table:: Virtual Table Layout for A, B, C
|
||||
:header: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||
|
||||
A::offset-to-top, &A::rtti, &A::f1, &A::f2, &A::f3, B::offset-to-top, &B::rtti, &B::f1, &B::f2, &B::f3, C::offset-to-top, &C::rtti, &C::f1, &C::f2, &C::f3
|
||||
|
||||
The bit vector for static types A, B and C will look like this:
|
||||
|
||||
.. csv-table:: Bit Vectors for A, B, C
|
||||
:header: Class, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||
|
||||
A, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
|
||||
B, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
|
||||
C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
|
||||
|
||||
Bit vectors are represented in the object file as byte arrays. By loading
|
||||
from indexed offsets into the byte array and applying a mask, a program can
|
||||
test bits from the bit set with a relatively short instruction sequence. Bit
|
||||
vectors may overlap so long as they use different bits. For the full details,
|
||||
see the `ByteArrayBuilder`_ class.
|
||||
|
||||
In this case, assuming A is laid out at offset 0 in bit 0, B at offset 0 in
|
||||
bit 1 and C at offset 0 in bit 2, the byte array would look like this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
char bits[] = { 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 5, 0, 0 };
|
||||
|
||||
To emit a virtual call, the compiler will assemble code that checks that
|
||||
the object's virtual table pointer is in-bounds and aligned and that the
|
||||
relevant bit is set in the bit vector.
|
||||
|
||||
For example on x86 a typical virtual call may look like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
ca7fbb: 48 8b 0f mov (%rdi),%rcx
|
||||
ca7fbe: 48 8d 15 c3 42 fb 07 lea 0x7fb42c3(%rip),%rdx
|
||||
ca7fc5: 48 89 c8 mov %rcx,%rax
|
||||
ca7fc8: 48 29 d0 sub %rdx,%rax
|
||||
ca7fcb: 48 c1 c0 3d rol $0x3d,%rax
|
||||
ca7fcf: 48 3d 7f 01 00 00 cmp $0x17f,%rax
|
||||
ca7fd5: 0f 87 36 05 00 00 ja ca8511
|
||||
ca7fdb: 48 8d 15 c0 0b f7 06 lea 0x6f70bc0(%rip),%rdx
|
||||
ca7fe2: f6 04 10 10 testb $0x10,(%rax,%rdx,1)
|
||||
ca7fe6: 0f 84 25 05 00 00 je ca8511
|
||||
ca7fec: ff 91 98 00 00 00 callq *0x98(%rcx)
|
||||
[...]
|
||||
ca8511: 0f 0b ud2
|
||||
|
||||
The compiler relies on co-operation from the linker in order to assemble
|
||||
the bit vectors for the whole program. It currently does this using LLVM's
|
||||
`type metadata`_ mechanism together with link-time optimization.
|
||||
|
||||
.. _address point: http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-general
|
||||
.. _type metadata: https://llvm.org/docs/TypeMetadata.html
|
||||
.. _ByteArrayBuilder: https://llvm.org/docs/doxygen/html/structllvm_1_1ByteArrayBuilder.html
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
The scheme as described above is the fully general variant of the scheme.
|
||||
Most of the time we are able to apply one or more of the following
|
||||
optimizations to improve binary size or performance.
|
||||
|
||||
In fact, if you try the above example with the current version of the
|
||||
compiler, you will probably find that it will not use the described virtual
|
||||
table layout or machine instructions. Some of the optimizations we are about
|
||||
to introduce cause the compiler to use a different layout or a different
|
||||
sequence of machine instructions.
|
||||
|
||||
Stripping Leading/Trailing Zeros in Bit Vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a bit vector contains leading or trailing zeros, we can strip them from
|
||||
the vector. The compiler will emit code to check if the pointer is in range
|
||||
of the region covered by ones, and perform the bit vector check using a
|
||||
truncated version of the bit vector. For example, the bit vectors for our
|
||||
example class hierarchy will be emitted like this:
|
||||
|
||||
.. csv-table:: Bit Vectors for A, B, C
|
||||
:header: Class, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||
|
||||
A, , , 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ,
|
||||
B, , , , , , , , 1, , , , , , ,
|
||||
C, , , , , , , , , , , , , 1, ,
|
||||
|
||||
Short Inline Bit Vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the vector is sufficiently short, we can represent it as an inline constant
|
||||
on x86. This saves us a few instructions when reading the correct element
|
||||
of the bit vector.
|
||||
|
||||
If the bit vector fits in 32 bits, the code looks like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
dc2: 48 8b 03 mov (%rbx),%rax
|
||||
dc5: 48 8d 15 14 1e 00 00 lea 0x1e14(%rip),%rdx
|
||||
dcc: 48 89 c1 mov %rax,%rcx
|
||||
dcf: 48 29 d1 sub %rdx,%rcx
|
||||
dd2: 48 c1 c1 3d rol $0x3d,%rcx
|
||||
dd6: 48 83 f9 03 cmp $0x3,%rcx
|
||||
dda: 77 2f ja e0b <main+0x9b>
|
||||
ddc: ba 09 00 00 00 mov $0x9,%edx
|
||||
de1: 0f a3 ca bt %ecx,%edx
|
||||
de4: 73 25 jae e0b <main+0x9b>
|
||||
de6: 48 89 df mov %rbx,%rdi
|
||||
de9: ff 10 callq *(%rax)
|
||||
[...]
|
||||
e0b: 0f 0b ud2
|
||||
|
||||
Or if the bit vector fits in 64 bits:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
11a6: 48 8b 03 mov (%rbx),%rax
|
||||
11a9: 48 8d 15 d0 28 00 00 lea 0x28d0(%rip),%rdx
|
||||
11b0: 48 89 c1 mov %rax,%rcx
|
||||
11b3: 48 29 d1 sub %rdx,%rcx
|
||||
11b6: 48 c1 c1 3d rol $0x3d,%rcx
|
||||
11ba: 48 83 f9 2a cmp $0x2a,%rcx
|
||||
11be: 77 35 ja 11f5 <main+0xb5>
|
||||
11c0: 48 ba 09 00 00 00 00 movabs $0x40000000009,%rdx
|
||||
11c7: 04 00 00
|
||||
11ca: 48 0f a3 ca bt %rcx,%rdx
|
||||
11ce: 73 25 jae 11f5 <main+0xb5>
|
||||
11d0: 48 89 df mov %rbx,%rdi
|
||||
11d3: ff 10 callq *(%rax)
|
||||
[...]
|
||||
11f5: 0f 0b ud2
|
||||
|
||||
If the bit vector consists of a single bit, there is only one possible
|
||||
virtual table, and the check can consist of a single equality comparison:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
9a2: 48 8b 03 mov (%rbx),%rax
|
||||
9a5: 48 8d 0d a4 13 00 00 lea 0x13a4(%rip),%rcx
|
||||
9ac: 48 39 c8 cmp %rcx,%rax
|
||||
9af: 75 25 jne 9d6 <main+0x86>
|
||||
9b1: 48 89 df mov %rbx,%rdi
|
||||
9b4: ff 10 callq *(%rax)
|
||||
[...]
|
||||
9d6: 0f 0b ud2
|
||||
|
||||
Virtual Table Layout
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The compiler lays out classes of disjoint hierarchies in separate regions
|
||||
of the object file. At worst, bit vectors in disjoint hierarchies only
|
||||
need to cover their disjoint hierarchy. But the closer that classes in
|
||||
sub-hierarchies are laid out to each other, the smaller the bit vectors for
|
||||
those sub-hierarchies need to be (see "Stripping Leading/Trailing Zeros in Bit
|
||||
Vectors" above). The `GlobalLayoutBuilder`_ class is responsible for laying
|
||||
out the globals efficiently to minimize the sizes of the underlying bitsets.
|
||||
|
||||
.. _GlobalLayoutBuilder: https://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/LowerTypeTests.h?view=markup
|
||||
|
||||
Alignment
|
||||
~~~~~~~~~
|
||||
|
||||
If all gaps between address points in a particular bit vector are multiples
|
||||
of powers of 2, the compiler can compress the bit vector by strengthening
|
||||
the alignment requirements of the virtual table pointer. For example, given
|
||||
this class hierarchy:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
struct A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
virtual void f3();
|
||||
virtual void f4();
|
||||
virtual void f5();
|
||||
virtual void f6();
|
||||
};
|
||||
|
||||
struct C : A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
};
|
||||
|
||||
The virtual tables will be laid out like this:
|
||||
|
||||
.. csv-table:: Virtual Table Layout for A, B, C
|
||||
:header: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
|
||||
A::offset-to-top, &A::rtti, &A::f1, &A::f2, B::offset-to-top, &B::rtti, &B::f1, &B::f2, &B::f3, &B::f4, &B::f5, &B::f6, C::offset-to-top, &C::rtti, &C::f1, &C::f2
|
||||
|
||||
Notice that each address point for A is separated by 4 words. This lets us
|
||||
emit a compressed bit vector for A that looks like this:
|
||||
|
||||
.. csv-table::
|
||||
:header: 2, 6, 10, 14
|
||||
|
||||
1, 1, 0, 1
|
||||
|
||||
At call sites, the compiler will strengthen the alignment requirements by
|
||||
using a different rotate count. For example, on a 64-bit machine where the
|
||||
address points are 4-word aligned (as in A from our example), the ``rol``
|
||||
instruction may look like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
dd2: 48 c1 c1 3b rol $0x3b,%rcx
|
||||
|
||||
Padding to Powers of 2
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Of course, this alignment scheme works best if the address points are
|
||||
in fact aligned correctly. To make this more likely to happen, we insert
|
||||
padding between virtual tables that in many cases aligns address points to
|
||||
a power of 2. Specifically, our padding aligns virtual tables to the next
|
||||
highest power of 2 bytes; because address points for specific base classes
|
||||
normally appear at fixed offsets within the virtual table, this normally
|
||||
has the effect of aligning the address points as well.
|
||||
|
||||
This scheme introduces tradeoffs between decreased space overhead for
|
||||
instructions and bit vectors and increased overhead in the form of padding. We
|
||||
therefore limit the amount of padding so that we align to no more than 128
|
||||
bytes. This number was found experimentally to provide a good tradeoff.
|
||||
|
||||
Eliminating Bit Vector Checks for All-Ones Bit Vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the bit vector is all ones, the bit vector check is redundant; we simply
|
||||
need to check that the address is in range and well aligned. This is more
|
||||
likely to occur if the virtual tables are padded.
|
||||
|
||||
Forward-Edge CFI for Virtual Calls by Interleaving Virtual Tables
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Dimitar et. al. proposed a novel approach that interleaves virtual tables in [1]_.
|
||||
This approach is more efficient in terms of space because padding and bit vectors are no longer needed.
|
||||
At the same time, it is also more efficient in terms of performance because in the interleaved layout
|
||||
address points of the virtual tables are consecutive, thus the validity check of a virtual
|
||||
vtable pointer is always a range check.
|
||||
|
||||
At a high level, the interleaving scheme consists of three steps: 1) split virtual table groups into
|
||||
separate virtual tables, 2) order virtual tables by a pre-order traversal of the class hierarchy
|
||||
and 3) interleave virtual tables.
|
||||
|
||||
The interleaving scheme implemented in LLVM is inspired by [1]_ but has its own
|
||||
enhancements (more in `Interleave virtual tables`_).
|
||||
|
||||
.. [1] `Protecting C++ Dynamic Dispatch Through VTable Interleaving <https://cseweb.ucsd.edu/~lerner/papers/ivtbl-ndss16.pdf>`_. Dimitar Bounov, Rami Gökhan Kıcı, Sorin Lerner.
|
||||
|
||||
Split virtual table groups into separate virtual tables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Itanium C++ ABI glues multiple individual virtual tables for a class into a combined virtual table (virtual table group).
|
||||
The interleaving scheme, however, can only work with individual virtual tables so it must split the combined virtual tables first.
|
||||
In comparison, the old scheme does not require the splitting but it is more efficient when the combined virtual tables have been split.
|
||||
The `GlobalSplit`_ pass is responsible for splitting combined virtual tables into individual ones.
|
||||
|
||||
.. _GlobalSplit: https://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/GlobalSplit.cpp?view=markup
|
||||
|
||||
Order virtual tables by a pre-order traversal of the class hierarchy
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This step is common to both the old scheme described above and the interleaving scheme.
|
||||
For the interleaving scheme, since the combined virtual tables have been split in the previous step,
|
||||
this step ensures that for any class all the compatible virtual tables will appear consecutively.
|
||||
For the old scheme, the same property may not hold since it may work on combined virtual tables.
|
||||
|
||||
For example, consider the following four C++ classes:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
struct A {
|
||||
virtual void f1();
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
};
|
||||
|
||||
struct C : A {
|
||||
virtual void f1();
|
||||
virtual void f3();
|
||||
};
|
||||
|
||||
struct D : B {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
};
|
||||
|
||||
This step will arrange the virtual tables for A, B, C, and D in the order of *vtable-of-A, vtable-of-B, vtable-of-D, vtable-of-C*.
|
||||
|
||||
Interleave virtual tables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This step is where the interleaving scheme deviates from the old scheme. Instead of laying out
|
||||
whole virtual tables in the previously computed order, the interleaving scheme lays out table
|
||||
entries of the virtual tables strategically to ensure the following properties:
|
||||
|
||||
(1) offset-to-top and RTTI fields layout property
|
||||
|
||||
The Itanium C++ ABI specifies that offset-to-top and RTTI fields appear at the offsets behind the
|
||||
address point. Note that libraries like libcxxabi do assume this property.
|
||||
|
||||
(2) virtual function entry layout property
|
||||
|
||||
For each virtual function the distance between an virtual table entry for this function and the corresponding
|
||||
address point is always the same. This property ensures that dynamic dispatch still works with the interleaving layout.
|
||||
|
||||
Note that the interleaving scheme in the CFI implementation guarantees both properties above whereas the original scheme proposed
|
||||
in [1]_ only guarantees the second property.
|
||||
|
||||
To illustrate how the interleaving algorithm works, let us continue with the running example.
|
||||
The algorithm first separates all the virtual table entries into two work lists. To do so,
|
||||
it starts by allocating two work lists, one initialized with all the offset-to-top entries of virtual tables in the order
|
||||
computed in the last step, one initialized with all the RTTI entries in the same order.
|
||||
|
||||
.. csv-table:: Work list 1 Layout
|
||||
:header: 0, 1, 2, 3
|
||||
|
||||
A::offset-to-top, B::offset-to-top, D::offset-to-top, C::offset-to-top
|
||||
|
||||
|
||||
.. csv-table:: Work list 2 layout
|
||||
:header: 0, 1, 2, 3,
|
||||
|
||||
&A::rtti, &B::rtti, &D::rtti, &C::rtti
|
||||
|
||||
Then for each virtual function the algorithm goes through all the virtual tables in the previously computed order
|
||||
to collect all the related entries into a virtual function list.
|
||||
After this step, there are the following virtual function lists:
|
||||
|
||||
.. csv-table:: f1 list
|
||||
:header: 0, 1, 2, 3
|
||||
|
||||
&A::f1, &B::f1, &D::f1, &C::f1
|
||||
|
||||
|
||||
.. csv-table:: f2 list
|
||||
:header: 0, 1
|
||||
|
||||
&B::f2, &D::f2
|
||||
|
||||
|
||||
.. csv-table:: f3 list
|
||||
:header: 0
|
||||
|
||||
&C::f3
|
||||
|
||||
Next, the algorithm picks the longest remaining virtual function list and appends the whole list to the shortest work list
|
||||
until no function lists are left, and pads the shorter work list so that they are of the same length.
|
||||
In the example, f1 list will be first added to work list 1, then f2 list will be added
|
||||
to work list 2, and finally f3 list will be added to the work list 2. Since work list 1 now has one more entry than
|
||||
work list 2, a padding entry is added to the latter. After this step, the two work lists look like:
|
||||
|
||||
.. csv-table:: Work list 1 Layout
|
||||
:header: 0, 1, 2, 3, 4, 5, 6, 7
|
||||
|
||||
A::offset-to-top, B::offset-to-top, D::offset-to-top, C::offset-to-top, &A::f1, &B::f1, &D::f1, &C::f1
|
||||
|
||||
|
||||
.. csv-table:: Work list 2 layout
|
||||
:header: 0, 1, 2, 3, 4, 5, 6, 7
|
||||
|
||||
&A::rtti, &B::rtti, &D::rtti, &C::rtti, &B::f2, &D::f2, &C::f3, padding
|
||||
|
||||
Finally, the algorithm merges the two work lists into the interleaved layout by alternatingly
|
||||
moving the head of each list to the final layout. After this step, the final interleaved layout looks like:
|
||||
|
||||
.. csv-table:: Interleaved layout
|
||||
:header: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
|
||||
A::offset-to-top, &A::rtti, B::offset-to-top, &B::rtti, D::offset-to-top, &D::rtti, C::offset-to-top, &C::rtti, &A::f1, &B::f2, &B::f1, &D::f2, &D::f1, &C::f3, &C::f1, padding
|
||||
|
||||
In the above interleaved layout, each virtual table's offset-to-top and RTTI are always adjacent, which shows that the layout has the first property.
|
||||
For the second property, let us look at f2 as an example. In the interleaved layout,
|
||||
there are two entries for f2: B::f2 and D::f2. The distance between &B::f2
|
||||
and its address point D::offset-to-top (the entry immediately after &B::rtti) is 5 entry-length, so is the distance between &D::f2 and C::offset-to-top (the entry immediately after &D::rtti).
|
||||
|
||||
Forward-Edge CFI for Indirect Function Calls
|
||||
============================================
|
||||
|
||||
Under forward-edge CFI for indirect function calls, each unique function
|
||||
type has its own bit vector, and at each call site we need to check that the
|
||||
function pointer is a member of the function type's bit vector. This scheme
|
||||
works in a similar way to forward-edge CFI for virtual calls, the distinction
|
||||
being that we need to build bit vectors of function entry points rather than
|
||||
of virtual tables.
|
||||
|
||||
Unlike when re-arranging global variables, we cannot re-arrange functions
|
||||
in a particular order and base our calculations on the layout of the
|
||||
functions' entry points, as we have no idea how large a particular function
|
||||
will end up being (the function sizes could even depend on how we arrange
|
||||
the functions). Instead, we build a jump table, which is a block of code
|
||||
consisting of one branch instruction for each of the functions in the bit
|
||||
set that branches to the target function, and redirect any taken function
|
||||
addresses to the corresponding jump table entry. In this way, the distance
|
||||
between function entry points is predictable and controllable. In the object
|
||||
file's symbol table, the symbols for the target functions also refer to the
|
||||
jump table entries, so that addresses taken outside the module will pass
|
||||
any verification done inside the module.
|
||||
|
||||
In more concrete terms, suppose we have three functions ``f``, ``g``,
|
||||
``h`` which are all of the same type, and a function foo that returns their
|
||||
addresses:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
f:
|
||||
mov 0, %eax
|
||||
ret
|
||||
|
||||
g:
|
||||
mov 1, %eax
|
||||
ret
|
||||
|
||||
h:
|
||||
mov 2, %eax
|
||||
ret
|
||||
|
||||
foo:
|
||||
mov f, %eax
|
||||
mov g, %edx
|
||||
mov h, %ecx
|
||||
ret
|
||||
|
||||
Our jump table will (conceptually) look like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
f:
|
||||
jmp .Ltmp0 ; 5 bytes
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
|
||||
g:
|
||||
jmp .Ltmp1 ; 5 bytes
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
|
||||
h:
|
||||
jmp .Ltmp2 ; 5 bytes
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
int3 ; 1 byte
|
||||
|
||||
.Ltmp0:
|
||||
mov 0, %eax
|
||||
ret
|
||||
|
||||
.Ltmp1:
|
||||
mov 1, %eax
|
||||
ret
|
||||
|
||||
.Ltmp2:
|
||||
mov 2, %eax
|
||||
ret
|
||||
|
||||
foo:
|
||||
mov f, %eax
|
||||
mov g, %edx
|
||||
mov h, %ecx
|
||||
ret
|
||||
|
||||
Because the addresses of ``f``, ``g``, ``h`` are evenly spaced at a power of
|
||||
2, and function types do not overlap (unlike class types with base classes),
|
||||
we can normally apply the `Alignment`_ and `Eliminating Bit Vector Checks
|
||||
for All-Ones Bit Vectors`_ optimizations thus simplifying the check at each
|
||||
call site to a range and alignment check.
|
||||
|
||||
Shared library support
|
||||
======================
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
The basic CFI mode described above assumes that the application is a
|
||||
monolithic binary; at least that all possible virtual/indirect call
|
||||
targets and the entire class hierarchy are known at link time. The
|
||||
cross-DSO mode, enabled with **-f[no-]sanitize-cfi-cross-dso** relaxes
|
||||
this requirement by allowing virtual and indirect calls to cross the
|
||||
DSO boundary.
|
||||
|
||||
Assuming the following setup: the binary consists of several
|
||||
instrumented and several uninstrumented DSOs. Some of them may be
|
||||
dlopen-ed/dlclose-d periodically, even frequently.
|
||||
|
||||
- Calls made from uninstrumented DSOs are not checked and just work.
|
||||
- Calls inside any instrumented DSO are fully protected.
|
||||
- Calls between different instrumented DSOs are also protected, with
|
||||
a performance penalty (in addition to the monolithic CFI
|
||||
overhead).
|
||||
- Calls from an instrumented DSO to an uninstrumented one are
|
||||
unchecked and just work, with performance penalty.
|
||||
- Calls from an instrumented DSO outside of any known DSO are
|
||||
detected as CFI violations.
|
||||
|
||||
In the monolithic scheme a call site is instrumented as
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
if (!InlinedFastCheck(f))
|
||||
abort();
|
||||
call *f
|
||||
|
||||
In the cross-DSO scheme it becomes
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
if (!InlinedFastCheck(f))
|
||||
__cfi_slowpath(CallSiteTypeId, f);
|
||||
call *f
|
||||
|
||||
CallSiteTypeId
|
||||
--------------
|
||||
|
||||
``CallSiteTypeId`` is a stable process-wide identifier of the
|
||||
call-site type. For a virtual call site, the type in question is the class
|
||||
type; for an indirect function call it is the function signature. The
|
||||
mapping from a type to an identifier is an ABI detail. In the current,
|
||||
experimental, implementation the identifier of type T is calculated as
|
||||
follows:
|
||||
|
||||
- Obtain the mangled name for "typeinfo name for T".
|
||||
- Calculate MD5 hash of the name as a string.
|
||||
- Reinterpret the first 8 bytes of the hash as a little-endian
|
||||
64-bit integer.
|
||||
|
||||
It is possible, but unlikely, that collisions in the
|
||||
``CallSiteTypeId`` hashing will result in weaker CFI checks that would
|
||||
still be conservatively correct.
|
||||
|
||||
CFI_Check
|
||||
---------
|
||||
|
||||
In the general case, only the target DSO knows whether the call to
|
||||
function ``f`` with type ``CallSiteTypeId`` is valid or not. To
|
||||
export this information, every DSO implements
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
void __cfi_check(uint64 CallSiteTypeId, void *TargetAddr, void *DiagData)
|
||||
|
||||
This function provides external modules with access to CFI checks for
|
||||
the targets inside this DSO. For each known ``CallSiteTypeId``, this
|
||||
function performs an ``llvm.type.test`` with the corresponding type
|
||||
identifier. It reports an error if the type is unknown, or if the
|
||||
check fails. Depending on the values of compiler flags
|
||||
``-fsanitize-trap`` and ``-fsanitize-recover``, this function may
|
||||
print an error, abort and/or return to the caller. ``DiagData`` is an
|
||||
opaque pointer to the diagnostic information about the error, or
|
||||
``null`` if the caller does not provide this information.
|
||||
|
||||
The basic implementation is a large switch statement over all values
|
||||
of CallSiteTypeId supported by this DSO, and each case is similar to
|
||||
the InlinedFastCheck() in the basic CFI mode.
|
||||
|
||||
CFI Shadow
|
||||
----------
|
||||
|
||||
To route CFI checks to the target DSO's __cfi_check function, a
|
||||
mapping from possible virtual / indirect call targets to the
|
||||
corresponding __cfi_check functions is maintained. This mapping is
|
||||
implemented as a sparse array of 2 bytes for every possible page (4096
|
||||
bytes) of memory. The table is kept readonly most of the time.
|
||||
|
||||
There are 3 types of shadow values:
|
||||
|
||||
- Address in a CFI-instrumented DSO.
|
||||
- Unchecked address (a “trusted” non-instrumented DSO). Encoded as
|
||||
value 0xFFFF.
|
||||
- Invalid address (everything else). Encoded as value 0.
|
||||
|
||||
For a CFI-instrumented DSO, a shadow value encodes the address of the
|
||||
__cfi_check function for all call targets in the corresponding memory
|
||||
page. If Addr is the target address, and V is the shadow value, then
|
||||
the address of __cfi_check is calculated as
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
__cfi_check = AlignUpTo(Addr, 4096) - (V + 1) * 4096
|
||||
|
||||
This works as long as __cfi_check is aligned by 4096 bytes and located
|
||||
below any call targets in its DSO, but not more than 256MB apart from
|
||||
them.
|
||||
|
||||
CFI_SlowPath
|
||||
------------
|
||||
|
||||
The slow path check is implemented in a runtime support library as
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
void __cfi_slowpath(uint64 CallSiteTypeId, void *TargetAddr)
|
||||
void __cfi_slowpath_diag(uint64 CallSiteTypeId, void *TargetAddr, void *DiagData)
|
||||
|
||||
These functions loads a shadow value for ``TargetAddr``, finds the
|
||||
address of ``__cfi_check`` as described above and calls
|
||||
that. ``DiagData`` is an opaque pointer to diagnostic data which is
|
||||
passed verbatim to ``__cfi_check``, and ``__cfi_slowpath`` passes
|
||||
``nullptr`` instead.
|
||||
|
||||
Compiler-RT library contains reference implementations of slowpath
|
||||
functions, but they have unresolvable issues with correctness and
|
||||
performance in the handling of dlopen(). It is recommended that
|
||||
platforms provide their own implementations, usually as part of libc
|
||||
or libdl.
|
||||
|
||||
Position-independent executable requirement
|
||||
-------------------------------------------
|
||||
|
||||
Cross-DSO CFI mode requires that the main executable is built as PIE.
|
||||
In non-PIE executables the address of an external function (taken from
|
||||
the main executable) is the address of that function’s PLT record in
|
||||
the main executable. This would break the CFI checks.
|
||||
|
||||
Backward-edge CFI for return statements (RCFI)
|
||||
==============================================
|
||||
|
||||
This section is a proposal. As of March 2017 it is not implemented.
|
||||
|
||||
Backward-edge control flow (`RET` instructions) can be hijacked
|
||||
via overwriting the return address (`RA`) on stack.
|
||||
Various mitigation techniques (e.g. `SafeStack`_, `RFG`_, `Intel CET`_)
|
||||
try to detect or prevent `RA` corruption on stack.
|
||||
|
||||
RCFI enforces the expected control flow in several different ways described below.
|
||||
RCFI heavily relies on LTO.
|
||||
|
||||
Leaf Functions
|
||||
--------------
|
||||
If `f()` is a leaf function (i.e. it has no calls
|
||||
except maybe no-return calls) it can be called using a special calling convention
|
||||
that stores `RA` in a dedicated register `R` before the `CALL` instruction.
|
||||
`f()` does not spill `R` and does not use the `RET` instruction,
|
||||
instead it uses the value in `R` to `JMP` to `RA`.
|
||||
|
||||
This flavour of CFI is *precise*, i.e. the function is guaranteed to return
|
||||
to the point exactly following the call.
|
||||
|
||||
An alternative approach is to
|
||||
copy `RA` from stack to `R` in the first instruction of `f()`,
|
||||
then `JMP` to `R`.
|
||||
This approach is simpler to implement (does not require changing the caller)
|
||||
but weaker (there is a small window when `RA` is actually stored on stack).
|
||||
|
||||
|
||||
Functions called once
|
||||
---------------------
|
||||
Suppose `f()` is called in just one place in the program
|
||||
(assuming we can verify this in LTO mode).
|
||||
In this case we can replace the `RET` instruction with a `JMP` instruction
|
||||
with the immediate constant for `RA`.
|
||||
This will *precisely* enforce the return control flow no matter what is stored on stack.
|
||||
|
||||
Another variant is to compare `RA` on stack with the known constant and abort
|
||||
if they don't match; then `JMP` to the known constant address.
|
||||
|
||||
Functions called in a small number of call sites
|
||||
------------------------------------------------
|
||||
We may extend the above approach to cases where `f()`
|
||||
is called more than once (but still a small number of times).
|
||||
With LTO we know all possible values of `RA` and we check them
|
||||
one-by-one (or using binary search) against the value on stack.
|
||||
If the match is found, we `JMP` to the known constant address, otherwise abort.
|
||||
|
||||
This protection is *near-precise*, i.e. it guarantees that the control flow will
|
||||
be transferred to one of the valid return addresses for this function,
|
||||
but not necessary to the point of the most recent `CALL`.
|
||||
|
||||
General case
|
||||
------------
|
||||
For functions called multiple times a *return jump table* is constructed
|
||||
in the same manner as jump tables for indirect function calls (see above).
|
||||
The correct jump table entry (or it's index) is passed by `CALL` to `f()`
|
||||
(as an extra argument) and then spilled to stack.
|
||||
The `RET` instruction is replaced with a load of the jump table entry,
|
||||
jump table range check, and `JMP` to the jump table entry.
|
||||
|
||||
This protection is also *near-precise*.
|
||||
|
||||
Returns from functions called indirectly
|
||||
----------------------------------------
|
||||
|
||||
If a function is called indirectly, the return jump table is constructed for the
|
||||
equivalence class of functions instead of a single function.
|
||||
|
||||
Cross-DSO calls
|
||||
---------------
|
||||
Consider two instrumented DSOs, `A` and `B`. `A` defines `f()` and `B` calls it.
|
||||
|
||||
This case will be handled similarly to the cross-DSO scheme using the slow path callback.
|
||||
|
||||
Non-goals
|
||||
---------
|
||||
|
||||
RCFI does not protect `RET` instructions:
|
||||
* in non-instrumented DSOs,
|
||||
* in instrumented DSOs for functions that are called from non-instrumented DSOs,
|
||||
* embedded into other instructions (e.g. `0f4fc3 cmovg %ebx,%eax`).
|
||||
|
||||
.. _SafeStack: https://clang.llvm.org/docs/SafeStack.html
|
||||
.. _RFG: http://xlab.tencent.com/en/2016/11/02/return-flow-guard
|
||||
.. _Intel CET: https://software.intel.com/en-us/blogs/2016/06/09/intel-release-new-technology-specifications-protect-rop-attacks
|
||||
|
||||
Hardware support
|
||||
================
|
||||
|
||||
We believe that the above design can be efficiently implemented in hardware.
|
||||
A single new instruction added to an ISA would allow to perform the forward-edge CFI check
|
||||
with fewer bytes per check (smaller code size overhead) and potentially more
|
||||
efficiently. The current software-only instrumentation requires at least
|
||||
32-bytes per check (on x86_64).
|
||||
A hardware instruction may probably be less than ~ 12 bytes.
|
||||
Such instruction would check that the argument pointer is in-bounds,
|
||||
and is properly aligned, and if the checks fail it will either trap (in monolithic scheme)
|
||||
or call the slow path function (cross-DSO scheme).
|
||||
The bit vector lookup is probably too complex for a hardware implementation.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
// This instruction checks that 'Ptr'
|
||||
// * is aligned by (1 << kAlignment) and
|
||||
// * is inside [kRangeBeg, kRangeBeg+(kRangeSize<<kAlignment))
|
||||
// and if the check fails it jumps to the given target (slow path).
|
||||
//
|
||||
// 'Ptr' is a register, pointing to the virtual function table
|
||||
// or to the function which we need to check. We may require an explicit
|
||||
// fixed register to be used.
|
||||
// 'kAlignment' is a 4-bit constant.
|
||||
// 'kRangeSize' is a ~20-bit constant.
|
||||
// 'kRangeBeg' is a PC-relative constant (~28 bits)
|
||||
// pointing to the beginning of the allowed range for 'Ptr'.
|
||||
// 'kFailedCheckTarget': is a PC-relative constant (~28 bits)
|
||||
// representing the target to branch to when the check fails.
|
||||
// If kFailedCheckTarget==0, the process will trap
|
||||
// (monolithic binary scheme).
|
||||
// Otherwise it will jump to a handler that implements `CFI_SlowPath`
|
||||
// (cross-DSO scheme).
|
||||
CFI_Check(Ptr, kAlignment, kRangeSize, kRangeBeg, kFailedCheckTarget) {
|
||||
if (Ptr < kRangeBeg ||
|
||||
Ptr >= kRangeBeg + (kRangeSize << kAlignment) ||
|
||||
Ptr & ((1 << kAlignment) - 1))
|
||||
Jump(kFailedCheckTarget);
|
||||
}
|
||||
|
||||
An alternative and more compact encoding would not use `kFailedCheckTarget`,
|
||||
and will trap on check failure instead.
|
||||
This will allow us to fit the instruction into **8-9 bytes**.
|
||||
The cross-DSO checks will be performed by a trap handler and
|
||||
performance-critical ones will have to be black-listed and checked using the
|
||||
software-only scheme.
|
||||
|
||||
Note that such hardware extension would be complementary to checks
|
||||
at the callee side, such as e.g. **Intel ENDBRANCH**.
|
||||
Moreover, CFI would have two benefits over ENDBRANCH: a) precision and b)
|
||||
ability to protect against invalid casts between polymorphic types.
|
@ -1,203 +0,0 @@
|
||||
===================================================================
|
||||
Cross-compilation using Clang
|
||||
===================================================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document will guide you in choosing the right Clang options
|
||||
for cross-compiling your code to a different architecture. It assumes you
|
||||
already know how to compile the code in question for the host architecture,
|
||||
and that you know how to choose additional include and library paths.
|
||||
|
||||
However, this document is *not* a "how to" and won't help you setting your
|
||||
build system or Makefiles, nor choosing the right CMake options, etc.
|
||||
Also, it does not cover all the possible options, nor does it contain
|
||||
specific examples for specific architectures. For a concrete example, the
|
||||
`instructions for cross-compiling LLVM itself
|
||||
<https://llvm.org/docs/HowToCrossCompileLLVM.html>`_ may be of interest.
|
||||
|
||||
After reading this document, you should be familiar with the main issues
|
||||
related to cross-compilation, and what main compiler options Clang provides
|
||||
for performing cross-compilation.
|
||||
|
||||
Cross compilation issues
|
||||
========================
|
||||
|
||||
In GCC world, every host/target combination has its own set of binaries,
|
||||
headers, libraries, etc. So, it's usually simple to download a package
|
||||
with all files in, unzip to a directory and point the build system to
|
||||
that compiler, that will know about its location and find all it needs to
|
||||
when compiling your code.
|
||||
|
||||
On the other hand, Clang/LLVM is natively a cross-compiler, meaning that
|
||||
one set of programs can compile to all targets by setting the ``-target``
|
||||
option. That makes it a lot easier for programmers wishing to compile to
|
||||
different platforms and architectures, and for compiler developers that
|
||||
only have to maintain one build system, and for OS distributions, that
|
||||
need only one set of main packages.
|
||||
|
||||
But, as is true to any cross-compiler, and given the complexity of
|
||||
different architectures, OS's and options, it's not always easy finding
|
||||
the headers, libraries or binutils to generate target specific code.
|
||||
So you'll need special options to help Clang understand what target
|
||||
you're compiling to, where your tools are, etc.
|
||||
|
||||
Another problem is that compilers come with standard libraries only (like
|
||||
``compiler-rt``, ``libcxx``, ``libgcc``, ``libm``, etc), so you'll have to
|
||||
find and make available to the build system, every other library required
|
||||
to build your software, that is specific to your target. It's not enough to
|
||||
have your host's libraries installed.
|
||||
|
||||
Finally, not all toolchains are the same, and consequently, not every Clang
|
||||
option will work magically. Some options, like ``--sysroot`` (which
|
||||
effectively changes the logical root for headers and libraries), assume
|
||||
all your binaries and libraries are in the same directory, which may not
|
||||
true when your cross-compiler was installed by the distribution's package
|
||||
management. So, for each specific case, you may use more than one
|
||||
option, and in most cases, you'll end up setting include paths (``-I``) and
|
||||
library paths (``-L``) manually.
|
||||
|
||||
To sum up, different toolchains can:
|
||||
* be host/target specific or more flexible
|
||||
* be in a single directory, or spread out across your system
|
||||
* have different sets of libraries and headers by default
|
||||
* need special options, which your build system won't be able to figure
|
||||
out by itself
|
||||
|
||||
General Cross-Compilation Options in Clang
|
||||
==========================================
|
||||
|
||||
Target Triple
|
||||
-------------
|
||||
|
||||
The basic option is to define the target architecture. For that, use
|
||||
``-target <triple>``. If you don't specify the target, CPU names won't
|
||||
match (since Clang assumes the host triple), and the compilation will
|
||||
go ahead, creating code for the host platform, which will break later
|
||||
on when assembling or linking.
|
||||
|
||||
The triple has the general format ``<arch><sub>-<vendor>-<sys>-<abi>``, where:
|
||||
* ``arch`` = ``x86_64``, ``i386``, ``arm``, ``thumb``, ``mips``, etc.
|
||||
* ``sub`` = for ex. on ARM: ``v5``, ``v6m``, ``v7a``, ``v7m``, etc.
|
||||
* ``vendor`` = ``pc``, ``apple``, ``nvidia``, ``ibm``, etc.
|
||||
* ``sys`` = ``none``, ``linux``, ``win32``, ``darwin``, ``cuda``, etc.
|
||||
* ``abi`` = ``eabi``, ``gnu``, ``android``, ``macho``, ``elf``, etc.
|
||||
|
||||
The sub-architecture options are available for their own architectures,
|
||||
of course, so "x86v7a" doesn't make sense. The vendor needs to be
|
||||
specified only if there's a relevant change, for instance between PC
|
||||
and Apple. Most of the time it can be omitted (and Unknown)
|
||||
will be assumed, which sets the defaults for the specified architecture.
|
||||
The system name is generally the OS (linux, darwin), but could be special
|
||||
like the bare-metal "none".
|
||||
|
||||
When a parameter is not important, it can be omitted, or you can
|
||||
choose ``unknown`` and the defaults will be used. If you choose a parameter
|
||||
that Clang doesn't know, like ``blerg``, it'll ignore and assume
|
||||
``unknown``, which is not always desired, so be careful.
|
||||
|
||||
Finally, the ABI option is something that will pick default CPU/FPU,
|
||||
define the specific behaviour of your code (PCS, extensions),
|
||||
and also choose the correct library calls, etc.
|
||||
|
||||
CPU, FPU, ABI
|
||||
-------------
|
||||
|
||||
Once your target is specified, it's time to pick the hardware you'll
|
||||
be compiling to. For every architecture, a default set of CPU/FPU/ABI
|
||||
will be chosen, so you'll almost always have to change it via flags.
|
||||
|
||||
Typical flags include:
|
||||
* ``-mcpu=<cpu-name>``, like x86-64, swift, cortex-a15
|
||||
* ``-mfpu=<fpu-name>``, like SSE3, NEON, controlling the FP unit available
|
||||
* ``-mfloat-abi=<fabi>``, like soft, hard, controlling which registers
|
||||
to use for floating-point
|
||||
|
||||
The default is normally the common denominator, so that Clang doesn't
|
||||
generate code that breaks. But that also means you won't get the best
|
||||
code for your specific hardware, which may mean orders of magnitude
|
||||
slower than you expect.
|
||||
|
||||
For example, if your target is ``arm-none-eabi``, the default CPU will
|
||||
be ``arm7tdmi`` using soft float, which is extremely slow on modern cores,
|
||||
whereas if your triple is ``armv7a-none-eabi``, it'll be Cortex-A8 with
|
||||
NEON, but still using soft-float, which is much better, but still not
|
||||
great.
|
||||
|
||||
Toolchain Options
|
||||
-----------------
|
||||
|
||||
There are three main options to control access to your cross-compiler:
|
||||
``--sysroot``, ``-I``, and ``-L``. The two last ones are well known,
|
||||
but they're particularly important for additional libraries
|
||||
and headers that are specific to your target.
|
||||
|
||||
There are two main ways to have a cross-compiler:
|
||||
|
||||
#. When you have extracted your cross-compiler from a zip file into
|
||||
a directory, you have to use ``--sysroot=<path>``. The path is the
|
||||
root directory where you have unpacked your file, and Clang will
|
||||
look for the directories ``bin``, ``lib``, ``include`` in there.
|
||||
|
||||
In this case, your setup should be pretty much done (if no
|
||||
additional headers or libraries are needed), as Clang will find
|
||||
all binaries it needs (assembler, linker, etc) in there.
|
||||
|
||||
#. When you have installed via a package manager (modern Linux
|
||||
distributions have cross-compiler packages available), make
|
||||
sure the target triple you set is *also* the prefix of your
|
||||
cross-compiler toolchain.
|
||||
|
||||
In this case, Clang will find the other binaries (assembler,
|
||||
linker), but not always where the target headers and libraries
|
||||
are. People add system-specific clues to Clang often, but as
|
||||
things change, it's more likely that it won't find than the
|
||||
other way around.
|
||||
|
||||
So, here, you'll be a lot safer if you specify the include/library
|
||||
directories manually (via ``-I`` and ``-L``).
|
||||
|
||||
Target-Specific Libraries
|
||||
=========================
|
||||
|
||||
All libraries that you compile as part of your build will be
|
||||
cross-compiled to your target, and your build system will probably
|
||||
find them in the right place. But all dependencies that are
|
||||
normally checked against (like ``libxml`` or ``libz`` etc) will match
|
||||
against the host platform, not the target.
|
||||
|
||||
So, if the build system is not aware that you want to cross-compile
|
||||
your code, it will get every dependency wrong, and your compilation
|
||||
will fail during build time, not configure time.
|
||||
|
||||
Also, finding the libraries for your target are not as easy
|
||||
as for your host machine. There aren't many cross-libraries available
|
||||
as packages to most OS's, so you'll have to either cross-compile them
|
||||
from source, or download the package for your target platform,
|
||||
extract the libraries and headers, put them in specific directories
|
||||
and add ``-I`` and ``-L`` pointing to them.
|
||||
|
||||
Also, some libraries have different dependencies on different targets,
|
||||
so configuration tools to find dependencies in the host can get the
|
||||
list wrong for the target platform. This means that the configuration
|
||||
of your build can get things wrong when setting their own library
|
||||
paths, and you'll have to augment it via additional flags (configure,
|
||||
Make, CMake, etc).
|
||||
|
||||
Multilibs
|
||||
---------
|
||||
|
||||
When you want to cross-compile to more than one configuration, for
|
||||
example hard-float-ARM and soft-float-ARM, you'll have to have multiple
|
||||
copies of your libraries and (possibly) headers.
|
||||
|
||||
Some Linux distributions have support for Multilib, which handle that
|
||||
for you in an easier way, but if you're not careful and, for instance,
|
||||
forget to specify ``-ccc-gcc-name armv7l-linux-gnueabihf-gcc`` (which
|
||||
uses hard-float), Clang will pick the ``armv7l-linux-gnueabi-ld``
|
||||
(which uses soft-float) and linker errors will happen.
|
||||
|
||||
The same is true if you're compiling for different ABIs, like ``gnueabi``
|
||||
and ``androideabi``, and might even link and run, but produce run-time
|
||||
errors, which are much harder to track down and fix.
|
@ -1,158 +0,0 @@
|
||||
=================
|
||||
DataFlowSanitizer
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
DataFlowSanitizerDesign
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
DataFlowSanitizer is a generalised dynamic data flow analysis.
|
||||
|
||||
Unlike other Sanitizer tools, this tool is not designed to detect a
|
||||
specific class of bugs on its own. Instead, it provides a generic
|
||||
dynamic data flow analysis framework to be used by clients to help
|
||||
detect application-specific issues within their own code.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
With no program changes, applying DataFlowSanitizer to a program
|
||||
will not alter its behavior. To use DataFlowSanitizer, the program
|
||||
uses API functions to apply tags to data to cause it to be tracked, and to
|
||||
check the tag of a specific data item. DataFlowSanitizer manages
|
||||
the propagation of tags through the program according to its data flow.
|
||||
|
||||
The APIs are defined in the header file ``sanitizer/dfsan_interface.h``.
|
||||
For further information about each function, please refer to the header
|
||||
file.
|
||||
|
||||
ABI List
|
||||
--------
|
||||
|
||||
DataFlowSanitizer uses a list of functions known as an ABI list to decide
|
||||
whether a call to a specific function should use the operating system's native
|
||||
ABI or whether it should use a variant of this ABI that also propagates labels
|
||||
through function parameters and return values. The ABI list file also controls
|
||||
how labels are propagated in the former case. DataFlowSanitizer comes with a
|
||||
default ABI list which is intended to eventually cover the glibc library on
|
||||
Linux but it may become necessary for users to extend the ABI list in cases
|
||||
where a particular library or function cannot be instrumented (e.g. because
|
||||
it is implemented in assembly or another language which DataFlowSanitizer does
|
||||
not support) or a function is called from a library or function which cannot
|
||||
be instrumented.
|
||||
|
||||
DataFlowSanitizer's ABI list file is a :doc:`SanitizerSpecialCaseList`.
|
||||
The pass treats every function in the ``uninstrumented`` category in the
|
||||
ABI list file as conforming to the native ABI. Unless the ABI list contains
|
||||
additional categories for those functions, a call to one of those functions
|
||||
will produce a warning message, as the labelling behavior of the function
|
||||
is unknown. The other supported categories are ``discard``, ``functional``
|
||||
and ``custom``.
|
||||
|
||||
* ``discard`` -- To the extent that this function writes to (user-accessible)
|
||||
memory, it also updates labels in shadow memory (this condition is trivially
|
||||
satisfied for functions which do not write to user-accessible memory). Its
|
||||
return value is unlabelled.
|
||||
* ``functional`` -- Like ``discard``, except that the label of its return value
|
||||
is the union of the label of its arguments.
|
||||
* ``custom`` -- Instead of calling the function, a custom wrapper ``__dfsw_F``
|
||||
is called, where ``F`` is the name of the function. This function may wrap
|
||||
the original function or provide its own implementation. This category is
|
||||
generally used for uninstrumentable functions which write to user-accessible
|
||||
memory or which have more complex label propagation behavior. The signature
|
||||
of ``__dfsw_F`` is based on that of ``F`` with each argument having a
|
||||
label of type ``dfsan_label`` appended to the argument list. If ``F``
|
||||
is of non-void return type a final argument of type ``dfsan_label *``
|
||||
is appended to which the custom function can store the label for the
|
||||
return value. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void f(int x);
|
||||
void __dfsw_f(int x, dfsan_label x_label);
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
void *__dfsw_memcpy(void *dest, const void *src, size_t n,
|
||||
dfsan_label dest_label, dfsan_label src_label,
|
||||
dfsan_label n_label, dfsan_label *ret_label);
|
||||
|
||||
If a function defined in the translation unit being compiled belongs to the
|
||||
``uninstrumented`` category, it will be compiled so as to conform to the
|
||||
native ABI. Its arguments will be assumed to be unlabelled, but it will
|
||||
propagate labels in shadow memory.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# main is called by the C runtime using the native ABI.
|
||||
fun:main=uninstrumented
|
||||
fun:main=discard
|
||||
|
||||
# malloc only writes to its internal data structures, not user-accessible memory.
|
||||
fun:malloc=uninstrumented
|
||||
fun:malloc=discard
|
||||
|
||||
# tolower is a pure function.
|
||||
fun:tolower=uninstrumented
|
||||
fun:tolower=functional
|
||||
|
||||
# memcpy needs to copy the shadow from the source to the destination region.
|
||||
# This is done in a custom function.
|
||||
fun:memcpy=uninstrumented
|
||||
fun:memcpy=custom
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The following program demonstrates label propagation by checking that
|
||||
the correct labels are propagated.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <sanitizer/dfsan_interface.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
int i = 1;
|
||||
dfsan_label i_label = dfsan_create_label("i", 0);
|
||||
dfsan_set_label(i_label, &i, sizeof(i));
|
||||
|
||||
int j = 2;
|
||||
dfsan_label j_label = dfsan_create_label("j", 0);
|
||||
dfsan_set_label(j_label, &j, sizeof(j));
|
||||
|
||||
int k = 3;
|
||||
dfsan_label k_label = dfsan_create_label("k", 0);
|
||||
dfsan_set_label(k_label, &k, sizeof(k));
|
||||
|
||||
dfsan_label ij_label = dfsan_get_label(i + j);
|
||||
assert(dfsan_has_label(ij_label, i_label));
|
||||
assert(dfsan_has_label(ij_label, j_label));
|
||||
assert(!dfsan_has_label(ij_label, k_label));
|
||||
|
||||
dfsan_label ijk_label = dfsan_get_label(i + j + k);
|
||||
assert(dfsan_has_label(ijk_label, i_label));
|
||||
assert(dfsan_has_label(ijk_label, j_label));
|
||||
assert(dfsan_has_label(ijk_label, k_label));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Current status
|
||||
==============
|
||||
|
||||
DataFlowSanitizer is a work in progress, currently under development for
|
||||
x86\_64 Linux.
|
||||
|
||||
Design
|
||||
======
|
||||
|
||||
Please refer to the :doc:`design document<DataFlowSanitizerDesign>`.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user