#
# Copyright (C) 2023 Isuru Fernando
# Copyright (C) 2023 Albin Ahlbäck
#
# This file is part of FLINT.
#
# FLINT is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License (LGPL) as published
# by the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version. See .
#
cmake_minimum_required(VERSION 3.22)
if(NOT WIN32)
message(FATAL_ERROR "Detected system is not Windows. Please use the Autotools configuration along with the Makefile instead as it is more up-to-date. Read INSTALL.md.")
endif()
include(CheckCCompilerFlag)
include(CheckCSourceRuns)
include(CheckIPOSupported)
include(CMakePackageConfigHelpers)
# Source of truth for project version
file(STRINGS VERSION FLINT_VERSION_FULL)
string(REGEX MATCH "([0-9]+\.[0-9]+\.[0-9]+)" _ ${FLINT_VERSION_FULL})
set(FLINT_VERSION ${CMAKE_MATCH_1})
project(flint
VERSION ${FLINT_VERSION}
DESCRIPTION "Fast Library for Number Theory"
HOMEPAGE_URL https://flintlib.org/
LANGUAGES C CXX)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" CONFIGURE_CONTENTS)
foreach(version in MAJOR MINOR PATCH)
# Set soname version for the library
string(REGEX MATCH "FLINT_${version}_SO=([0-9]*)" _ ${CONFIGURE_CONTENTS})
set(FLINT_${version}_SO ${CMAKE_MATCH_1})
# Set flint version for the header
set(FLINT_${version} ${PROJECT_VERSION_${version}})
endforeach()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
# Build options
option(BUILD_SHARED_LIBS "Build shared libs" on)
option(WITH_NTL "Build tests for NTL interface or not" off)
# MSVC has no equivalent to "-march="
if(NOT MSVC)
set(ENABLE_ARCH "native" CACHE STRING "Enable and push -march=ARCH option to C compiler")
if(NOT "${ENABLE_ARCH}" STREQUAL "NO")
check_c_compiler_flag("-march=${ENABLE_ARCH}" HAS_FLAG_ARCH)
endif()
endif()
if(MSVC)
set(avx2_flag "/arch:AVX2")
else()
set(avx2_flag "-mavx2")
endif()
check_c_compiler_flag("${avx2_flag}" HAS_FLAG_AVX2)
option(ENABLE_AVX2 "Enable AVX2 instructions" ${HAS_FLAG_AVX2})
option(ENABLE_AVX512 "Enable AVX512 instructions" OFF)
# Check if AVX2 is available
if(ENABLE_AVX2)
if(HAS_FLAG_AVX2)
check_c_compiler_flag("-mfma" HAS_FLAG_MFMA)
else()
message(FATAL_ERROR "${CMAKE_C_COMPILER}} does not support the flag ${avx2_flag} needed for AVX2 instructions")
endif()
endif()
if(ENABLE_AVX512)
if(MSVC)
set(avx512_flag "/arch:AVX512")
else()
set(avx512_flag "-mavx512f")
endif()
check_c_compiler_flag("${avx512_flag}" HAS_FLAG_AVX512)
if(NOT HAS_FLAG_AVX512)
message(FATAL_ERROR "${CMAKE_C_COMPILER}} does not support the flag ${avx512_flag} needed for AVX512 instructions")
endif()
endif()
# Check if strongly ordered memory
set(STRONGLY_ORDERED_CPUS x86_64 x86 i386 i586 AMD64)
if(CMAKE_SYSTEM_PROCESSOR IN_LIST STRONGLY_ORDERED_CPUS)
message(STATUS "Checking if system is strongly ordered - yes")
set(FLINT_KNOW_STRONG_ORDER ON)
else()
message(STATUS "Checking if system is strongly ordered - unsure")
set(FLINT_KNOW_STRONG_ORDER OFF)
endif()
# Find dependencies
set(GMP_MIN_VERSION 6.2.1)
find_package(gmp ${GMP_MIN_VERSION} REQUIRED)
set(MPFR_MIN_VERSION 4.1.0)
find_package(mpfr ${MPFR_MIN_VERSION} REQUIRED)
if (WITH_NTL)
find_package(NTL REQUIRED)
endif()
find_package(Python REQUIRED)
find_package(CBLAS)
set(FLINT_USES_BLAS ${CBLAS_FOUND})
if(CMAKE_BUILD_TYPE STREQUAL Debug)
set(FLINT_WANT_ASSERT ON)
endif()
# pthread configuration
if(MSVC)
# Prefer vcpkg's pthreads
find_package(PThreads4W)
if(PThreads4W_FOUND)
set(PThreads_LIBRARIES PThreads4W::PThreads4W)
set(PThreads_INCLUDE_DIRS ${PThreads4W_INCLUDE_DIRS})
else()
find_package(PThreads REQUIRED)
endif()
set(FLINT_USES_PTHREAD ON CACHE BOOL "Use POSIX Threads.")
else()
option(CMAKE_THREAD_PREFER_PTHREAD "Prefer pthreads" yes)
option(THREADS_PREFER_PTHREAD_FLAG "Prefer -pthread flag" yes)
find_package(Threads REQUIRED)
set(PThreads_LIBRARIES Threads::Threads)
set(FLINT_USES_PTHREAD ON CACHE BOOL "Use POSIX Threads.")
endif()
# Check if fft_small module is available
message(STATUS "Checking whether fft_small module is available")
set(CMAKE_REQUIRED_LIBRARIES gmp::gmp)
if(HAS_FLAG_ARCH)
set(CMAKE_REQUIRED_FLAGS "-march=${ENABLE_ARCH}")
endif()
if(ENABLE_AVX2)
set(CMAKE_REQUIRED_FLAGS "${avx2_flag}")
endif()
check_c_source_compiles([[
#include
#if GMP_LIMB_BITS != 64
# error
error
#endif
#include
#if !(defined(__GNUC__) && defined(__ARM_NEON))
# if !(defined(_MSC_VER) && defined(_M_ARM64))
# error
error
# endif
#endif
void main(){};]] FLINT_FFT_SMALL_ARM)
check_c_source_compiles([[
#include
#if GMP_LIMB_BITS != 64
# error
error
#endif
#if defined(__GNUC__)
# include
#elif defined(_MSC_VER)
# include
#else
# error
error
#endif
#if !defined(__AVX2__)
# error
error
#endif
void main(){};]] FLINT_FFT_SMALL_X86)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_FLAGS)
if(FLINT_FFT_SMALL_ARM OR FLINT_FFT_SMALL_X86)
message(STATUS "Checking whether fft_small module is available - yes")
set(FFT_SMALL fft_small)
set(FLINT_HAVE_FFT_SMALL ON)
else()
message(STATUS "Checking whether fft_small module is available - no")
set(FFT_SMALL)
endif()
# Find sources
set(_BUILD_DIRS
generic_files
thread_pool thread_support
ulong_extras
long_extras
perm
double_extras d_vec d_mat
nfloat
mpn_extras
nmod nmod_vec nmod_mat nmod_poly
mpn_mod
fmpz fmpz_vec fmpz_mat fmpz_poly
fmpz_mod fmpz_mod_vec fmpz_mod_mat fmpz_mod_poly
fmpq fmpq_vec fmpq_mat fmpq_poly
fq fq_vec fq_mat fq_poly
fq_nmod fq_nmod_vec fq_nmod_mat fq_nmod_poly
fq_zech fq_zech_vec fq_zech_mat fq_zech_poly
fq_default fq_default_mat fq_default_poly
fq_embed
fq_nmod_embed
fq_zech_embed
padic padic_mat padic_poly
qadic
nmod_poly_factor fmpz_factor
fmpz_poly_factor fmpz_mod_poly_factor
fq_poly_factor fq_nmod_poly_factor
fq_zech_poly_factor fq_default_poly_factor
nmod_poly_mat fmpz_poly_mat
mpoly nmod_mpoly fmpz_mpoly fmpz_mod_mpoly
fmpq_mpoly fq_nmod_mpoly fq_zech_mpoly fmpz_mod_mpoly_q
nmod_mpoly_factor fmpz_mpoly_factor
fmpz_mod_mpoly_factor fmpq_mpoly_factor
fq_nmod_mpoly_factor fq_zech_mpoly_factor
fft ${FFT_SMALL} fmpz_poly_q fmpz_lll
n_poly arith qsieve aprcl
nf nf_elem qfb
double_interval dlog
fmpz_extras fmpzi
bool_mat partitions
mag
arf acf arb acb
arb_mat arb_poly arb_calc arb_hypgeom
acb_mat acb_poly acb_calc acb_hypgeom
arb_fmpz_poly arb_fpwrap
acb_dft acb_elliptic acb_modular acb_dirichlet
acb_theta dirichlet bernoulli hypgeom
gr gr_generic gr_vec gr_mat
gr_poly gr_mpoly gr_series gr_special
calcium
fmpz_mpoly_q
fexpr fexpr_builtin
qqbar
ca ca_ext ca_field ca_vec
ca_poly ca_mat
)
string(REGEX REPLACE "([A-Za-z0-9_-]+;|[A-Za-z0-9_-]+$)" "src/\\1" BUILD_DIRS "${_BUILD_DIRS}")
# NOTE: Template directories are not supposed to be used.
set(_HEADERS
NTL-interface.h flint.h longlong.h flint-config.h gmpcompat.h flint-mparam.h
profiler.h templates.h
)
string(REGEX REPLACE "([A-Za-z0-9_-]+\.h;|[A-Za-z0-9_-]+\.h$)" "src/\\1" HEADERS "${_HEADERS}")
# Setup for flint-config.h
check_c_compiler_flag("-mpopcnt" HAS_FLAG_MPOPCNT)
check_c_compiler_flag("-funroll-loops" HAS_FLAG_UNROLL_LOOPS)
# fenv configuration
check_c_source_compiles([[#include
#ifndef FE_DOWNWARD
# error FE_DOWNWARD not available
#endif
void main(){};]] FLINT_USES_FENV)
# cpu_set_t configuration
set(CMAKE_REQUIRED_FLAGS "${PThreads_LIBRARIES}")
check_c_source_compiles([[#define _GNU_SOURCE
#include
#include
int main() { cpu_set_t s; CPU_ZERO(&s);
pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), 0);
return 0; }]] FLINT_USES_CPUSET)
unset(CMAKE_REQUIRED_FLAGS)
# Thread-local storage configuration
set(FLINT_USES_TLS ON CACHE BOOL "Use thread local storage.")
# Memory manager configuration
set(MEMORY_MANAGER "single" CACHE STRING "The FLINT memory manager.")
set_property(CACHE MEMORY_MANAGER PROPERTY STRINGS single reentrant gc)
message(STATUS "Using FLINT memory manager: ${MEMORY_MANAGER}")
if(MEMORY_MANAGER STREQUAL "reentrant")
set(FLINT_REENTRANT ON)
else()
set(FLINT_REENTRANT OFF)
endif()
# gmpcompat.h configuration
set(CMAKE_REQUIRED_LIBRARIES gmp::gmp)
check_c_source_compiles([[#include
#ifndef _LONG_LONG_LIMB
# error mp_limb_t != unsigned long long limb
#endif
void main(){};]] FLINT_LONG_LONG)
check_c_source_compiles([[#include
#if GMP_LIMB_BITS == 32
# error
#endif
void main(){};]] FLINT64)
if(FLINT64)
set(FLINT_BITS 64)
else()
set(FLINT_BITS 32)
endif()
if(MSVC AND NOT BUILD_SHARED_LIBS)
set(FLINT_STATIC_BUILD 1)
endif()
# Populate headers
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_config.h.in
${CMAKE_CURRENT_SOURCE_DIR}/src/flint-config.h
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/src/flint.h.in
${CMAKE_CURRENT_SOURCE_DIR}/src/flint.h
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/src/fmpz/link/fmpz_${MEMORY_MANAGER}.c
${CMAKE_CURRENT_SOURCE_DIR}/src/fmpz/fmpz.c
COPYONLY
)
if(FLINT_LONG_LONG)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/src/gmpcompat-longlong.h.in
${CMAKE_CURRENT_SOURCE_DIR}/src/gmpcompat.h
COPYONLY
)
else()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/src/gmpcompat.h.in
${CMAKE_CURRENT_SOURCE_DIR}/src/gmpcompat.h
COPYONLY
)
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/src/mpn_extras/generic/flint-mparam.h
${CMAKE_CURRENT_SOURCE_DIR}/src/flint-mparam.h
COPYONLY
)
endif()
foreach (build_dir IN LISTS BUILD_DIRS)
file(GLOB TEMP RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${build_dir}/*.c")
list(APPEND SOURCES ${TEMP})
file(GLOB TEMP RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${build_dir}/*.h")
list(APPEND HEADERS ${TEMP})
endforeach ()
set(TEMP ${HEADERS})
set(HEADERS )
foreach(header IN LISTS TEMP)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${header})
list(APPEND HEADERS ${header})
else()
list(APPEND HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${header})
endif()
endforeach()
file(GLOB TEMP "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
list(APPEND HEADERS ${TEMP})
add_library(flint ${SOURCES})
target_link_libraries(flint PUBLIC
gmp::gmp
mpfr::mpfr
${PThreads_LIBRARIES}
)
if(MSVC AND NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC")
target_compile_options(flint PUBLIC "/experimental:c11atomics")
endif()
if(FLINT_USES_BLAS)
target_link_libraries(flint PUBLIC CBLAS::CBLAS)
endif()
# Include directories
target_include_directories(flint PUBLIC
"$"
"$"
${PThreads_INCLUDE_DIRS}
)
if(BUILD_SHARED_LIBS AND MSVC)
# Export all functions automatically (except global data)
set_target_properties(flint PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Export flint's global data that are marked manually
target_compile_definitions(flint PRIVATE "FLINT_BUILD_DLL")
endif()
if (HAS_FLAG_MPOPCNT)
target_compile_options(flint PUBLIC "-mpopcnt")
endif()
if (HAS_FLAG_UNROLL_LOOPS)
target_compile_options(flint PUBLIC "-funroll-loops")
endif()
if(NOT MSVC AND NOT "${ENABLE_ARCH}" STREQUAL "NO")
target_compile_options(flint PUBLIC "-march=${ENABLE_ARCH}")
endif()
if(ENABLE_AVX2)
target_compile_options(flint PUBLIC "${avx2_flag}")
if(HAS_FLAG_MFMA)
target_compile_options(flint PUBLIC "-mfma")
endif()
endif()
if(ENABLE_AVX512)
target_compile_options(flint PUBLIC "${avx512_flag}")
endif()
# Versioning
set_target_properties(flint PROPERTIES
VERSION ${FLINT_MAJOR_SO}.${FLINT_MINOR_SO}.${FLINT_PATCH_SO}
SOVERSION ${FLINT_MAJOR_SO}
)
# Following versioning parts are optional
# Match versioning scheme in configure based build system.
if (APPLE)
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
message(WARNING "To match the versioning scheme of configure based build system, switch to cmake 3.17.0")
else ()
set_target_properties(flint PROPERTIES
MACHO_COMPATIBILITY_VERSION ${FLINT_MAJOR_SO}.${FLINT_MINOR_SO}
MACHO_CURRENT_VERSION ${FLINT_MAJOR_SO}.${FLINT_MINOR_SO}.${FLINT_PATCH_SO}
)
endif()
elseif (WIN32)
set_target_properties(flint PROPERTIES RUNTIME_OUTPUT_NAME "flint-${FLINT_MAJOR_SO}")
endif()
set_property(TARGET flint PROPERTY C_STANDARD 11)
if(NOT DEFINED IPO_SUPPORTED)
message(STATUS "Checking for IPO")
check_ipo_supported(RESULT ipo_supported LANGUAGES C)
if(ipo_supported)
if (MSVC)
message(STATUS "Checking for IPO - found, but disabled for MSVC")
set(ipo_supported FALSE)
else()
message(STATUS "Checking for IPO - found")
endif()
else()
message(STATUS "Checking for IPO - not found")
endif()
set(IPO_SUPPORTED ${ipo_supported} CACHE INTERNAL "Introprocedural Optimization" FORCE)
endif()
# allow overriding IPO by setting -DCMAKE_INTERPROCEDURAL_OPTIMIZATION
if (IPO_SUPPORTED AND "${CMAKE_INTERPROCEDURAL_OPTIMIZATION}" STREQUAL "")
set_target_properties(flint PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
if(NOT MSVC)
target_link_libraries(flint PUBLIC m)
endif()
include(GNUInstallDirs)
install(TARGETS flint
EXPORT flintTargets
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
install(FILES ${HEADERS} DESTINATION include/flint)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/flintConfigVersion.cmake"
VERSION "${PROJECT_VERSION}"
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/flintConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/flintConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flint
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/flintConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/flintConfigVersion.cmake"
"CMake/FindCBLAS.cmake"
"CMake/Findgmp.cmake"
"CMake/Findmpfr.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flint
)
install(EXPORT flintTargets
FILE flintTargets.cmake
NAMESPACE flint::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flint
)
set_target_properties(flint
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
)
# Install PkgConfig file
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\$\{prefix\}")
set(includedir "\$\{prefix\}/include")
set(libdir "\$\{prefix\}/${CMAKE_INSTALL_LIBDIR}")
set(PACKAGE_NAME ${PROJECT_NAME})
set(PACKAGE_VERSION ${PROJECT_VERSION})
configure_file(flint.pc.in flint.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flint.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
if(BUILD_TESTING)
set(FLINT_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) # To get src/test/main
enable_testing()
foreach(dir IN LISTS BUILD_DIRS FLINT_SRC)
file(GLOB test_file "${dir}/test/main.c")
if(test_file)
file(RELATIVE_PATH test_name ${CMAKE_CURRENT_SOURCE_DIR} ${test_file})
string(REPLACE "/" "-" test_name ${test_name})
get_filename_component(test_name ${test_name} NAME_WE)
add_executable(${test_name} ${test_file})
target_link_libraries(${test_name} flint)
add_test(NAME ${test_name} COMMAND $)
set_target_properties(${test_name}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
endif()
endforeach ()
if(WITH_NTL)
file(GLOB test_file "src/interfaces/test/t-NTL-interface.cpp")
file(RELATIVE_PATH test_name ${CMAKE_CURRENT_SOURCE_DIR} ${test_file})
string(REPLACE "/" "-" test_name ${test_name})
get_filename_component(test_name ${test_name} NAME_WE)
add_executable(${test_name} ${test_file})
target_link_libraries(${test_name} flint ${NTL_LIBRARY})
target_include_directories(${test_name} PUBLIC ${NTL_INCLUDE_DIR})
add_test(NAME ${test_name} COMMAND $)
set_target_properties(${test_name}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
endif()
endif()
if(BUILD_DOCS)
find_package(Sphinx REQUIRED)
file(GLOB DOC_SOURCES doc/source/*.rst)
add_custom_target(html
COMMAND ${SPHINX_EXECUTABLE} -b html "${CMAKE_CURRENT_SOURCE_DIR}/doc/source" "${CMAKE_CURRENT_BINARY_DIR}/html"
SOURCES ${DOC_SOURCES})
add_custom_target(latex
COMMAND ${SPHINX_EXECUTABLE} -b latex "${CMAKE_CURRENT_SOURCE_DIR}/doc/source" "${CMAKE_CURRENT_BINARY_DIR}/latex"
SOURCES ${DOC_SOURCES})
add_custom_target(pdf DEPENDS latex COMMAND make WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/latex")
endif()