# Default install location. Must be set here, before setting the project.
if (NOT DEFINED CMAKE_INSTALL_PREFIX)
    set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "" FORCE)
    set(LOCAL_INSTALL "ON")
endif()

cmake_minimum_required(VERSION 3.25)
project(lomiri-storage-framework VERSION "0.5.0" LANGUAGES C CXX)

option(ENABLE_QT6 "Enable Qt6 build" OFF)

# These variables should be incremented when we wish to create a new
# source incompatible version of the library where users of the
# old API will not compile against the new one.  It is not
# necessary to increment this for ABI breaks that are source compatible.
if(ENABLE_QT6)
    set(LSF_CLIENT_API_VERSION "1")
else()
    set(LSF_CLIENT_API_VERSION "2")
endif()
set(LSF_PROVIDER_API_VERSION "1")

# These two should be incremented when the ABI changes.
set(LSF_CLIENT_SOVERSION "0")
set(LSF_PROVIDER_SOVERSION "1")
set(LSF_CLIENT_LIBVERSION "${LSF_CLIENT_SOVERSION}.${PROJECT_VERSION}")
set(LSF_PROVIDER_LIBVERSION "${LSF_PROVIDER_SOVERSION}.${PROJECT_VERSION}")


string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower) # Build types should always be lower case

set(ACCEPTED_BUILD_TYPES "" none release debug relwithdebinfo coverage)
list(FIND ACCEPTED_BUILD_TYPES "${cmake_build_type_lower}" IS_BUILD_TYPE_ACCEPTED)
if (${IS_BUILD_TYPE_ACCEPTED} EQUAL -1)
    message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\nValid types are: ${ACCEPTED_BUILD_TYPES}")
endif()

# Ensure that generated files can be linked with.
set(CMAKE_INCLUDE_CURRENT_DIR ON)

include_directories(include)
include_directories(${CMAKE_BINARY_DIR}/include)  # For generated headers

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")

# Some additional warnings not included by the general flags set above.
set(EXTRA_C_WARNINGS "-Wcast-align -Wcast-qual -Wformat -Wredundant-decls -Wswitch-default")
set(EXTRA_CXX_WARNINGS "-Wnon-virtual-dtor -Wctor-dtor-privacy -Wold-style-cast")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_C_WARNINGS} ${EXTRA_CXX_WARNINGS}")

# By default, for release builds, warnings become hard errors.
if ("${cmake_build_type_lower}" STREQUAL "release" OR "${cmake_build_type_lower}" STREQUAL "relwithdebinfo")
    option(Werror "Treat warnings as errors" ON)
else()
    option(Werror "Treat warnings as errors" OFF)
endif()

# If warnings are errors, don't error on deprecated declarations.
if (${Werror})
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
    if ("${cmake_build_type_lower}" STREQUAL "release" OR "${cmake_build_type_lower}" STREQUAL "relwithdebinfo")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations")
    endif()
endif()

set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)

# Flags for address and undefined behavior sanitizer
set(SANITIZER "" CACHE STRING "Build with -fsanitize=<value> (legal values: address, ub)")

if ("${SANITIZER}" STREQUAL "")
    # Do nothing
elseif (${SANITIZER} STREQUAL "ub")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-omit-frame-pointer -g")
elseif (${SANITIZER} STREQUAL "address")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -g")
else()
    message(FATAL_ERROR "Invalid SANITIZER setting: ${SANITIZER}")
endif()

# Some tests are slow, so make it possible not to run them
# during day-to-day development.
option(slowtests "Run slow tests" ON)
if (${slowtests})
    add_definitions(-DSLOW_TESTS=1)
else()
    add_definitions(-DSLOW_TESTS=0)
endif()

# Definitions for testing with valgrind.

configure_file(CTestCustom.cmake.in CTestCustom.cmake) # Tests in CTestCustom.cmake are skipped for valgrind

find_program(MEMORYCHECK_COMMAND NAMES valgrind)
if (MEMORYCHECK_COMMAND)
    set(MEMORYCHECK_COMMAND_OPTIONS
        "--suppressions=${CMAKE_SOURCE_DIR}/valgrind-suppress --errors-for-leak-kinds=definite --show-leak-kinds=definite --leak-check=full --num-callers=50 --error-exitcode=3"
    )
    add_custom_target(valgrind DEPENDS NightlyMemCheck)
else()
    message(WARNING "Cannot find valgrind: valgrind target will not be available")
endif()

include(CTest)
enable_testing()

find_package(CoverageReport)

option(SNAP_BUILD "Build for snap release")
if("${SNAP_BUILD}")
    add_definitions(-DSNAP_BUILD=1)
endif()

include(GNUInstallDirs)

find_package(Boost 1.56 COMPONENTS filesystem system thread REQUIRED)

if(ENABLE_QT6)
    find_package(Qt6
        COMPONENTS Core Concurrent DBus Network Qml Test
        REQUIRED)

    qt_standard_project_setup()

    set(QT_VERSION_MAJOR 6)
else()
    find_package(Qt5
        COMPONENTS Core Concurrent DBus Network Qml Test
        REQUIRED)

    set(QT_VERSION_MAJOR 5)
endif()

include(FindPkgConfig)
pkg_check_modules(APPARMOR_DEPS REQUIRED IMPORTED_TARGET libapparmor)
pkg_check_modules(GIO_DEPS REQUIRED IMPORTED_TARGET gio-2.0 gio-unix-2.0)
pkg_check_modules(GLIB_DEPS REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(LIBLOMIRI_API_DEPS REQUIRED IMPORTED_TARGET liblomiri-api)
if(ENABLE_QT6)
    pkg_check_modules(ONLINEACCOUNTS_DEPS REQUIRED IMPORTED_TARGET LomiriOnlineAccountsQt6)
else()
    pkg_check_modules(ONLINEACCOUNTS_DEPS REQUIRED IMPORTED_TARGET LomiriOnlineAccountsQt5)
endif()

add_definitions(-DQT_NO_KEYWORDS)

add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(plugins/Lomiri/StorageFramework)
add_subdirectory(tests)
add_subdirectory(demo)
add_subdirectory(tools)

enable_coverage_report(
    TARGETS
        local-provider-lib
        qt-client-lib-common
        lomiri-storage-framework-common-internal
        lomiri-storage-framework-qt-client
        lomiri-storage-framework-qt-client-v2
        lomiri-storage-framework-qt-local-client
        lsf-provider-objects
        lomiri-storage-framework-provider
        registry-static
        lomiri-storage-framework-registry
        lomiri-storage-provider-local
    FILTER
        ${CMAKE_SOURCE_DIR}/tests/*
        ${CMAKE_BINARY_DIR}/*
    TESTS
        ${UNIT_TEST_TARGETS}
)
