cmake_minimum_required(VERSION 3.4...3.27) project(primecount CXX) set(PRIMECOUNT_VERSION_MAJOR 7) set(PRIMECOUNT_VERSION_MINOR 20) set(PRIMECOUNT_VERSION "${PRIMECOUNT_VERSION_MAJOR}.${PRIMECOUNT_VERSION_MINOR}") # Build options ###################################################### option(BUILD_PRIMECOUNT "Build the primecount binary" ON) option(BUILD_LIBPRIMESIEVE "Build libprimesieve" ON) option(BUILD_SHARED_LIBS "Build the shared libprimecount" OFF) option(BUILD_STATIC_LIBS "Build the static libprimecount" ON) option(BUILD_MANPAGE "Regenerate man page using a2x program" OFF) option(BUILD_TESTS "Build the test programs" OFF) option(WITH_OPENMP "Enable OpenMP multi-threading" ON) option(WITH_MULTIARCH "Enable runtime dispatching to fastest supported CPU instruction set" ON) option(WITH_DIV32 "Use 32-bit division instead of 64-bit division whenever possible" OFF) option(WITH_MSVC_CRT_STATIC "Link primecount.lib with /MT instead of the default /MD" OFF) option(WITH_FLOAT128 "Use __float128 (requires libquadmath), increases precision of Li(x) & RiemannR" OFF) option(WITH_JEMALLOC "Use jemalloc allocator" OFF) # Enable/Disable libdivide ########################################### # Historically, integer division has been one of the slowest # instructions on most CPU architectures. Hence by default we # enable libdivide which replaces expensive integer division # instructions by a sequence of shift, add and multiply # instructions which is often much faster. set(DEFAULT_LIBDIVIDE ON) # Apple Silicon CPUs first released in 2020 have very fast integer # division instructions. For Apple Silicon CPUs on Apple OSes # we disable libdivide to get the best performance. if(APPLE) include("${PROJECT_SOURCE_DIR}/cmake/Apple_ARM64.cmake") if(Apple_ARM64) set(DEFAULT_LIBDIVIDE OFF) endif() endif() option(WITH_LIBDIVIDE "Use libdivide.h" ${DEFAULT_LIBDIVIDE}) # Option sanity checks ############################################### # We don't yet support building libprimecount as a shared DLL # library on Windows using the MSVC compiler. The MSVC compiler # by default does not allow accessing global variables that # are inside a DLL, unless all those variables are annotated # using __declspec(dllexport) and __declspec(dllimport). if(WIN32 AND NOT MINGW) set(BUILD_SHARED_LIBS OFF) endif() if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON!") endif() # Set default build type to Release ################################## if(NOT CMAKE_VERSION VERSION_LESS 3.9) get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) elseif(CMAKE_CONFIGURATION_TYPES) set(isMultiConfig TRUE) endif() if(NOT isMultiConfig AND NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND PRIMECOUNT_COMPILE_DEFINITIONS "ENABLE_ASSERT") endif() # primecount binary source files ##################################### set(BIN_SRC src/app/CmdOptions.cpp src/app/main.cpp src/app/help.cpp src/app/test.cpp) # primecount library source files #################################### set(LIB_SRC src/api.cpp src/api_c.cpp src/BitSieve240.cpp src/FactorTable.cpp src/RiemannR.cpp src/P2.cpp src/P3.cpp src/PhiTiny.cpp src/PiTable.cpp src/S1.cpp src/Sieve.cpp src/LoadBalancerP2.cpp src/LoadBalancerS2.cpp src/LogarithmicIntegral.cpp src/StatusS2.cpp src/generate_primes.cpp src/nth_prime.cpp src/phi.cpp src/phi_vector.cpp src/pi_legendre.cpp src/pi_lehmer.cpp src/pi_meissel.cpp src/pi_primesieve.cpp src/print.cpp src/util.cpp src/lmo/pi_lmo1.cpp src/lmo/pi_lmo2.cpp src/lmo/pi_lmo3.cpp src/lmo/pi_lmo4.cpp src/lmo/pi_lmo5.cpp src/lmo/pi_lmo_parallel.cpp src/deleglise-rivat/S2_hard.cpp src/deleglise-rivat/S2_trivial.cpp src/deleglise-rivat/pi_deleglise_rivat.cpp src/gourdon/pi_gourdon.cpp src/gourdon/Phi0.cpp src/gourdon/B.cpp src/gourdon/D.cpp src/gourdon/LoadBalancerAC.cpp src/gourdon/SegmentedPiTable.cpp src/gourdon/Sigma.cpp) # Use libdivide.h (fast integer divison) ############################# if(WITH_LIBDIVIDE) set(LIB_SRC ${LIB_SRC} src/deleglise-rivat/S2_easy_libdivide.cpp) set(LIB_SRC ${LIB_SRC} src/gourdon/AC_libdivide.cpp) else() set(LIB_SRC ${LIB_SRC} src/deleglise-rivat/S2_easy.cpp) set(LIB_SRC ${LIB_SRC} src/gourdon/AC.cpp) endif() # Check if compiler supports CPU multiarch ########################### if(WITH_MULTIARCH) include("${PROJECT_SOURCE_DIR}/cmake/multiarch_x86_popcnt.cmake") include("${PROJECT_SOURCE_DIR}/cmake/multiarch_avx512_vpopcnt.cmake") if(multiarch_x86_popcnt OR multiarch_avx512_vpopcnt) set(LIB_SRC ${LIB_SRC} src/arch/x86/cpuid.cpp) if (multiarch_avx512_vpopcnt) set(LIB_SRC ${LIB_SRC} src/deleglise-rivat/S2_hard_multiarch_avx512.cpp) set(LIB_SRC ${LIB_SRC} src/gourdon/D_multiarch_avx512.cpp) endif() else() include("${PROJECT_SOURCE_DIR}/cmake/multiarch_arm_sve.cmake") if(multiarch_arm_sve) set(LIB_SRC ${LIB_SRC} src/arch/arm/sve.cpp) set(LIB_SRC ${LIB_SRC} src/deleglise-rivat/S2_hard_multiarch_arm_sve.cpp) set(LIB_SRC ${LIB_SRC} src/gourdon/D_multiarch_arm_sve.cpp) endif() endif() endif() # Enable __float128 support (requires libquadmath) ################### if(WITH_FLOAT128) list(APPEND PRIMECOUNT_LINK_LIBRARIES "quadmath") list(APPEND PRIMECOUNT_COMPILE_DEFINITIONS "HAVE_FLOAT128") endif() # Use 32-bit integer division ######################################## # Check at runtime if the dividend and divisor are < 2^32 and # if so use 32-bit integer division instead of 64-bit integer # division. On most CPUs before 2020 this significantly # improves performance. if(WITH_DIV32) list(APPEND PRIMECOUNT_COMPILE_DEFINITIONS "ENABLE_DIV32") endif() # Use -Wno-uninitialized with GCC compiler ########################### # GCC's -Wuninitialized enabled with -Wall -pedantic causes # false postive warnings in libprimecount: # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107287 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(WNO_UNINITIALIZED "-Wno-uninitialized") endif() # Check if compiler supports C++11 or later ########################## include("${PROJECT_SOURCE_DIR}/cmake/compiler_supports_cpp11.cmake") # Check if int128_t is supported ##################################### include("${PROJECT_SOURCE_DIR}/cmake/int128_t.cmake") # Check for OpenMP ################################################### if(WITH_OPENMP) include("${PROJECT_SOURCE_DIR}/cmake/OpenMP.cmake") endif() # Required includes ################################################## include(GNUInstallDirs) include(CMakePackageConfigHelpers) # libprimesieve ###################################################### # By default the libprimesieve dependency is built from source # (BUILD_LIBPRIMESIEVE=ON). However when packaging primecount # for e.g. a Linux distro this is not a good idea. For this use # case it is better to install the libprimesieve package # (e.g. libprimesieve-dev) and link against that version. if(BUILD_LIBPRIMESIEVE) set(COPY_BUILD_EXAMPLES "${BUILD_EXAMPLES}") set(COPY_BUILD_MANPAGE "${BUILD_MANPAGE}") set(COPY_BUILD_TESTS "${BUILD_TESTS}") set(BUILD_EXAMPLES OFF CACHE BOOL "Build example programs" FORCE) set(BUILD_MANPAGE OFF CACHE BOOL "Build primesieve manpage" FORCE) set(BUILD_TESTS OFF CACHE BOOL "Build primesieve tests" FORCE) option(BUILD_PRIMESIEVE "Build primesieve binary" OFF) add_subdirectory(lib/primesieve) set(BUILD_EXAMPLES "${COPY_BUILD_EXAMPLES}" CACHE BOOL "Build example programs" FORCE) set(BUILD_MANPAGE "${COPY_BUILD_MANPAGE}" CACHE BOOL "Regenerate man page using a2x" FORCE) set(BUILD_TESTS "${COPY_BUILD_TESTS}" CACHE BOOL "Build test programs" FORCE) else() find_package(primesieve REQUIRED) if(primesieve_VERSION VERSION_LESS 11.0) message(FATAL_ERROR "Found libprimesive-${primesieve_VERSION}, but primecount requires libprimesive >= 11.0") endif() endif() # libprimecount ###################################################### if(BUILD_SHARED_LIBS) add_library(libprimecount SHARED ${LIB_SRC}) set_target_properties(libprimecount PROPERTIES OUTPUT_NAME primecount) set_target_properties(libprimecount PROPERTIES SOVERSION ${PRIMECOUNT_VERSION_MAJOR}) set_target_properties(libprimecount PROPERTIES VERSION ${PRIMECOUNT_VERSION}) target_compile_options(libprimecount PRIVATE "${WNO_UNINITIALIZED}") target_compile_definitions(libprimecount PRIVATE ${PRIMECOUNT_COMPILE_DEFINITIONS}) target_link_libraries(libprimecount PRIVATE primesieve::primesieve ${PRIMECOUNT_LINK_LIBRARIES}) target_compile_features(libprimecount PRIVATE cxx_constexpr cxx_lambdas cxx_range_for) target_include_directories(libprimecount PUBLIC $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) install(TARGETS libprimecount EXPORT primecountShared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() # libprimecount-static ############################################### if(BUILD_STATIC_LIBS) add_library(libprimecount-static STATIC ${LIB_SRC}) set_target_properties(libprimecount-static PROPERTIES OUTPUT_NAME primecount) target_compile_options(libprimecount-static PRIVATE "${WNO_UNINITIALIZED}") target_compile_definitions(libprimecount-static PRIVATE ${PRIMECOUNT_COMPILE_DEFINITIONS}) target_link_libraries(libprimecount-static PRIVATE primesieve::primesieve ${PRIMECOUNT_LINK_LIBRARIES}) if(WITH_MSVC_CRT_STATIC) set_target_properties(libprimecount-static PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded") endif() target_compile_features(libprimecount-static PRIVATE cxx_constexpr cxx_lambdas cxx_range_for) target_include_directories(libprimecount-static PUBLIC $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) install(TARGETS libprimecount-static EXPORT primecountStatic RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() # Shared vs. static linking ########################################## # On Unix-like OSes we use shared linking against libprimecount by # default mainly because this is required by Linux distributions. # On Windows we use static linking by default because the library's # path is not encoded into the binary. This means that the binary # will only work if the DLL is in the same directory or a directory # that is in the PATH environment variable. if(WIN32 AND TARGET libprimecount-static) set(STATICALLY_LINK_LIBPRIMECOUNT ON) elseif(NOT TARGET libprimecount) set(STATICALLY_LINK_LIBPRIMECOUNT ON) endif() if(STATICALLY_LINK_LIBPRIMECOUNT) add_library(primecount::primecount ALIAS libprimecount-static) else() add_library(primecount::primecount ALIAS libprimecount) endif() # primecount binary ################################################## if(BUILD_PRIMECOUNT) add_executable(primecount ${BIN_SRC}) target_link_libraries(primecount PRIVATE primecount::primecount primesieve::primesieve) target_include_directories(primecount PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_compile_definitions(primecount PRIVATE ${PRIMECOUNT_COMPILE_DEFINITIONS}) target_compile_features(primecount PRIVATE cxx_auto_type) install(TARGETS primecount DESTINATION ${CMAKE_INSTALL_BINDIR}) # Copy primesieve.dll into the same directory as primecount.exe. # On Windows the DLLs must be in the same directory as the # binaries that depend on them. if (WIN32 AND NOT STATICALLY_LINK_LIBPRIMECOUNT) add_custom_command(TARGET primecount POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) endif() if(WITH_MSVC_CRT_STATIC) set_target_properties(primecount PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded") endif() endif() # Use jemalloc allocator ############################################# if(WITH_JEMALLOC) find_package(PkgConfig REQUIRED) pkg_check_modules(JEMALLOC jemalloc) pkg_search_module(JEMALLOC REQUIRED jemalloc) if(BUILD_PRIMECOUNT) target_link_libraries(primecount PRIVATE ${JEMALLOC_LIBRARIES}) target_include_directories(primecount PRIVATE ${JEMALLOC_INCLUDE_DIRS}) endif() if(BUILD_SHARED_LIBS) target_link_libraries(libprimecount PRIVATE ${JEMALLOC_LIBRARIES}) target_include_directories(libprimecount PRIVATE ${JEMALLOC_INCLUDE_DIRS}) endif() if(BUILD_STATIC_LIBS) target_link_libraries(libprimecount-static PRIVATE ${JEMALLOC_LIBRARIES}) target_include_directories(libprimecount-static PRIVATE ${JEMALLOC_INCLUDE_DIRS}) endif() endif() # Install header ##################################################### install(FILES include/primecount.h include/primecount.hpp COMPONENT libprimecount-headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # CMake find_package(primecount) support ############################# write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/primecountConfigVersion.cmake" VERSION ${PRIMECOUNT_VERSION} COMPATIBILITY SameMajorVersion) configure_package_config_file( "${PROJECT_SOURCE_DIR}/cmake/primecountConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/primecountConfig.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/primecount") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/primecountConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/primecountConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/primecount") if(TARGET libprimecount) install(EXPORT primecountShared NAMESPACE primecount:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/primecount") endif() if(TARGET libprimecount-static) install(EXPORT primecountStatic NAMESPACE primecount:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/primecount") endif() # Regenerate man page ################################################ if(BUILD_MANPAGE) find_program(A2X_EXECUTABLE a2x) if(NOT A2X_EXECUTABLE) message(FATAL_ERROR "Missing a2x program (required for man page generation)!") else() message(STATUS "Found a2x: ${A2X_EXECUTABLE}") add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${A2X_EXECUTABLE} ARGS --format=manpage -D "${PROJECT_SOURCE_DIR}/doc" "${PROJECT_SOURCE_DIR}/doc/primecount.txt" VERBATIM) endif() endif() # Install man page ################################################### if(BUILD_PRIMECOUNT) install(FILES ${PROJECT_SOURCE_DIR}/doc/primecount.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif() # Install primecount.pc (pkgconf) #################################### configure_file(primecount.pc.in primecount.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/primecount.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # Testing ############################################################ if(BUILD_TESTS) enable_testing() add_subdirectory(test) endif()