You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
11 KiB
294 lines
11 KiB
cmake_minimum_required(VERSION 3.5)
|
|
cmake_policy(SET CMP0043 OLD) # testing the old behavior
|
|
|
|
project(Preprocess)
|
|
|
|
# This test is meant both as a test and as a reference for supported
|
|
# syntax on native tool command lines.
|
|
|
|
# Determine the build tool being used. Not all characters can be
|
|
# escaped for all build tools. This test checks all characters known
|
|
# to work with each tool and documents those known to not work.
|
|
if("${CMAKE_GENERATOR}" MATCHES "Xcode")
|
|
set(PP_XCODE 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles")
|
|
set(PP_UMAKE 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "NMake Makefiles")
|
|
set(PP_NMAKE 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "MinGW Makefiles")
|
|
set(PP_MINGW 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "Borland Makefiles")
|
|
set(PP_BORLAND 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "Watcom WMake")
|
|
set(PP_WATCOM 1)
|
|
endif()
|
|
if("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
|
|
set(PP_VS 1)
|
|
endif()
|
|
if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND
|
|
"x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
|
|
set(CLANG_MSVC_WINDOWS 1)
|
|
endif()
|
|
if(CLANG_MSVC_WINDOWS AND
|
|
"x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
|
|
set(CLANG_GNULIKE_WINDOWS 1)
|
|
endif()
|
|
|
|
# Some tests below check the PP_* variables set above. They are meant
|
|
# to test the case that the build tool is at fault. Other tests below
|
|
# check the compiler that will be used when the compiler is at fault
|
|
# (does not work even from a command shell).
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Construct a C-string literal to test passing through a definition on
|
|
# the command line. We configure the value into a header so it can be
|
|
# checked in the executable at runtime. The semicolon is handled
|
|
# specially because it needs to be escaped in the COMPILE_DEFINITIONS
|
|
# property value to avoid separating definitions but the string value
|
|
# must not have it escaped inside the configured header.
|
|
set(STRING_EXTRA "")
|
|
|
|
if(NOT BORLAND)
|
|
# Borland: ;
|
|
# The Borland compiler will simply not accept a non-escaped semicolon
|
|
# on the command line. If it is escaped \; then the escape character
|
|
# shows up in the preprocessing output too.
|
|
set(SEMICOLON "\;")
|
|
endif()
|
|
|
|
string(APPEND STRING_EXTRA " ")
|
|
|
|
if(NOT PP_BORLAND AND NOT PP_WATCOM AND NOT CLANG_GNULIKE_WINDOWS)
|
|
# Borland, WMake: multiple spaces
|
|
# The make tool seems to remove extra whitespace from inside
|
|
# quoted strings when passing to the compiler. It does not have
|
|
# trouble passing to other tools, and the compiler may be directly
|
|
# invoked from the command line.
|
|
string(APPEND STRING_EXTRA " ")
|
|
endif()
|
|
|
|
if(NOT PP_VS)
|
|
# VS: ,
|
|
# Visual Studio will not accept a comma in the value of a definition.
|
|
# The comma-separated list of PreprocessorDefinitions in the project
|
|
# file seems to be parsed before the content of entries is examined.
|
|
string(APPEND STRING_EXTRA ",")
|
|
endif()
|
|
|
|
if(NOT PP_MINGW AND NOT CLANG_GNULIKE_WINDOWS)
|
|
# MinGW: &
|
|
# When inside -D"FOO=\"a & b\"" MinGW make wants -D"FOO=\"a "&" b\""
|
|
# but it does not like quoted ampersand elsewhere.
|
|
string(APPEND STRING_EXTRA "&")
|
|
endif()
|
|
|
|
if(NOT PP_MINGW AND NOT CLANG_GNULIKE_WINDOWS)
|
|
# MinGW: |
|
|
# When inside -D"FOO=\"a | b\"" MinGW make wants -D"FOO=\"a "|" b\""
|
|
# but it does not like quoted pipe elsewhere.
|
|
string(APPEND STRING_EXTRA "|")
|
|
endif()
|
|
|
|
if(NOT PP_BORLAND AND NOT PP_MINGW AND NOT PP_NMAKE)
|
|
# Borland, NMake, MinGW: ^
|
|
# When inside -D"FOO=\"a ^ b\"" the make tools want -D"FOO=\"a "^" b\""
|
|
# but do not like quoted carrot elsewhere. In NMake the non-quoted
|
|
# syntax works when the flags are not in a make variable.
|
|
string(APPEND STRING_EXTRA "^")
|
|
endif()
|
|
|
|
if(NOT PP_BORLAND AND NOT PP_MINGW AND NOT PP_NMAKE)
|
|
# Borland, MinGW: < >
|
|
# Angle-brackets have funny behavior that is hard to escape.
|
|
string(APPEND STRING_EXTRA "<>")
|
|
endif()
|
|
|
|
set(EXPR_OP1 "/")
|
|
if((NOT MSVC OR PP_NMAKE) AND
|
|
NOT CMAKE_C_COMPILER_ID STREQUAL "Intel" AND
|
|
NOT CLANG_MSVC_WINDOWS)
|
|
# MSVC cl, Intel icl: %
|
|
# When the cl compiler is invoked from the command line then % must
|
|
# be written %% (to distinguish from %ENV% syntax). However cl does
|
|
# not seem to accept the syntax when it is invoked from inside a
|
|
# make tool (nmake, mingw32-make, etc.). Instead the argument must
|
|
# be placed inside a response file. Then cl accepts it because it
|
|
# parses the response file as it would the normal windows command
|
|
# line. Currently only NMake supports running cl with a response
|
|
# file. Supporting other make tools would require CMake to generate
|
|
# response files explicitly for each object file.
|
|
#
|
|
# When the icl compiler is invoked from the command line then % must
|
|
# be written just '%'. However nmake requires '%%' except when using
|
|
# response files. Currently we have no way to affect escaping based
|
|
# on whether flags go in a response file, so we just have to skip it.
|
|
string(APPEND STRING_EXTRA "%")
|
|
set(EXPR_OP1 "%")
|
|
endif()
|
|
|
|
# XL: )(
|
|
# The XL compiler cannot pass unbalanced parens correctly to a tool
|
|
# it launches internally.
|
|
if(CMAKE_C_COMPILER_ID STREQUAL "XL")
|
|
string(APPEND STRING_EXTRA "()")
|
|
else()
|
|
string(APPEND STRING_EXTRA ")(")
|
|
endif()
|
|
|
|
# General: \"
|
|
# Make tools do not reliably accept \\\" syntax:
|
|
# - MinGW and MSYS make tools crash with \\\"
|
|
# - Borland make actually wants a mis-matched quote \\"
|
|
# or $(BACKSLASH)\" where BACKSLASH is a variable set to \\
|
|
# - VS IDE gets confused about the bounds of the definition value \\\"
|
|
# - NMake is okay with just \\\"
|
|
# - The XL compiler does not re-escape \\\" when launching an
|
|
# internal tool to do preprocessing .
|
|
# - The IntelLLVM C and C++ compiler drivers do not re-escape the \\\" when
|
|
# launching the underlying compiler. FIXME: this bug is expected to be fixed
|
|
# in a future release.
|
|
if((PP_NMAKE OR PP_UMAKE) AND
|
|
NOT CMAKE_C_COMPILER_ID STREQUAL "XL" AND
|
|
NOT CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM" AND
|
|
NOT CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
string(APPEND STRING_EXTRA "\\\"")
|
|
endif()
|
|
|
|
# General: #
|
|
# MSVC will not accept a # in the value of a string definition on the
|
|
# command line. The character seems to be simply replaced by an
|
|
# equals =. According to "cl -help" definitions may be specified by
|
|
# -DMACRO#VALUE as well as -DMACRO=VALUE. It must be implemented by a
|
|
# simple search-and-replace.
|
|
#
|
|
# The Borland compiler will parse both # and \# as just # but the make
|
|
# tool seems to want \# sometimes and not others.
|
|
#
|
|
# Unix make does not like # in variable settings without extra
|
|
# escaping. This could probably be fixed but since MSVC does not
|
|
# support it and it is not an operator it is not worthwhile.
|
|
|
|
# Compose the final test string.
|
|
set(STRING_VALUE "hello`~!@$*_+-=}{][:'.?/${STRING_EXTRA}world")
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Function-style macro command-line support:
|
|
# - Borland does not support
|
|
# - MSVC does not support
|
|
# - Watcom does not support
|
|
# - GCC supports
|
|
|
|
# Too few platforms support this to bother implementing.
|
|
# People can just configure headers with the macros.
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Construct a sample expression to pass as a macro definition.
|
|
|
|
set(EXPR "x*y+!(x==(y+1*2))*f(x${EXPR_OP1}2)")
|
|
|
|
if(NOT WATCOM)
|
|
# Watcom does not support - or / because it parses them as options.
|
|
string(APPEND EXPR " + y/x-x")
|
|
endif()
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
# Inform the test if the debug configuration is getting built.
|
|
string(APPEND CMAKE_C_FLAGS_DEBUG " -DPREPROCESS_DEBUG")
|
|
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -DPREPROCESS_DEBUG")
|
|
string(APPEND CMAKE_C_FLAGS_RELEASE " -DPREPROCESS_NDEBUG")
|
|
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -DPREPROCESS_NDEBUG")
|
|
string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " -DPREPROCESS_NDEBUG")
|
|
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -DPREPROCESS_NDEBUG")
|
|
string(APPEND CMAKE_C_FLAGS_MINSIZEREL " -DPREPROCESS_NDEBUG")
|
|
string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " -DPREPROCESS_NDEBUG")
|
|
|
|
# Inform the test if it built from Xcode.
|
|
if(PP_XCODE)
|
|
set(PREPROCESS_XCODE 1)
|
|
endif()
|
|
|
|
# Test old-style definitions.
|
|
add_definitions(-DOLD_DEF -DOLD_EXPR=2)
|
|
|
|
# Make sure old-style definitions are converted to directory property.
|
|
set(OLD_DEFS_EXPECTED "OLD_DEF;OLD_EXPR=2")
|
|
get_property(OLD_DEFS DIRECTORY PROPERTY COMPILE_DEFINITIONS)
|
|
if(NOT "${OLD_DEFS}" STREQUAL "${OLD_DEFS_EXPECTED}")
|
|
message(SEND_ERROR "add_definitions not converted to directory property!")
|
|
endif()
|
|
|
|
add_executable(Preprocess preprocess.c preprocess.cxx)
|
|
|
|
set(FILE_PATH "${Preprocess_SOURCE_DIR}/file_def.h")
|
|
set(TARGET_PATH "${Preprocess_SOURCE_DIR}/target_def.h")
|
|
|
|
# Set some definition properties.
|
|
foreach(c "" "_DEBUG" "_RELEASE" "_RELWITHDEBINFO" "_MINSIZEREL")
|
|
set(FLAVOR "${c}")
|
|
# Treat RelWithDebInfo and MinSizeRel as Release to avoid having
|
|
# an exponentional matrix of inclusions and exclusions of defines
|
|
if("${c}" STREQUAL "_RELWITHDEBINFO" OR "${c}" STREQUAL "_MINSIZEREL")
|
|
set(FLAVOR "_RELEASE")
|
|
endif()
|
|
set_property(
|
|
DIRECTORY .
|
|
APPEND PROPERTY COMPILE_DEFINITIONS${c} "DIRECTORY_DEF${FLAVOR}"
|
|
)
|
|
set_property(
|
|
TARGET Preprocess
|
|
PROPERTY COMPILE_DEFINITIONS${c} "TARGET_DEF${FLAVOR}"
|
|
)
|
|
set_property(
|
|
SOURCE preprocess.c preprocess.cxx
|
|
PROPERTY COMPILE_DEFINITIONS${c} "FILE_DEF${FLAVOR}"
|
|
)
|
|
endforeach()
|
|
|
|
# Add definitions with values.
|
|
set(DEF_TARGET_PATH "TARGET_PATH=\"${TARGET_PATH}\"")
|
|
set(DEF_FILE_PATH "FILE_PATH=\"${FILE_PATH}\"")
|
|
set_property(
|
|
TARGET Preprocess
|
|
APPEND PROPERTY COMPILE_DEFINITIONS
|
|
"TARGET_STRING=\"${STRING_VALUE}${SEMICOLON}\""
|
|
"TARGET_EXPR=${EXPR}"
|
|
${DEF_TARGET_PATH}
|
|
)
|
|
set_property(
|
|
SOURCE preprocess.c preprocess.cxx
|
|
APPEND PROPERTY COMPILE_DEFINITIONS
|
|
"FILE_STRING=\"${STRING_VALUE}${SEMICOLON}\""
|
|
"FILE_EXPR=${EXPR}"
|
|
${DEF_FILE_PATH}
|
|
)
|
|
|
|
# Try reading and writing the property value to ensure the string is
|
|
# preserved.
|
|
get_property(defs1 TARGET Preprocess PROPERTY COMPILE_DEFINITIONS)
|
|
set_property(TARGET Preprocess PROPERTY COMPILE_DEFINITIONS "${defs1}")
|
|
get_property(defs2 TARGET Preprocess PROPERTY COMPILE_DEFINITIONS)
|
|
if(NOT "x${defs1}" STREQUAL "x${defs2}")
|
|
message(FATAL_ERROR "get/set/get COMPILE_DEFINITIONS round trip failed. "
|
|
"First get:\n"
|
|
" ${defs1}\n"
|
|
"Second get:\n"
|
|
" ${defs2}")
|
|
endif()
|
|
|
|
# Helper target for running test manually in build tree.
|
|
add_custom_target(drive COMMAND Preprocess)
|
|
|
|
# Configure the header file with the desired string value.
|
|
if(SEMICOLON)
|
|
string(APPEND STRING_VALUE ";")
|
|
endif()
|
|
configure_file(${Preprocess_SOURCE_DIR}/preprocess.h.in
|
|
${Preprocess_BINARY_DIR}/preprocess.h)
|
|
include_directories(${Preprocess_BINARY_DIR})
|