3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 09:05:31 +00:00

Implement a CMake build system.

This is a large rework of my first attempt at this (#459).

This implementation calls into the recently implemented python scripts
to generate the necessary generated ``.h`` and ``.cpp`` files but is
independent from Python building system otherwise.  Unlike the Python
build system, the generated files are emitted into the build tree to
avoid polluting the source tree. The build system is setup to refuse to
configure if it detects generated files in the source tree. If your
source tree is dirty you can run ``git clean -fx`` to clean your working
directory.

Currently the build succeeds on Linux using CMake 3.4.3 using
the "Unix Makefiles" generator with gcc or clang.

The following notable features are implemented:

* Building of the C and C++ examples and the ``test-z3`` executable.
  These are included from the ``all`` target so you have to tell the
  build system (e.g. make) to build them manually.

* Install (``make install``) and uninstall (``make uninstall``) of libz3
  and its header files. This supports ``DESTDIR`` out of the box because
  CMake supports it.

* An option (``BUILD_LIBZ3_SHARED``) to build libz3 as a static or dynamic library.

* Support for using/not using OpenMP (``USE_OPENMP``)

* Support for using/not using libgmp (``USE_LIB_GMP``)

* Setting the SOVERSION for libz3. I'm not sure if I'm setting the
* number correctly though. This is required by Linux distrubtions that
  wills ship libz3. This needs discussion.

The following notable features are currently not implemented
and are left for future work.

* Support for ARM.
* Support for the foci2 library.
* Support for creating/installing/uninstalling the dotnet, java, python and ml
  bindings.
* Full support for MSVC. Although I've tried to write the CMake code
  with MSVC in mind not all the correct flags are passed to it.
* Support for using the git hash.

This new build system has several advantages other the old build system.

* It is easier for outside contributors to contribute to Z3 when the
  build system is something more standard.
* Incremental builds work properly. With the old build system when
  new code is pulled down the old build directory would need to thrown
  out and a new fresh build had to be performed because the build system
  didn't know how to correctly rebuild the project (e.g. couldn't handle
  new sources being added/removed, compiler flags changing, generated
  files changing, etc...). This is a MASSIVE boost to productivity!
* We now have access rich array of features that CMake provides for
  building C/C++ projects. This means less time spent implementing
  custom build system logic in Python that is already supported by
  CMake.
* CMake supports many IDEs out of the box so it should be fairly
  straight forward to build Z3 with Visual Studio (once support for MSVC
  is added), Xcode, Eclipse CDT, CLion, ..etc.
This commit is contained in:
Dan Liew 2016-02-20 23:21:00 +00:00
parent db34baa979
commit 251527603d
86 changed files with 2259 additions and 0 deletions

View file

@ -0,0 +1,24 @@
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: "
"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
set(_full_file_path "$ENV{DESTDIR}${file}")
message(STATUS "Uninstalling ${_full_file_path}")
if(IS_SYMLINK "${_full_file_path}" OR EXISTS "${_full_file_path}")
# We could use ``file(REMOVE ...)`` here but then we wouldn't
# know if the removal failed.
execute_process(COMMAND
"@CMAKE_COMMAND@" "-E" "remove" "${_full_file_path}"
RESULT_VARIABLE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing \"${_full_file_path}\"")
endif()
else()
message(STATUS "File \"${_full_file_path}\" does not exist.")
endif()
endforeach()

View file

@ -0,0 +1,30 @@
# This file overrides the default compiler flags for CMake's built-in
# configurations (CMAKE_BUILD_TYPE). Most compiler flags should not be set here.
# The main purpose is to make sure ``-DNDEBUG`` is never set by default.
if (CMAKE_C_COMPILER_ID)
set(_lang C)
elseif(CMAKE_CXX_COMPILER_ID)
set(_lang CXX)
else()
message(FATAL_ERROR "Unknown language")
endif()
if (("${CMAKE_${_lang}_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_${_lang}_COMPILER_ID}" MATCHES "GNU"))
# Taken from Modules/Compiler/GNU.cmake but -DNDEBUG is removed
set(CMAKE_${_lang}_FLAGS_INIT "")
# FIXME: should we have -D_DEBUG here to match MSVC build?
set(CMAKE_${_lang}_FLAGS_DEBUG_INIT "-g -O0")
set(CMAKE_${_lang}_FLAGS_MINSIZEREL_INIT "-Os")
set(CMAKE_${_lang}_FLAGS_RELEASE_INIT "-O3")
set(CMAKE_${_lang}_FLAGS_RELWITHDEBINFO_INIT "-O2 -g")
elseif ("${CMAKE_${_lang}_COMPILER_ID}" MATCHES "MSVC")
# Not tested!
set(CMAKE_${_lang}_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_${_lang}_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1")
set(CMAKE_${_lang}_FLAGS_RELEASE_INIT "/MT /O2 /Ob2")
set(CMAKE_${_lang}_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1")
else()
message(FATAL_ERROR "Overrides not set for ${_lang} compiler \"${CMAKE_${_lang}_COMPILER_ID}\"")
endif()
unset(_lang)

View file

@ -0,0 +1,38 @@
set(GCC_AND_CLANG_WARNINGS
"-Wall"
)
set(GCC_ONLY_WARNINGS "")
set(CLANG_ONLY_WARNINGS "")
set(MSVC_WARNINGS "/W3")
set(WARNING_FLAGS_TO_CHECK "")
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS})
list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_ONLY_WARNINGS})
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS})
list(APPEND WARNING_FLAGS_TO_CHECK ${CLANG_ONLY_WARNINGS})
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")
list(APPEND WARNING_FLAGS_TO_CHECK ${MSVC_WARNINGS})
else()
message(AUTHOR_WARNING "Unknown compiler")
endif()
# Loop through flags and use the ones which the compiler supports
foreach (flag ${WARNING_FLAGS_TO_CHECK})
z3_add_cxx_flag("${flag}")
endforeach()
option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
if (WARNINGS_AS_ERRORS)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))
list(APPEND Z3_COMPONENT_CXX_FLAGS "-Werror")
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")
list(APPEND Z3_COMPONENT_CXX_FLAGS "/WX")
else()
message(AUTHOR_WARNING "Unknown compiler")
endif()
message(STATUS "Treating compiler warnings as errors")
else()
message(STATUS "Not treating compiler warnings as errors")
endif()

View file

@ -0,0 +1,64 @@
# Tries to find an install of the GNU multiple precision library
#
# Once done this will define
# GMP_FOUND - BOOL: System has the GMP library installed
# GMP_INCLUDE_DIRS - LIST:The GMP include directories
# GMP_C_LIBRARIES - LIST:The libraries needed to use GMP via it's C interface
# GMP_CXX_LIBRARIES - LIST:The libraries needed to use GMP via it's C++ interface
include(FindPackageHandleStandardArgs)
# Try to find libraries
find_library(GMP_C_LIBRARIES
NAMES gmp
DOC "GMP C libraries"
)
if (GMP_C_LIBRARIES)
message(STATUS "Found GMP C library: \"${GMP_C_LIBRARIES}\"")
else()
message(STATUS "Could not find GMP C library")
endif()
find_library(GMP_CXX_LIBRARIES
NAMES gmpxx
DOC "GMP C++ libraries"
)
if (GMP_CXX_LIBRARIES)
message(STATUS "Found GMP C++ library: \"${GMP_CXX_LIBRARIES}\"")
else()
message(STATUS "Could not find GMP C++ library")
endif()
# Try to find headers
find_path(GMP_C_INCLUDES
NAMES gmp.h
DOC "GMP C header"
)
if (GMP_C_INCLUDES)
message(STATUS "Found GMP C include path: \"${GMP_C_INCLUDES}\"")
else()
message(STATUS "Could not find GMP C include path")
endif()
find_path(GMP_CXX_INCLUDES
NAMES gmpxx.h
DOC "GMP C++ header"
)
if (GMP_CXX_INCLUDES)
message(STATUS "Found GMP C++ include path: \"${GMP_CXX_INCLUDES}\"")
else()
message(STATUS "Could not find GMP C++ include path")
endif()
if (GMP_C_LIBRARIES AND GMP_CXX_LIBRARIES AND GMP_C_INCLUDES AND GMP_CXX_INCLUDES)
set(GMP_INCLUDE_DIRS "${GMP_C_INCLUDES}" "${GMP_CXX_INCLUDES}")
list(REMOVE_DUPLICATES GMP_INCLUDE_DIRS)
message(STATUS "Found GMP")
else()
message(STATUS "Could not find GMP")
endif()
# TODO: We should check we can link some simple code against libgmp and libgmpxx
# Handle QUIET and REQUIRED and check the necessary variables were set and if so
# set ``GMP_FOUND``
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIRS GMP_C_LIBRARIES GMP_CXX_LIBRARIES)

View file

@ -0,0 +1,22 @@
###############################################################################
# Target detection
#
# We abuse the compiler preprocessor to work out what target the compiler is
# building for. The nice thing about this approach is that we'll detect the
# right target even if we are using a cross compiler.
###############################################################################
function(detect_target_architecture OUTPUT_VAR)
try_run(run_result
compile_result
"${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/target_arch_detect.cpp"
COMPILE_OUTPUT_VARIABLE compiler_output
)
if (compile_result)
message(FATAL_ERROR "Expected compile to fail")
endif()
string(REGEX MATCH "CMAKE_TARGET_ARCH_([a-zA-Z0-9_]+)" arch "${compiler_output}")
# Strip out prefix
string(REPLACE "CMAKE_TARGET_ARCH_" "" arch "${arch}")
set(${OUTPUT_VAR} "${arch}" PARENT_SCOPE)
endfunction()

View file

@ -0,0 +1,10 @@
// This is used by the CMake build to detect
// what architecture the compiler is targeting.
// TODO: Add more targets here
#if defined(__i386__) || defined(_M_IX86)
#error CMAKE_TARGET_ARCH_i686
#elif defined(__x86_64__) || defined(_M_X64)
#error CMAKE_TARGET_ARCH_x86_64
#else
#error CMAKE_TARGET_ARCH_unknown
#endif

View file

@ -0,0 +1,245 @@
include(CMakeParseArguments)
define_property(GLOBAL PROPERTY Z3_LIBZ3_COMPONENTS
BRIEF_DOCS "List of Z3 components to use in libz3"
FULL_DOCS "List of Z3 components to use in libz3")
function(z3_expand_dependencies output_var)
if (ARGC LESS 2)
message(FATAL_ERROR "Invalid number of arguments")
endif()
# Remaing args should be component names
set(_expanded_deps ${ARGN})
set(_old_number_of_deps 0)
list(LENGTH _expanded_deps _number_of_deps)
while (_number_of_deps GREATER _old_number_of_deps)
set(_old_number_of_deps "${_number_of_deps}")
# Loop over the known dependencies and retrieve their dependencies
set(_old_expanded_deps ${_expanded_deps})
foreach (dependency ${_old_expanded_deps})
get_property(_depdeps GLOBAL PROPERTY Z3_${dependency}_DEPS)
list(APPEND _expanded_deps ${_depdeps})
unset(_depdeps)
endforeach()
list(REMOVE_DUPLICATES _expanded_deps)
list(LENGTH _expanded_deps _number_of_deps)
endwhile()
set(${output_var} ${_expanded_deps} PARENT_SCOPE)
endfunction()
function(z3_add_component_dependencies_to_target target_name)
if (ARGC LESS 2)
message(FATAL_ERROR "Invalid number of arguments")
endif()
if (NOT (TARGET ${target_name}))
message(FATAL_ERROR "Target \"${target_name}\" does not exist")
endif()
# Remaing args should be component names
set(_expanded_deps ${ARGN})
foreach (dependency ${_expanded_deps})
# FIXME: Adding these include paths wouldn't be necessary if the sources
# used include paths rooted in the ``src`` directory.
get_property(_dep_include_dirs GLOBAL PROPERTY Z3_${dependency}_INCLUDES)
foreach (inc_dir ${_dep_include_dirs})
target_include_directories(${target_name} PRIVATE "${inc_dir}")
endforeach()
unset(_dep_include_dirs)
# Ensure this component's dependencies are built before this component.
# This important because we might need the generated header files in
# other components.
add_dependencies(${target_name} ${dependency})
endforeach()
endfunction()
macro(z3_add_component component_name)
CMAKE_PARSE_ARGUMENTS("Z3_MOD" "NOT_LIBZ3_COMPONENT" "" "SOURCES;COMPONENT_DEPENDENCIES;PYG_FILES" ${ARGN})
message(STATUS "Adding component ${component_name}")
# Note: We don't check the sources exist here because
# they might be generated files that don't exist yet.
set(_list_generated_headers "")
foreach (pyg_file ${Z3_MOD_PYG_FILES})
set(_full_pyg_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${pyg_file}")
if (NOT (EXISTS "${_full_pyg_file_path}"))
message(FATAL_ERROR "\"${_full_pyg_file_path}\" does not exist")
endif()
string(REPLACE ".pyg" ".hpp" _output_file "${pyg_file}")
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_output_file}")
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${_output_file}\" "
${z3_polluted_tree_msg}
)
endif()
set(_full_output_file_path "${CMAKE_CURRENT_BINARY_DIR}/${_output_file}")
message(STATUS "Adding rule to generate \"${_output_file}\"")
add_custom_command(OUTPUT "${_output_file}"
COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/pyg2hpp.py" "${_full_pyg_file_path}" "${CMAKE_CURRENT_BINARY_DIR}"
MAIN_DEPENDENCY "${_full_pyg_file_path}"
DEPENDS "${CMAKE_SOURCE_DIR}/scripts/pyg2hpp.py" "${CMAKE_SOURCE_DIR}/scripts/mk_util.py"
COMMENT "Generating \"${_full_output_file_path}\" from \"${pyg_file}\""
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
USES_TERMINAL
VERBATIM
)
list(APPEND _list_generated_headers "${_full_output_file_path}")
endforeach()
unset(_full_include_dir_path)
unset(_full_output_file_path)
unset(_output_file)
# Using "object" libraries here means we have a convenient
# name to refer to a component in CMake but we don't actually
# create a static/library from them. This allows us to easily
# build a static or dynamic library from the object libraries
# on all platforms. Is this added flexibility worth the linking
# overhead it adds?
add_library(${component_name} OBJECT ${Z3_MOD_SOURCES} ${_list_generated_headers})
unset(_list_generated_headers)
# Add definitions
foreach (define ${Z3_COMPONENT_CXX_DEFINES})
target_compile_definitions(${component_name} PRIVATE ${define})
endforeach()
# Add compiler flags
foreach (flag ${Z3_COMPONENT_CXX_FLAGS})
target_compile_options(${component_name} PRIVATE ${flag})
endforeach()
# It's unfortunate that we have to manage the include directories and dependencies ourselves.
#
# If we weren't building "object" libraries we could use
# ```
# target_include_directories(${component_name} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
# target_link_libraries(${component_name} INTERFACE ${Z3_MOD_COMPONENT_DEPENDENCIES})
# ```
# but we can't do that with "object" libraries.
# Record this component's include directories
set_property(GLOBAL PROPERTY Z3_${component_name}_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}")
set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_INCLUDES "${CMAKE_CURRENT_BINARY_DIR}")
set_property(GLOBAL PROPERTY Z3_${component_name}_DEPS "")
# Record this component's dependencies
foreach (dependency ${Z3_MOD_COMPONENT_DEPENDENCIES})
if (NOT (TARGET ${dependency}))
message(FATAL_ERROR "Component \"${component_name}\" depends on a non existant component \"${dependency}\"")
endif()
set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_DEPS "${dependency}")
endforeach()
# Determine all the components that this component depends on
set(_expanded_deps "")
if (DEFINED Z3_MOD_COMPONENT_DEPENDENCIES)
z3_expand_dependencies(_expanded_deps ${Z3_MOD_COMPONENT_DEPENDENCIES})
z3_add_component_dependencies_to_target(${component_name} ${_expanded_deps})
endif()
#message(STATUS "Component \"${component_name}\" has the following dependencies ${_expanded_deps}")
# For any generated header files for this component
target_include_directories(${component_name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
# So that any generated header files can refer to source files in the component's
# source tree
target_include_directories(${component_name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
# Add any extra include directories
foreach (extra_include ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS})
target_include_directories(${component_name} PRIVATE "${extra_include}")
endforeach()
if (NOT Z3_MOD_NOT_LIBZ3_COMPONENT)
# Add this component to the global list of Z3 components for libz3
set_property(GLOBAL APPEND PROPERTY Z3_LIBZ3_COMPONENTS ${component_name})
endif()
endmacro()
macro(z3_add_install_tactic_rule)
# Arguments should be component names to use
if (ARGC LESS 1)
message(FATAL_ERROR "There should be at least one component")
endif()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install_tactic.cpp")
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/install_tactic.cpp\""
${z3_polluted_tree_msg}
)
endif()
z3_expand_dependencies(_expanded_components ${ARGN})
# Get paths to search
set(_search_paths "")
foreach (dependency ${_expanded_components})
get_property(_dep_include_dirs GLOBAL PROPERTY Z3_${dependency}_INCLUDES)
list(APPEND _search_paths ${_dep_include_dirs})
endforeach()
list(APPEND _search_paths "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "install_tactic.cpp"
COMMAND "${PYTHON_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/mk_install_tactic_cpp.py"
"${CMAKE_CURRENT_BINARY_DIR}"
${_search_paths}
DEPENDS "${CMAKE_SOURCE_DIR}/scripts/mk_install_tactic_cpp.py"
"${CMAKE_SOURCE_DIR}/scripts/mk_util.py"
${_expanded_components}
COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp\""
USES_TERMINAL
VERBATIM
)
endmacro()
macro(z3_add_memory_initializer_rule)
# Arguments should be component names to use
if (ARGC LESS 1)
message(FATAL_ERROR "There should be at least one component")
endif()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mem_initializer.cpp")
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/mem_initializer.cpp\""
${z3_polluted_tree_msg}
)
endif()
z3_expand_dependencies(_expanded_components ${ARGN})
# Get paths to search
set(_search_paths "")
foreach (dependency ${_expanded_components})
get_property(_dep_include_dirs GLOBAL PROPERTY Z3_${dependency}_INCLUDES)
list(APPEND _search_paths ${_dep_include_dirs})
endforeach()
list(APPEND _search_paths "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "mem_initializer.cpp"
COMMAND "${PYTHON_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/mk_mem_initializer_cpp.py"
"${CMAKE_CURRENT_BINARY_DIR}"
${_search_paths}
DEPENDS "${CMAKE_SOURCE_DIR}/scripts/mk_mem_initializer_cpp.py"
"${CMAKE_SOURCE_DIR}/scripts/mk_util.py"
${_expanded_components}
COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp\""
USES_TERMINAL
VERBATIM
)
endmacro()
macro(z3_add_gparams_register_modules_rule)
# Arguments should be component names to use
if (ARGC LESS 1)
message(FATAL_ERROR "There should be at least one component")
endif()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gparams_register_modules.cpp")
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/gparams_register_modules.cpp\""
${z3_polluted_tree_msg}
)
endif()
z3_expand_dependencies(_expanded_components ${ARGN})
# Get paths to search
set(_search_paths "")
foreach (dependency ${_expanded_components})
get_property(_dep_include_dirs GLOBAL PROPERTY Z3_${dependency}_INCLUDES)
list(APPEND _search_paths ${_dep_include_dirs})
endforeach()
list(APPEND _search_paths "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "gparams_register_modules.cpp"
COMMAND "${PYTHON_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/mk_gparams_register_modules_cpp.py"
"${CMAKE_CURRENT_BINARY_DIR}"
${_search_paths}
DEPENDS "${CMAKE_SOURCE_DIR}/scripts/mk_gparams_register_modules_cpp.py"
"${CMAKE_SOURCE_DIR}/scripts/mk_util.py"
${_expanded_components}
COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp\""
USES_TERMINAL
VERBATIM
)
endmacro()

View file

@ -0,0 +1,22 @@
include(CheckCXXCompilerFlag)
include(CMakeParseArguments)
function(z3_add_cxx_flag flag)
CMAKE_PARSE_ARGUMENTS(z3_add_flag "REQUIRED" "" "" ${ARGN})
string(REPLACE "-" "_" SANITIZED_FLAG_NAME "${flag}")
string(REPLACE "/" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}")
string(REPLACE "=" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}")
string(REPLACE " " "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}")
unset(HAS_${SANITIZED_FLAG_NAME})
CHECK_CXX_COMPILER_FLAG("${flag}" HAS_${SANITIZED_FLAG_NAME})
if (z3_add_flag_REQUIRED AND NOT HAS_${SANITIZED_FLAG_NAME})
message(FATAL_ERROR "The flag \"${flag}\" is required but your C++ compiler doesn't support it")
endif()
if (HAS_${SANITIZED_FLAG_NAME})
message(STATUS "C++ compiler supports ${flag}")
list(APPEND Z3_COMPONENT_CXX_FLAGS "${flag}")
set(Z3_COMPONENT_CXX_FLAGS "${Z3_COMPONENT_CXX_FLAGS}" PARENT_SCOPE)
else()
message(STATUS "C++ compiler does not support ${flag}")
endif()
endfunction()