如何将配置特定的cmake生成器表达式添加到add_custom_target

时间:2016-07-07 12:05:58

标签: build msbuild cmake

我试图说服cmake添加一个在Visual Studio上构建预编译头的自定义目标(注意:请不要建议我使用自定义构建步骤,我特别需要构建目标< / strong>构建预编译头文件。请注意,您不能简单地在配置阶段添加CMAKE_CXX_FLAGS _ $ {CMAKE_BUILD_TYPE},因为在用户可以更改构建配置的生成的.vcxproj中无法工作,因此我需要以某种方式告诉cmake在其构建中阶段为每个可能的配置输出正确的东西,即适当的配置CMAKE_CXX_FLAGS和CMAKE_CXX_FLAGS _ $&lt; CMAKE_BUILD_TYPE&gt;

我几乎已经有了这个解决方案,除了我要求帮助的一个问题:

# Adds a custom target which generates a precompiled header
function(add_precompiled_header outvar headerpath)
  get_filename_component(header "${headerpath}" NAME)
  set(pchpath ${CMAKE_CURRENT_BINARY_DIR}/${header}.dir/${CMAKE_CFG_INTDIR}/${header}.pch)
  set(flags ${CMAKE_CXX_FLAGS})
  separate_arguments(flags)
  set(flags ${flags}
    $<$<CONFIG:Debug>:${CMAKE_CXX_FLAGS_DEBUG}>
    $<$<CONFIG:Release>:${CMAKE_CXX_FLAGS_RELEASE}>
    $<$<CONFIG:RelWithDebInfo>:${CMAKE_CXX_FLAGS_RELWITHDEBINFO}>
    $<$<CONFIG:MinSizeRel>:${CMAKE_CXX_FLAGS_MINSIZEREL}>
  )
  if(MSVC)
    add_custom_target(${outvar}
      COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${header}.dir/${CMAKE_CFG_INTDIR}"
      COMMAND ${CMAKE_CXX_COMPILER} /c ${flags} /Fp"${pchpath}" /Yc"${header}" /Tp"${CMAKE_CURRENT_SOURCE_DIR}/${headerpath}"
      COMMENT "Precompiling header ${headerpath} ..."
      SOURCES "${headerpath}"
    )
  endif()
endfunction()

这几乎适用于每个构建配置的正确标志已扩展到.vcxproj文件中各自的命令节的位置:

<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">setlocal
  "G:\Program Files\CMake\bin\cmake.exe" -E make_directory G:/boost.afio/cmake/afio.hpp.dir/$(Configuration)
  if %errorlevel% neq 0 goto :cmEnd
  "G:\Program Files\Microsoft Visual Studio 14.0\VC\bin\cl.exe" /c /DWIN32 /D_WINDOWS /W3 /GR /EHsc  "/MD /O2 /Ob2 /D NDEBUG"   /Fp"G:/boost.afio/cmake/afio.hpp.dir/$(Configuration)/afio.hpp.pch" /Yc"afio.hpp" /Tp"G:/boost.afio/include/boost/afio/afio.hpp"
  if %errorlevel% neq 0 goto :cmEnd
:cmEnd
  endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
:cmErrorLevel
  exit /b %1
:cmDone
  if %errorlevel% neq 0 goto :VCEnd
</Command>

问题是由于包含空格而导致的生成器$<$<CONFIG:Release>:${CMAKE_CXX_FLAGS_RELEASE}>扩展为带引号的字符串"/MD /O2 /Ob2 /D NDEBUG",这当然会导致编译器大声抱怨。

因此我需要的是:

之一
  1. 告诉cmake生成器表达式不将包含空格的内容扩展为带引号的字符串的一些方法。
  2. OR

    1. 将CMAKE_CXX_FLAGS _ $ {CMAKE_BUILD_TYPE}扩展到add_custom_target的每个配置特定部分的其他一些方法。
    2. 非常感谢提前。

      编辑:根据Tsyvarev的回答,我想出了这个:

      # Add generator expressions to appendvar expanding at build time any remaining parameters
      # if the build configuration is config
      function(expand_at_build_if_config config appendvar)
        set(ret ${${appendvar}})
        set(items ${ARGV})
        list(REMOVE_AT items 0 1)
        separate_arguments(items)
        foreach(item ${items})
          list(APPEND ret $<$<CONFIG:${config}>:${item}>)
        endforeach()
        set(${appendvar} ${ret} PARENT_SCOPE)
      endfunction()
      

      这很好用。非常感谢Tsyvarev!

      编辑2 :事实证明,至少对于MSVC,cmake对预编译的头文件生成没有文档支持。看一下https://github.com/ned14/boost-lite/blob/master/cmake/BoostLitePrecompiledHeader.cmake中的add_precompiled_header()函数,您需要做的就是将/Yc标志提供给OBJECT类型库,并且Visual Studio生成器做正确的事情,正确.vcxproj XML节和所有。

2 个答案:

答案 0 :(得分:3)

按空格拆分 FLAGS ,并为每个标记追加相应的生成器表达式:

# Process Release flags.
set(FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
separate_arguments(FLAGS_RELEASE) # Flags are ready for iterate

foreach(FLAG_RELEASE ${FLAGS_RELEASE})
    list(APPEND flags $<$<CONFIG:Release>:${FLAG_RELEASE}>)
endforeach(FLAGS_RELEASE ${FLAGS_RELEASE})
# Flags for other build types are processed in the same way.
# ...

# Now 'flags' variable may be used for COMMAND

答案 1 :(得分:1)

我已经给了@ Tsyvarev的答案一试,它确实运作得很好。

一般来说,您遇到的问题在CMake本身被视为错误或功能请求。您可能希望将支持添加到仍然打开的CMake Issue #14353

当编译器选项的生成器表达式不能解决时,我只想使用response files添加一个简单的替代方法:

file(
    WRITE "${CMAKE_CURRENT_BINARY_DIR}/Debug.flags" 
        "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}"
)
file(
    WRITE "${CMAKE_CURRENT_BINARY_DIR}/Release.flags" 
        "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}"
)
file(
    WRITE "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo.flags" 
        "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}"
)
file(
    WRITE "${CMAKE_CURRENT_BINARY_DIR}/MinSizeRel.flags" 
        "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}"
)

# Now 'flags' files may be used for COMMAND
add_custom_target(
    ${outvar}
    COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${header}.dir/${CMAKE_CFG_INTDIR}"
    COMMAND ${CMAKE_CXX_COMPILER} /c @$<CONFIG>.flags /Fp"${pchpath}" /Yc"${header}" /Tp"${CMAKE_CURRENT_SOURCE_DIR}/${headerpath}"
    COMMENT "Precompiling header ${headerpath} ..."
    SOURCES "${headerpath}"
)

<强>参考