# Init CMake (require at least version 3.22)
CMAKE_MINIMUM_REQUIRED(VERSION 3.22)

# Project name and version
PROJECT(
	dv-runtime
	VERSION 1.7.2
	LANGUAGES C CXX)

OPTION(DVR_ENABLE_TCMALLOC "Use Google Perftools' TCMalloc (fast memory allocation, heap checking/profiling)." ON)
OPTION(DVR_ENABLE_PROFILER "Use Google Perftools' Profiler (CPU profiling)." ON)

# Git submodule automatic update support.
FIND_PACKAGE(Git QUIET)

IF(Git_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
	# Update submodules as needed
	OPTION(GIT_SUBMODULE "Check-out submodules during build" ON)

	IF(GIT_SUBMODULE)
		MESSAGE(STATUS "Git submodule update")

		EXECUTE_PROCESS(
			COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
			WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
			RESULT_VARIABLE GIT_SUBMOD_RESULT)
		IF(NOT
		   GIT_SUBMOD_RESULT
		   EQUAL
		   "0")
			MESSAGE(
				FATAL_ERROR
					"${GIT_EXECUTABLE} submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please check-out submodules manually."
			)
		ENDIF()
	ENDIF()
ENDIF()

# Configurable features.
IF(NOT DVR_ENABLE_SANITIZER)
	SET(DVR_ENABLE_SANITIZER
		"None"
		CACHE STRING "Possible sanitizers: None, Address, Memory[Only with Clang], Undefined, Thread." FORCE)
	# Set the possible values for cmake-gui
	SET_PROPERTY(
		CACHE DVR_ENABLE_SANITIZER
		PROPERTY STRINGS
				 "None"
				 "Address"
				 "Memory"
				 "Undefined"
				 "Thread")
ENDIF()

IF(NOT DVR_ENABLE_STATIC_ANALYSIS)
	SET(DVR_ENABLE_STATIC_ANALYSIS
		"None"
		CACHE STRING "Possible static analysis tools: clang-tidy, iwyu, cppcheck." FORCE)
	# Set the possible values for cmake-gui
	SET_PROPERTY(
		CACHE DVR_ENABLE_STATIC_ANALYSIS
		PROPERTY STRINGS
				 "clang-tidy"
				 "iwyu"
				 "cppcheck")
ENDIF()

# dv-processing base classes.
FIND_PACKAGE(dv-processing 2.0.0 REQUIRED)

# Custom cmake modules. inivation-setup is included by dv-processing already.
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})

# Boost support for extra components. dv-processing will already have set this up.
FIND_PACKAGE(Boost 1.80 REQUIRED COMPONENTS nowide filesystem program_options)

# Cross-compile support
IF(NOT USER_LOCAL_PREFIX)
	SET(USER_LOCAL_PREFIX ${CMAKE_INSTALL_PREFIX})
ENDIF()

# Define module installation paths.
SET(DV_MODULES_DIR ${CMAKE_INSTALL_DATAROOTDIR}/dv/modules)
MESSAGE(STATUS "Filesystem modules installation directory is: ${CMAKE_INSTALL_PREFIX}/${DV_MODULES_DIR}")

ADD_DEFINITIONS(-DDV_MODULES_DIR=${USER_LOCAL_PREFIX}/${DV_MODULES_DIR})
MESSAGE(STATUS "Actual modules installation directory is: ${USER_LOCAL_PREFIX}/${DV_MODULES_DIR}")

# Static analysis tools support
IF(NOT
   DVR_ENABLE_STATIC_ANALYSIS
   STREQUAL
   "None")
	MESSAGE(STATUS "Enabling static analysis tool: ${DVR_ENABLE_STATIC_ANALYSIS}")

	IF(DVR_ENABLE_STATIC_ANALYSIS STREQUAL "clang-tidy")
		FIND_PROGRAM(CLANG_TIDY_PATH NAMES clang-tidy)

		IF(CLANG_TIDY_PATH)
			SET(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_PATH})
			SET(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_PATH})
		ENDIF()
	ENDIF()

	IF(DVR_ENABLE_STATIC_ANALYSIS STREQUAL "cppcheck")
		FIND_PROGRAM(CPPCHECK_PATH NAMES cppcheck)

		IF(CPPCHECK_PATH)
			SET(CMAKE_C_CPPCHECK ${CPPCHECK_PATH})
			SET(CMAKE_CXX_CPPCHECK ${CPPCHECK_PATH})
		ENDIF()
	ENDIF()

	IF(DVR_ENABLE_STATIC_ANALYSIS STREQUAL "iwyu")
		FIND_PROGRAM(IWYU_PATH NAMES include-what-you-use iwyu)

		IF(IWYU_PATH)
			SET(CMAKE_C_INCLUDE_WHAT_YOU_USE ${IWYU_PATH})
			SET(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH})
		ENDIF()
	ENDIF()
ENDIF()

# Sanitizers and testing support
SET(SANITIZER_TEST_FLAGS "")

IF(NOT
   DVR_ENABLE_SANITIZER
   STREQUAL
   "None")
	MESSAGE(STATUS "Enabling sanitizer: ${DVR_ENABLE_SANITIZER}")

	IF(DVR_ENABLE_SANITIZER STREQUAL "Address")
		SET(SANITIZER_TEST_FLAGS "ASAN_OPTIONS=detect_stack_use_after_return=1:intercept_tls_get_addr=0")
		SET(SANITIZER_FLAGS "-fno-common -fsanitize=address -fsanitize-address-use-after-scope")
	ENDIF()

	IF(DVR_ENABLE_SANITIZER STREQUAL "Memory" AND CC_CLANG)
		SET(SANITIZER_TEST_FLAGS "MSAN_OPTIONS=poison_in_dtor=1")
		SET(SANITIZER_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor")
	ENDIF()

	IF(DVR_ENABLE_SANITIZER STREQUAL "Undefined")
		SET(SANITIZER_TEST_FLAGS
			"UBSAN_OPTIONS=print_stacktrace=1,suppressions=${CMAKE_CURRENT_SOURCE_DIR}/docs/undefined_sanitizer_suppress.txt"
		)

		IF(CC_CLANG)
			SET(SANITIZER_FLAGS "-fsanitize=undefined,float-divide-by-zero,implicit-conversion,local-bounds")
		ELSE()
			SET(SANITIZER_FLAGS "-fsanitize=undefined,float-divide-by-zero,float-cast-overflow")
		ENDIF()
	ENDIF()

	IF(DVR_ENABLE_SANITIZER STREQUAL "Thread")
		SET(SANITIZER_TEST_FLAGS
			"TSAN_OPTIONS=detect_deadlocks=1,second_deadlock_stack=1,suppressions=${CMAKE_CURRENT_SOURCE_DIR}/docs/thread_sanitizer_suppress.txt"
		)
		SET(SANITIZER_FLAGS "-fsanitize=thread")
	ENDIF()

	ADD_COMPILE_DEFINITIONS(DISABLE_SHARED_UNLOAD)

	SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}")
	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
	SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
	SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_FLAGS}")

	# Enable minimal optimization to get acceptable performance in debug mode
	SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O1 -fno-omit-frame-pointer")
	SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fno-omit-frame-pointer")
ENDIF()

IF(OS_WINDOWS)
	# Set common output directory for build results, for packaging and testing.
	SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/all_bins/)
ENDIF()

# Install header files.
ADD_SUBDIRECTORY(include)

# Compile libdvsdk and dv-runtime main executable.
ADD_SUBDIRECTORY(src)

# Compile extra modules and utilities.
ADD_SUBDIRECTORY(modules)
ADD_SUBDIRECTORY(utils)

# Packaging and tests.
ADD_SUBDIRECTORY(packaging)
ADD_SUBDIRECTORY(automated_test)

# Install CMake file for modules
INSTALL(FILES cmake/dv-modules.cmake DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dv)

# Install systemd unit file
IF(OS_LINUX)
	MESSAGE(STATUS "Installing systemd service into ${CMAKE_INSTALL_PREFIX}/lib/systemd/system/")

	INSTALL(FILES service/dv-runtime.service DESTINATION lib/systemd/system/)
	INSTALL(FILES service/dv-runtime.sysusers.conf DESTINATION lib/sysusers.d/)
ENDIF()

# Support automatic RPM generation
SET(CPACK_PACKAGE_NAME ${PROJECT_NAME})
SET(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
SET(CPACK_PACKAGE_RELEASE 1)
SET(CPACK_PACKAGE_CONTACT "support@inivation.com")
SET(CPACK_PACKAGE_VENDOR "iniVation AG")
SET(CPACK_PACKAGE_DESCRIPTION
	"C++ event-based processing framework for neuromorphic cameras, targeting embedded and desktop systems.")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION})
SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}.${CMAKE_SYSTEM_PROCESSOR}")
SET(CPACK_GENERATOR "RPM" "DEB")
SET(CPACK_RPM_PACKAGE_AUTOREQ 1)
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS 1)

INCLUDE(CPack)
