###########################################################################
# top-level CMakeLists.txt for building BornAgain
############################################################################

### CMake configuration

cmake_minimum_required(VERSION 3.20 FATAL_ERROR)

set(CMAKE_MODULE_PATH
    ${CMAKE_SOURCE_DIR}/cmake
    ${CMAKE_SOURCE_DIR}/cmake/find
    ${CMAKE_SOURCE_DIR}/cmake/pack)
set(CONFIGURABLES_DIR ${CMAKE_SOURCE_DIR}/cmake/configurables)

include(BornAgain/Git)
message(STATUS "Git branch '${GIT_BRANCH}' at commit ${GIT_COMMIT}, tag: '${GIT_TAG}'")

include(commons/PreventInSourceBuilds)


### Project settings

project(BornAgain
    VERSION 21.1
    DESCRIPTION "BornAgain: simulate and fit reflectometry and grazing-incidence scattering."
    HOMEPAGE_URL https://www.bornagainproject.org
    LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)


### Options

# options that are on by default (switch off for accelerated builds of limited scope)
option(BORNAGAIN_PYTHON "Build with Python support" ON)
option(BA_GUI "Build a graphical user interface" ON)
option(BA_TIFF_SUPPORT "Tiff files read/write support" ON)
option(BA_TESTS "Build tests" ON)

# options that are off by default (switch on for additional functionality)
option(CONFIGURE_BINDINGS "Generate python bindings during build (requires swig)" OFF)
option(CONFIGURE_EXAMPLES "Configure examples (requires ruby)" OFF)
option(CONFIGURE_DOXY "Configure Doxygen files" OFF)
option(ALLCONFIG "Regenerate Py wrappers, examples, Doxyfiles" OFF)
option(ZERO_TOLERANCE "Terminate compilation on warnings" OFF)
option(DEVELOPER_CHECKS "Checks required from developers but not from external users" OFF)
option(DEV "Development: turns ALLCONFIG, ZERO_TOLERANCE, DEVELOPER_CHECKS on" OFF)
option(BA_COVERAGE "Build with test coverage information" OFF)
option(BA_DEBUG_OPTIMIZATION "Build with debug optimization (gcc only)" OFF)
option(BA_TIDY "Invokes clang-tidy" OFF)
option(BATCH_MODE "Suppress some output" OFF)
option(ALGORITHM_DIAGNOSTIC "Let some algorithms set diagnostic variables" OFF)
option(BA_PY_PACKAGE "Build a Python wheel with the default Python platform, or a given Python platform (via BA_PY_PLATFORM)" OFF)
option(BUILD_DEBIAN "Build a debian package" OFF)
option(BA_APPLE_BUNDLE "Create a MacOS bundle" OFF)
option(BA_CPP_API "Install header files" OFF)

# options that set other options
set(BA_PY_PLATFORM "" CACHE INTERNAL "Path to Python platform to build a Python package")
string(STRIP "${BA_PY_PLATFORM}" BA_PY_PLATFORM)
if(BA_PY_PLATFORM)
    set(BA_PY_PACKAGE ON)
endif()

if(DEV)
    set(ALLCONFIG ON)
    set(ZERO_TOLERANCE ON)
    set(DEVELOPER_CHECKS ON)
endif()
if(ALLCONFIG)
    set(CONFIGURE_BINDINGS ON)
    set(CONFIGURE_EXAMPLES ON)
    set(CONFIGURE_DOXY ON)
endif()
if(BATCH_MODE)
    set(BA_GUI OFF)
endif()

# check compatibility of options

if(BA_TIDY AND BORNAGAIN_PYTHON)
    message(FATAL_ERROR "BA_TIDY is incompatible with BORNAGAIN_PYTHON")
endif()

if(BA_GUI AND NOT BORNAGAIN_PYTHON AND NOT BA_TIDY)
    message(FATAL_ERROR "BA_GUI without BORNAGAIN_PYTHON is currently unsupported"
        " (except with BA_TIDY)")
endif()

### Generator type (Release|Debug)

# Show info about generator type; set CMAKE_BUILD_TYPE if not given
if(CMAKE_CONFIGURATION_TYPES)
    message(STATUS "Generator type: Multi-configuration generator")
else()
    message(STATUS "Generator type: Single-configuration generator")
endif()
message("    CMAKE_CONFIGURATION_TYPES: ${CMAKE_CONFIGURATION_TYPES}")
# The following is not correct/does not have any effect for multi-configuration generators.
# But when correcting this, be aware that CMAKE_BUILD_TYPE is used in more scripts!
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
    message("    CMAKE_BUILD_TYPE type not given - forced to 'Release'")
else()
    string(TOUPPER "${CMAKE_BUILD_TYPE}" _upper_build_type)
    set(BUILD_${_upper_build_type} 1)
endif()
message("    CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
configure_file("${PROJECT_SOURCE_DIR}/cmake/configurables/config_build.h.in"
    "${PROJECT_BINARY_DIR}/inc/config_build.h"
    @ONLY)


### Configure tests

if(BA_TESTS)
    # GoogleTest: `gtest_discover_tests` discovers tests by asking the _compiled test executable_
    # to enumerate its tests. `PRE_TEST` delays test discovery until just prior to test execution;
    # this avoids calling the executables during the build phase.
    set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE PRE_TEST)

    include(CTest) # equivalent to "enable_testing() ???
    add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -LE Fullcheck)
       # => 'make check' is an alias for 'ctest'
    add_custom_target(fullcheck COMMAND ${CMAKE_CTEST_COMMAND})
       # => 'make check' is an alias for 'ctest'
   endif()


### Operating system

# introduce `LINUX` flag (along with `APPLE` and `WIN32`)
if(UNIX AND CMAKE_SYSTEM_NAME MATCHES Linux)
    set(LINUX ON)
endif()

# the host system must be Linux, Apple or Windows
if(NOT (LINUX OR APPLE OR WIN32))
    message(FATAL_ERROR "Operating system ${CMAKE_SYSTEM_NAME} is not supported")
endif()


### Further configuration
# list of original core libraries needed to build the MacOS package
set(BornAgain_LIBRARIES "" CACHE INTERNAL "BornAgain libraries")

# function definitions
include(commons/GetFilenameComponent) # fct get_filename_component (overwrites CMake's built-in)
include(BornAgain/SwigLib) # fct SwigLib
include(BornAgain/MakeLib) # fct MakeLib

# main settings
include(BornAgain/Directories)
include(BornAgain/Dependences)

if(WIN32)
    include(BornAgain/InstallDll)
endif()

# Python
if(BORNAGAIN_PYTHON OR BA_PY_PACKAGE)
    include(multipython/PyDependences)
endif()

if(BORNAGAIN_PYTHON)
    include(BornAgain/PythonAPI)
endif()

if(BA_GUI)
    include(BornAgain/Qt)
    string(APPEND CMAKE_CXX_FLAGS " -DHAVE_QT=ON")
endif()

if(UNIX)
    if(LINUX)
        include(BornAgain/Linux)
    elseif(APPLE)
        include(BornAgain/MacOS)
    endif()
    include(BornAgain/NixInstall)
elseif(WIN32)
    include(BornAgain/Windows)
endif()

include(BornAgain/Config)

include(BornAgain/Compiler)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    set(CLANG ON)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(GCC ON)
    string(APPEND CMAKE_CXX_FLAGS " -fdiagnostics-color=always")
elseif(NOT WIN32)
    message(FATAL_ERROR "Unsupported compiler, id=${CMAKE_CXX_COMPILER_ID}")
endif()

if(ZERO_TOLERANCE)
    string(APPEND CMAKE_CXX_FLAGS " -Werror -Wfatal-errors")
endif()
if(ALGORITHM_DIAGNOSTIC)
    string(APPEND CMAKE_CXX_FLAGS " -DALGORITHM_DIAGNOSTIC=ON")
endif()
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -DBA_DEBUG")

# debug optimization
if(BA_DEBUG_OPTIMIZATION)
  include(commons/DebugOptimization)
endif()


### Configure source components

# library Fit
add_subdirectory(Fit/3rdparty)
add_subdirectory(Fit)

# other components
set(CoreComponents "PyCore;Base;Param;Sample;Resample;Device;Sim")
set(AllComponents "${CoreComponents};Img3D;GUI")

# code analysis
include(BornAgain/LineLength)
include(BornAgain/Format)
if(BA_COVERAGE)
  include(commons/CoverageFunction)
  include(BornAgain/Coverage)
endif()

# third-party code
add_subdirectory(3rdparty/common)
add_subdirectory(3rdparty/Core)
if(BA_GUI)
    add_subdirectory(3rdparty/GUI/qcustomplot)
endif()

# from here on our own code, occasionally scrutinized by clang-tidy
if(BA_TIDY)
    set(CMAKE_CXX_CLANG_TIDY "clang-tidy") # has effect only if compiler is clang; uses .clang-tidy
endif()

# configure core component libraries
foreach(lib ${CoreComponents})
    add_subdirectory(${lib})
endforeach()
if(BORNAGAIN_PYTHON)
    add_dependencies(BornAgainFit swig_runtime)
    if (CONFIGURE_EXAMPLES)
        add_subdirectory(rawEx)
    endif()
endif()

# GUI
if(BA_GUI)
    add_subdirectory(Img3D)
    add_subdirectory(GUI)
    add_subdirectory(App)
endif()

# tests
if(BA_TESTS)
    add_subdirectory(Tests/Unit)
    if(BORNAGAIN_PYTHON)
        add_subdirectory(Tests/Examples)
        add_subdirectory(Tests/Py)
    endif()
    add_subdirectory(Tests/SimFactory)
    add_subdirectory(Tests/Functional)
    add_subdirectory(Tests/Suite)
endif()

# documentation
# TODO: 'hugo' depends on 'excopy' and 'figures' which are produced in Tests/Examples
# The tests and documentation should be decoupled.
if(BORNAGAIN_PYTHON AND BA_TESTS) # required for examples
    add_subdirectory(hugo)
endif()
add_subdirectory(Doc/man)
if(CONFIGURE_DOXY)
    add_subdirectory(Doc/Doxygen)
endif()


### Finalize

# after-install message
add_subdirectory(cmake)

# make package targets
include(BornAgain/Pack)

# build log
include(BornAgain/BuildLog)
install(FILES ${BA_BUILD_LOG} DESTINATION ${destination_share})
message(STATUS "BornAgain build log written to '${BA_BUILD_LOG}'")

message(STATUS "CMake done")
