在跨平台cmake项目中设置编译器标志的现代方法 [英] Modern way to set compiler flags in cross-platform cmake project
问题描述
我想编写一个cmake文件,该文件在调试和发行版本中为clang ++,g ++和MSVC设置不同的编译器选项。
我目前正在做什么:
if(MSVC)
set(CMAKE_CXX_FLAGS $ {CMAKE_CXX_FLAGS} / std:c ++ latest / W4)
#默认调试标志可以
设置(CMAKE_CXX_FLAGS_RELEASE {CMAKE_CXX_FLAGS_RELEASE} / O2)
else()
set(CMAKE_CXX_FLAGS $ {CMAKE_CXX_FLAGS} -std = c ++ 1z -Wall -Wextra -Werror)
set(CMAKE_CXX_FLAGS_DEBUG $ {CMAKE_CXX_FLAGS_DEBUG}其他标志)$ b $ RASE(CMAKE $ {CMAKE_CXX_FLAGS_RELEASE} -O3)
if( $ {CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
set(CMAKE_CXX_FLAGS $ {CMAKE_CXX_FLAGS} -stdlib = libc ++)
b else()
#目前gcc没什么特别的
endif()
endif()
但是我有几个问题:
- 首先,琐碎的事:有没有相对没有命令就像appen一样,它可以让我替换
set(CMAKE_CXX_FLAGS $ {CMAKE_CXX_FLAGS} Foo)
加上附加(CMAKE_CXX_FLAGS Foo)
? - 我已经读过很多遍了,一开始不应该手动设置
CMAKE_CXX_FLAGS
和类似变量,但是我不确定 - 最重要的是:在这里,我需要为每个编译器和配置使用单独的构建目录,理想情况下,我希望将其转换为具有多个目标在同一目录中,所以我可以例如呼叫
make foo_debug_clang
。
所以我的问题是
- a)有没有更好的方法来编写可以解决我的痛点的cmake脚本?
解决上述问题的方法? - b)是否存在类似的公认的现代最佳实践来建立此类项目?
我在互联网上可以找到的大多数参考文献已经过时或仅显示了一些琐碎的例子。我目前正在使用cmake3.8,但如果有什么不同,我对最新版本的答案更感兴趣。
正如@Tsyvarev所评论的那样,您的方法绝对可以,因为自从您在CMake中请求新方法后,这就是您的代码将转换为的内容:
cmake_minimum_required(版本3.8)
项目(HelloWorld)
字符串(
APPEND _opts
$< IF:$< CXX_COMPILER_ID:MSVC>,
/ W4; $< $< CONFIG:RELEASE>:/ O2> ;,
-Wall; -Wextra; -Werror;
$&$; $< CONFIG:RELEASE>:-O3>
$< $< CXX_COMPILER_ID:Clang> ::-stdlib = libc ++>
>
)
add_compile_options( $ {_ opts}))
add_executable(HelloWorld main .cpp)
target_compile_features(HelloWorld PUBLIC cxx_lambda_init_captures)
您采用
CMakeSettings.json
{
//有关此文件的更多信息,请参见https://go.microsoft.com//fwlink//?linkid=834763。
configurations:[
{
name: x86-Debug,
generator: Visual Studio 15 2017,
configurationType : Debug,
buildRoot: $ {env.LOCALAPPDATA} \\CMakeBuild\\ $ {workspaceHash} \\build\\ $ {name},
buildCommandArgs: -m -v:minimal,
},
{
name: x86-Release,
generator: Visual Studio 15 2017,
configurationType:发布,
buildRoot: $ {env.LOCALAPPDATA} \\CMakeBuild\\ $ {workspaceHash} \\build \\ $ {name},
buildCommandArgs: -m -v:minimal,
},
{
name: Clang-Debug ,
generator: Visual Studio 15 2017,
configurationType: Debug,
buildRoot: $ {env.LOCALAPPDATA} \\CMakeBuild\ \\ $ {workspaceHash} \\build\\ $ {name},
cmakeCommandArgs:- T\ LLVM-vs2014\,
buildCommandArgs: -m -v:minimal,
},
{
name: Clang -Release,
generator: Visual Studio 15 2017,
configurationType: Release,
buildRoot: $ {env.LOCALAPPDATA} \\ CMakeBuild\\ $ {workspaceHash} \\build\\ $ {name},
cmakeCommandArgs: -T\ LLVM-vs2014\,
buildCommandArgs: -m -v:minimal,
},
{
name: GNU-Debug,
generator: MinGW Makefiles ,
configurationType:调试,
buildRoot: $ {env.LOCALAPPDATA} \\CMakeBuild\\ $ {workspaceHash} \\build\\ $ {name},
变量:[
{
name: CMAKE_MAKE_PROGRAM,
value: $ {projectDir} \\mingw32 -make.cmd
}
]
},
{
name: GNU-Release,
generator: Unix Makefiles,
configurationType: Release,
buildRoot: $ {env。 LOCALAPPDATA} \\CMakeBuild\\ $ {workspaceHash} \\build\\ $ {name},
变量:[
{
name : CMAKE_MAKE_PROGRAM,
值: $ {projectDir} \\mingw32-make.cmd
}
]
}
]
}
mingw32-make.cmd
@echo off
mingw32-make.exe%〜1%〜2%〜3%〜4
因此,您可以在Visual Studio 2017中使用任何CMake生成器,但存在一些不健康的报价(截至2017年9月,可能已修复)稍后),需要 mingw32-make.cmd
中介(删除引号)。
I want to write a cmake file that sets different compiler options for clang++, g++ and MSVC in debug and release builds. What I'm doing currently looks something like this:
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4")
# Default debug flags are OK
set(CMAKE_CXX_FLAGS_RELEASE "{CMAKE_CXX_FLAGS_RELEASE} /O2")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -Wall -Wextra -Werror")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} some other flags")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
else()
# nothing special for gcc at the moment
endif()
endif()
But I have a couple of problems with this:
- First the trivial: Is there relly no command like appen that would allow me to replace
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo")
withappend(CMAKE_CXX_FLAGS "Foo")
? - I've read multiple times, that one should not manually set
CMAKE_CXX_FLAGS
and similar variables in the first place, but im not sure what other mechanism to use. - Most importantly: The way I do it here, I need a separate build directory for each compiler and configuration Ideally I'd like to transform that into havin multiple targets in the same directory so I can e.g. call
make foo_debug_clang
.
So my questions are
- a) Is there a better way to write th cmake script that solves my "pain points"? solution to the points mentioned above?
- b) Is there something like an accepted, modern best practice of how to set up such projects?
Most references I could find on the internet are either out of date or show only trivial examples. I currently using cmake3.8, but if that makes any difference, I'm even more interested in the answer for more recent versions.
Your approach would - as @Tsyvarev has commented - be absolutely fine, just since you've asked for the "new" approach in CMake here is what your code would translate to:
cmake_minimum_required(VERSION 3.8)
project(HelloWorld)
string(
APPEND _opts
"$<IF:$<CXX_COMPILER_ID:MSVC>,"
"/W4;$<$<CONFIG:RELEASE>:/O2>,"
"-Wall;-Wextra;-Werror;"
"$<$<CONFIG:RELEASE>:-O3>"
"$<$<CXX_COMPILER_ID:Clang>:-stdlib=libc++>"
">"
)
add_compile_options("${_opts}")
add_executable(HelloWorld "main.cpp")
target_compile_features(HelloWorld PUBLIC cxx_lambda_init_captures)
You take add_compile_options()
and - as @Al.G. has commented - "use the dirty generator expressions".
There are some downsides of generator expressions:
- The very helpful
$<IF:...,...,...>
expression is only available in CMake version >= 3.8 - You have to write it in a single line. To avoid it I used the
string(APPEND ...)
, which you can also use to "optimize" yourset(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ...
calls. - It's difficult to read and understand. E.g. the semicolons are needed to make it a list of compile options (otherwise CMake will quote it).
So better use a more readable and backward compatible approach with add_compile_options()
:
if(MSVC)
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
else()
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_compile_options("-stdlib=libc++")
else()
# nothing special for gcc at the moment
endif()
endif()
And yes, you don't explicitly specify the C++ standard anymore, you just name the C++ feature your code/target does depend on with target_compile_features()
calls.
For this example I've chosen cxx_lambda_init_captures
which would for e.g. an older GCC compiler give the following error (as an example what happens if a compiler does not support this feature):
The compiler feature "cxx_lambda_init_captures" is not known to CXX compiler
"GNU"
version 4.8.4.
And you need to write a wrapper script to build multiple configurations with a "single configuration" makefile generator or use a "multi configuration" IDE as Visual Studio.
Here are the references to examples:
- Does CMake always generate configurations for all possible project configurations?
- How do I tell CMake to use Clang on Windows?
- How to Add Linux Compilation to Cmake Project in Visual Studio
So I've tested the following with the Open Folder
Visual Studio 2017 CMake support to combine in this example the cl, clang and mingw compilers:
CMakeSettings.json
{
// See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
"configurations": [
{
"name": "x86-Debug",
"generator": "Visual Studio 15 2017",
"configurationType": "Debug",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"buildCommandArgs": "-m -v:minimal",
},
{
"name": "x86-Release",
"generator": "Visual Studio 15 2017",
"configurationType": "Release",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"buildCommandArgs": "-m -v:minimal",
},
{
"name": "Clang-Debug",
"generator": "Visual Studio 15 2017",
"configurationType": "Debug",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"cmakeCommandArgs": "-T\"LLVM-vs2014\"",
"buildCommandArgs": "-m -v:minimal",
},
{
"name": "Clang-Release",
"generator": "Visual Studio 15 2017",
"configurationType": "Release",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"cmakeCommandArgs": "-T\"LLVM-vs2014\"",
"buildCommandArgs": "-m -v:minimal",
},
{
"name": "GNU-Debug",
"generator": "MinGW Makefiles",
"configurationType": "Debug",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"variables": [
{
"name": "CMAKE_MAKE_PROGRAM",
"value": "${projectDir}\\mingw32-make.cmd"
}
]
},
{
"name": "GNU-Release",
"generator": "Unix Makefiles",
"configurationType": "Release",
"buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
"variables": [
{
"name": "CMAKE_MAKE_PROGRAM",
"value": "${projectDir}\\mingw32-make.cmd"
}
]
}
]
}
mingw32-make.cmd
@echo off
mingw32-make.exe %~1 %~2 %~3 %~4
So you can use any CMake generator from within Visual Studio 2017, there is some unhealthy quoting going on (as for September 2017, maybe fixed later) that requires that mingw32-make.cmd
intermediator (removing the quotes).
这篇关于在跨平台cmake项目中设置编译器标志的现代方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!