# zhengrr
# 2017-12-18 – 2021-09-23
# Unlicense

#[=======================================================================[.rst:
rrAddExecutable
---------------

提供若干命令，以便利添加可执行构建目标。
#]=======================================================================]

cmake_minimum_required(VERSION 3.21)
cmake_policy(VERSION 3.21)

include_guard()  # 3.10

if(NOT COMMAND rr_check_cmake_name OR
   NOT COMMAND rr_check_target)
  include("${CMAKE_CURRENT_LIST_DIR}/rrCheck.cmake")
endif()
if(NOT COMMAND rr_get_aam_tag OR
   NOT COMMAND rr_get_toolset_tag)
  include("${CMAKE_CURRENT_LIST_DIR}/rrTag.cmake")
endif()
if(NOT COMMAND rr_set_target OR
   NOT COMMAND rr_set_target_helper_collect_arguments OR
   NOT COMMAND rr_set_target_helper_parse_arguments)
  include("${CMAKE_CURRENT_LIST_DIR}/rrSetTarget.cmake")
endif()


#[=======================================================================[.rst:
.. command:: rr_add_executable

  添加可执行构建目标。

  该命令将：

  - `add_executable`
  - `set_target_properties`
  - `target_compile_definitions`
  - `target_compile_features`
  - `target_compile_options`
  - `target_include_directories`
  - `target_link_directories`
  - `target_link_libraries`
  - `target_link_options`
  - `target_precompile_headers`
  - `target_sources`

  等 11 条命令整合起来，以便于书写。

  .. code-block:: cmake

    rr_add_executable(
      <name> <argument-of-"add_executable"-command>...
      [PROPERTIES          {<property-key> <property-value>}...]
      [COMPILE_DEFINITIONS {{INTERFACE|PUBLIC|PRIVATE} <definition>...}...]
      [COMPILE_FEATURES    {{INTERFACE|PUBLIC|PRIVATE} <feature>...}...]
      [COMPILE_OPTIONS     {{INTERFACE|PUBLIC|PRIVATE} <option>...}...]
      [INCLUDE_DIRECTORIES {{INTERFACE|PUBLIC|PRIVATE} <directory>...}...]
      [LINK_DIRECTORIES    {{INTERFACE|PUBLIC|PRIVATE} <directory>...}...]
      [LINK_LIBRARIES      {{INTERFACE|PUBLIC|PRIVATE} <library>...}...]
      [LINK_OPTIONS        {{INTERFACE|PUBLIC|PRIVATE} <option>...}...]
      [PRECOMPILE_HEADERS  {{INTERFACE|PUBLIC|PRIVATE} <header>...}...]
      [SOURCES             {{INTERFACE|PUBLIC|PRIVATE} <source>...}...])

  参见：

  - :command:`rr_set_target`
  - `add_executable <https://cmake.org/cmake/help/latest/command/add_executable.html>`_
#]=======================================================================]
function(rr_add_executable sName)
  rr_set_target_helper_parse_arguments(zArgumentsOfAddExecutable ${ARGN})

  rr_check_cmake_name("${sName}" AUTHOR_WARNING)

  add_executable("${sName}" ${zArgumentsOfAddExecutable})
  rr_set_target_helper_collect_arguments(zRrSetTargetRestArgs)
  rr_set_target("${sName}" ${zRrSetTargetRestArgs})
endfunction()


#[=======================================================================[.rst:
.. command:: rr_post_copy_runtime_dlls

  为目标添加构建后事件，将运行时依赖复制到目标生成目录。

  参见：

  - :command:`rr_set_target`
  - `$<TARGET_RUNTIME_DLLS:tgt> <https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS>`_
#]=======================================================================]
function(rr_post_copy_runtime_dlls tTarget)
  rr_check_target("${tTarget}" FATAL_ERROR)

  add_custom_command(
    TARGET  "${tTarget}" POST_BUILD
    COMMAND "${CMAKE_COMMAND}" "-E" "$<IF:$<BOOL:$<TARGET_RUNTIME_DLLS:${tTarget}>>,copy_if_different,true>"
                               $<TARGET_RUNTIME_DLLS:${tTarget}> "$<TARGET_FILE_DIR:${tTarget}>"  # 3.21
    COMMAND_EXPAND_LISTS  # 3.8
    VERBATIM
    COMMENT "Copying rumtime DLLs to build directory...")
endfunction()


#[=======================================================================[.rst:
.. command:: rr_add_executable_wcon

  依据惯例，添加可执行构建目标。
  
  注意，该命令所指的“惯例”以本人（作者）便利为准，随时可能改变，不应用于生产环境。

  该命令接受与 :command:`rr_add_executable` 命令类似的参数，并依据惯例进行更多配置（``wcon``，with convention）：

  - 提供构建开关，以供精确开关目标构建，默认勾选；
  - 依据 Boost 命名规则，为生成的二进制文件附加前缀和后缀；
  - 构建完成后，复制已知依赖到可执行文件目录；
  - 配置目标的安装目录。

  .. code-block:: cmake

    rr_add_executable_wcon(
      <name> <argument-of-"add_executable"-command>...
      [NO_OPTION]          [OPTION_NAME    <option-name>]
                           [OPTION_HELP    <help-string>]
                           [OPTION_INITIAL {ON|OFF}]
      [NO_BOOST_NAMING]
      [NO_COPY_DLLS]
      [NO_INSTALL]
      [PROPERTIES          {<property-key> <property-value>}...]
      [COMPILE_DEFINITIONS {{INTERFACE|PUBLIC|PRIVATE} <definition>...}...]
      [COMPILE_FEATURES    {{INTERFACE|PUBLIC|PRIVATE} <feature>...}...]
      [COMPILE_OPTIONS     {{INTERFACE|PUBLIC|PRIVATE} <option>...}...]
      [INCLUDE_DIRECTORIES {{INTERFACE|PUBLIC|PRIVATE} <directory>...}...]
      [LINK_DIRECTORIES    {{INTERFACE|PUBLIC|PRIVATE} <directory>...}...]
      [LINK_LIBRARIES      {{INTERFACE|PUBLIC|PRIVATE} <library>...}...]
      [LINK_OPTIONS        {{INTERFACE|PUBLIC|PRIVATE} <option>...}...]
      [PRECOMPILE_HEADERS  {{INTERFACE|PUBLIC|PRIVATE} <header>...}...]
      [SOURCES             {{INTERFACE|PUBLIC|PRIVATE} <source>...}...])

  参见：

  - :command:`rr_add_executable`
  - `option <https://cmake.org/cmake/help/latest/command/option.html>`_
  - `install <https://cmake.org/cmake/help/latest/command/install.html>`_
#]=======================================================================]
function(rr_add_executable_wcon sName)
  rr_set_target_helper_parse_arguments(zRestArgn ${ARGN})
  set(zOptKws    NO_OPTION
                 NO_BOOST_NAMING
                 NO_COPY_DLLS
                 NO_INSTALL)
  set(zOneValKws OPTION_NAME
                 OPTION_HELP
                 OPTION_INITIAL)
  set(zMutValKws)
  cmake_parse_arguments("ARG" "${zOptKws}" "${zOneValKws}" "${zMutValKws}" ${zRestArgn})

  rr_check_cmake_name("${sName}" AUTHOR_WARNING)

  # 构建开关
  if(NOT ARG_NO_OPTION)
    if(DEFINED ARG_OPTION_NAME)
      set(xOptVar "${ARG_OPTION_NAME}")
    else()
      string(TOUPPER "${sName}" sNameUpper)
      string(TOUPPER "${PROJECT_NAME}" sProjectNameUpper)
      string(REGEX REPLACE "^${sProjectNameUpper}_?" "" sTrimmedNameUpper "${sNameUpper}")
      string(LENGTH "${sTrimmedNameUpper}" nLen)
      if(0 LESS nLen)
        set(xOptVar "${sProjectNameUpper}_${sTrimmedNameUpper}_EXECUTABLE")
      else()
        set(xOptVar "${sProjectNameUpper}_EXECUTABLE")
      endif()
    endif()

    if(DEFINED ARG_OPTION_HELP)
      set(sOptTxt "${ARG_OPTION_HELP}")
    else()
      set(sOptTxt "Build ${sName} executable.")
    endif()

    if(DEFINED ARG_OPTION_INITIAL)
      set(bOptVal ${ARG_OPTION_INITIAL})
    else()
      set(bOptVal ON)
    endif()

    option("${xOptVar}" "${sOptTxt}" "${bOptVal}")
    if(NOT "${${xOptVar}}")
      return()
    endif()
  endif()

  # Boost 命名规则
  if(NOT ARG_NO_BOOST_NAMING)
    if(NOT OUTPUT_NAME IN_LIST zProperties)
      rr_get_boost_style_tag(gPostfix)
      list(APPEND zProperties OUTPUT_NAME "${sName}${gPostfix}")
    endif()
  endif()

  # 添加可执行构建目标
  add_executable("${sName}" ${ARG_UNPARSED_ARGUMENTS})
  rr_set_target_helper_collect_arguments(zRrSetTargetRestArgs)
  rr_set_target("${sName}" ${zRrSetTargetRestArgs})

  # 复制依赖
  if(NOT ARG_NO_COPY_DLLS)
    rr_post_copy_runtime_dlls("${sName}")
  endif()

  # 安装目录
  if(NOT ARG_NO_INSTALL)
    install(
      TARGETS             "${sName}"
      ARCHIVE DESTINATION "lib/"
      LIBRARY DESTINATION "lib/"
      RUNTIME DESTINATION "bin/")
  endif()
endfunction()
