# zhengrr
# 2018-06-06 – 2021-10-20
# Unlicense

#[=======================================================================[.rst:
rrTag
-----

提供若干获取命令，以获取与构建相关的若干标签。

参见：

- `Boost Getting Started on Unix Variants § Library Naming <https://www.boost.org/doc/libs/master/more/getting_started/unix-variants.html#library-naming>`_
- `Boost Getting Started on Windows § Library Naming <https://www.boost.org/doc/libs/master/more/getting_started/windows.html#library-naming>`_
- `FindBoost.cmake <https://gitlab.kitware.com/cmake/cmake/blob/master/Modules/FindBoost.cmake>`_
#]=======================================================================]

cmake_minimum_required(VERSION 3.12)
cmake_policy(VERSION 3.12)

include_guard()  # 3.10

if(NOT COMMAND rr_check_cmake_name OR
   NOT COMMAND rr_check_no_argn)
  include("${CMAKE_CURRENT_LIST_DIR}/rrCheck.cmake")
endif()


#[=======================================================================[.rst:
.. command:: rr_get_toolset_tag

  获取工具集标签。
  
  .. code-block:: cmake

    rr_get_toolset_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_toolset_tag(sTag)

  常见的工具集标签有：

  - ``vc142``（Visual Studio 2019）
  - ``vc141``（Visual Studio 2017）
#]=======================================================================]
function(rr_get_toolset_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  get_cmake_property(zLangs ENABLED_LANGUAGES)
  foreach(sLang IN LISTS zLangs)

    # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
    if(CMAKE_${sLang}_COMPILER_ID              STREQUAL "Intel"     OR
       CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID STREQUAL "IntelLLVM")
      if(WIN32)
        set("${xVariable}" "${ARG_PREFIX}iw" PARENT_SCOPE)
      else()
        set("${xVariable}" "${ARG_PREFIX}il" PARENT_SCOPE)
      endif()
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/GHS-MULTI.html
    if(GHS-MULTI)
      set("${xVariable}" "${ARG_PREFIX}ghs" PARENT_SCOPE)
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/MSVC.html
    # https://cmake.org/cmake/help/latest/variable/MSVC_TOOLSET_VERSION.html
    if(MSVC)
      if(80 LESS_EQUAL MSVC_TOOLSET_VERSION)
        set("${xVariable}" "${ARG_PREFIX}vc${MSVC_TOOLSET_VERSION}" PARENT_SCOPE)
      elseif(1310 LESS_EQUAL MSVC_VERSION)
        set("${xVariable}" "${ARG_PREFIX}vc71" PARENT_SCOPE)
      elseif(1300 LESS_EQUAL MSVC_VERSION)
        set("${xVariable}" "${ARG_PREFIX}vc7" PARENT_SCOPE)
      elseif(1200 LESS_EQUAL MSVC_VERSION)
        set("${xVariable}" "${ARG_PREFIX}vc6" PARENT_SCOPE)
      else()
        set("${xVariable}" "${ARG_PREFIX}vc" PARENT_SCOPE)
      endif()
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/BORLAND.html
    if(BORLAND)
      set("${xVariable}" "${ARG_PREFIX}bcb" PARENT_SCOPE)
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
    if(CMAKE_${sLang}_COMPILER_ID STREQUAL "SunPro")
      set("${xVariable}" "${ARG_PREFIX}sw" PARENT_SCOPE)
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
    if(CMAKE_${sLang}_COMPILER_ID STREQUAL "XL")
      set("${xVariable}" "${ARG_PREFIX}xlc" PARENT_SCOPE)
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/MINGW.html
    if(MINGW)
      string(REGEX REPLACE [[([0-9]+)\.([0-9]+)(\.[0-9]+)?]] [[\1\2]] sVer "${CMAKE_${sLang}_COMPILER_VERSION}")
      set("${xVariable}" "${ARG_PREFIX}mgw${sVer}" PARENT_SCOPE)
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/UNIX.html
    # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
    if(UNIX AND CMAKE_${sLang}_COMPILER_ID STREQUAL "GNU")
      string(REGEX REPLACE [[([0-9]+)\.([0-9]+)(\.[0-9]+)?]] [[\1\2]] sVer "${CMAKE_${sLang}_COMPILER_VERSION}")
      if(APPLE)
        set("${xVariable}" "${ARG_PREFIX}xgcc${sVer}" PARENT_SCOPE)
      else()
        set("${xVariable}" "${ARG_PREFIX}gcc${sVer}" PARENT_SCOPE)
      endif()
      return()
    endif()

    # https://cmake.org/cmake/help/latest/variable/UNIX.html
    # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
    if(UNIX AND CMAKE_${sLang}_COMPILER_ID STREQUAL "Clang")
      string(REGEX REPLACE [[([0-9]+)\.([0-9]+)(\.[0-9]+)?]] [[\1\2]] sVer "${CMAKE_${sLang}_COMPILER_VERSION}")
      set("${xVariable}" "${ARG_PREFIX}clang${sVer}" PARENT_SCOPE)
      return()
    endif()

  endforeach()
  set("${xVariable}" "" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command:: rr_get_threading_tag

  获取线程标签。如 ``mt``。

  .. code-block:: cmake

    rr_get_threading_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_threading_tag(sTag)

  常见的线程标签有：

  - ``mt``（多线程）
#]=======================================================================]
function(rr_get_threading_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  get_cmake_property(zLangs ENABLED_LANGUAGES)

  if(CMAKE_BUILD_TYPE)
    set(zConfigs ${CMAKE_BUILD_TYPE})
  else()
    set(zConfigs ${CMAKE_CONFIGURATION_TYPES})
  endif()
  list(TRANSFORM zConfigs TOUPPER)  # 3.12

  foreach(sLang IN LISTS zLangs)
    foreach(sConfig IN LISTS zConfigs)
      string(REPLACE " " ";" zFlags "${CMAKE_${sLang}_FLAGS} ${CMAKE_${sLang}_FLAGS_${sConfig}}")
      if("/MD" IN_LIST zFlags)  # 3.3
        set("${xVariable}" "${ARG_PREFIX}mt" PARENT_SCOPE)
        return()
      endif()
      if("/MDd" IN_LIST zFlags)  # 3.3
        set("${xVariable}" "${ARG_PREFIX}mt" PARENT_SCOPE)
        return()
      endif()
      if("/MT" IN_LIST zFlags)  # 3.3
        set("${xVariable}" "${ARG_PREFIX}mt" PARENT_SCOPE)
        return()
      endif()
      if("/MTd" IN_LIST zFlags)  # 3.3
        set("${xVariable}" "${ARG_PREFIX}mt" PARENT_SCOPE)
        return()
      endif()
    endforeach()
  endforeach()

  set("${xVariable}" "" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command:: rr_get_abi_tag

  获取二进制接口标签。注意，返回的字符串包含生成器表达式。

  .. code-block:: cmake

    rr_get_abi_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_abi_tag(gTag)

  常见的二进制接口标签有：

  - ``s``（静态链接到依赖库）
  - ``g``（使用调试版本的依赖库）
  - ``d``（使用调试版本的构建）
  - ``p``（使用 STLPort 提供的标准库实现，而非编译器提供的默认实现）
#]=======================================================================]
function(rr_get_abi_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  set(gPrefix "$<$<OR:$<CONFIG:Debug>>:${ARG_PREFIX}>")
  set(gDebug  "$<$<CONFIG:Debug>:d>")

  set("${xVariable}" "${gPrefix}${gDebug}" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command:: rr_get_architecture_tag

  获取架构标签。

  .. code-block:: cmake

    rr_get_architecture_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_architecture_tag(sTag)

  常见的架构标签有：

  - ``x``（x86-32 或 x86-64）
  - ``a``（ARM）
#]=======================================================================]
function(rr_get_architecture_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  get_cmake_property(zLangs ENABLED_LANGUAGES)
  foreach(sLang IN LISTS zLangs)

    if(CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID STREQUAL "IA64")
      set("${xVariable}" "${ARG_PREFIX}i" PARENT_SCOPE)
      return()
    endif()

    if(CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID STREQUAL "X86" OR
       CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID STREQUAL "x64")
      set("${xVariable}" "${ARG_PREFIX}x" PARENT_SCOPE)
      return()
    endif()

    if(CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID MATCHES "^ARM")
      set("${xVariable}" "${ARG_PREFIX}a" PARENT_SCOPE)
      return()
    endif()

    if(CMAKE_${sLang}_COMPILER_ARCHITECTURE_ID STREQUAL "MIPS")
      set("${xVariable}" "${ARG_PREFIX}m" PARENT_SCOPE)
      return()
    endif()

  endforeach()
  set("${xVariable}" "" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command::rr_get_address_model_tag

  获取地址模型标签。

  .. code-block:: cmake

    rr_get_address_model_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_address_model_tag(sTag)

  常见的地址模型标签有：

  - ``64``（64 位）
  - ``32``（32 位）
#]=======================================================================]
function(rr_get_address_model_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  if(0 LESS CMAKE_SIZEOF_VOID_P)
    math(EXPR nBits "${CMAKE_SIZEOF_VOID_P} * 8")
    set("${xVariable}" "${ARG_PREFIX}${nBits}" PARENT_SCOPE)
    return()
  endif()

  set("${xVariable}" "" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command::rr_get_aam_tag

  获取架构和地址模型标签。

  .. code-block:: cmake

    rr_get_aam_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_aam_tag(sTag)

  常见的架构和地址模型标签有：

  - ``x64``（x86-64）
  - ``x32``（x86-32）

  参见：

  - :command:`rr_get_architecture_tag`
  - :command:`rr_get_address_model_tag`
#]=======================================================================]
function(rr_get_aam_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  rr_get_architecture_tag(s1)
  rr_get_address_model_tag(s2)
  if(NOT "${s1}${s2}" STREQUAL "")
    set("${xVariable}" "${ARG_PREFIX}${s1}${s2}" PARENT_SCOPE)
  else()
    set("${xVariable}" "" PARENT_SCOPE)
  endif()
endfunction()


#[=======================================================================[.rst:
.. command::rr_get_version_tag

  获取版本标签。

  .. code-block:: cmake

    rr_get_version_tag(<variable> [PREFIX <prefix>])

  参考用例：

  .. code-block:: cmake

    rr_get_version_tag(sTag)
#]=======================================================================]
function(rr_get_version_tag xVariable)
  cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "PREFIX" "")
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARG_UNPARSED_ARGUMENTS}" FATAL_ERROR)

  if(NOT "${PROJECT_VERSION}" STREQUAL "")
    string(REPLACE "." "_" sVersion "${PROJECT_VERSION}")
    set("${xVariable}" "${ARG_PREFIX}${sVersion}" PARENT_SCOPE)
    return()
  endif()

  if(NOT "${CMAKE_PROJECT_VERSION}" STREQUAL "")  # 3.12
    string(REPLACE "." "_" sVersion "${CMAKE_PROJECT_VERSION}")
    set("${xVariable}" "${ARG_PREFIX}${sVersion}" PARENT_SCOPE)
    return()
  endif()

  set("${xVariable}" "" PARENT_SCOPE)
endfunction()


#[=======================================================================[.rst:
.. command::rr_get_boost_style_tag

  获取 Boost 风格标签。注意，返回的字符串包含生成器表达式。

  .. code-block:: cmake

    rr_get_boost_style_tag(<variable>)

  参考用例：

  .. code-block:: cmake

    rr_get_boost_style_tag(gTag)

  该标签形如：

  .. code-block::

    [-toolset-tag][-threading-tag][-abi-tag][-architecture-address-model-tag][-version-tag]

  参见：

  - :command:`rr_get_toolset_tag`
  - :command:`rr_get_threading_tag`
  - :command:`rr_get_abi_tag`
  - :command:`rr_get_aam_tag`
  - :command:`rr_get_version_tag`
#]=======================================================================]
function(rr_get_boost_style_tag xVariable)
  rr_check_cmake_name("${xVariable}" AUTHOR_WARNING)
  rr_check_no_argn("${ARGN}" FATAL_ERROR)

  rr_get_toolset_tag(s1 PREFIX "-")
  rr_get_threading_tag(s2 PREFIX "-")
  rr_get_abi_tag(g3 PREFIX "-")
  rr_get_aam_tag(s4 PREFIX "-")
  rr_get_version_tag(s5 PREFIX "-")
  set("${xVariable}" "${s1}${s2}${g3}${s4}${s5}" PARENT_SCOPE)
endfunction()
