function(exclude OUTPUT INPUT)
    set(EXCLUDES ${ARGN})
    foreach(EXCLUDE ${EXCLUDES})
        list(REMOVE_ITEM INPUT "${EXCLUDE}")
    endforeach()
    set(${OUTPUT} ${INPUT} PARENT_SCOPE)
endfunction(exclude)

function(resolve_env ENV_VAR)
  set(extra_args ${ARGN})
  list(LENGTH extra_args num_extra_args)
  if (DEFINED ENV{${ENV_VAR}})
    set(ENV_VAL $ENV{${ENV_VAR}})
    message(STATUS "SET ${ENV_VAR}=${ENV_VAL}")
  elseif(DEFINED ${ENV_VAR})
    set(ENV_VAL ${${ENV_VAR}})
    message(STATUS "SET ${ENV_VAR}=${ENV_VAL}")
  else()
    if(${num_extra_args} GREATER 0)
      list(GET extra_args 0 ENV_VAL)
      message(STATUS "SET ${ENV_VAR}=${ENV_VAL}")
    else()
      message(FATAL_ERROR "ENV{${ENV_VAR}} haven't been defined")
    endif()
  endif()
  set(${ENV_VAR} ${ENV_VAL} PARENT_SCOPE)
endfunction(resolve_env)

include(CMakeParseArguments)
cmake_policy(SET CMP0054 NEW)
include(GoogleTest)
function(add_gtest target)
  cmake_parse_arguments(
    PARSED_ARGS
    "" # options
    "" # one_value_keywords
    "SRCS;INCS;LIBS;LDIRS" # multi_value_keywords
    ${ARGN} # the original inputs
  )
  add_executable("${target}" ${PARSED_ARGS_SRCS})
  target_include_directories("${target}" PUBLIC ${PARSED_ARGS_INCS})
  target_link_directories("${target}" PUBLIC ${PARSED_ARGS_LDIRS})
  target_link_libraries("${target}" PUBLIC gtest gtest_main pthread ${PARSED_ARGS_LIBS})
  gtest_add_tests(TARGET "${target}")
  set_target_properties("${target}" PROPERTIES SKIP_BUILD_RPATH FALSE BUILD_WITH_INSTALL_RPATH FALSE)
endfunction(add_gtest)
