cmake_minimum_required(VERSION 2.8)

project(uncrustify)

find_package(ament_cmake_core REQUIRED)

include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
include(CheckSymbolExists)
include(CheckCXXSymbolExists)
include(CheckTypeSize)
include(CTest)

#
# Check compiler flags
#
if(MSVC)
  add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /wd4267)
elseif(CMAKE_COMPILER_IS_GNUCXX)
  set(gcc_warning_flags
    -Wall
    -Wextra
    -Wshadow
    -Wpointer-arith
    -Wcast-qual
    -Wcast-align
  )
  foreach(flag ${gcc_warning_flags})
    string(REGEX REPLACE "[^a-zA-Z0-9]+" "_" flag_var "CXXFLAG_${flag}")
    CHECK_CXX_COMPILER_FLAG("${flag}" ${flag_var})
    if(${flag_var})
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}")
    endif()
    unset(flag_var)
  endforeach()
  unset(gcc_warning_flags)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()

include_directories(${CMAKE_CURRENT_BINARY_DIR})

#
# Determine config
#
if(WIN32)
  # Windows builds use win32/windows_compat.h instead of config.h
  include_directories(${PROJECT_SOURCE_DIR}/win32)
else()
  # Generate CMake config.h.in
  file(READ src/config.h.in config_lines)
  string(REPLACE "#undef" "#cmakedefine" config_lines "${config_lines}")
  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.h.in ${config_lines})

  unset(config_lines)

  # Generate config.h
  set(avail_headers "")

  set(headers
    inttypes.h
    memory.h
    stdint.h
    stdlib.h
    strings.h
    string.h
    sys/stat.h
    sys/types.h
    unistd.h
    utime.h
  )
  foreach(header ${headers})
    string(TOUPPER "${header}" header_uc)
    string(REGEX REPLACE "[^A-Z0-9_]" "_" include_var "HAVE_${header_uc}")
    check_include_file_cxx("${header}" ${include_var})
    if(${include_var})
      list(APPEND avail_headers ${header})
    endif()
    unset(include_var)
    unset(header_uc)
  endforeach()
  unset(headers)

  check_include_file("stdbool.h" HAVE_STDBOOL_H)

  set(symbols
    memset
    strcasecmp
    strchr
    strdup
    strerror
    strtol
    strtoul
  )
  foreach(symbol ${symbols})
    string(TOUPPER "${symbol}" symbol_uc)
    string(REGEX REPLACE "[^A-Z0-9_]" "_" symbol_var "HAVE_${symbol_uc}")
    check_cxx_symbol_exists("${symbol}" "${avail_headers}" ${symbol_var})
    unset(symbol_var)
    unset(symbol_uc)
  endforeach()
  unset(symbols)

  unset(avail_headers)

  check_type_size(_Bool _BOOL LANGUAGE C)

  configure_file(${CMAKE_CURRENT_BINARY_DIR}/config.h.in config.h @ONLY)
endif()

#
# Generate uncrustify_version.h
#
file(STRINGS configure.ac PACKAGE_VERSION REGEX "^AC_INIT")

if(${PACKAGE_VERSION} MATCHES "^AC_INIT\\(uncrustify,([^,]+),")
  set(PACKAGE_VERSION "${CMAKE_MATCH_1}")
  message(STATUS "PACKAGE_VERSION: ${PACKAGE_VERSION}")
else()
  message(FATAL_ERROR "Unable to determine PACKAGE_VERSION")
endif()

configure_file(src/uncrustify_version.h.in uncrustify_version.h @ONLY)

#
# Generate token_names.h
#
set(tokens "")

file(READ src/token_enum.h token_lines)
string(REGEX REPLACE ";|\\[|\\]" " " token_lines "${token_lines}")
string(REPLACE "\n" ";" token_lines "${token_lines}")

foreach(token_line ${token_lines})
  if(${token_line} MATCHES "^[ \t]*CT_([A-Z0-9_]+),.*$")
    list(APPEND tokens "   \"${CMAKE_MATCH_1}\",\n")
  endif()
endforeach()

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/token_names.h
  "/*\n"
  " * Generated by CMake\n"
  " */\n"
  "#ifndef TOKEN_NAMES_H_INCLUDED\n"
  "#define TOKEN_NAMES_H_INCLUDED\n"
  "\n"
  "const char *token_names[] =\n"
  "{\n"
  ${tokens}
  "};\n"
  "\n"
  "#endif /* TOKEN_NAMES_H_INCLUDED */\n"
)

unset(token_lines)
unset(tokens)

#
# Uncrustify
#
add_executable(uncrustify
  src/align.cpp
  src/align_stack.cpp
  src/args.cpp
  src/backup.cpp
  src/braces.cpp
  src/brace_cleanup.cpp
  src/ChunkStack.cpp
  src/chunk_list.cpp
  src/combine.cpp
  src/compat_posix.cpp
  src/compat_win32.cpp
  src/defines.cpp
  src/detect.cpp
  src/indent.cpp
  src/keywords.cpp
  src/lang_pawn.cpp
  src/logger.cpp
  src/logmask.cpp
  src/md5.cpp
  src/newlines.cpp
  src/options.cpp
  src/options_for_QT.cpp
  src/output.cpp
  src/parens.cpp
  src/parse_frame.cpp
  src/punctuators.cpp
  src/semicolons.cpp
  src/sorting.cpp
  src/space.cpp
  src/tokenize.cpp
  src/tokenize_cleanup.cpp
  src/uncrustify.cpp
  src/unc_text.cpp
  src/unc_tools.cpp
  src/unicode.cpp
  src/universalindentgui.cpp
  src/width.cpp
  src/align_stack.h
  src/args.h
  src/backup.h
  src/base_types.h
  src/char_table.h
  src/ChunkStack.h
  src/chunk_list.h
  src/ListManager.h
  src/logger.h
  src/logmask.h
  src/log_levels.h
  src/md5.h
  src/options.h
  src/options_for_QT.h
  src/prototypes.h
  src/punctuators.h
  src/token_enum.h
  src/uncrustify_types.h
  src/unc_ctype.h
  src/unc_text.h
  src/unc_tools.h
)

#
# Tests
#
if(BUILD_TESTING)
  find_package(PythonInterp REQUIRED)
  enable_testing()
  add_test(NAME test_uncrustify
    COMMAND ${PYTHON_EXECUTABLE}
      "${PROJECT_SOURCE_DIR}/tests/run_tests.py" "--exe=$<TARGET_FILE:uncrustify>"
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tests/"
  )
endif()

#
# Install
#
install(TARGETS uncrustify
  RUNTIME DESTINATION bin
)

# this ensures that the package has an environment hook setting the PATH
ament_package()
