# ==============================
# File:     CMakeLists.txt
# Project:  Einstein
#
# Copyright 2003-2026 by Paul Guyot (pguyot@kallisys.net) and others.
# ==============================

cmake_minimum_required(VERSION 3.15.0 FATAL_ERROR)

# Minimum macOS version. This must be set before project(), so it applies to
# all targets including FLTK and NEWT64. Override with -D CMAKE_OSX_DEPLOYMENT_TARGET=...
set( CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS version to deploy to" )

project( "Einstein" VERSION "2026.10.11" )

# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
	cmake_policy(SET CMP0135 NEW)
endif()

# ---- Configuration options

# Default to FLTK front end. The SDL3 option is under development.
option (EINSTEIN_FLTK_FRONTEND "Use FLTK for the frontend" OFF)
option (EINSTEIN_SDL3_FRONTEND "Use SDL3 for the frontend" OFF)

# fluid is only needed to edit .fl files and for the 'fluid_update' target.
# The CI builds don't need it. It is never built when cross compiling.
option (EINSTEIN_BUILD_FLUID "Build the FLTK fluid UI designer" ON)

# The unit tests (mainly for the JIT) are built and run by their own CI workflow.
# Turning this on downloads googletest and adds the EinsteinTests target.
option (EINSTEIN_BUILD_TESTS "Build the EinsteinTests unit tests" OFF)

if ( EINSTEIN_FLTK_FRONTEND AND EINSTEIN_SDL3_FRONTEND )
	message( FATAL_ERROR "Cannot select the FLTK and SDL3 frontend at the same time. Please choose either EINSTEIN_FLTK_FRONTEND or EINSTEIN_SDL3_FRONTEND." )
elseif ( NOT EINSTEIN_FLTK_FRONTEND AND NOT EINSTEIN_SDL3_FRONTEND )
	message( STATUS "Defaulting to FLTK frontend." )
	set ( EINSTEIN_FLTK_FRONTEND ON )
endif()

# ---- Configuration for all targets on all platforms

set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
set( CMAKE_CXX_EXTENSIONS OFF )

# ---- Include some additional functionality

include( CheckSymbolExists )
include( CheckFunctionExists )
include( CheckIncludeFile )
include( FetchContent )

# ---- Configuration per platform

if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" )

	# TODO: enable this for release builds
	#set( CMAKE_OSX_ARCHITECTURES arm64;x86_64 )
	# TODO: Fix this for release builds
	set( CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO" )
	set( CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" )

elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" )

	# Linux: nothing to do here

elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" )

	set_property ( GLOBAL PROPERTY FIND_LIBRARY_USE_OPENBSD_VERSIONING 1 )

elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" )

	# allow to build with multithreading libraries with VisualC
	#	cmake_policy( SET CMP0091 NEW )
	#	set( CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" )

elseif ( ANDROID )

	if ( NOT EINSTEIN_SDL3_FRONTEND )
	    message( WARNING "Changing frontend for Android to SDL3." )
    	set ( EINSTEIN_FLTK_FRONTEND OFF )
    	set ( EINSTEIN_SDL3_FRONTEND ON )
	endif ()

else ()

	# Warn the caller that this is not supported
	message( FATAL_ERROR "Einstein is not configured to build on this platform: " ${CMAKE_SYSTEM_NAME} "." )

endif ()


#
# ==== Embedding Binary Data ===================================================
#

function(generate_embedded_file resource_file_name source_file_name variable_name)
    file(READ ${resource_file_name} hex_content HEX)
    string(REPEAT "[0-9a-f]" 32 column_pattern)
    string(REGEX REPLACE "(${column_pattern})" "\\1\n" content "${hex_content}")
    string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " content "${content}")
    string(REGEX REPLACE ", $" "" content "${content}")
    set(array_extern "extern const unsigned char ${variable_name}[];\n")
    set(array_definition "const unsigned char ${variable_name}[] =\n{\n${content}\n};")
    set(source "// Auto generated file.\n${array_extern}${array_definition}\n\nunsigned int ${variable_name}Size = sizeof(${variable_name});\n\n")
    file(WRITE "${source_file_name}" "${source}")
endfunction()

function ( embed_resource resource_file_name source_file_name variable_name )
	set_source_files_properties (
		${CMAKE_SOURCE_DIR}/${source_file_name}
		PROPERTIES GENERATED TRUE
	)
    if ( NOT EXISTS ${CMAKE_SOURCE_DIR}/${source_file_name}
         OR ${CMAKE_SOURCE_DIR}/${resource_file_name} IS_NEWER_THAN ${CMAKE_SOURCE_DIR}/${source_file_name}
       )
		generate_embedded_file ( ${resource_file_name} ${source_file_name} ${variable_name} )
	endif ()
endfunction ()


#
# ==== External Dependencies ===================================================
#
# ---- Declare dependencies on outside content
#

if ( EINSTEIN_FLTK_FRONTEND )

FetchContent_Declare(
  FLTK
  GIT_REPOSITORY  https://github.com/fltk/fltk
  GIT_TAG         release-1.4.5
  GIT_SHALLOW     TRUE
)

FetchContent_Declare(
  NEWT64
  GIT_REPOSITORY  https://github.com/MatthiasWM/NEWT64
  GIT_TAG         master
)

endif ()

if ( EINSTEIN_SDL3_FRONTEND )

if ( NOT ANDROID )
FetchContent_Declare(
    SDL3
    GIT_REPOSITORY https://github.com/libsdl-org/SDL
    # GIT_TAG release-3.2.x
    GIT_TAG release-3.2.16
    # GIT_TAG "main"
    EXCLUDE_FROM_ALL
)
endif ()

endif ()


#
# ---- Newt/64 library ---------------------------------------------------------
#

if ( EINSTEIN_FLTK_FRONTEND )

message(STATUS "Downloading and configuring NEWT64 if necessary, please wait...")
FetchContent_MakeAvailable(NEWT64)
message(STATUS "Downloading and configuring NEWT64 - done.")
# Newt64_BINARY_DIR
set( NEWT64_FOUND true )
set( newt64_incl_path "${Newt64_SOURCE_DIR}/src/newt_core/incs/" )
set( newt64_lib libnewt64 )

endif ()

#
# ---- FLTK library ------------------------------------------------------------
#

if ( EINSTEIN_FLTK_FRONTEND )

message(STATUS "Downloading and configuring FLTK if necessary, please wait...")
set( FLTK_USE_SYSTEM_LIBJPEG OFF )
set( FLTK_USE_SYSTEM_ZLIB OFF )
set( FLTK_USE_SYSTEM_LIBPNG OFF )
set( FLTK_BUILD_TEST OFF )
set( FLTK_BUILD_GL OFF )
set( FLTK_BUILD_FORMS OFF )
set( FLTK_BUILD_FLTK_OPTIONS OFF )
if ( CMAKE_CROSSCOMPILING OR NOT EINSTEIN_BUILD_FLUID )
	# fluid is not needed to build Einstein, and it can't run on the build host when cross compiling
	set( FLTK_BUILD_FLUID OFF )
endif ()
FetchContent_MakeAvailable(FLTK)
message(STATUS "Downloading and configuring FLTK - done.")

# The code that fluid generates from .fl files is stored in git next to the .fl
# files, so Einstein can be built without running fluid (which is not possible
# when cross compiling). After changing a .fl file, regenerate the code by
# saving the file in fluid, or by building the 'fluid_update' target.
function ( check_fluid_sources name dir )
	set( fl_file "${CMAKE_SOURCE_DIR}/${dir}/${name}.fl" )
	# the code is written by fluid, so clang-format must not check it
	set_source_files_properties (
		"${CMAKE_SOURCE_DIR}/${dir}/${name}.cpp"
		"${CMAKE_SOURCE_DIR}/${dir}/${name}.h"
		PROPERTIES EINSTEIN_FLUID_OUTPUT TRUE
	)
	# re-run this check when the .fl file changes
	set_property ( DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${fl_file}" )
	file ( TIMESTAMP "${fl_file}" fl_time "%s" )
	foreach ( ext cpp h )
		file ( TIMESTAMP "${CMAKE_SOURCE_DIR}/${dir}/${name}.${ext}" code_time "%s" )
		if ( "${code_time}" STREQUAL "" OR fl_time GREATER code_time )
			message ( WARNING "${dir}/${name}.fl is newer than ${dir}/${name}.${ext}. "
				"Please regenerate the code with fluid, or build the 'fluid_update' target." )
		endif ()
	endforeach ()
	if ( TARGET fltk::fluid )
		add_custom_target ( fluid_update_${name}
			COMMAND fltk::fluid -c ${name}.fl
			WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/${dir}"
			COMMENT "Generating ${dir}/${name}.cpp and .h with fluid"
			VERBATIM
		)
		if ( NOT TARGET fluid_update )
			add_custom_target ( fluid_update )
		endif ()
		add_dependencies ( fluid_update fluid_update_${name} )
	endif ()
endfunction ()

endif ()

#
# ---- SDL3 library ------------------------------------------------------------
#

if ( EINSTEIN_SDL3_FRONTEND )

if ( ANDROID )

# find "android-project/app/libs/SDL3-3.2.16.aar"
message(STATUS "Fetching prebuilt SDL3...")
set( SDL_SHARED ON )
set( SDL_STATIC ON )
find_package(SDL3 REQUIRED CONFIG)

else ()

message(STATUS "Downloading and configuring SDL3 if necessary, please wait...")
set( SDL_SHARED OFF )
set( SDL_STATIC ON )
FetchContent_MakeAvailable(SDL3)
message(STATUS "Downloading and configuring SDL3 - done.")

endif ()

endif ()

#
# ==== Configuring Einstein Build ==============================================
#
# ---- Einstein Source Files ---------------------------------------------------
#

set( cmake_sources )
set( common_sources )
set( app_sources )
set( test_sources )
set( fluid_sources )

# collect source code from the source code directory tree
include( app/CMakeLists.txt )
include( Emulator/CMakeLists.txt )
include( Monitor/CMakeLists.txt )
include( K/CMakeLists.txt )
if ( NEWT64_FOUND MATCHES true )
	include( Toolkit/CMakeLists.txt )
endif ()
include( _Tests_/CMakeLists.txt )

if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" )
	# additional source code
	list ( APPEND app_sources
		# Apple macOS required resources
		Resources/macOS/Info.plist.in
		Resources/macOS/Einstein.icns
		Resources/macOS/Entitlements.plist
	)
endif ()

#
# ---- Common IDE setup --------------------------------------------------------
#

set_property( GLOBAL PROPERTY USE_FOLDERS ON )
source_group( TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES ${cmake_sources} )
source_group( TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES ${common_sources} )
source_group( TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES ${app_sources} )
source_group( TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES ${test_sources} )
source_group( TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES ${fluid_sources} )
if ( WIN32 )
	source_group(TREE "${CMAKE_SOURCE_DIR}" PREFIX "Sources" FILES Resources/MSWindows/Einstein.rc.in)
endif ()

#
# ---- Platform specific rules ----------------------------------------------------------
#

if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" )

	# take care of the app icon
	set( MACOSX_BUNDLE_ICON_FILE "Einstein.icns" )
    set( MACOSX_BUNDLE_GUI_IDENTIFIER "org.messagepad.einstein" )
	set_source_files_properties (
		Resources/macOS/${MACOSX_BUNDLE_ICON_FILE}
		PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
	)

	# create the applications
	add_executable ( Einstein MACOSX_BUNDLE
		${common_sources}
		${app_sources}
		${cmake_sources}
	)
	if ( EINSTEIN_FLTK_FRONTEND )
		add_library ( EinsteinFLGUI STATIC
			${fluid_sources}
		)
	endif()

	# how to compile and link
	target_compile_options( Einstein PUBLIC
		-Wall -Wextra -Wpedantic -Wno-missing-field-initializers -Werror
	)
	if ( EINSTEIN_FLTK_FRONTEND )
		target_compile_options( EinsteinFLGUI PUBLIC
			-Wall -Wextra -Wpedantic -Wno-missing-field-initializers -Werror
		)
	endif ()
	if ( EINSTEIN_FLTK_FRONTEND )
		target_compile_definitions ( Einstein PRIVATE
			TARGET_UI_FLTK=1 NO_PORT_AUDIO NO_X11 TARGET_OS_OPENSTEP=1
			TARGET_OS_MAC=1 NS_BLOCK_ASSERTIONS=1
		)
	endif ()
	if ( EINSTEIN_SDL3_FRONTEND )
		target_compile_definitions ( Einstein PRIVATE
			TARGET_UI_SDL=1 NO_PORT_AUDIO NO_X11 TARGET_OS_OPENSTEP=1
			TARGET_OS_MAC=1 NS_BLOCK_ASSERTIONS=1
		)
	endif ()
	if ( EINSTEIN_FLTK_FRONTEND )
		target_compile_definitions ( EinsteinFLGUI PRIVATE
			TARGET_UI_FLTK=1 NO_PORT_AUDIO NO_X11 TARGET_OS_OPENSTEP=1
			TARGET_OS_MAC=1 NS_BLOCK_ASSERTIONS=1
		)
	endif ()
	target_link_libraries ( Einstein
		${system_libs}
		pthread
		"-framework AddressBook"
		"-framework AudioUnit"
		"-framework AppKit"
		"-framework CoreAudio"
		"-framework Cocoa"
	)
	if ( EINSTEIN_FLTK_FRONTEND )
		target_link_libraries ( Einstein
			EinsteinFLGUI
		)
	endif ()
	if ( EINSTEIN_SDL3_FRONTEND )
		target_link_libraries ( Einstein
			SDL3::SDL3-static
		)
		target_include_directories( Einstein SYSTEM PUBLIC
			$ENV{ANDROID_NDK_HOME}/sysroot/usr/include
		)
	endif ()
	if ( EINSTEIN_FLTK_FRONTEND )
		target_link_libraries ( EinsteinFLGUI
			fltk::images fltk::png fltk::z
		)
  	endif ()

	set_target_properties ( Einstein PROPERTIES
		MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Resources/macOS/Info.plist.in"
	)
	set_target_properties ( Einstein PROPERTIES
		CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/Resources/macOS/Entitlements.plist"
	)
	if ( XCODE )
		# Fix indentation settings that are cleared by CMake at every generator call.
		add_custom_target ( FixTabSettings
			COMMAND ${CMAKE_HOME_DIRECTORY}/cmake/xcodeFixIndentation
			WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
		)
	endif ()

elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" )

	# create the application
	add_executable ( Einstein
		${common_sources}
		${app_sources}
		${cmake_sources}
	)
	add_library ( EinsteinFLGUI STATIC
		${fluid_sources}
	)

	# how to compile and link
	target_compile_options ( Einstein PUBLIC
		-Wall -Wno-multichar -Wno-misleading-indentation -Wno-unused-result
		-Wno-missing-field-initializers -Wno-stringop-truncation # -Werror
		# Werror is disabled for testing purposes. Must reenable as soon as all Linux warnings are fixed.
	)
	target_compile_options ( EinsteinFLGUI PUBLIC
		-Wall -Wno-multichar -Wno-misleading-indentation -Wno-unused-result
		-Wno-missing-field-initializers -Wno-stringop-truncation -Werror
	)
	target_compile_definitions ( Einstein PRIVATE
		TARGET_UI_FLTK=1 TARGET_OS_LINUX=1
	)
	target_compile_definitions ( EinsteinFLGUI PRIVATE
		TARGET_UI_FLTK=1 TARGET_OS_LINUX=1
	)
	target_link_libraries ( Einstein
		${system_libs}
		fltk::images fltk::png fltk::z
		EinsteinFLGUI
		pthread
	)
	target_link_libraries ( EinsteinFLGUI
		fltk::images fltk::png fltk::z
	)

	if ( ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" )
		# Under OpenBSD, libffi is in ports (i.e. /usr/local) not base (i.e. /usr)
		find_library ( ffi_lib NAMES ffi )
		find_file ( ffi_incl NAMES ffi.h )
		if ( ffi_lib MATCHES ".*NOTFOUND" OR ffi_incl MATCHES ".*NOTFOUND" )
			message ( FATAL_ERROR "libffi not found!" )
		else ()
			get_filename_component ( ffi_lib_path ${ffi_lib} DIRECTORY )
			get_filename_component ( ffi_incl_path ${ffi_incl} DIRECTORY )
			message ( STATUS "libffi found in " ${ffi_lib_path} )
			target_include_directories ( Einstein SYSTEM PUBLIC ${ffi_incl_path} )
			target_link_libraries ( Einstein ${ffi_lib} )
		endif ()

		# Under OpenBSD, pulseaudio is in ports (i.e. /usr/local) not base (i.e. /usr)
		find_library ( pulse_lib NAMES pulse )
		if ( pulse_lib MATCHES ".*NOTFOUND" )
			message ( FATAL_ERROR "libpulse not found! " )
		else ()
			get_filename_component ( pulse_lib_path ${pulse_lib} DIRECTORY )
			message ( STATUS "libpulse found in " ${pulse_lib_path} )
			target_link_libraries ( Einstein ${pulse_lib} )
		endif ()

		# Under OpenBSD, X11 is in /usr/X11R6
		include ( FindX11 )
		if ( X11_FOUND )
			target_include_directories ( Einstein SYSTEM PUBLIC ${X11_INCLUDE_DIR} )
			target_link_libraries ( Einstein ${X11_LIBRARIES} )
		endif ()
	else ()
		# Under Linux, link pulseaudio normally
		target_link_libraries ( Einstein pulse )
	endif ()

elseif ( WIN32 )

	# Create a resources file for Windows
	configure_file (
		Resources/MSWindows/Einstein.rc.in
		Einstein.rc
	)

	# create the binaries
	add_executable ( Einstein WIN32
		${common_sources} ${app_sources} ${cmake_sources} ${data}
		${CMAKE_CURRENT_BINARY_DIR}/Einstein.rc
	)
	add_library ( EinsteinFLGUI STATIC
		${fluid_sources}
	)

	# how to compile and link
	target_compile_options( Einstein PUBLIC "/bigobj" )
	target_compile_options( EinsteinFLGUI PUBLIC "/bigobj" )
	if ( $<CONFIG:Debug> )
		target_compile_options( Einstein PUBLIC "/ZI" )
		target_compile_options( EinsteinFLGUI PUBLIC "/ZI" )
	endif ()
	target_compile_definitions ( Einstein PRIVATE
		TARGET_UI_FLTK=1 TARGET_OS_WIN32=1
		WIN32_LEAN_AND_MEAN=1 _CRT_SECURE_NO_WARNINGS=1
	)
	target_compile_definitions ( EinsteinFLGUI PRIVATE
		TARGET_UI_FLTK=1 TARGET_OS_WIN32=1
		WIN32_LEAN_AND_MEAN=1 _CRT_SECURE_NO_WARNINGS=1
	)
	target_link_libraries ( Einstein
		${system_libs}
		fltk::images fltk::png fltk::z
		EinsteinFLGUI
		winmm
		gdiplus
	)
	target_link_libraries ( EinsteinFLGUI
		fltk::images fltk::png fltk::z
	)

elseif ( ANDROID )

	# create the applications
	add_library ( einstein SHARED
		${common_sources}
		${app_sources}
		${cmake_sources}
	)
	target_compile_definitions ( einstein PRIVATE
		TARGET_UI_SDL=1 TARGET_OS_ANDROID=1
	)
	target_link_libraries ( einstein PRIVATE
		SDL3::SDL3
	)

endif ()

# ---- Late NEWT64 Processing --------------------------------------------------

if ( NEWT64_FOUND MATCHES true )
	if ( WIN32 )
		target_include_directories ( Einstein SYSTEM PUBLIC
			${newt64_incl_path}
			${newt64_incl_path}/win
		)
		target_link_libraries ( Einstein shlwapi )
	else ()
		# On Linux/macOS, use darwin includes
		target_include_directories ( Einstein SYSTEM PUBLIC
			${newt64_incl_path}
			${newt64_incl_path}/darwin
		)
	endif ()
	target_link_libraries ( Einstein ${newt64_lib} )
	message ( STATUS "NEWT64 library at " ${newt64_lib} )
	target_compile_definitions ( Einstein PRIVATE USE_TOOLKIT=1 )
    add_dependencies( Einstein ${newt64_lib} )
endif ()

#
# ---- Einstein Platform Independent Build Instructions ------------------------
#

if ( ANDROID )

target_include_directories (
	einstein PUBLIC
	${CMAKE_SOURCE_DIR}
)

else ()

target_include_directories (
	Einstein PUBLIC
	${CMAKE_SOURCE_DIR}
	${FLTK_INCLUDE_DIRS}
)
if ( EINSTEIN_FLTK_FRONTEND )
	target_include_directories (
		EinsteinFLGUI PUBLIC
		${CMAKE_SOURCE_DIR}
		${FLTK_INCLUDE_DIRS}
	)
endif ()

target_compile_definitions ( Einstein PUBLIC "$<$<CONFIG:DEBUG>:_DEBUG>" USE_CMAKE )
if ( EINSTEIN_FLTK_FRONTEND )
	target_compile_definitions ( EinsteinFLGUI PUBLIC "$<$<CONFIG:DEBUG>:_DEBUG>" USE_CMAKE )
endif ()

endif ()

# ---- Unit Tests --------------------------------------------------------------

if ( EINSTEIN_BUILD_TESTS AND NOT ANDROID )
	include( _Tests_/EinsteinTests.cmake )
endif ()


# ---- Generated Files ---------------------------------------------------------

set_source_files_properties (
	/app/Version_CMake.h
	PROPERTIES GENERATED TRUE
)

string ( TIMESTAMP COMPILE_TIME_YYYY "%Y" )
string ( TIMESTAMP COMPILE_TIME_MM "%m" )
string ( TIMESTAMP COMPILE_TIME_DD "%d" )

configure_file (
	${CMAKE_CURRENT_SOURCE_DIR}/app/Version_CMake.h.in
	${CMAKE_CURRENT_SOURCE_DIR}/app/Version_CMake.h
)


#
# ---- clang-format ------------------------
#

find_program(CLANG_FORMAT_EXECUTABLE
    NAMES clang-format-14 clang-format-mp-14 clang-format
    HINTS /usr/local/opt/clang-format@14/bin/ /usr/lib/llvm-14/bin/
    DOC "clang-format executable")
if(CLANG_FORMAT_EXECUTABLE)
  execute_process(COMMAND ${CLANG_FORMAT_EXECUTABLE} -version
                  OUTPUT_VARIABLE clang_format_version
                  ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
  # Ubuntu clang-format version 13.0.1-++20220112083154+b9a243d1cac2-1~exp1~20220112203239.53
  # clang-format version 13.0.0
  # Note, Matt, Aug 2025: we are at V14 or even V17 by now.

  if(clang_format_version MATCHES ".*clang-format version ([0-9]+).*")
    string(REGEX
           REPLACE ".*clang-format version ([0-9]+).*"
                   "\\1"
                   CLANG_FORMAT_VERSION
                   "${clang_format_version}")
    list(GET clang_format_version 0 CLANG_FORMAT_VERSION_MAJOR)
  endif()
endif()

if(CLANG_FORMAT_EXECUTABLE)
  if (CLANG_FORMAT_VERSION GREATER 12)
    set(CLANG_FORMAT_FOUND TRUE)
    message(STATUS "Found clang-format at ${CLANG_FORMAT_EXECUTABLE}")
  else()
    message(WARNING "clang-format not usable, as version of ${CLANG_FORMAT_EXECUTABLE} doesn't match : ${CLANG_FORMAT_VERSION_MAJOR} < 13")
    set(CLANG_FORMAT_FOUND FALSE)
  endif()
else()
  set(CLANG_FORMAT_FOUND FALSE)
    message(WARNING "clang-format not found")
endif()

if(CLANG_FORMAT_FOUND)
    set( format_sources )
    list( APPEND format_sources ${common_sources} )
    list( APPEND format_sources ${app_sources} )
    list( APPEND format_sources ${tests_sources} )
    list( FILTER format_sources INCLUDE REGEX "\\.(h|cpp|mm|t|m)$" )

    foreach (SOURCE ${format_sources})
        get_property (SOURCE_IS_GENERATED
                      SOURCE "${SOURCE}"
                      PROPERTY GENERATED)
        get_property (SOURCE_IS_FLUID_OUTPUT
                      SOURCE "${SOURCE}"
                      PROPERTY EINSTEIN_FLUID_OUTPUT)
        if (NOT SOURCE_IS_GENERATED AND NOT SOURCE_IS_FLUID_OUTPUT)
            list (APPEND format_sources_not_generated "${SOURCE}")
        endif ()
    endforeach ()

    add_custom_target(
            clang-format-check
            COMMAND ${CLANG_FORMAT_EXECUTABLE}
            -n
            ${format_sources_not_generated}
            WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    )
    add_custom_target(
            clang-format
            COMMAND ${CLANG_FORMAT_EXECUTABLE}
            -i
            ${format_sources_not_generated}
            WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    )
endif()
