cmake_minimum_required (VERSION 3.14)
project (checkit_tiff)
include_directories("${PROJECT_SOURCE_DIR}/headers")
include_directories("${PROJECT_SOURCE_DIR}/lib/include")

include(CheckSymbolExists)
include(CheckFunctionExists)
include(CheckIPOSupported)
include(CheckCCompilerFlag)
include(CheckPIESupported)

file( GLOB checkit_tiff_SOURCES
  "${PROJECT_SOURCE_DIR}/tagrules/*.c"
  "${PROJECT_SOURCE_DIR}/ifdrules/*.c"
  "${PROJECT_SOURCE_DIR}/messages/*.c"
  "${PROJECT_SOURCE_DIR}/helper/*.c"
  "${PROJECT_SOURCE_DIR}/helper/renderer/*.c"
  "${PROJECT_SOURCE_DIR}/parser/*parser.c"
  "${PROJECT_SOURCE_DIR}/validate_icc/*.c"
  "${PROJECT_SOURCE_DIR}/risk_analyzer/*.c"
  )

file( GLOB checkit_tiff_risk_SOURCES
  "${PROJECT_SOURCE_DIR}/helper/check_helper.c"
  "${PROJECT_SOURCE_DIR}/helper/check_print.c"
  "${PROJECT_SOURCE_DIR}/helper/check_tiffparse.c"
  "${PROJECT_SOURCE_DIR}/messages/*.c"
  "${PROJECT_SOURCE_DIR}/risk_analyzer/*.c"
  )

#
# Make a version file containing the current version from git.
#
execute_process( COMMAND git rev-list HEAD --count OUTPUT_VARIABLE REPO_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE )
execute_process( COMMAND git rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE BUILD_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process( COMMAND git describe --tags OUTPUT_VARIABLE BUILD_TAG OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process( COMMAND git rev-parse HEAD OUTPUT_VARIABLE BUILD_REV_ID OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process( COMMAND git describe --long --tags --dirty --always OUTPUT_VARIABLE BUILD_REV_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE)
add_definitions(-DVERSION="${BUILD_BRANCH}_${BUILD_TAG}")
add_definitions(-DREPO_REVISION="${REPO_REVISION}")
add_definitions(-D_GNU_SOURCE)


check_ipo_supported(RESULT LTO_IS_SUPPORTED OUTPUT LTO_CHECK_ERROR)
if (LTO_IS_SUPPORTED AND NOT CMAKE_C_CLANG_TIDY)
  message(STATUS "IPO / LTO enabled")
  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
  if (CMAKE_C_CLANG_TIDY)
    set (LTO_CHECK_ERROR "clang-tidy does not support interprocedural optimization!")
  endif()
  message(STATUS "IPO / LTO unsupported: ${LTO_CHECK_ERROR}")
endif()

check_pie_supported(OUTPUT_VARIABLE output LANGUAGES C)
if(NOT CMAKE_C_LINK_PIE_SUPPORTED)
  message(WARNING "PIE is not supported at link time: ${output}.\n"
          "PIE link options will not be passed to linker.")
  set(CMAKE_POSITION_INDEPENDENT_CODE FALSE)
endif()

# add default CFLAGS
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat -Werror=format-security -ffunction-sections -fdata-sections -Wwrite-strings")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat -Werror=format-security -ffunction-sections -fdata-sections -Wwrite-strings")
endif()

set(CMAKE_OPTIMIZE_DEPENDENCIES TRUE)

CHECK_C_COMPILER_FLAG("-fstack-protector-strong" COMPILER_SUPPORTS_STACKPROTECTOR )
if(COMPILER_SUPPORTS_STACKPROTECTOR)
  if (EMSCRIPTEN)
    message(STATUS "Emscripten does not support STACKPROTECTOR")
    set(COMPILER_SUPPORTS_STACKPROTECTOR FALSE)
  elseif (CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
    message(STATUS "TinyCC does not support STACKPROTECTOR")
    set(COMPILER_SUPPORTS_STACKPROTECTOR FALSE)
  else()
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong")
  endif()
else()
  message(STATUS "Your compiler (${CMAKE_C_COMPILER}) has no strong stack protector functionality.")
endif()

# Check for compatibility with gnu11
# Compile with gnu11 support.

CHECK_C_COMPILER_FLAG("-std=gnu11" COMPILER_SUPPORTS_GNU11)

if(COMPILER_SUPPORTS_GNU11)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
else()
  message(STATUS "Your compiler (${CMAKE_C_COMPILER}) has no gnu11 support.")
  message(STATUS "If you are using clang on Apple OS X, please switch to gcc (version > 4.7) which is compliant with the C11 standard.")
  message(STATUS "If you are using MinGW or compile under MS Windows, the code could compile, but without any warranty and without any support!")
endif()

# we need reallocarray support, which  is not available in all versions of glibc
# COMPACT_NEED_REALLOCARRAY to be set
INCLUDE(CheckCSourceCompiles)
CHECK_C_SOURCE_COMPILES(
        "
#define _GNU_SOURCE
#include <stdlib.h>
int main(void)
{
        return !!reallocarray(NULL, 1, 1);
}
" HAVE_REALLOCARRAY_SUPPORT)
if (HAVE_REALLOCARRAY_SUPPORT)
  message(STATUS "Your compiler (${CMAKE_C_COMPILER}) has reallocarray support, fine :)")
  set(CMAKE_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
else()
  message(STATUS "You compiler (${CMAKE_C_COMPILER}) has no reallocarray support, using workaround.")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCOMPAT_NEED_REALLOCARRAY")
endif()

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)


if(WIN32)
  add_definitions(-D__WIN32__=1)
  set(STATIC 1)
  # needed to increase default stack, because Win7 has only 1MB
  set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,20000000")
  add_definitions(-D_POSIX_C_SOURCE=200809L)
  # FIXME: needed for MingW only
  add_definitions(-D_GNU_SOURCE=1)
  message(STATUS "target WIN32 detected.")
endif()

if(EMSCRIPTEN_WEB)
  message(STATUS "found EMSCRIPTEN_WEB, enabling singlemode for web")
  set(STATIC 1)
  set(INCOMING "['ENVIRONMENT','INITIAL_MEMORY', 'wasmMemory', 'arguments','buffer', 'canvas', 'doNotCaptureKeyboard', 'dynamicLibraries', 'elementPointerLock', 'extraStackTrace', 'forcedAspectRatio', 'instantiateWasm', 'keyboardListeningElement', 'freePreloadedMediaOnUse', 'locateFile', 'mainScriptUrlOrBlob', 'mem',  'monitorRunDependencies', 'noExitRuntime', 'noInitialRun', 'onAbort', 'onExit', 'onFullScreen', 'onRuntimeInitialized', 'postMainLoop', 'postRun', 'preInit', 'preMainLoop', 'preRun', 'preloadPlugins', 'print', 'printErr', 'setStatus', 'statusMessage', 'stderr', 'stdin', 'stdout', 'thisProgram', 'wasm', 'wasmBinary', 'websocket', 'logReadFiles']")
  #set(EMSCRIPTEN_OPTS "-s SINGLE_FILE=1 -s RUNTIME_LOGGING=1 -s EXIT_RUNTIME=1 -s INVOKE_RUN=0 -U_FORTIFY_SOURCE -s LLD_REPORT_UNDEFINED -s INITIAL_MEMORY=24051712 -s ALLOW_MEMORY_GROWTH")
  set(EMSCRIPTEN_OPTS "-sSINGLE_FILE=1 -sRUNTIME_LOGGING=1 -sEXIT_RUNTIME=1 -sINVOKE_RUN=0 -U_FORTIFY_SOURCE -sLLD_REPORT_UNDEFINED -sINITIAL_MEMORY=24051712 -sSTACK_SIZE=1048576 -sINCOMING_MODULE_JS_API=\"${INCOMING}\"")
  # file(REAL_PATH ${PROJECT_SOURCE_DIR}/../example_configs EMBED_DIR)
  #get_filename_component(EMBED_DIR "${PROJECT_SOURCE_DIR}/../example_configs" REALPATH)
  set(EMBED_DIR "../example_configs")
  message(STATUS "set EMBED_DIR to ${EMBED_DIR}")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_OPTS}")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EMSCRIPTEN_OPTS}")
  set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} --embed-file \"${EMBED_DIR}\"@/ -lidbfs.js")
  set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} --embed-file \"${EMBED_DIR}\"@/ -lidbfs.js")
elseif(EMSCRIPTEN)
  message(STATUS "found EMSCRIPTEN, enabling singlemode")
  set(STATIC 1)
  #set(EMSCRIPTEN_OPTS "-s SINGLE_FILE=1 -s RUNTIME_LOGGING=1 -s EXIT_RUNTIME=1 -U_FORTIFY_SOURCE -s LLD_REPORT_UNDEFINED -s INITIAL_MEMORY=24051712 -s ALLOW_MEMORY_GROWTH -s NODERAWFS=1")
  # with emscripten 5.0.1 LogReadFiles not part of default INCOMING_MODULE_JS_API anymore, therefore
  # added here:
  set(EMSCRIPTEN_OPTS "-sSINGLE_FILE=1 -sRUNTIME_LOGGING=1 -sEXIT_RUNTIME=1 -U_FORTIFY_SOURCE -sLLD_REPORT_UNDEFINED -sINITIAL_MEMORY=24051712 -sNODERAWFS=1 -sSTACK_SIZE=1048576 -sINCOMING_MODULE_JS_API=\"${INCOMING}\"")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_OPTS}")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EMSCRIPTEN_OPTS}")
  set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} -lnodefs.js -lnoderawfs.js")
  set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} -lnodefs.js -lnoderawfs.js")
endif()

if(STATIC)
  set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
  set(BUILD_SHARED_LIBRARIES OFF)
  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
  message(STATUS "STATIC compilation requested.")
endif()


if(CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
  message(STATUS "TinyCC detected: disabling static linking to prevent glibc CRT segfaults")
  set(CMAKE_C_LINK_PIE_SUPPORTED FALSE)
  set(CMAKE_POSITION_INDEPENDENT_CODE FALSE)
  
  # Fortify Source für TCC deaktivieren
  string(REGEX REPLACE "-D_FORTIFY_SOURCE=[0-9]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
  add_compile_definitions(_FORTIFY_SOURCE=0)
  
  # Statisches Linken bei TCC erzwungen deaktivieren
  if(STATIC)
	  message(FATAL_ERROR "Static linking with TinyCC and glibc causes SIGSEGV.")
  endif()
  # C-Flags für glibc/regex Kompatibilität setzen
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_POSIX_C_SOURCE=200809L -D_REGEX_NELTS\\(x\\)= -Wunsupported")

endif()

add_executable(checkit_tiff checkit_tiff.c ${checkit_tiff_SOURCES})
add_executable(checkit_check_config checkit_check_config.c ${checkit_tiff_SOURCES}
        lib/include/icc_loader.h)
add_executable(checkit_tiff_risk checkit_tiff_risk.c ${checkit_tiff_risk_SOURCES})
set(CMAKE_EXTRA_INCLUDE_FILES tiff.h)

#find mmap
CHECK_FUNCTION_EXISTS(mmap   HAVE_MMAP)
CHECK_FUNCTION_EXISTS(munmap HAVE_MUNMAP)
if(HAVE_MMAP AND HAVE_MUNMAP)
  set(HAVE__MMAP 1)
  add_definitions(-DHAVE_MMAP)
endif()

# find strndup
CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP)
if (NOT HAVE_STRNDUP)
  message(WARNING "You compiler (${CMAKE_C_COMPILER}) has no strndup support, using workaround.")
endif()

# needed for dlopen / dlsym (POSIX-Systems)
find_package(Threads REQUIRED)


if(STATIC)
  message(STATUS "patching to use static libraries: ")
  target_link_libraries(checkit_tiff PRIVATE -static ${CMAKE_DL_LIBS} libcheckit_tiff)
  target_link_libraries(checkit_check_config -static libcheckit_tiff)
  target_link_libraries(checkit_tiff_risk -static libcheckit_tiff)
else()
  target_link_libraries(checkit_tiff PRIVATE ${CMAKE_DL_LIBS} libcheckit_tiff)
  target_link_libraries(checkit_check_config libcheckit_tiff)
  target_link_libraries(checkit_tiff_risk libcheckit_tiff)
endif()

add_subdirectory(lib)

install( TARGETS checkit_tiff
  RUNTIME DESTINATION bin
  LIBRARY DESTINATION lib
  )
install(DIRECTORY
  ../example_configs
  ../tiffs_should_fail
  ../tiffs_should_pass
  DESTINATION share/checkit_tiff/
  )

file( GLOB README_FILES "../README*" "../FAQ" "../LICENSE" "../TODO" "../BUGS")
install(FILES ${README_FILES} DESTINATION share/checkit_tiff/)

include(FindPerl)
if (PERL_FOUND)
  enable_testing()
  set(T_DIR ${PROJECT_SOURCE_DIR}/../t )
  add_executable(test_stack "../t/01_test_stack.c")
  add_test(NAME 01_test_stack COMMAND test_stack WORKING_DIRECTORY ${T_DIR})
  add_test(NAME 01_grammar      COMMAND ${PERL_EXECUTABLE} -I ${T_DIR} 01_test_grammar.t         WORKING_DIRECTORY  ${T_DIR} )
  add_test(NAME 02_config       COMMAND ${PERL_EXECUTABLE} -I ${T_DIR} 02_test_config_examples.t WORKING_DIRECTORY  ${T_DIR} )
  add_test(NAME 03_validfiles   COMMAND ${PERL_EXECUTABLE} -I ${T_DIR} 03_test_should_pass.t     WORKING_DIRECTORY  ${T_DIR} )
  add_test(NAME 03_invalidfiles COMMAND ${PERL_EXECUTABLE} -I ${T_DIR} 03_test_should_fail.t     WORKING_DIRECTORY  ${T_DIR} )
else()
  message(WARNING "for testsuite an installed Perl is needed")
endif()

# CPACK definitions
include(InstallRequiredSystemLibraries)

set (CPACK_PACKAGE_VENDOR "Andreas Romeyke")
string(REGEX REPLACE "^v([0-9]+).*" "\\1" MAJOR_VERSION ${BUILD_TAG})
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*"  "\\1" MINOR_VERSION ${BUILD_TAG})
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PATCH_VERSION ${BUILD_TAG})

message(STATUS "BUILDTAG (after) : '${BUILD_TAG}'")
message(STATUS "MAJOR VERSION: ${MAJOR_VERSION}")
message(STATUS "MINOR VERSION: ${MINOR_VERSION}")
message(STATUS "PATCH VERSION: ${PATCH_VERSION}")
set (CPACK_PACKAGE_NAME "checkit-tiff")
set (CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
set (CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
set (CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/../LICENSE")
set (CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/../README.1st_checkit")
set (CPACK_GENERATOR "TGZ;STGZ;TZ;DEB;RPM")
#set (CPACK_GENERATOR "DEB")
set (CPACK_PACKAGE_CONTACT "Andreas Romeyke checkit_tiff@andreas-romeyke.de")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "checkit-tiff is a policy-oriented conformance checker for baseline TIFFs")
set(CPACK_PACKAGE_DESCRIPTION "checkit-tiff is a conformance checker for baseline TIFFs (with various extensions). It works in different way than other tools. Instead to use a hardcoded rule set with predefined values, you are able to define tags and corresponding values more fine granulary. The rulesets are human readable and could be adapted to own needs.")

### Debian related
# https://cmake.org/cmake/help/latest/cpack_gen/deb.html

#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc (>= 6.0)")
set(CPACK_DEBIAN_PACKAGE_SECTION "misc")
set(CPACK_DEBIAN_COMPRESSION_TYPE "xz")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_DEBIAN_PACKAGE_VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION})
#set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)
set (CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_CONTACT})
set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY} ${CPACK_PACKAGE_DESCRIPTION}")
set (CPACK_DEBIAN_PACKAGE_PRIORITY "optional")

### RedHat related
# documentation: https://cmake.org/cmake/help/latest/cpack_gen/rpm.html

include(CPack)
