# zhengrr
# 2016-10-08 – 2021-10-18
# Unlicense

cmake_minimum_required(VERSION 3.20)
cmake_policy(VERSION 3.20)

include_guard()  # 3.10

#[=======================================================================[.rst:
.. command:: rr_add_compile_options

  基于 ``add_compile_options`` 命令，提供更多选项和功能。

  .. code-block:: cmake

    rr_add_compile_options(
      <argument-of-"add_compile_options"-command>...
      [ANALYZE]
      [DISABLE_LANGUAGE_EXTENSIONS]
      [HIGHEST_WARNING_LEVEL | RECOMMENDED_WARNING_LEVEL | NO_WARNINGS]
      [MULTIPLE_PROCESSES]
      [SECURITY_DEVELOPMENT_LIFECYCLE]
      [UTF-8]
      [VISIBLE_DEFAULT_HIDDEN]
      [WARNING_AS_ERROR])

  ``ANALYZE``
    启用代码分析（仅 MSVC）。

  ``DISABLE_EXTERNAL_HEADERS_DIAGNOSTICS``
    禁用外部标头诊断。

  ``DISABLE_LANGUAGE_EXTENSIONS``
    禁用语言扩展。

  ``HIGHEST_WARNING_LEVEL``、``RECOMMENDED_WARNING_LEVEL``、``NO_WARNINGS``
    设定警告级别。

  ``MULTIPLE_PROCESSES``
    使用多个进程生成（仅 MSVC）。

  ``SECURITY_DEVELOPMENT_LIFECYCLE``
    启用附加安全检查（仅 MSVC）。

  ``UTF-8``
    将源和可执行字符集设置为 UTF-8（仅 MSVC）。

  ``VISIBLE_DEFAULT_HIDDEN``
    将符号的默认导出策略设置为“隐藏”（仅 GCC）。

  ``WARNING_AS_ERROR``
    将警告视为错误。

  参见：

  - `add_compile_options <https://cmake.org/cmake/help/latest/command/add_compile_options.html>`_
#]=======================================================================]
function(rr_add_compile_options)
  set(zOptKws    ANALYZE
                 DISABLE_EXTERNAL_HEADERS_DIAGNOSTICS
                 DISABLE_LANGUAGE_EXTENSIONS
                 HIGHEST_WARNING_LEVEL
                 MULTIPLE_PROCESSES
                 NO_WARNINGS
                 RECOMMENDED_WARNING_LEVEL
                 SECURITY_DEVELOPMENT_LIFECYCLE
                 UTF-8
                 VISIBLE_DEFAULT_HIDDEN
                 WARNING_AS_ERROR)
  set(zOneValKws)
  set(zMutValKws)
  cmake_parse_arguments(PARSE_ARGV 0 "ARG" "${zOptKws}" "${zOneValKws}" "${zMutValKws}")

  # 基础 add_compile_options 命令
  add_compile_options(${ARG_UNPARSED_ARGUMENTS})

  # 与 MSVC 标识类似，定义一个 GCC 标识
  set(GCC "FALSE")
  get_cmake_property(zLangs ENABLED_LANGUAGES)
  foreach(sLang IN LISTS zLangs)
    if(CMAKE_${sLang}_COMPILER_ID STREQUAL "GNU")
      set(GCC "TRUE")
      break()
    endif()
  endforeach()

  # 代码分析
  # MSVC: https://docs.microsoft.com/cpp/build/reference/analyze-code-analysis
  if(ARG_ANALYZE)
    if(MSVC_VERSION GREATER_EQUAL 1910 AND CMAKE_CXX_COMPILER)
      # ${CMAKE_CXX_COMPILER}
      # Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\cl.exe
      # ${CMAKE_MAKE_PROGRAM}
      # Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe
      # ${pRuleSetDirectory}
      # Microsoft Visual Studio\2019\Community\Team Tools\Static Analysis Tools\Rule Sets
      cmake_path(CONVERT "${CMAKE_MAKE_PROGRAM}/../../../../Team Tools/Static Analysis Tools/Rule Sets" TO_NATIVE_PATH_LIST pRuleSetDirectory NORMALIZE)  # 3.20
      add_compile_options("/analyze" "/analyze:WX-"
                          "/analyze:rulesetdirectory" "${pRuleSetDirectory}"
                          "/analyze:ruleset"          "AllRules.ruleset")
      #                                               "CppCoreCheckRules.ruleset"
    endif()
  endif()

  # 禁用外部标头诊断
  if(ARG_DISABLE_EXTERNAL_HEADERS_DIAGNOSTICS)
    if(MSVC)
      add_compile_options("/external:anglebrackets" "/external:W0" "/analyze:external-")
    endif()
  endif()

  # 禁用语言扩展
  # GCC:  https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
  # MSVC: https://docs.microsoft.com/cpp/build/reference/za-ze-disable-language-extensions
  if(ARG_DISABLE_LANGUAGE_EXTENSIONS)
    if(GCC)
      add_compile_options("-Wpedantic")
    endif()
    if(MSVC)
      add_compile_options("/Za")
    endif()
  endif()

  # 警告级别
  # GCC:  https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
  # MSVC: https://docs.microsoft.com/cpp/build/reference/compiler-option-warning-level
  if(ARG_HIGHEST_WARNING_LEVEL)
    if(GCC)
      add_compile_options("-Wall" "-Wextra")
    endif()
    if(MSVC)
      add_compile_options("/Wall")
    endif()
  elseif(ARG_RECOMMENDED_WARNING_LEVEL)
    if(GCC)
      add_compile_options("-Wall")
    endif()
    if(MSVC)
      add_compile_options("/W4")
    endif()
  elseif(ARG_NO_WARNINGS)
    if(GCC)
      add_compile_options("-w")
    endif()
    if(MSVC)
      add_compile_options("/w")
    endif()
  endif()

  # 使用多个进程生成
  # MSVC: https://docs.microsoft.com/cpp/build/reference/mp-build-with-multiple-processes
  if(ARG_MULTIPLE_PROCESSES)
    if(MSVC)
      add_compile_options("/MP")
    endif()
  endif()

  # 启用附加安全检查
  # MSVC: https://docs.microsoft.com/cpp/build/reference/sdl-enable-additional-security-checks
  if(ARG_SECURITY_DEVELOPMENT_LIFECYCLE)
    if(MSVC)
      add_compile_options("/sdl")
    endif()
  endif()

  # 将源和可执行字符集设置为 UTF-8
  # MSVC: https://docs.microsoft.com/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8
  if(ARG_UTF-8)
    if(MSVC)
      add_compile_options("/utf-8")
    endif()
  endif()

  # 将符号的默认导出策略设置为“隐藏”
  # GCC: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
  if(ARG_VISIBLE_DEFAULT_HIDDEN)
    if(GCC)
      add_compile_options("-fvisibility=hidden")
    endif()
  endif()

  # 将警告视为错误
  # GCC:  https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
  # MSVC: https://docs.microsoft.com/cpp/build/reference/compiler-option-warning-level
  if(ARG_WARNING_AS_ERROR)
    if(GCC)
      add_compile_options("-Werror")
    endif()
    if(MSVC)
      add_compile_options("/WX")
    endif()
  endif()
endfunction()
