# Copyright (c)      2020 Mikael Simberg
# Copyright (c) 2007-2020 Hartmut Kaiser
# Copyright (c) 2011-2014 Thomas Heller
# Copyright (c) 2007-2008 Chirag Dekate
# Copyright (c)      2011 Bryce Lelbach
# Copyright (c)      2011 Vinay C Amatya
# Copyright (c)      2013 Jeroen Habraken
# Copyright (c) 2014-2016 Andreas Schaefer
# Copyright (c) 2017      Abhimanyu Rawat
# Copyright (c) 2017      Google
# Copyright (c) 2017      Taeguk Kwon
#
# SPDX-License-Identifier: BSL-1.0
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

cmake_minimum_required(VERSION 3.22 FATAL_ERROR)

# Overrides must go before the project() statement, otherwise they are ignored.

# ##################################################################################################
# C++ overrides
# ##################################################################################################
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pika_cxx_overrides.cmake")

# ##################################################################################################
# Build type (needs to be handled before project command below)
# ##################################################################################################
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE
      "Release"
      CACHE STRING "Configuration type (one of Debug, RelWithDebInfo, Release, MinSizeRel)" FORCE
  )
endif()

# ##################################################################################################
# project metadata
# ##################################################################################################
project(pika CXX)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

# ##################################################################################################
# Build all of pika
# ##################################################################################################
set(PIKA_VERSION_MAJOR 0)
set(PIKA_VERSION_MINOR 30)
set(PIKA_VERSION_PATCH 1)
set(PIKA_VERSION_DATE 20241119)
set(PIKA_VERSION_TAG "")

set(PIKA_VERSION "${PIKA_VERSION_MAJOR}.${PIKA_VERSION_MINOR}.${PIKA_VERSION_PATCH}")
set(PIKA_LIBRARY_VERSION "${PIKA_VERSION}")
set(PIKA_SOVERSION ${PIKA_VERSION_MAJOR})
set(PIKA_PACKAGE_NAME pika)
# To keep track of the pika root when other subprojects are declared
set(PIKA_SOURCE_DIR "${PROJECT_SOURCE_DIR}")
set(PIKA_BINARY_DIR "${PROJECT_BINARY_DIR}")

# ##################################################################################################
# CMake configuration
# ##################################################################################################
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")

include(GNUInstallDirs)
include(pika_utils)

# explicitly set certain policies
pika_set_cmake_policy(CMP0144 NEW)

# We save the passed compiler flag to a special variable. This is needed for our build system unit
# tests. Some flags might influence the created symbols (_GLIBCXX_DEBUG i look at you)
set(CMAKE_CXX_FLAGS_SAFE ${CMAKE_CXX_FLAGS})
include(pika_check_cxx_standard)

include(CheckCXXCompilerFlag)
include(CMakeDependentOption)

# include additional macro definitions
include(pika_add_config_test)
include(pika_add_definitions)
include(pika_create_symbolic_link)

pika_force_out_of_tree_build(
  "This project requires an out-of-source-tree build. See README.rst. Clean your CMake cache and CMakeFiles if this message persists."
)

if(NOT PIKA_CMAKE_LOGLEVEL)
  set(PIKA_CMAKE_LOGLEVEL "WARN")
endif()

# print initial diagnostics
pika_info("CMake version: ${CMAKE_VERSION}")
pika_info("pika version: ${PIKA_VERSION}")

# ##################################################################################################
# reset cached variables that need to be re-filled
unset(PIKA_EXPORT_TARGETS CACHE)
unset(PIKA_EXPORT_INTERNAL_TARGETS CACHE)
unset(PIKA_LIBS CACHE)
unset(PIKA_ENABLED_MODULES CACHE)

# ##################################################################################################
# Set up dummy compiler flags targets
# ##################################################################################################
include(pika_compiler_flags_targets)

# ##################################################################################################
# Use shared libraries by default
pika_option(
  BUILD_SHARED_LIBS BOOL "Build using shared libraries (default: ON)" ON CATEGORY "Build Targets"
)

# ##################################################################################################
# Setup platform for which pika should be compiled.
#
include(pika_set_platform)
if("${PIKA_PLATFORM_UC}" STREQUAL "ANDROID")
  unset(PIKA_LIBRARY_VERSION)
  unset(PIKA_SOVERSION)
endif()

if(MSVC)
  pika_option(
    PIKA_WITH_VS_STARTUP_PROJECT STRING
    "Define the startup project for the pika solution (default: ALL_BUILD)." "ALL_BUILD" ADVANCED
  )
  if(PIKA_WITH_VS_STARTUP_PROJECT)
    set(VS_STARTUP_PROJECT ${PIKA_WITH_VS_STARTUP_PROJECT})
  endif()
endif()

# ##################################################################################################

# ##################################################################################################
# Set our build options cache variables which are customizable by users
#

pika_option(
  PIKA_WITH_DEPRECATION_WARNINGS BOOL "Enable warnings for deprecated facilities. (default: ON)" ON
  ADVANCED
)

if(PIKA_WITH_DEPRECATION_WARNINGS)
  # enable deprecation warnings globally
  pika_add_config_cond_define(PIKA_HAVE_DEPRECATION_WARNINGS 1)
endif()

# Generic build options
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
  set(PIKA_WITH_STACKOVERFLOW_DETECTION_DEFAULT OFF)
  string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
  if("${CMAKE_BUILD_TYPE_UC}" STREQUAL "DEBUG")
    set(PIKA_WITH_STACKOVERFLOW_DETECTION_DEFAULT ON)
  endif()
  pika_option(
    PIKA_WITH_STACKOVERFLOW_DETECTION BOOL
    "Enable stackoverflow detection for pika threads/coroutines. (default: OFF, debug: ON)"
    ${PIKA_WITH_STACKOVERFLOW_DETECTION_DEFAULT} ADVANCED
  )
  if(PIKA_WITH_STACKOVERFLOW_DETECTION)
    pika_add_config_define(PIKA_HAVE_STACKOVERFLOW_DETECTION)
  endif()
endif()

set(DEFAULT_MALLOC "mimalloc")
pika_option(
  PIKA_WITH_MALLOC
  STRING
  "Define which allocator should be linked in. Options are: system, tcmalloc, jemalloc, mimalloc, tbbmalloc, and custom (default: ${DEFAULT_MALLOC})"
  ${DEFAULT_MALLOC}
  STRINGS "system;tcmalloc;jemalloc;mimalloc;tbbmalloc;custom"
)

# On some systems jemalloc requires an explicit prefix for the API functions (i.e. 'malloc' is
# called 'je_malloc', etc.)
if(${PIKA_WITH_MALLOC} STREQUAL "jemalloc")
  if(MSVC)
    set(PIKA_WITH_JEMALLOC_PREFIX_DEFAULT "je_")
  else()
    set(PIKA_WITH_JEMALLOC_PREFIX_DEFAULT "<none>")
  endif()
  pika_option(
    PIKA_WITH_JEMALLOC_PREFIX STRING "Optional naming prefix for jemalloc API functions"
    ${PIKA_WITH_JEMALLOC_PREFIX_DEFAULT} ADVANCED
  )
endif()

# Compiler related build options
pika_option(PIKA_WITH_COMPILER_WARNINGS BOOL "Enable compiler warnings (default: ON)" ON ADVANCED)

pika_option(
  PIKA_WITH_COMPILER_WARNINGS_AS_ERRORS BOOL "Turn compiler warnings into errors (default: OFF)"
  OFF ADVANCED
)

pika_option(
  PIKA_WITH_EXECUTABLE_PREFIX STRING "Executable prefix (default: none)." ""
  CATEGORY "Build Targets"
  ADVANCED
)

pika_option(
  PIKA_WITH_BINARY_DIR
  PATH
  "Root directory for libraries and executables for the build. This does not affect the installed location of libraries and executables."
  "${PROJECT_BINARY_DIR}"
  CATEGORY "Build Targets"
  ADVANCED
)

if(WIN32)
  set(PIKA_WITH_PSEUDO_DEPENDENCIES
      OFF
      CACHE INTERNAL "" FORCE
  )
else()
  set(PIKA_WITH_PSEUDO_DEPENDENCIES
      ON
      CACHE INTERNAL "" FORCE
  )
endif()

pika_option(
  PIKA_WITH_UNITY_BUILD BOOL "Enable unity build for certain build targets (default: ON)" ON
  ADVANCED
)
if(PIKA_WITH_UNITY_BUILD)
  set(PIKA_WITH_UNITY_BUILD_OPTION UNITY_BUILD)
endif()

pika_option(
  PIKA_WITH_PRECOMPILED_HEADERS BOOL
  "Enable precompiled headers for certain build targets (experimental) (default: OFF)" OFF ADVANCED
)
if(PIKA_WITH_PRECOMPILED_HEADERS)
  set(PIKA_WITH_PRECOMPILED_HEADERS_INTERNAL ON)
  # Only create the targets here. They will be set up later once all modules are known.
  add_library(pika_precompiled_headers OBJECT libs/src/dummy.cpp)
  add_executable(pika_exe_precompiled_headers libs/src/dummy_main.cpp)

  set_target_properties(pika_precompiled_headers PROPERTIES FOLDER "Core")
  set_target_properties(pika_exe_precompiled_headers PROPERTIES FOLDER "Core")
endif()

# ##################################################################################################
pika_option(PIKA_WITH_EXAMPLES BOOL "Build examples (default: OFF)" OFF CATEGORY "Build Targets")
pika_option(PIKA_WITH_TESTS BOOL "Build tests (default: OFF)" OFF CATEGORY "Build Targets")
pika_option(
  PIKA_WITH_TESTS_BENCHMARKS BOOL "Build benchmark tests (default: ON)" ON DEPENDS "PIKA_WITH_TESTS"
  CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_TESTS_REGRESSIONS BOOL "Build regression tests (default: ON)" ON DEPENDS
  "PIKA_WITH_TESTS" CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_TESTS_UNIT BOOL "Build unit tests (default: ON)" ON DEPENDS "PIKA_WITH_TESTS"
  CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_TESTS_HEADERS
  BOOL
  "Build header tests (default: ON)"
  ON
  ADVANCED
  DEPENDS
  "PIKA_WITH_TESTS"
  CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_TESTS_EXTERNAL_BUILD
  BOOL
  "Build external CMake build tests (default: ON)"
  ON
  ADVANCED
  DEPENDS
  "PIKA_WITH_TESTS"
  CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_TESTS_EXAMPLES
  BOOL
  "Add examples as tests (default: ON)"
  ON
  DEPENDS
  "PIKA_WITH_TESTS;PIKA_WITH_EXAMPLES"
  ADVANCED
  CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_COMPILE_ONLY_TESTS BOOL
  "Create build system support for compile time only tests (default: ON)" ON DEPENDS
  "PIKA_WITH_TESTS" CATEGORY "Build Targets"
)
pika_option(
  PIKA_WITH_FAIL_COMPILE_TESTS BOOL
  "Create build system support for fail compile tests (default: ON)" ON DEPENDS "PIKA_WITH_TESTS"
  CATEGORY "Build Targets"
)

set(PIKA_FULL_RPATH_DEFAULT ON)
if(APPLE OR WIN32)
  set(PIKA_FULL_RPATH_DEFAULT OFF)
endif()
pika_option(
  PIKA_WITH_FULL_RPATH BOOL
  "Build and link libraries and executables with full RPATHs (default: ${PIKA_FULL_RPATH_DEFAULT})"
  ${PIKA_FULL_RPATH_DEFAULT} ADVANCED
)

# ##################################################################################################
# CUDA/HIP configuration
# ##################################################################################################
set(CUDA_OPTION_STRING "Enable support for CUDA (default: OFF)")
set(HIP_OPTION_STRING "Enable support for HIP (default: OFF)")
pika_option(PIKA_WITH_CUDA BOOL "${CUDA_OPTION_STRING}" OFF ADVANCED)

# No need for the user to specify the option explicitly
pika_option(PIKA_WITH_HIP BOOL "${HIP_OPTION_STRING}" OFF ADVANCED)
if("${CMAKE_CXX_COMPILER}" MATCHES "hipcc$")
  set(PIKA_WITH_HIP
      ON
      CACHE BOOL "${HIP_OPTION_STRING}" FORCE
  )
endif()

if(PIKA_WITH_CUDA AND PIKA_WITH_HIP)
  pika_error(
    "PIKA_WITH_CUDA=ON and PIKA_WITH_HIP=ON. Only one of them can be on at the same time.\
 Note: PIKA_WITH_HIP is automatically enabled when compiling with hipcc."
  )
endif()

if(PIKA_WITH_CUDA OR PIKA_WITH_HIP)
  pika_add_config_define(PIKA_HAVE_GPU_SUPPORT)
  set(PIKA_WITH_GPU_SUPPORT ON)
  set(PIKA_WITH_GPU_SUPPORT
      ON
      CACHE INTERNAL "" FORCE
  )
else()
  set(PIKA_WITH_GPU_SUPPORT
      OFF
      CACHE INTERNAL "" FORCE
  )
endif()

# ##################################################################################################
# Threadlevel Nice option
# ##################################################################################################
pika_option(
  PIKA_WITH_NICE_THREADLEVEL BOOL
  "Set pika worker threads to have high NICE level (may impact performance) (default: OFF)" OFF
  ADVANCED
)
if(PIKA_WITH_NICE_THREADLEVEL)
  pika_info("Nice threadlevel is enabled.")
  pika_add_config_define(PIKA_HAVE_NICE_THREADLEVEL)
else()
  pika_info("Nice threadlevel is disabled.")
endif()

# ##################################################################################################
# Utility configuration
# ##################################################################################################
set(PIKA_HIDDEN_VISIBILITY_DEFAULT ON)
if(CMAKE_COMPILER_IS_GNUCXX)
  if("${PIKA_PLATFORM_UC}" STREQUAL "ANDROID")
    set(PIKA_HIDDEN_VISIBILITY_DEFAULT OFF)
  endif()
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
  set(PIKA_HIDDEN_VISIBILITY_DEFAULT OFF)
endif()
if(APPLE)
  set(PIKA_HIDDEN_VISIBILITY_DEFAULT OFF)
endif()

pika_option(
  PIKA_WITH_HIDDEN_VISIBILITY
  BOOL
  "Use -fvisibility=hidden for builds on platforms which support it (default: ${PIKA_HIDDEN_VISIBILITY_DEFAULT})"
  ${PIKA_HIDDEN_VISIBILITY_DEFAULT}
  ADVANCED
)

# Thread Manager related build options

set(PIKA_MAX_CPU_COUNT_DEFAULT "")
pika_option(
  PIKA_WITH_MAX_CPU_COUNT
  STRING
  "pika applications will not use more that this number of OS-Threads (empty string means dynamic) (default: \"${PIKA_MAX_CPU_COUNT_DEFAULT}\")"
  "${PIKA_MAX_CPU_COUNT_DEFAULT}"
  CATEGORY "Thread Manager"
  ADVANCED
)
if(PIKA_WITH_MAX_CPU_COUNT)
  pika_add_config_define(PIKA_HAVE_MAX_CPU_COUNT ${PIKA_WITH_MAX_CPU_COUNT})
endif()
if((NOT PIKA_WITH_MAX_CPU_COUNT) OR (PIKA_WITH_MAX_CPU_COUNT GREATER 64))
  pika_add_config_define(PIKA_HAVE_MORE_THAN_64_THREADS)
endif()

set(PIKA_MAX_NUMA_DOMAIN_COUNT_DEFAULT "8")
pika_option(
  PIKA_WITH_MAX_NUMA_DOMAIN_COUNT
  STRING
  "pika applications will not run on machines with more NUMA domains (default: ${PIKA_MAX_NUMA_DOMAIN_COUNT_DEFAULT})"
  ${PIKA_MAX_NUMA_DOMAIN_COUNT_DEFAULT}
  CATEGORY "Thread Manager"
  ADVANCED
)
pika_add_config_define(PIKA_HAVE_MAX_NUMA_DOMAIN_COUNT ${PIKA_WITH_MAX_NUMA_DOMAIN_COUNT})

pika_option(
  PIKA_WITH_THREAD_STACK_MMAP BOOL "Use mmap for stack allocation on appropriate platforms" ON
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_option(
  PIKA_WITH_THREAD_MANAGER_IDLE_BACKOFF BOOL
  "pika scheduler threads do exponential backoff on idle queues (default: ON)" ON
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_option(
  PIKA_WITH_STACKTRACES BOOL "Attach backtraces to pika exceptions (default: ON)" ON
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_option(
  PIKA_WITH_THREAD_BACKTRACE_ON_SUSPENSION BOOL
  "Enable thread stack back trace being captured on suspension (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_option(
  PIKA_WITH_SWAP_CONTEXT_EMULATION BOOL
  "Emulate SwapContext API for coroutines (Windows only, default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

# We create a target to contain libraries like rt, dl etc. in order to remove global variables
add_library(pika_base_libraries INTERFACE)

if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
  target_link_libraries(pika_base_libraries INTERFACE imf svml irng intlc)
endif()

if(PIKA_WITH_STACKTRACES OR PIKA_WITH_THREAD_BACKTRACE_ON_SUSPENSION)
  pika_info("Stack traces are enabled.")
  pika_add_config_define(PIKA_HAVE_STACKTRACES)
  if(WIN32)
    target_link_libraries(pika_base_libraries INTERFACE dbghelp)
  endif()

  pika_option(
    PIKA_WITH_THREAD_BACKTRACE_DEPTH STRING
    "Thread stack back trace depth being captured (default: 20)" "20"
    CATEGORY "Thread Manager"
    ADVANCED
  )
  pika_add_config_define(PIKA_HAVE_THREAD_BACKTRACE_DEPTH ${PIKA_WITH_THREAD_BACKTRACE_DEPTH})

  if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"
                                                    OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
  )

    # This option is OFF by default as we have seen random segfaults out in the wild if this is
    # enabled.
    pika_option(
      PIKA_WITH_STACKTRACES_STATIC_SYMBOLS BOOL
      "Thread stack back trace will resolve static symbols (default: OFF)" OFF
      CATEGORY "Thread Manager"
      ADVANCED
    )
    pika_add_config_define(
      PIKA_HAVE_STACKTRACES_STATIC_SYMBOLS ${PIKA_WITH_STACKTRACES_STATIC_SYMBOLS}
    )

    # Demangling can segfault in certain configurations.
    pika_option(
      PIKA_WITH_STACKTRACES_DEMANGLE_SYMBOLS BOOL
      "Thread stack back trace symbols will be demangled (default: ON)" ON
      CATEGORY "Thread Manager"
      ADVANCED
    )
    if(PIKA_WITH_STACKTRACES_DEMANGLE_SYMBOLS)
      pika_add_config_define(PIKA_HAVE_STACKTRACES_DEMANGLE_SYMBOLS)
    endif()
  endif()
endif()

if(PIKA_WITH_THREAD_BACKTRACE_ON_SUSPENSION)
  pika_add_config_define(PIKA_HAVE_THREAD_BACKTRACE_ON_SUSPENSION)

  pika_option(
    PIKA_WITH_THREAD_FULLBACKTRACE_ON_SUSPENSION BOOL
    "Enable thread stack back trace being captured on suspension (default: OFF)" OFF
    CATEGORY "Thread Manager"
    ADVANCED
  )
  if(PIKA_WITH_THREAD_FULLBACKTRACE_ON_SUSPENSION)
    pika_add_config_define(PIKA_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION)
  endif()
endif()

pika_option(
  PIKA_WITH_THREAD_QUEUE_WAITTIME BOOL
  "Enable collecting queue wait times for threads (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_THREAD_QUEUE_WAITTIME)
  pika_add_config_define(PIKA_HAVE_THREAD_QUEUE_WAITTIME)
endif()

pika_option(
  PIKA_WITH_THREAD_IDLE_RATES BOOL
  "Enable measuring the percentage of overhead times spent in the scheduler (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_option(
  PIKA_WITH_THREAD_CREATION_AND_CLEANUP_RATES BOOL
  "Enable measuring thread creation and cleanup times (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_THREAD_IDLE_RATES)
  pika_add_config_define(PIKA_HAVE_THREAD_IDLE_RATES)
  if(PIKA_WITH_THREAD_CREATION_AND_CLEANUP_RATES)
    pika_add_config_define(PIKA_HAVE_THREAD_CREATION_AND_CLEANUP_RATES)
  endif()
endif()

pika_option(
  PIKA_WITH_THREAD_CUMULATIVE_COUNTS BOOL
  "Enable keeping track of cumulative thread counts in the schedulers (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_THREAD_CUMULATIVE_COUNTS)
  pika_add_config_define(PIKA_HAVE_THREAD_CUMULATIVE_COUNTS)
endif()

pika_option(
  PIKA_WITH_THREAD_STEALING_COUNTS BOOL
  "Enable keeping track of counts of thread stealing incidents in the schedulers (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_THREAD_STEALING_COUNTS)
  pika_add_config_define(PIKA_HAVE_THREAD_STEALING_COUNTS)
endif()

pika_option(
  PIKA_WITH_COROUTINE_COUNTERS BOOL
  "Enable keeping track of coroutine creation and rebind counts (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)
if(PIKA_WITH_COROUTINE_COUNTERS)
  pika_add_config_define(PIKA_HAVE_COROUTINE_COUNTERS)
endif()

pika_option(
  PIKA_WITH_THREAD_LOCAL_STORAGE BOOL
  "Enable thread local storage for all pika threads (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_THREAD_LOCAL_STORAGE)
  pika_add_config_define(PIKA_HAVE_THREAD_LOCAL_STORAGE)
endif()

pika_option(
  PIKA_WITH_SCHEDULER_LOCAL_STORAGE BOOL
  "Enable scheduler local storage for all pika schedulers (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_SCHEDULER_LOCAL_STORAGE)
  pika_add_config_define(PIKA_HAVE_SCHEDULER_LOCAL_STORAGE)
endif()

pika_option(
  PIKA_WITH_SPINLOCK_POOL_NUM STRING "Number of elements a spinlock pool manages (default: 128)" 128
  CATEGORY "Thread Manager"
  ADVANCED
)

pika_add_config_define(PIKA_HAVE_SPINLOCK_POOL_NUM ${PIKA_WITH_SPINLOCK_POOL_NUM})

pika_option(
  PIKA_WITH_SPINLOCK_DEADLOCK_DETECTION BOOL "Enable spinlock deadlock detection (default: OFF)" OFF
  CATEGORY "Thread Manager"
  ADVANCED
)

if(PIKA_WITH_SPINLOCK_DEADLOCK_DETECTION)
  pika_add_config_define(PIKA_HAVE_SPINLOCK_DEADLOCK_DETECTION)
endif()

pika_option(
  PIKA_WITH_ITTNOTIFY BOOL "Enable Amplifier (ITT) instrumentation support." OFF
  CATEGORY "Profiling"
  ADVANCED
)

pika_option(
  PIKA_WITH_MPI BOOL
  "Enable support for returning futures from MPI asynchronous calls (default: ON)" OFF
  CATEGORY "MPI"
)

if(PIKA_WITH_MPI)
  pika_add_config_define(PIKA_HAVE_MPI)

  # mpi settings
  pika_option(
    PIKA_WITH_MPI_ENV
    STRING
    "List of environment variables checked to detect MPI (default: MV2_COMM_WORLD_RANK;PMI_RANK;OMPI_COMM_WORLD_SIZE;ALPS_APP_PE;PMIX_RANK;PALS_NODEID)."
    "MV2_COMM_WORLD_RANK;PMI_RANK;OMPI_COMM_WORLD_SIZE;ALPS_APP_PE;PMIX_RANK;PALS_NODEID"
    CATEGORY "MPI"
    ADVANCED
  )

  # This list is to detect whether we run inside an mpi environment.
  if(PIKA_WITH_MPI_ENV)
    string(REPLACE ";" "," pika_mpi_env_ "${PIKA_WITH_MPI_ENV}")
    pika_add_config_define(PIKA_HAVE_MPI_ENV "\"${pika_mpi_env_}\"")
  endif()

  if(MSVC)
    # FIXME: add OpenMPI specific flag here for now as the pika_add_compile_flag() below does not
    # add the extra options to the top level directory
    pika_add_config_define(OMPI_IMPORTS)
  endif()
endif()

# External libraries/frameworks used by sme of the examples and benchmarks
pika_option(
  PIKA_WITH_EXAMPLES_OPENMP BOOL "Enable examples requiring OpenMP support (default: OFF)." OFF
  CATEGORY "Build Targets" DEPENDS "PIKA_WITH_EXAMPLES"
  ADVANCED
)
if(PIKA_WITH_EXAMPLES_OPENMP)
  find_package(OpenMP REQUIRED)
endif()
pika_option(
  PIKA_WITH_EXAMPLES_TBB BOOL "Enable examples requiring TBB support (default: OFF)." OFF
  CATEGORY "Build Targets" DEPENDS "PIKA_WITH_EXAMPLES"
  ADVANCED
)
if(PIKA_WITH_EXAMPLES_TBB)
  find_package(TBB)
  if(NOT TBB_FOUND)
    set(PIKA_WITH_EXAMPLES_TBB OFF)
  endif()
endif()
pika_option(
  PIKA_WITH_EXAMPLES_QTHREADS BOOL "Enable examples requiring QThreads support (default: OFF)." OFF
  CATEGORY "Build Targets" DEPENDS "PIKA_WITH_EXAMPLES"
  ADVANCED
)
if(PIKA_WITH_EXAMPLES_QTHREADS)
  find_package(QThreads)
  if(NOT QTHREADS_FOUND)
    set(PIKA_WITH_EXAMPLES_QTHREADS OFF)
  endif()
endif()
pika_option(
  PIKA_WITH_EXAMPLES_HDF5 BOOL "Enable examples requiring HDF5 support (default: OFF)." OFF
  CATEGORY "Build Targets" DEPENDS "PIKA_WITH_EXAMPLES"
  ADVANCED
)
if(PIKA_WITH_EXAMPLES_HDF5)
  find_package(HDF5 COMPONENTS CXX)
  if(NOT HDF5_FOUND)
    set(PIKA_WITH_EXAMPLES_HDF5 OFF)
  endif()
endif()

# Disabling the Qt example on BG/Q as GUIs don't make sense there anyways
if(NOT "${PIKA_PLATFORM_UC}" STREQUAL "BLUEGENEQ")
  pika_option(
    PIKA_WITH_EXAMPLES_QT4 BOOL "Enable examples requiring Qt4 support (default: OFF)." OFF
    CATEGORY "Build Targets" DEPENDS "PIKA_WITH_EXAMPLES"
    ADVANCED
  )
  if(PIKA_WITH_EXAMPLES_QT4)
    find_package(Qt4)
    if(NOT QT4_FOUND)
      set(PIKA_WITH_EXAMPLES_QT4 OFF)
    endif()
  endif()
endif()

# Debugging related build options
pika_option(
  PIKA_WITH_VALGRIND BOOL "Enable Valgrind instrumentation support." OFF
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_TESTS_VALGRIND BOOL "Run selected tests with valgrind." OFF
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_TESTS_VALGRIND_OPTIONS STRING "Use these options when running tests under valgrind."
  "--error-exitcode=1"
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_SANITIZERS BOOL "Configure with sanitizer instrumentation support." OFF
  CATEGORY "Debugging"
  ADVANCED
)

set(PIKA_WITH_VERIFY_LOCKS_DEFAULT OFF)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
  set(PIKA_WITH_VERIFY_LOCKS_DEFAULT ON)
endif()

pika_option(
  PIKA_WITH_VERIFY_LOCKS BOOL
  "Enable lock verification code (default: OFF, enabled in debug builds)"
  ${PIKA_WITH_VERIFY_LOCKS_DEFAULT}
  CATEGORY "Debugging"
  ADVANCED
)
pika_option(
  PIKA_WITH_VERIFY_LOCKS_BACKTRACE
  BOOL
  "Enable thread stack back trace being captured on lock registration (to be used in combination with PIKA_WITH_VERIFY_LOCKS=ON, default: OFF)"
  OFF
  CATEGORY "Debugging"
  ADVANCED
)
pika_option(
  PIKA_WITH_THREAD_DEBUG_INFO BOOL "Enable thread debugging information (default: OFF)" OFF
  CATEGORY "Debugging"
  ADVANCED
)

if(PIKA_WITH_VERIFY_LOCKS)
  pika_add_config_define(PIKA_HAVE_VERIFY_LOCKS)
  if(PIKA_WITH_VERIFY_LOCKS_BACKTRACE)
    pika_add_config_define(PIKA_HAVE_VERIFY_LOCKS_BACKTRACE)
  endif()
endif()

# Additional debug support
if(NOT WIN32 AND PIKA_WITH_THREAD_STACK_MMAP)
  pika_add_config_define(PIKA_HAVE_THREAD_STACK_MMAP)
endif()

if(PIKA_WITH_THREAD_MANAGER_IDLE_BACKOFF)
  pika_add_config_define(PIKA_HAVE_THREAD_MANAGER_IDLE_BACKOFF)
endif()

pika_option(
  PIKA_WITH_THREAD_DESCRIPTION_FULL BOOL
  "Use function address for thread description (default: OFF)" OFF
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_TESTS_DEBUG_LOG BOOL "Turn on debug logs for tests (default: OFF)" OFF
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_TESTS_DEBUG_LOG_DESTINATION STRING "Destination for test debug logs (default: cout)"
  "cout"
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_TESTS_MAX_THREADS
  STRING
  "Maximum number of threads to use for tests (default: 0, use the number of threads specified by the test)"
  0
  CATEGORY "Debugging"
  ADVANCED
)

pika_option(
  PIKA_WITH_PARALLEL_TESTS_BIND_NONE BOOL
  "Pass --pika:bind=none to tests that may run in parallel (cmake -j flag) (default: OFF)" OFF
  CATEGORY "Debugging"
  ADVANCED
)

# Special option to enable testing of FreeBSD specific limitations This is purely an option allowing
# to test whether things work properly on systems that may not report cores in the topology at all
# (e.g. FreeBSD). There is no need for a user to every enable this.
pika_option(
  PIKA_WITH_ADDITIONAL_HWLOC_TESTING
  BOOL
  "Enable HWLOC filtering that makes it report no cores, this is purely an
  option supporting better testing - do not enable under normal circumstances.
  (default: OFF)"
  OFF
  ADVANCED
  CATEGORY "Debugging"
)

if(PIKA_WITH_ADDITIONAL_HWLOC_TESTING)
  pika_add_config_define_namespace(DEFINE PIKA_HAVE_ADDITIONAL_HWLOC_TESTING NAMESPACE TOPOLOGY)
endif()

# If APEX is defined, the action timers need thread debug info.
pika_option(PIKA_WITH_APEX BOOL "Enable APEX instrumentation support." OFF CATEGORY "Profiling")
if(PIKA_WITH_APEX)
  pika_add_config_define(PIKA_HAVE_APEX)
  pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION)
  pika_add_config_define(PIKA_HAVE_THREAD_PARENT_REFERENCE)
  if(PIKA_WITH_THREAD_DESCRIPTION_FULL)
    pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION_FULL)
  endif()
endif()

pika_option(PIKA_WITH_TRACY BOOL "Enable Tracy instrumentation support." OFF CATEGORY "Profiling")
if(PIKA_WITH_TRACY)
  pika_add_config_define(PIKA_HAVE_TRACY)
  pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION)
  pika_add_config_define(PIKA_HAVE_THREAD_PARENT_REFERENCE)
  if(PIKA_WITH_THREAD_DESCRIPTION_FULL)
    pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION_FULL)
  endif()
  find_package(Tracy REQUIRED)
  target_link_libraries(pika_base_libraries INTERFACE Tracy::TracyClient)
endif()

if(PIKA_WITH_THREAD_DEBUG_INFO)
  pika_add_config_define(PIKA_HAVE_THREAD_PARENT_REFERENCE)
  pika_add_config_define(PIKA_HAVE_THREAD_PHASE_INFORMATION)
  pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION)
  pika_add_config_define(PIKA_HAVE_THREAD_DEADLOCK_DETECTION)
  if(PIKA_WITH_THREAD_DESCRIPTION_FULL)
    pika_add_config_define(PIKA_HAVE_THREAD_DESCRIPTION_FULL)
  endif()
endif()

pika_option(
  PIKA_WITH_STDEXEC BOOL "Use stdexec for sender/receiver functionality." OFF CATEGORY "Advanced"
)
if(PIKA_WITH_STDEXEC)
  pika_add_config_define(PIKA_HAVE_STDEXEC)
endif()

# Developer tools
pika_option(
  PIKA_WITH_VIM_YCM BOOL "Generate completion file for VIM YouCompleteMe plugin" OFF ADVANCED
)

# ##################################################################################################
# Special diagnostic flags, do not enable by default, only if needed
pika_option(
  PIKA_WITH_CHECK_MODULE_DEPENDENCIES BOOL
  "Verify that no modules are cross-referenced from a different module category \
  (default: OFF)" OFF ADVANCED
)

# ##################################################################################################
# C++ feature tests
# ##################################################################################################
# Need to set up CUDA/HIP before feature tests to enable the respective languages
include(pika_setup_cuda)
include(pika_setup_hip)
include(pika_perform_cxx_feature_tests)
pika_perform_cxx_feature_tests()

# ##################################################################################################
# Set configuration option to use Boost.Context or not. This depends on the platform.
set(__use_generic_coroutine_context OFF)
if(APPLE)
  set(__use_generic_coroutine_context ON)
endif()
if("${PIKA_PLATFORM_UC}" STREQUAL "BLUEGENEQ")
  set(__use_generic_coroutine_context ON)
endif()
pika_option(
  PIKA_WITH_BOOST_CONTEXT BOOL "Use Boost.Context for user-level thread context switching."
  ${__use_generic_coroutine_context} ADVANCED
)

# ##################################################################################################
# Check for misc system headers
# ##################################################################################################

pika_check_for_unistd_h(DEFINITIONS PIKA_HAVE_UNISTD_H)

if(NOT WIN32)
  # ################################################################################################
  # Macro definitions for system headers
  # ################################################################################################
  add_definitions(-D_GNU_SOURCE)

  # ################################################################################################
  # System libraries
  # ################################################################################################
  if(NOT MSVC)
    if(PIKA_CXX11_STD_ATOMIC_LIBRARIES)
      target_link_libraries(pika_base_libraries INTERFACE ${PIKA_CXX11_STD_ATOMIC_LIBRARIES})
    endif()
  endif()

  if(PIKA_HAVE_LIBSUPCPP)
    target_link_libraries(pika_base_libraries INTERFACE supc++)
  endif()

  if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
    target_link_libraries(pika_base_libraries INTERFACE dl)
  endif()

  if(NOT APPLE AND NOT ("${PIKA_PLATFORM_UC}" STREQUAL "ANDROID"))
    target_link_libraries(pika_base_libraries INTERFACE rt)
  endif()

  if("${PIKA_PLATFORM_UC}" STREQUAL "ANDROID")
    target_link_libraries(pika_base_libraries INTERFACE log)
  endif()

  if(APPLE)
    pika_add_compile_flag_if_available(-ftemplate-depth=256)
  endif()

  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
    pika_add_link_flag_if_available(-Wl,-lstdc++fs)
  endif()
endif()

if(WIN32)
  if(MSVC)
    pika_add_target_compile_option(-Ox PUBLIC CONFIGURATIONS Release)

    # even VS2017 has an ICE when compiling with -Ob2
    pika_add_target_compile_option(-Ob1 PUBLIC CONFIGURATIONS Release)

    # VS2012 and above has a special flag for improving the debug experience by adding more symbol
    # information to the build (-d2Zi)
    pika_add_target_compile_option(-d2Zi+ PUBLIC CONFIGURATIONS RelWithDebInfo)

    # VS2013 and above know how to do link time constant data segment folding VS2013 update 2 and
    # above know how to remove debug information for non-referenced functions and data (-Zc:inline)
    pika_add_target_compile_option(-Zc:inline PUBLIC)
    pika_add_target_compile_option(-Gw PUBLIC CONFIGURATIONS Release RelWithDebInfo MinSizeRel)
    pika_add_target_compile_option(-Zo PUBLIC CONFIGURATIONS RelWithDebInfo)

    # Exceptions
    pika_add_target_compile_option(-EHsc)
    if(MSVC_VERSION GREATER_EQUAL 1900)
      # assume conforming (throwing) operator new implementations
      pika_add_target_compile_option(-Zc:throwingNew PUBLIC)

      # enable faster linking (requires VS2015 Update1) disabled for now as this flag crashes
      # debugger pika_remove_link_flag(/debug CONFIGURATIONS Debug)
      # pika_add_link_flag(/debug:fastlink CONFIGURATIONS Debug)

      # Update 2 requires to set _ENABLE_ATOMIC_ALIGNMENT_FIX for it to compile atomics
      pika_add_config_cond_define(_ENABLE_ATOMIC_ALIGNMENT_FIX)

      # Update 3 allows to flag rvalue misuses and enforces strict string const- qualification
      # conformance
      pika_add_target_compile_option(-Zc:rvalueCast PUBLIC)
      pika_add_target_compile_option(-Zc:strictStrings PUBLIC)

      # Update 8 requires to set _ENABLE_EXTENDED_ALIGNED_STORAGE for it to compile atomics
      pika_add_config_cond_define(_ENABLE_EXTENDED_ALIGNED_STORAGE)

      # Make sure that __cplusplus is properly defined
      pika_add_target_compile_option(-Zc:__cplusplus PUBLIC)

      # Silence C++17 deprecation warnings
      pika_add_config_cond_define(_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS)

      # Silence C++20 deprecation warnings
      pika_add_config_cond_define(_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS)

      # ASan is available in Visual Studion starting V16.8
      if((MSVC_VERSION GREATER_EQUAL 1928) AND PIKA_WITH_SANITIZERS)
        pika_add_target_compile_option(
          -fsanitize=address PUBLIC CONFIGURATIONS RelWithDebInfo Debug
        )
      endif()
    endif()

    # Runtime type information
    pika_add_target_compile_option(-GR PUBLIC)
    # Multiprocessor build
    pika_add_target_compile_option(-MP PUBLIC)
    # Increase the maximum size of object file sections
    pika_add_target_compile_option(-bigobj PUBLIC)
  endif()

  target_link_libraries(pika_base_libraries INTERFACE psapi shlwapi)

  # ################################################################################################
  # Macro definitions for system headers
  # ################################################################################################
  add_definitions(-D_WINDOWS)
  add_definitions(-D_WIN32)
  pika_add_config_cond_define(_WIN32_WINNT 0x0602)
  pika_add_config_cond_define(_SCL_SECURE_NO_WARNINGS)
  pika_add_config_cond_define(_CRT_SECURE_NO_WARNINGS)
  pika_add_config_cond_define(_SCL_SECURE_NO_DEPRECATE)
  pika_add_config_cond_define(_CRT_SECURE_NO_DEPRECATE)
  pika_add_config_cond_define(_CRT_NONSTDC_NO_WARNINGS)
  pika_add_config_cond_define(_WINSOCK_DEPRECATED_NO_WARNINGS)
  pika_add_config_cond_define(_CRT_NON_CONFORMING_SWPRINTFS)
  pika_add_config_cond_define(_SILENCE_FPOS_SEEKPOS_DEPRECATION_WARNING)

  # ################################################################################################
  # Boost
  # ################################################################################################

  pika_add_config_cond_define(BOOST_USE_WINDOWS_H)
  if(NOT CMAKE_CL_64)
    pika_add_config_cond_define(BOOST_NO_ALIGNMENT)
  endif()
  if(NOT PIKA_WITH_BOOST_CONTEXT)
    pika_add_config_define(PIKA_HAVE_FIBER_BASED_COROUTINES)
  endif()
  pika_add_config_cond_define(PSAPI_VERSION 1)
endif()

# Configure Warnings
if(PIKA_WITH_COMPILER_WARNINGS)
  if(MSVC) # Adding special warning settings for the MSVC compiler ...
    pika_add_compile_flag(-W3)

    # MSVC2012/2013 are overeager to report 'qualifier applied to function type has no meaning;
    # ignored'
    pika_add_compile_flag(-wd4180)

    # Boost.Lockfree triggers 'warning C4307: '+' : integral constant overflow' which is benign
    pika_add_compile_flag(-wd4307)

    # object allocated on the heap may not be aligned
    pika_add_compile_flag(-wd4316)

    # max symbol length exceeded
    pika_add_compile_flag(-wd4503)

    # 'int': forcing value to bool 'true' or 'false' (performance warning)
    pika_add_compile_flag(-wd4800)

    # vcpkg enables the /utf-8 option which causes (benign) warnings in the Spirit headers: The file
    # contains a character starting at offset ... that is illegal in the current source character
    # set
    pika_add_compile_flag(-wd4828)

  else() # Trial and error approach for any other compiler ...
    pika_add_compile_flag_if_available(-Wall)
    pika_add_compile_flag_if_available(-Wextra)
    pika_add_compile_flag_if_available(-Wpedantic)
    pika_add_compile_flag_if_available(-Wno-strict-aliasing)
    pika_add_compile_flag_if_available(-Wno-sign-promo)
    pika_add_compile_flag_if_available(-Wno-attributes)
    pika_add_compile_flag_if_available(-Wno-cast-align)

    # We do not in general guarantee ABI compatibility between C++ standards, so we ignore this
    # warning
    pika_add_compile_flag_if_available(-Wno-noexcept-type)

    # Be extra strict about format checks Boost.Logging is built on fprintf, sadly
    pika_add_compile_flag_if_available(-Wformat=2)
    pika_add_compile_flag_if_available(-Wno-format-nonliteral)

    # Self initialization is dangerous
    pika_add_compile_flag_if_available(-Winit-self)

    # For portability
    pika_add_compile_flag_if_available(-Wdouble-promotion)

    # Warn about casting that violates qualifiers or alignment
    pika_add_compile_flag_if_available(-Wcast-qual)
    if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
      # Clang is overeager in reporting cast alignment problems in Boost
      pika_add_compile_flag_if_available(-Wcast-align)
    endif()

    pika_add_compile_flag_if_available(-Werror=parentheses)
    pika_add_compile_flag_if_available(-Werror=reorder)
    pika_add_compile_flag_if_available(-Werror=return-type)
    pika_add_compile_flag_if_available(-Werror=sequence-point)
    pika_add_compile_flag_if_available(-Werror=uninitialized)
    pika_add_compile_flag_if_available(-Werror=format)
    pika_add_compile_flag_if_available(-Werror=missing-braces)
    pika_add_compile_flag_if_available(-Werror=sign-compare)
  endif()
endif()

# GCC gives a bogus warning in stdexec
if(PIKA_WITH_STDEXEC AND CMAKE_COMPILER_IS_GNUCXX)
  pika_add_compile_flag_if_available(-Wno-non-template-friend)
endif()

if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
  pika_add_compile_flag_if_available(-Wno-interference-size)
endif()

# Configure compiler warnings as errors
if(PIKA_WITH_COMPILER_WARNINGS_AS_ERRORS)
  if(MSVC)
    pika_add_compile_flag(-WX)
  else()
    pika_add_compile_flag_if_available(-Werror)
  endif()
endif()

# Diagnostics
if(MSVC)
  # Display full paths in diagnostics
  pika_add_compile_flag(-FC)
  if(CMAKE_CL_64)
    set(__target_arch "x86_64")
  else()
    set(__target_arch "x86")
  endif()
  pika_info("Architecture detected: ${__target_arch}")
else()
  # Show the flags that toggle each warning
  pika_add_compile_flag_if_available(-fdiagnostics-show-option)

  # VLAs are a GNU extensions that we forbid as they are not supported on MSVC
  pika_add_compile_flag_if_available(-Werror=vla)
  # No return statement in a non-void function can lead to garbage return values in GCC.
  pika_add_compile_flag_if_available(-Werror=return-type)

  # We get false positives all over the place with this.
  if(CMAKE_COMPILER_IS_GNUCXX)
    pika_add_compile_flag_if_available(-Wno-unused-but-set-parameter)
    pika_add_compile_flag_if_available(-Wno-unused-but-set-variable)
    # Uninitialized variables are bad, earlier compilers issue spurious warnings
    pika_add_compile_flag_if_available(-Werror=uninitialized)
    pika_add_compile_flag_if_available(-Wno-unused-local-typedefs)
    # -Werror=maybe-uninitialized leads to false positives.
    pika_add_compile_flag_if_available(-Wno-maybe-uninitialized)
  endif()

  # Silence warning about __sync_fetch_and_nand changing semantics
  pika_add_compile_flag_if_available(-Wno-sync-nand)

  # Silence warnings about deleting polymorphic objects with non-virtual dtors. These come from
  # within Boost.
  if(CMAKE_COMPILER_IS_GNUCXX)
    pika_add_compile_flag_if_available(-Wno-delete-non-virtual-dtor)
  endif()

  # Check if our libraries have unresolved symbols
  if(NOT APPLE
     AND NOT WIN32
     AND NOT PIKA_WITH_SANITIZERS
  )
    pika_add_link_flag_if_available(-Wl,-z,defs)
  endif()
  if(WIN32)
    target_link_libraries(pika_base_libraries INTERFACE psapi WS2_32 mswsock)
  endif()

  if("${PIKA_PLATFORM_UC}" STREQUAL "BLUEGENEQ")
    pika_add_compile_flag_if_available(-Wno-deprecated-register)
  endif()

  if(PIKA_WITH_HIDDEN_VISIBILITY)
    pika_add_compile_flag_if_available(-fvisibility=hidden)
    pika_add_link_flag_if_available(-fvisibility=hidden)
    pika_add_config_define(PIKA_HAVE_ELF_HIDDEN_VISIBILITY)
  endif()

  if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
    pika_add_compile_flag_if_available(-Wno-cast-align)
  endif()

  if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
    # Disable the following warnings: #1170: invalid redeclaration of nested class
    pika_add_compile_flag_if_available(-wd1170)
    # #858: type qualifier on return type is meaningless
    pika_add_compile_flag_if_available(-wd858)
    # #1098: the qualifier on this friend declaration is ignored
    pika_add_compile_flag_if_available(-wd1098)
    # #488: template parameter not used in declaring the parameter type
    pika_add_compile_flag_if_available(-wd488)
    # #2203: cast discards qualifiers from target type (needed for mvapich2 mpi header)
    pika_add_compile_flag_if_available(-wd2203)
    # #2536: cannot specify explicit initializer for arrays
    pika_add_compile_flag_if_available(-wd2536)
    # #1286: invalid attribute
    pika_add_compile_flag_if_available(-wd1286)
  endif()

  set(__has_timestamp_support OFF)
  include(pika_target_arch)

  pika_target_architecture(__target_arch)
  if("${__target_arch}" STREQUAL "i386"
     OR "${__target_arch}" STREQUAL "ix86"
     OR "${__target_arch}" STREQUAL "x86_64"
     OR "${__target_arch}" STREQUAL "ia64"
  )

    # rdtsc is an x86 instruction that reads the value of a CPU time stamp counter. rdtscp is an
    # extension to rdtsc. The difference is that rdtscp is a serializing instruction.
    pika_cpuid("rdtsc" PIKA_WITH_RDTSC DEFINITIONS PIKA_HAVE_RDTSC)

    # XeonPhi's do not support RDTSCP
    if(NOT ("${PIKA_PLATFORM_UC}" STREQUAL "XEONPHI"))
      pika_cpuid("rdtscp" PIKA_WITH_RDTSCP DEFINITIONS PIKA_HAVE_RDTSCP)
    endif()

    if(PIKA_WITH_RDTSC OR PIKA_WITH_RDTSCP)
      set(__has_timestamp_support ON)
    endif()
  elseif(
    "${__target_arch}" STREQUAL "arm"
    OR "${__target_arch}" STREQUAL "armv5"
    OR "${__target_arch}" STREQUAL "armv6"
    OR "${__target_arch}" STREQUAL "armv7"
    OR "${__target_arch}" STREQUAL "arm64"
    OR "${__target_arch}" STREQUAL "ppc"
    OR "${__target_arch}" STREQUAL "ppc64"
    OR "${__target_arch}" STREQUAL "bgq"
    OR "${__target_arch}" STREQUAL "s390x"
  )
    set(__has_timestamp_support ON)
  endif()

  pika_info("Architecture detected: ${__target_arch}")
  if(NOT __has_timestamp_support)
    pika_warn(
      "No timestamp support is available; some performance counters may report incorrect results"
    )
  endif()
endif()

# store target architecture for later use
set(PIKA_WITH_TARGET_ARCHITECTURE
    ${__target_arch}
    CACHE INTERNAL "" FORCE
)

# Compatibility with using Boost.Iterator traversal tags
pika_option(
  PIKA_WITH_BOOST_ITERATOR_TRAVERSAL_TAG_COMPATIBILITY BOOL
  "Enable Boost.Iterator traversal tag compatibility. (default: OFF)" OFF ADVANCED
  CATEGORY "Generic"
)
if(PIKA_WITH_BOOST_ITERATOR_TRAVERSAL_TAG_COMPATIBILITY)
  pika_add_config_define_namespace(
    DEFINE PIKA_HAVE_BOOST_ITERATOR_TRAVERSAL_TAG_COMPATIBILITY NAMESPACE ITERATOR_SUPPORT
  )
endif()

# ##################################################################################################
# Find Our dependencies: These are all dependencies needed to build the core library. Dependencies
# that are only needed by plugins, examples or tests should be found separately in the appropriate
# subdirectory.

include(pika_setup_threads)
include(pika_setup_boost)
include(pika_setup_fmt)
include(pika_setup_spdlog)
include(pika_setup_allocator)
include(pika_setup_hwloc)
include(pika_setup_mpi)
include(pika_setup_whip)
include(pika_setup_apex)
include(pika_setup_valgrind)
include(pika_setup_stdexec)

if(PIKA_WITH_SANITIZERS)
  pika_add_config_define(PIKA_HAVE_SANITIZERS)
endif()

if(PIKA_WITH_VIM_YCM)
  set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()

# ##################################################################################################
# Check Build Options based on the found dependencies. We also check for errors with incompatible
# options with the currently selected platform.
#

if(PIKA_WITH_BOOST_CONTEXT)
  # Check if we can use generic coroutine contexts without any problems
  if(NOT Boost_CONTEXT_FOUND)
    pika_error("The usage of Boost.Context was selected but Boost.Context was not found.")
  endif()
  pika_add_config_define(PIKA_HAVE_BOOST_CONTEXT)
endif()

# ##################################################################################################
# search path configuration
# ##################################################################################################
if(PIKA_WITH_FULL_RPATH)
  include(pika_set_full_rpath)
endif()

# ##################################################################################################
# Git commit detection
# ##################################################################################################
include(pika_git_commit)
pika_add_config_define(PIKA_HAVE_GIT_COMMIT "\"${PIKA_WITH_GIT_COMMIT}\"")

include(pika_set_output_paths)

# ##################################################################################################
# Add custom targets for tests
# ##################################################################################################
if(PIKA_WITH_TESTS)
  pika_add_pseudo_target(tests)
  if(PIKA_WITH_TESTS_UNIT)
    pika_add_pseudo_target(tests.unit)
    pika_add_pseudo_dependencies(tests tests.unit)
  endif()
  if(PIKA_WITH_TESTS_REGRESSIONS)
    pika_add_pseudo_target(tests.regressions)
    pika_add_pseudo_dependencies(tests tests.regressions)
  endif()
  if(PIKA_WITH_TESTS_BENCHMARKS)
    pika_add_pseudo_target(tests.performance)
    pika_add_pseudo_dependencies(tests tests.performance)
  endif()
  if(PIKA_WITH_TESTS_HEADERS)
    pika_add_pseudo_target(tests.headers)
    pika_add_pseudo_dependencies(tests tests.headers)
  endif()
  if(PIKA_WITH_EXAMPLES AND PIKA_WITH_TESTS_EXAMPLES)
    pika_add_pseudo_target(tests.examples)
    pika_add_pseudo_dependencies(tests tests.examples)
  endif()

  enable_testing()
  include(CTest)

  add_subdirectory(testing)
endif()

if(PIKA_WITH_EXAMPLES)
  pika_add_pseudo_target(examples)
endif()

# ##################################################################################################
# Debug library postfix
# ##################################################################################################
set(CMAKE_DEBUG_POSTFIX "d")
set(PIKA_DEBUG_POSTFIX "d")

# ##################################################################################################
# Add libraries
# ##################################################################################################
add_subdirectory(libs)

# ##################################################################################################
# Target specification
# ##################################################################################################
pika_add_pseudo_target(tools)
add_subdirectory(tools)

# ##################################################################################################
# Tests
# ##################################################################################################
if(PIKA_WITH_TESTS)
  add_subdirectory(tests)
endif()

if(PIKA_WITH_EXAMPLES)
  add_subdirectory(examples)
endif()

# Set up precompiled headers
if(PIKA_WITH_PRECOMPILED_HEADERS)
  set(system_precompiled_headers
      <algorithm>
      <any>
      <array>
      <atomic>
      <bitset>
      <cassert>
      <cctype>
      <cerrno>
      <chrono>
      <climits>
      <cmath>
      <complex>
      <condition_variable>
      <cstddef>
      <cstdint>
      <cstdio>
      <cstdlib>
      <cstring>
      <ctime>
      <deque>
      <exception>
      <filesystem>
      <forward_list>
      <fstream>
      <functional>
      <iomanip>
      <ios>
      <iosfwd>
      <iostream>
      <iterator>
      <limits>
      <list>
      <locale>
      <map>
      <memory>
      <mutex>
      <new>
      <numeric>
      <optional>
      <ostream>
      <random>
      <regex>
      <set>
      <shared_mutex>
      <sstream>
      <stack>
      <stdexcept>
      <string>
      <string_view>
      <system_error>
      <thread>
      <tuple>
      <type_traits>
      <typeinfo>
      <unordered_map>
      <unordered_set>
      <utility>
      <variant>
      <vector>
      <hwloc.h>
      <boost/config.hpp>
      <boost/intrusive/slist.hpp>
      <boost/lockfree/queue.hpp>
      <boost/optional.hpp>
      <boost/tokenizer.hpp>
      <fmt/format.h>
      <fmt/printf.h>
  )

  if(PIKA_WITH_CXX17_STD_EXECUTION_POLICIES)
    list(APPEND system_precompiled_headers <execution>)
  endif()

  set(system_precompiled_headers_dependencies pika_dependencies_boost pika_private_flags
                                              pika_public_flags
  )

  target_link_libraries(
    pika_precompiled_headers PRIVATE pika_public_flags pika_private_flags pika_base_libraries
                                     ${system_precompiled_headers_dependencies}
  )
  set_target_properties(pika_precompiled_headers PROPERTIES POSITION_INDEPENDENT_CODE ON)
  target_precompile_headers(pika_precompiled_headers PRIVATE ${system_precompiled_headers})

  # Headers that should be precompiled for things depending on pika (executables, libraries).
  set(pika_precompiled_headers_modules ${PIKA_ENABLED_MODULES})
  # The init_runtime module cannot be precompiled because it uses macros that can depend on the
  # application (PIKA_PREFIX and PIKA_APPLICATION_NAME_DEFAULT).
  list(REMOVE_ITEM pika_precompiled_headers_modules init_runtime)
  # config, filesystem, version, and the include modules don't have pika/module headers.
  list(
    REMOVE_ITEM
    pika_precompiled_headers_modules
    assertion
    config
    filesystem
    include
    logging
    version
  )
  list(TRANSFORM pika_precompiled_headers_modules PREPEND "<pika/modules/")
  list(TRANSFORM pika_precompiled_headers_modules APPEND ".hpp>")

  target_link_libraries(pika_exe_precompiled_headers PRIVATE pika)
  target_precompile_headers(
    pika_exe_precompiled_headers PRIVATE ${pika_precompiled_headers_modules}
  )
endif()

# ##################################################################################################
# installation instructions
# ##################################################################################################
# Install all pika header that have been configured using various CMake options
install(
  DIRECTORY "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/pika/"
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pika
  COMPONENT core
  FILES_MATCHING
  PATTERN "*.hpp"
  PATTERN ".git" EXCLUDE
  PATTERN "CMakeFiles" EXCLUDE
  PATTERN "CTestFiles" EXCLUDE
)

install(
  # Install all pika CMake utility files
  DIRECTORY cmake/
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pika
  COMPONENT core
  PATTERN "templates" EXCLUDE
  PATTERN "packaging" EXCLUDE
  PATTERN ".git" EXCLUDE
)

install(
  FILES "${PROJECT_SOURCE_DIR}/LICENSE_1_0.txt"
  DESTINATION ${CMAKE_INSTALL_DATADIR}/pika
  COMPONENT license
)

# Install pika-bind script
configure_file(
  ${PROJECT_SOURCE_DIR}/scripts/pika-bind.sh.in ${PROJECT_BINARY_DIR}/scripts/pika-bind @ONLY
)

install(PROGRAMS ${PROJECT_BINARY_DIR}/scripts/pika-bind DESTINATION ${CMAKE_INSTALL_BINDIR})

if(PIKA_WITH_VIM_YCM)
  set(build_dir_file ${PROJECT_BINARY_DIR}/.ycm_extra_conf.py)
  set(source_dir_file ${PROJECT_SOURCE_DIR}/.ycm_extra_conf.py)
  configure_file(${PROJECT_SOURCE_DIR}/tools/vim/.ycm_extra_conf.py ${build_dir_file} @ONLY)
  add_custom_target(
    configure_ycm
    COMMAND ${CMAKE_COMMAND} -E copy ${build_dir_file} ${source_dir_file}
    COMMENT "Copying YCM config file to source directory"
    VERBATIM
  )
  pika_info(
    "VIM YouCompleteMe: run 'make configure_ycm' to copy config file to source directory and enable support in YCM. To enable automatic loading of configure file, add to your .vimrc option: \"let g:ycm_extra_conf_globlist = ['${PROJECT_SOURCE_DIR}/*']\""
  )
endif()

# ##################################################################################################
# print overall configuration summary
include(pika_print_summary)
pika_create_configuration_summary("Configuration summary:\n--" "pika")

include(pika_export_targets)
# Modules can't link to this if not exported
install(
  TARGETS pika_base_libraries
  EXPORT pika_internal_targets
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT pika_base_libraries
)
pika_export_internal_targets(pika_base_libraries)

# ##################################################################################################
# store cache vars and their values in order for them to be forwarded to the projects (needs to be
# before the PIKA_GeneratePackage call)
include(pika_forward_cache_variables)

# ##################################################################################################
# External build system support.
include(pika_generate_package)

message("")
message("pika will be installed to ${CMAKE_INSTALL_PREFIX}")
message("")
