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")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,--gc-sections -fPIE")
endif()

set(CMAKE_OPTIMIZE_DEPENDENCIES TRUE)
CHECK_C_COMPILER_FLAG("-fstack-protector-strong" COMPILER_SUPPORTS_STACKPROTECTOR )
if(COMPILER_SUPPORTS_STACKPROTECTOR)
  if (EMSCRIPTEN)
  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")
else()
  message(STATUS "enabling relro PIE linker flags")
  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,--gc-sections -fPIE")
  set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
endif()

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


add_executable(checkit_tiff checkit_tiff.c ${checkit_tiff_SOURCES})
add_executable(checkit_check_config checkit_check_config.c ${checkit_tiff_SOURCES})
add_executable(checkit_tiff_risk checkit_tiff_risk.c ${checkit_tiff_risk_SOURCES})
set(CMAKE_EXTRA_INCLUDE_FILES tiff.h)
#libpcre2
message(STATUS "check if PCRE_DIR exists, otherwise standard pkgconfig is used")
if (PCRE2_INCLUDE_DIR AND PCRE2_LIBRARY AND PCRE2_POSIX_LIBRARY)
  include(FindPackageHandleStandardArgs)
  FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY PCRE2_POSIX_LIBRARY PCRE2_INCLUDE_DIR)
  set(PCRE2_LIBRARIES ${PCRE2_POSIX_LIBRARY} ${PCRE2_LIBRARY} )
  set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
  get_filename_component(PCRE2_LIBDIR ${PCRE2_LIBRARY} DIRECTORY)
  get_filename_component(PCRE2_LIBRARY_CALC ${PCRE2_LIBRARY} NAME_WE)
  get_filename_component(PCRE2_POSIXLIBRARY_CALC ${PCRE2_POSIX_LIBRARY} NAME_WE)
  set(PCRE2_LDFLAGS "-L ${PCRE2_LIBDIR};-l${PCRE2_LIBRARY_CALC};-l${PCRE2_POSIX_LIBRARY_CALC}")
  set(PCRE2_STATIC_LDFLAGS "-L ${PCRE2_LIBDIR};-lpcre2-8")
  set(PCRE2_FOUND 1)
else()
  find_package(PkgConfig)
  if(PkgConfig_FOUND)
    pkg_check_modules(PCRE2 libpcre2-8)
  endif()
endif()
if (PCRE2_FOUND)
  add_compile_definitions(USE_LIBPCRE2)
  include_directories(${PCRE2_INCLUDE_DIRS})
  find_package_message(PCRE2 "found PCRE2" "[${PCRE2_INCLUDE_DIRS}][${PCRE2_LIBRARIES}][${PCRE2_STATIC_LIBRARY}]")
  get_cmake_property(_variableNames VARIABLES)
  message("STATUS pcre compile static: ${PCRE2_STATIC_LIBRARIES}")
else()
  message(FATAL_ERROR " No PCRE2 library found, add $PCRE2_INCLUDE_DIRS and $PCRE2_LIBRARIES manually")
endif()

#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()

#find IccProfLib, a part of DemoIccMAX, a reference implementation of International Color Consortium (https://github.com/InternationalColorConsortium/DemoIccMAX)
message(STATUS "checking IccProflib2")
find_package(RefIccMAX CONFIG)
#find_package(RefIccMAX)
if( WITHOUT_ICC_PROFLIB)
    unset(RefIccMAX_FOUND)
endif()

if (RefIccMAX_FOUND)
  set(REFICCMAX_FOUND 1) # patch to fix broken RefIccMAXConfig.cmake of older versions
  if (NOT DEFINED REFICCMAX_VERSION)
    set(REFICCMAX_VERSION "0.0.0")
  endif()
  add_definitions(-DHAVE_IccProfLib)
  include_directories(${REFICCMAX_INCLUDE_DIRS})
  # check if older or equal v2.2.6 then use older Vars
  # otherwise use newer vars
  message(STATUS "version ${REFICCMAX_VERSION}")
  if (${REFICCMAX_VERSION} VERSION_GREATER "2.2.5" )
    if (${REFICCMAX_VERSION} VERSION_EQUAL "2.3.1.7" AND STATIC)
      message(WARNING "the RefICCMAX library of version ${REFICCMAX_VERSION} has broken static library, therefore not compilable!")
      #message(FATAL_ERROR "the RefICCMAX library of version ${REFICCMAX_VERSION} has broken static library, therefore not compilable!")
    endif ()
    message(STATUS "found RefIccMAX include" ${REFICCMAX_INCLUDE_DIRS})
    include_directories(${REFICCMAX_INCLUDE_DIRS})
    set (WITH_OPTIONAL_REFICCMAX PRIVATE RefIccMAX::IccProfLib2)
  else ()
    message(STATUS "found RefIccMAX include" ${REFICCMAX_INCLUDE_DIR})
    include_directories(${REFICCMAX_INCLUDE_DIR}/IccProfLib2 ${REFICCMAX_INCLUDE_DIR}/IccXML2)
    # patch broken config of older IccProfLib providing trailing "}" in RefICCMAX_LIBRARIES setting
    if( ${REFICCMAX_LIBRARIES} MATCHES \}$ )
      message( WARNING "patching REFICCMAX_LIBRARIES ('${REFICCMAX_LIBRARIES}'), because old RefICCMAX library provides broken value" )
      string( REGEX REPLACE \}$ "" REFICCMAX_LIBRARIES ${REFICCMAX_LIBRARIES})
    endif()
    set (WITH_OPTIONAL_REFICCMAX ${REFICCMAX_LIBRARIES} )
  endif ()
  message(STATUS "found RefIccMAX libraries" ${REFICCMAX_LIBRARIES})
else()
  message(STATUS "no IccProfLib found, disabled full ICC profile checking")
  message(STATUS "You could provide the searchpath by using '-DRefIccMAX:DIR=$path' on 'cmake' call, where $path contains the missed RefIccMAXConfig.cmake file.")
  message(VERBOSE "the IccProfLib is available at https://github.com/InternationalColorConsortium/DemoIccMAX")
endif()


if(STATIC)
  string(APPEND WITH_OPTIONAL_REFICCMAX "-static")
  message(STATUS "patching to use static libraries: " ${WITH_OPTIONAL_REFICCMAX})
  target_link_libraries(checkit_tiff -static  libcheckit_tiff ${PCRE2_STATIC_LDFLAGS} ${PCRE2_STATIC_LIBRARIES} libcheckit_tiff)
  target_link_libraries(checkit_check_config -static libcheckit_tiff ${PCRE2_STATIC_LDFLAGS} ${PCRE2_STATIC_LIBRARIES})
  target_link_libraries(checkit_tiff_risk -static libcheckit_tiff)
else()
  target_link_libraries(checkit_tiff ${PCRE2_LDFLAGS} ${PCRE2_LIBRARIES} libcheckit_tiff)
  target_link_libraries(checkit_check_config ${PCRE2_LDFLAGS} ${PCRE2_LIBRARIES} 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

set (CPACK_RPM_PACKAGE_REQUIRES "pcre2 >= 8.32")

include(CPack)
