# This is the top-level CMakeLists.txt file for the Clazy project.
#
# To build the man page from POD, run 'make man' after CMake (assumes perl is available)
# To install the resulting man page, run 'make install'
# The man page is not available on Windows.
#

project(ClangLazy)

cmake_minimum_required(VERSION 3.0)
include(FeatureSummary)
include(GenerateExportHeader)
include("GNUInstallDirs")

# Version setup
set(CLAZY_VERSION_MAJOR "1")
set(CLAZY_VERSION_MINOR "2")
set(CLAZY_VERSION_PATCH "0")
set(CLAZY_VERSION "${CLAZY_VERSION_MAJOR}.${CLAZY_VERSION_MINOR}.${CLAZY_VERSION_PATCH}")
set(CLAZY_PRINT_VERSION "${CLAZY_VERSION_MAJOR}.${CLAZY_VERSION_MINOR}")

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
find_package(Clang 3.8 MODULE REQUIRED)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

add_definitions(-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS)
add_definitions(-D_GNU_SOURCE -DHAVE_CLANG_CONFIG_H)

option(CLAZY_BUILD_UTILS_LIB "Enable this option to build a library so you can reuse clazy's utility functions" OFF)

if (CLAZY_BUILD_UTILS_LIB)
  add_definitions(-DCLAZY_BUILD_UTILS_LIB)
endif()

if (MSVC AND NOT CLANG_LIBRARY_IMPORT)
  message(FATAL_ERROR "\nOn MSVC you need to pass -DCLANG_LIBRARY_IMPORT=C:/path/to/llvm-build/lib/clang.lib to cmake when building Clazy.\nAlso make sure you've built LLVM with -DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON")
endif()

if(MSVC)
  # disable trigger-happy warnings from Clang/LLVM headers
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4291 /wd4800 /wd4141 /wd4146 /wd4251")
elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -fno-exceptions -fno-rtti")
endif()

set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-flat_namespace -Wl,-undefined -Wl,suppress")
if(WIN32)
    add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()

# Look for std::regex support
message("Looking for std::regex support...")
try_run(RUN_RESULT COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake_has_regex_test.cpp)

if(RUN_RESULT EQUAL 0)
  set(HAS_STD_REGEX TRUE)
else()
  set(HAS_STD_REGEX FALSE)
  add_definitions(-DNO_STD_REGEX)
  message("old-style-connect check is disabled due to missing std::regex support")
  message("Suppressions are disabled due to missing std::regex support")
endif()

include(ClazySources.cmake)

if (MSVC)
  string(REPLACE "\\" "/" LLVM_LIBS ${LLVM_LIBS})
endif()

string(REPLACE " " ";" LLVM_LIBS_LIST ${LLVM_LIBS}) # Transform into a list

set(CLAZY_STANDALONE_LLVM_LIBS ${LLVM_LIBS_LIST})

if(NOT APPLE AND NOT MINGW)
  # Don't link against LLVMSupport, causes: CommandLine Error: Option 'view-background' registered more than once!
  list(REMOVE_ITEM LLVM_LIBS_LIST "-lLLVMSupport")    # Remove element
endif()

if (MSVC)
  list(REMOVE_ITEM CLANG_LIBS "-lFrontend")
endif()

include_directories(${CMAKE_BINARY_DIR})
include_directories(${CLANG_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src)
link_directories("${LLVM_INSTALL_PREFIX}/lib" ${LLVM_LIBRARY_DIRS})

macro(link_to_llvm name llvm_libs)
  foreach(clang_lib ${CLANG_LIBS})
    if (MSVC)
        get_filename_component(LIB_FILENAME ${clang_lib} NAME)
        if (LIB_FILENAME STREQUAL "clangFrontend.lib")
            # On MSVC we don't link against clangFrontend.lib, instead we link against clang.exe (via clang.lib)
            # Otherwise the clazy plugin would have it's own plugin registry and clang wouldn't see it.
            # This way clazy registers with clang.
            continue()
        endif()
    endif()

    target_link_libraries(${name} ${clang_lib})
  endforeach()

  foreach(llvm_lib ${llvm_libs})
    target_link_libraries(${name} ${llvm_lib})
  endforeach()

  foreach(user_lib ${USER_LIBS})
    target_link_libraries(${name} ${user_lib})
  endforeach()

  foreach(llvm_system_lib ${LLVM_SYSTEM_LIBS})
    target_link_libraries(${name} ${llvm_system_lib})
  endforeach()

  if(MSVC)
    target_link_libraries(${name} version.lib)
  endif()
endmacro()

macro(add_clang_plugin name)
  set(srcs ${ARGN})

  add_library(${name} SHARED ${srcs})

  if(SYMBOL_FILE)
    set_target_properties(${name} PROPERTIES LINK_FlAGS "-exported_symbols_list ${SYMBOL_FILE}")
  endif()

  link_to_llvm(${name} "${LLVM_LIBS_LIST}")

  if(MSVC)
    target_link_libraries(${name} ${CLANG_LIBRARY_IMPORT}) # Link against clang.exe to share the plugin registry
  endif()

  if(CLAZY_BUILD_UTILS_LIB)
    target_link_libraries(${name} clazylib)
  endif()
endmacro()

if(CLAZY_BUILD_UTILS_LIB)
  # clazylib version
  set(clazylib_VERSION_MAJOR 0)
  set(clazylib_VERSION_MINOR 1)
  # Enable the full x.y.z version only for release versions
  set(clazylib_VERSION_PATCH 0)

  set(clazylib_VERSION ${clazylib_VERSION_MAJOR}.${clazylib_VERSION_MINOR})
  add_library(clazylib SHARED ${CLAZY_LIB_SRC})
  link_to_llvm(clazylib)
  generate_export_header(clazylib)
  set_target_properties(clazylib PROPERTIES VERSION ${clazylib_VERSION} SOVERSION ${clazylib_VERSION_MAJOR})
  install(TARGETS clazylib EXPORT LibClazyExport
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  )
endif()

set(SYMBOL_FILE Lazy.exports)

add_clang_plugin(ClangLazy ${CLAZY_PLUGIN_SRCS})

set_target_properties(ClangLazy PROPERTIES
  LINKER_LANGUAGE CXX
  PREFIX ""
)

install(TARGETS ClangLazy
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATAROOTDIR} CACHE STRING "Share directory name")

if(NOT WIN32)
  configure_file(${CMAKE_SOURCE_DIR}/clazy.cmake ${CMAKE_BINARY_DIR}/clazy @ONLY)
  install(FILES ${CMAKE_BINARY_DIR}/clazy DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
else()
  install(FILES ${CMAKE_SOURCE_DIR}/clazy.bat DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
  if (MSVC)
    install(FILES ${CMAKE_SOURCE_DIR}/clazy-cl.bat DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
  endif()
endif()

# Install the explanation README's
set(DOC_INSTALL_DIR ${SHARE_INSTALL_DIR}/clazy/doc)
file(GLOB README_LEVEL0_FILES src/checks/level0/README-*)
file(GLOB README_LEVEL1_FILES src/checks/level1/README-*)
file(GLOB README_LEVEL2_FILES src/checks/level2/README-*)
file(GLOB README_LEVEL3_FILES src/checks/level3/README-*)
file(GLOB README_HIDDENLEVEL_FILES src/checks/hiddenlevel/README-*)
install(FILES ${README_LEVEL0_FILES} DESTINATION ${DOC_INSTALL_DIR}/level0)
install(FILES ${README_LEVEL1_FILES} DESTINATION ${DOC_INSTALL_DIR}/level1)
install(FILES ${README_LEVEL2_FILES} DESTINATION ${DOC_INSTALL_DIR}/level2)
install(FILES ${README_LEVEL3_FILES} DESTINATION ${DOC_INSTALL_DIR}/level3)
install(FILES ${README_HIDDENLEVEL_FILES} DESTINATION ${DOC_INSTALL_DIR}/hiddenlevel)

# Install more doc files
install(FILES README.md COPYING-LGPL2.txt DESTINATION ${DOC_INSTALL_DIR})

# Build docs
set(MAN_INSTALL_DIR "man/man1")
add_subdirectory(docs)

if (CLAZY_BUILD_UTILS_LIB)
  # Install public headers
  set(CLAZY_LIB_INCLUDES
    src/AccessSpecifierManager.h
    src/checkbase.h
    src/checkmanager.h
    ${CMAKE_BINARY_DIR}/clazy_export.h
    src/clazy_stl.h
    src/ContextUtils.h
    src/FixItUtils.h
    src/HierarchyUtils.h
    src/LoopUtils.h
    src/MacroUtils.h
    src/QtUtils.h
    src/SuppressionManager.h
    src/StmtBodyRange.h
    src/StringUtils.h
    src/TemplateUtils.h
    src/TypeUtils.h
    src/Utils.h
  )
  install(FILES ${CLAZY_LIB_INCLUDES} DESTINATION include/clazy)
endif()


# Build clazy-standalone
add_executable(clazy-standalone ${CLAZY_STANDALONE_SRCS})

if (MSVC)
  # On MSVC clang-standalone crashes with a meaningless backtrace if linked to ClangLazy.dll
  target_link_libraries(clazy-standalone clangFrontend)
else()
  target_link_libraries(clazy-standalone ClangLazy)
endif()

link_to_llvm(clazy-standalone "${CLAZY_STANDALONE_LLVM_LIBS}")

install(TARGETS clazy-standalone DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
