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

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

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)
  message(STATUS "IPO / LTO enabled")
  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
  message(STATUS "IPO / LTO unsupported: <${LTO_CHECK_ERROR}>")
endif()

# add default CFLAGS
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat -Werror=format-security -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat -Werror=format-security -ffunction-sections -fdata-sections")
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)
  elseif()
    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(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")
  # 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")
  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")
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)
if( WITHOUT_ICC_PROFLIB)
    unset(RefIccMAX_FOUND)
endif()
if (RefIccMAX_FOUND)
  message(STATUS "found RefIccMAX include ${REFICCMAX_INCLUDE_DIR}")
  add_definitions(-DHAVE_IccProfLib)
  include_directories(${REFICCMAX_INCLUDE_DIR})
  include_directories("${REFICCMAX_INCLUDE_DIR}/IccProfLib2")
  message(STATUS "found RefIccMAX libraries ${REFICCMAX_LIBRARIES}")
  message(STATUS "found RefIccMAX static library  ${REFICCMAX_STATIC_LIBRARY}")
else()
    message(STATUS "no IccProfLib found, disabled full ICC profile checking")
    message(VERBOSE "the IccProfLib is available at https://github.com/InternationalColorConsortium/DemoIccMAX")
endif()
#find_path(IccProfLib_INCLUDE_DIR NAMES IccProfileHeader.h)
#find_library(IccProfLib_LIBRARY NAMES libIccProfLib2  HINTS "{CMAKE_PREFIX_PATH}/lib" "{CMAKE_PREFIX_PATH}/.libs" )
#if (IccProfLib_INCLUDE_DIR AND IccProfLib_LIBRARY)
#  add_definitions(-DHAVE_IccProfLib)
#  message(STATUS "found IccProfLib, enabling full ICC profile checking")
#else()
#  message(STATUS "no IccProfLib found, disabled full ICC profile checking")
#  message(VERBOSE "the IccProfLib is available at https://github.com/InternationalColorConsortium/DemoIccMAX")
#endif()



add_subdirectory(lib)
if(STATIC)
    #target_link_libraries(checkit_tiff -static libcheckit_tiff ${PCRE2_POSIX_LIBRARY} ${PCRE2_LIBRARY} libcheckit_tiff Threads::Threads)
    #target_link_libraries(checkit_check_config -static libcheckit_tiff ${PCRE2_POSIX_LIBRARY} ${PCRE2_LIBRARY}  Threads::Threads)
    #target_link_libraries(checkit_tiff_risk -static libcheckit_tiff Threads::Threads)
    message(STATUS "LINKFLAGS: ${PCRE2_STATIC_LDFLAGS}")
    message(STATUS "LIBS: ${PCRE2_STATIC_LIBRARIES}")
    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_LIBRARIES}  libcheckit_tiff Threads::Threads)
  #target_link_libraries(checkit_check_config ${PCRE2_LIBRARIES} libcheckit_tiff Threads::Threads)
  #target_link_libraries(checkit_tiff_risk  libcheckit_tiff Threads::Threads)
  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()

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)
