project(OpenGTL)

cmake_minimum_required(VERSION 2.6)

set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" )

option(OPENGTL_BUILD_TESTS "Build tests" OFF)
option(OPENGTL_ENABLE_DEBUG_OUTPUT "Enable (heavy) debug output (only do that for debugging OpenGTL" OFF)
option(OPENGTL_ENABLE_DEBUG_THREAD "Test if functions are accessed by different thread" OFF)
option(OPENGTL_FULL_WARNINGS "Build OpenGTL with all warnings" OFF)
option(OPENGTL_CODE_COVERAGE "Activate code coverage for tests" OFF)
option(OVERIDE_LLVM_ASSERT "Use this only for debug purposes, this break thread safety." OFF)

set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )

set(OPENGTL_VERSION "0.9.18")
set(OPENGTL_LIB_VERSION ${OPENGTL_VERSION})
set(OPENGTL_LIB_SOVERSION "0.8")

set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/)
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/)
set(BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/bin/)
set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/OpenGTL/)
set(DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/OpenGTL/)
set(GTLIMAGEIO_EXTENSIONS_INSTALL_DIR ${LIB_INSTALL_DIR}/GTLImageIO/Extensions)

#
# Define INSTALL_TARGETS_DEFAULT_ARGS to be used as install target for program and library. It will do the right thing on all platform
#
set(INSTALL_TARGETS_DEFAULT_ARGS  RUNTIME DESTINATION "${BIN_INSTALL_DIR}"
                                  LIBRARY DESTINATION "${LIB_INSTALL_DIR}"
                                  ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT Devel )


# on the Mac support an extra install directory for application bundles starting with cmake 2.6
if(APPLE)
   set(INSTALL_TARGETS_DEFAULT_ARGS  ${INSTALL_TARGETS_DEFAULT_ARGS}
                               BUNDLE DESTINATION "${BUNDLE_INSTALL_DIR}" )
endif(APPLE)


set(OPENGTL_TESTS_DATA "" CACHE FILEPATH "Directory that contains the data of the tests.")

include (TestBigEndian)
test_big_endian(CMAKE_WORDS_BIGENDIAN)
configure_file(config-endian.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-endian.h )

find_package(LLVM REQUIRED)

# Find llvm jit, interpreter and native
if(MSVC)
  set( LLVM_LIBS
      ${LLVM_LIBRARIES_PATH}/LLVMCore.lib
      ${LLVM_LIBRARIES_PATH}/LLVMipo.lib
      ${LLVM_LIBRARIES_PATH}/LLVMInterpreter.lib
      ${LLVM_LIBRARIES_PATH}/LLVMJIT.lib
      ${LLVM_LIBRARIES_PATH}/LLVMExecutionEngine.lib
      ${LLVM_LIBRARIES_PATH}/LLVMLinker.lib
      ${LLVM_LIBRARIES_PATH}/LLVMArchive.lib
      ${LLVM_LIBRARIES_PATH}/LLVMBitReader.lib
      ${LLVM_LIBRARIES_PATH}/LLVMBitWriter.lib
      ${LLVM_LIBRARIES_PATH}/LLVMX86AsmParser.lib
      ${LLVM_LIBRARIES_PATH}/LLVMX86AsmPrinter.lib
      ${LLVM_LIBRARIES_PATH}/LLVMX86CodeGen.lib
      ${LLVM_LIBRARIES_PATH}/LLVMSelectionDAG.lib
      ${LLVM_LIBRARIES_PATH}/LLVMAsmPrinter.lib
      ${LLVM_LIBRARIES_PATH}/LLVMCodeGen.lib
      ${LLVM_LIBRARIES_PATH}/LLVMScalarOpts.lib
      ${LLVM_LIBRARIES_PATH}/LLVMTransformUtils.lib
      ${LLVM_LIBRARIES_PATH}/LLVMipa.lib
      ${LLVM_LIBRARIES_PATH}/LLVMAnalysis.lib
      ${LLVM_LIBRARIES_PATH}/LLVMTarget.lib
      ${LLVM_LIBRARIES_PATH}/LLVMMC.lib
      ${LLVM_LIBRARIES_PATH}/LLVMX86Info.lib
      ${LLVM_LIBRARIES_PATH}/LLVMSupport.lib
      ${LLVM_LIBRARIES_PATH}/LLVMSystem.lib )
else(MSVC)
  FIND_LLVM_LIBS( ${LLVM_CONFIG_EXECUTABLE} "native bitwriter linker bitreader jit interpreter support ipo" LLVM_LIBS LLVM_NATIVE_OBJECTS )
endif(MSVC)

if( NOT MSVC AND NOT (LLVM_VERSION STREQUAL "3.1" OR LLVM_VERSION STREQUAL "3.0" OR LLVM_VERSION STREQUAL "3.2svn"  OR LLVM_VERSION STREQUAL "3.2" ) ) # There is no way with MSVC to know the llvm version
  message(FATAL_ERROR "LLVM 3.0 or 3.1 or 3.2 is required.")
endif()

if(MSVC)
  add_definitions(/FIiso646.h)
  include_directories(${CMAKE_CURRENT_SOURCE_DIR}/msvc/build)
  install( FILES msvc/inst/stdint.h DESTINATION ${INCLUDE_INSTALL_DIR} )
  set(LLVM_COMPILED_WITHOUT_ASSERTS true)
  add_definitions(/W0)
endif(MSVC)

find_package(Threads)

if(CMAKE_USE_PTHREADS_INIT)
  set(PTHREAD_FOUND TRUE)
  set(GTLTEST_THREAD_SUPPORT TRUE)
  add_definitions(-DOPENGTL_HAVE_PTHREAD)
  add_definitions(-DGTLTEST_HAVE_THREAD)
  message(STATUS "pthread was found.")
else(CMAKE_USE_PTHREADS_INIT)
  message(STATUS "pthread wasn't found, thread tests won't be available and memory manager will use hash.")
endif(CMAKE_USE_PTHREADS_INIT)

if(OPENGTL_BUILD_TESTS)
  include(CTest)
  add_definitions(-DOPENGTL_ENABLE_TESTS)
endif(OPENGTL_BUILD_TESTS)

if(OPENGTL_ENABLE_DEBUG_OUTPUT)
  add_definitions(-DOPENGTL_ENABLE_DEBUG_OUTPUT)
endif(OPENGTL_ENABLE_DEBUG_OUTPUT)

if(OPENGTL_ENABLE_DEBUG_THREAD)
  add_definitions(-DOPENGTL_ENABLE_DEBUG_THREAD)
endif(OPENGTL_ENABLE_DEBUG_THREAD)

if(NOT LLVM_COMPILED_WITHOUT_ASSERTS)
  if(OVERIDE_LLVM_ASSERT)
    message(STATUS "llvm was build with asserts, this is not supported by OpenGTL but you have enable the overide")
  else()
    message(FATAL_ERROR "llvm was build with asserts, this is not supported by OpenGTL (llvm with asserts is not thread safe).")
  endif()
endif(NOT LLVM_COMPILED_WITHOUT_ASSERTS)

if(OPENGTL_FULL_WARNINGS)
  set(OPENGTL_BUILD_DEFINITION "-Waddress -Warray-bounds -Wc++0x-compat -Wchar-subscripts -Wcomment -Wformat -Wmissing-braces -Wparentheses -Wreorder -Wreturn-type -Wsequence-point -Wsign-compare -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value -Wvolatile-register-var -Werror -Wno-deprecated-declarations") # -Wall without -Wunused-variable  since it is buggy in gcc 4.6
endif(OPENGTL_FULL_WARNINGS)

if(CMAKE_SIZEOF_VOID_P EQUAL 4)
  # 32 bits
  message(STATUS "OpenGTL detected that you use a 32 bit processor.")
  set(OPENGTL_TESTS_DATA_RESULTS_DIR results32)
  set(OPENGTL_32_BITS TRUE)
  
else(CMAKE_SIZEOF_VOID_P EQUAL 4)
  # 32 bits
  message(STATUS "OpenGTL detected that you use a 64 bit processor.")
  set(OPENGTL_TESTS_DATA_RESULTS_DIR results64)
  set(OPENGTL_32_BITS FALSE)
  # check that llvm was build with -fPIC
  if(NOT LLVM_COMPILED_WITH_PIC AND NOT APPLE)
    message(FATAL_ERROR "llvm wasn't build with PIC enabled, this is not surported on 64bits.")
  endif()
endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/OpenGTL ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/OpenGTL
)

if(OPENGTL_32_BITS)
  add_definitions( -DOPENGTL_32_BITS )
endif(OPENGTL_32_BITS)

# Test for visibility in gcc

if (CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
  include (MacroEnsureVersion)
  include (CheckCXXCompilerFlag)

  # visibility support
  check_cxx_compiler_flag(-fvisibility=hidden __GTL_HAVE_GCC_VISIBILITY)
  set( __GTL_HAVE_GCC_VISIBILITY ${__GTL_HAVE_GCC_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")

   # get the gcc version
   exec_program(${CMAKE_C_COMPILER} ARGS ${CMAKE_C_COMPILER_ARG1} --version OUTPUT_VARIABLE _gcc_version_info)

   string (REGEX MATCH "[345]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
   # gcc on mac just reports: "gcc (GCC) 3.3 20030304 ..." without the patch level, handle this here:
   if (NOT _gcc_version)
      string (REGEX REPLACE ".*\\(GCC\\).* ([34]\\.[0-9]) .*" "\\1.0" _gcc_version "${_gcc_version_info}")
   endif (NOT _gcc_version)

   macro_ensure_version("4.1.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_1)
   macro_ensure_version("4.2.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_2)
   macro_ensure_version("4.3.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_3)

   # save a little by making local statics not threadsafe
   # ### do not enable it for older compilers, see
   # ### http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31806
   if (GCC_IS_NEWER_THAN_4_3)
       set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
   endif (GCC_IS_NEWER_THAN_4_3)

   set(_GCC_COMPILED_WITH_BAD_ALLOCATOR FALSE)
   if (GCC_IS_NEWER_THAN_4_1)
      exec_program(${CMAKE_C_COMPILER} ARGS ${CMAKE_C_COMPILER_ARG1} -v OUTPUT_VARIABLE _gcc_alloc_info)
      string(REGEX MATCH "(--enable-libstdcxx-allocator=mt)" _GCC_COMPILED_WITH_BAD_ALLOCATOR "${_gcc_alloc_info}")
   endif (GCC_IS_NEWER_THAN_4_1)

   if (__GTL_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32)
      set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")

      if (GCC_IS_NEWER_THAN_4_2)
          set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
      endif (GCC_IS_NEWER_THAN_4_2)
   else (__GTL_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32)
      set (__GTL_HAVE_GCC_VISIBILITY 0)
   endif (__GTL_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32)

endif ()

# Enable code coverage

if(OPENGTL_CODE_COVERAGE)
    find_program( LCOV lcov )
    find_program( LCOV_GENHTML genhtml )
    if( LCOV )
        set( LCOV_RDIR "${CMAKE_BINARY_DIR}/lcov-report" )
        add_definitions(-fprofile-arcs -ftest-coverage)
        set (CMAKE_SHARED_LINKER_FLAGS  "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
        set (CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
        add_custom_target( coverage-clean
          COMMAND mkdir -p ${LCOV_RDIR}
          COMMAND ${LCOV} -d ${CMAKE_BINARY_DIR} -z
          COMMAND ${LCOV} -c -i -d ${CMAKE_BINARY_DIR} -o ${LCOV_RDIR}/base.info
        )
        add_custom_target( coverage
          COMMAND mkdir -p ${LCOV_RDIR}
          COMMAND ${LCOV} --quiet -c -d ${CMAKE_BINARY_DIR} -o ${LCOV_RDIR}/base.info
          COMMAND ${LCOV_GENHTML} ${LCOV_RDIR}/base.info -o ${LCOV_RDIR}/html
        )
    else( LCOV )
        message( SEND_ERROR "Could not find LCOV, disabling coverage report generation." )
    endif( LCOV )
endif(OPENGTL_CODE_COVERAGE)

# Add subdirectories

option(OPENGTL_BUILD_OPENRIJN "Build OpenRijn" OFF)

add_subdirectory(OpenGTL)
add_subdirectory(OpenCTL)
add_subdirectory(OpenShiva)
add_subdirectory(Extensions)

if(OPENGTL_BUILD_OPENRIJN)
  add_subdirectory(OpenRijn)
endif(OPENGTL_BUILD_OPENRIJN)
