mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 09:55:19 +00:00
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.
246 lines
10 KiB
CMake
246 lines
10 KiB
CMake
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()
|