cmake_minimum_required(VERSION 3.25)

# Needed so GENERATED is propogated See:
# https://cmake.org/cmake/help/latest/policy/CMP0118.html Discussion here:
# https://discourse.cmake.org/t/bug-with-generated-file-and-target-sources-private/5262/11
cmake_policy(SET CMP0118 NEW)

include(FetchContent)

# Needed to generate documentation, changelogs
find_package(Python 3.12 COMPONENTS Interpreter)
option(BUILD_SHARED_LIBS
       "Build using shared libraries. May be ignored on some platforms" ON)
option(PEPP_BUILD_PYTHON "Build only Python bindings" OFF)
option(PEPP_BUILD_GUI "Build GUI applications" ON)
option(PEPP_PRECOMPILE_HEADERS "Enable precompiled headers" ON)
option(SANITIZE "Enable sanitizers" OFF)
option(CODECOV "Enable code coverage" OFF)
option(TIME_TRACE "Enable clang time trace" OFF)

# DO NOT AUTO-FORMAT. CI looks for a line with this format to extract the
# version.
# cmake-format: off
project(Pepp VERSION 0.16.0 LANGUAGES C CXX)
# cmake-format: on

# Allow normalized paths (e.g., "toolchain/pas" ) without nesting that directory
# folder inside a nested "src" hierarchy. May cause linking issues if you do not
# pay attention.
include_directories(${CMAKE_CURRENT_LIST_DIR}/lib)

if(SANITIZE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  add_compile_options(-fsanitize=undefined -fsanitize=address)
  add_link_options(-fsanitize=undefined -fsanitize=address)
endif()
if(TIME_TRACE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  message(STATUS "Enabling clang time trace")
  add_compile_options(-ftime-trace)
endif()

if(PEPP_PRECOMPILE_HEADERS)
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    # Clang stamps the PCH with its sources' times and rejects the PCH when they
    # differ. If using PCH with a compiler cache, this would effectively
    # invalidate the cache. See:
    # https://ccache.dev/manual/latest.html#_precompiled_headers
    add_compile_options(-Xclang -fno-pch-timestamp)
  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    # /Yl is on by default with precompiled headers.
    # CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS exports a marker type and the linker
    # produces a warning (LNK4022 / LNK4002). /Yl- omits the marker, and is a
    # no-op wherever /Yc is not used. Do not combine with /Z7, else it produces
    # LNK1211/LNK4206 instead.
    # https://learn.microsoft.com/en-us/cpp/build/reference/yl-inject-pch-reference-for-debug-library
    add_compile_options(/Yl-)
  endif()
endif()

include(config/cmake/inject_defs.cmake)
include(config/cmake/pch.cmake)
# Not respected by all generators, but incredibly useful when debugging builds.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Needed to avoid exporting every symbol in every DLL on Windows. Some corner
# cases still require we export symbols manually.
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
inject_clang_tidy()
inject_dwarf_debug()
inject_code_coverage(${CODECOV})

# Only overwrite the install prefix if it is the default value.
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX
      "${PROJECT_BINARY_DIR}/install"
      CACHE PATH "Installation Directory" FORCE)
endif()

# 0 is TRACE, which enables all logging in every location. Only works if set as
# a global compile option and before 3rd-party. Default to 3, since I am tired
# of seeing all of my  info messages.
add_compile_options(-DSPDLOG_ACTIVE_LEVEL=3)

if(EMSCRIPTEN)
  add_compile_options(-O1 -gsource-map)
  add_link_options(-sALLOW_MEMORY_GROWTH=1 -sTOTAL_MEMORY=1GB
                   -sINITIAL_MEMORY=150MB -g2 -gsource-map)
  set(QT_WASM_INITIAL_MEMORY
      150MB
      CACHE INTERNAL "" FORCE)
  set(BUILD_SHARED_LIBS FALSE)
endif()

# Use to all output object files (applications, libraries, etc) in a single
# directory. Without this, Windows does a bad job of copying DLLs on change for
# the terminal application. However, it confuses iOS builds and must be disabled
# there.
if(NOT IOS)
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/output")
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/output")
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/output")
endif()
set(PROJECT_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data")

if(PEPP_BUILD_GUI)
  set(QT_QML_GENERATE_QMLLS_INI ON)
  # We always want universal builds, so do not set on a per-target basis
  find_package(
    Qt6 6.9
    COMPONENTS
    REQUIRED
    Svg
    Quick
    Core
    Gui
    Qml
    Test
    Widgets
    QuickControls2
    Xml
    Svg
    Sql
    OPTIONAL_COMPONENTS Concurrent)
  # Starting in Qt 6.10, we get a cmake error in one of our deps if we do not
  # explicitly find these private components. These targets are not explicitly
  # exported prior to 6.10, and attempting to find them on 6.9- will cause
  # configuration to fail.
  if(Qt6_VERSION VERSION_GREATER_EQUAL 6.10)
    find_package(
      Qt6 ${Qt6_VERSION}
      COMPONENTS
      REQUIRED GuiPrivate QuickPrivate)
  endif()
  qt_policy(SET QTP0003 NEW) # Use BUILD_SHARED_LIBS as the default library type
  set(CMAKE_AUTOMOC ON)
  set(CMAKE_AUTORCC ON)
endif()

# Do not enable QtConcurrent on WASM at this time.
if(TARGET Qt6::Concurrent AND NOT EMSCRIPTEN)
  add_compile_definitions(PEPP_HAS_QTCONCURRENT=1)
else()
  add_compile_definitions(PEPP_HAS_QTCONCURRENT=0)
endif()

if(Qt6_VERSION VERSION_GREATER_EQUAL 6.8)
  # Qt6.8/WASM stopped building without this flag being set.
  if(EMSCRIPTEN)
    add_link_options(--bind)
  endif()
  qt_policy(SET QTP0001 NEW)
  qt_policy(SET QTP0004 NEW)
endif()

# Must preced 3rd-party, else our dependencies' exceptions will crash us.
# Qt-dependents would pick this flag up automatically from Qt6::Platform, but
# the non-qt-linked deps need it too.
include(config/cmake/wasm_exception_support.cmake)
add_subdirectory(3rd-party)

enable_testing()

include(config/compile_tests/TryBuildStdRanges.cmake)
include(config/cmake/FindRISCVToolchain.cmake)

add_subdirectory(core)
if(PEPP_BUILD_GUI)
  # Creates special test targets.
  include(config/compile_tests/TryFindWebP.cmake)
  include(config/cmake/GenerateChangelog.cmake)
  add_subdirectory(lib)
  add_subdirectory(test)
endif()
add_subdirectory(bin)
