We have a CMakeList.txt prepared to work with IDE's like QT Creator, so some team members are using two different solutions, one for Release and a different one for Debug, respectively.
I would like to use Visual Studio 2015, so I need to create one single solution considering both configurations (Release & Debug). How can I set the different configuration flags properly?
Now, CMakeList file looks like this:
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Debug Build")
set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /W3 /wd4996 /wd4251 /wd4275 /wd4267 /GR /EHsc /MP")
else()
message("Release Build")
set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /WX /W3 /wd4996 /wd4251 /wd4275 /wd4267 /GR /EHsc /MP /Ox")
endif()
So I just can create one single solution adding -D CMAKE_BUILD_TYPE = Release or Debug.
I would like to generate a single solution and then, from VS decide to compile as Debug or Release.
Visual Studio is a multi-configuration generator, so you can easily switch between Release and Debug. No need to use a CUSTOM_BUILD_TYPE argument. In addition, CMake provides generator expressions (e.g. $<$<CONFIG:Debug>: ... >) for setting configuration-specific options such as those in your example. The flags look very similar between Release and Debug, so you can use a generator expression to simply add the extra Release flags.
Also, manually manipulating the CMAKE_CXX_FLAGS variable is discouraged. It is much safer and cleaner to set these flags using target_compile_options():
target_compile_options(YourTarget PRIVATE
/DWIN32 /D_WINDOWS W3 /wd4996 /wd4251 /wd4275 /wd4267 /GR /EHsc /MP
$<$<CONFIG:Release>:/WX;/Ox>
)
Related
I'd like to use C++20 in MSVC but I can't use the new char8_t in my project. MSVC provides a flag to disable char8_t: /Zc:char8_t-. This works fine if I use a MSVC solution project but it is ignored somehow in my CMAKE project and the compilation fails.
My CMakeLists.txt:
cmake_minimum_required (VERSION 3.12)
project ("CmakeTest")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:char8_t-")
add_executable (CmakeTest "CmakeTest.cpp" )
CMakeTest.cpp
#include <filesystem>
int main()
{
std::filesystem::path path;
std::string test = path.u8string();
return 0;
}
Until C++17 u8string() returned a std::string, so this compiled fine. But since C++ 20 it returns a std::u8string and the compilation fails. The flag "/Zc:char8_t-" should return to the old functionality, but the compilation still fails with the same error that there is no conversion possible from std::u8string to std::string.
The resulting call to cl.exe looks fine and includes the flag:
C:\PROGRA~1\MIB055~1\2022\COMMUN~1\VC\Tools\MSVC\1434~1.319\bin\Hostx64\x64\cl.exe /nologo /TP /DWIN32 /D_WINDOWS /W3 /GR /EHsc /Zc:char8_t- /MDd /Zi /Ob0 /Od /RTC1 -std:c++20 /showIncludes /FoCMakeFiles\CmakeTest.dir\CmakeTest.cpp.obj /FdCMakeFiles\CmakeTest.dir\ /FS -c D:\Projects\cmaketest\CmakeTest.cpp
Executing it manually leads to the same error.
Instead of using CMAKE_CXX_STANDARD I am specifying "-std:c++20" manually. I think this is more of workaround but it works.
CMakeLists.txt
cmake_minimum_required (VERSION 3.12)
project ("CmakeTest")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std:c++20 /Zc:char8_t-")
add_executable (CmakeTest "CmakeTest.cpp" )
I use cmake (3.23+) to generate solution files for MSVC (via CMakeLists.txt).
I'd like to enable static analysis for generated projects, and make it use my custom ruleset. I am aware that MSVC supports a CmakeSettings.json file that can do this, but I don't understand how it relates to CMakeLists.txt/how it should be included here.
I've tried to do it via command line parameters:
target_compile_options ( ${PROJECT_NAME}
PRIVATE /MP /analyze /analyze:ruleset ${CMAKE_CURRENT_SOURCE_DIR}/use-after-move.ruleset
)
But the compiler treats the ruleset as another .cpp file to compile. The generated command line looks like this:
CL.exe /c ...list of include directories... /Zi /nologo /W1 /WX- /diagnostics:column /MP /Od /Ob0 /D _MBCS /D WIN32 /D _WINDOWS /D "CMAKE_INTDIR=\"Debug\"" /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /GR /std:c++latest /Fo"editor-lib.dir\Debug\\" /Fd".\vsbuild\lib\Debug\editor-lib-d.pdb" /external:W1 /Gd /TP /analyze /errorReport:prompt path/to/use-after-move.ruleset ...list of source files...
I tried wrapping the /analyze:ruleset into double quotes, but with the same outcome. I also tried to wrapping it with single quotes which made it show up in the compile options, however like this:
'/analyze:ruleset' path/to/ruleset
I also might be using these options wrong as the analysis is not performed when I manually plug them in project properties. Analysis is only performed when I drop the /analyze:ruleset and keep in just /analyze (but then it defaults to Microsoft's All Rules).
I found an answer in this SO thread: How to add Property to Affect Code Analysis in CMake
Create user property sheet (saved as EnableUseAfterMoveAnalysis.props):
<Project>
<PropertyGroup>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRuleset>path/to/use-after-move.ruleset</CodeAnalysisRuleset>
</PropertyGroup>
</Project>
And then in CMakeLists.txt bind it to the project using set_property:
set_property(
TARGET ${PROJECT_NAME}
PROPERTY VS_USER_PROPS "path/to/EnableUseAfterMoveAnalysis.props"
)
Has anyone ever tried, to cross compile Windows to Windows in cmake?
I have an exotic scenario. I'm participating in an open source project, built with cmake. The project has several platform ports. Eg. Windows, Linux, etc. Each platform has its own cmake subscript and some also have separate toolchain scripts.
I'd like to add another port/platform: Win32 FreeRTOS. The win32 FreeRTOS is a curiosity in itself. Educational at best. And so it shouldn't pollute the existing Windows port.
So, I've created a new platform script called win32freertos.cmake. In which a different set of files and libs are compiled. And I've created a new toolchain script, as one does when cross compiling:
It shouldn't really contain much, I'd think.
include_guard()
# The name of the target operating system
set(CMAKE_SYSTEM_NAME win32freertos)
And then it's built with the Microsoft Visual Studio CMake: (Same as the other Windows port.)
cmake -B build.win32freertos -DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/win32freertos.cmake
This works, to some degree. There're a few missing compiler args. This can be fixed with:
add_definitions(-DWIN32 -D_WINDOWS)
add_compile_options(/Zi /Od /Ob0 /WX /RTC1 /GR /MDd /EHsc)
add_link_options(/DEBUG /INCREMENTAL)
At this point, all seems fairly good, on the surface at least. The only thing is, that the compiled output is named ".a" (for libs) and "" for exe files. (Unix styled output files.) This makes the unit tests fail. (And it's just bad.)
Normally this would be fixed with:
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
Unfortunately, this doesn't work. (???) Nothing seems to work. It seems that when creating a new platform, there's some vital initialization that's missing from the MSVC (Visual Studio) / CMake.
So, instead I've come up with this:
set(CMAKE_CXX_COMPILER_VERSION "19.28") #Visual Studio 16 2019
include("${CMAKE_ROOT}/Modules/Platform/Windows-MSVC-CXX.cmake")
include("${CMAKE_ROOT}/Modules/Platform/Windows.cmake")
This works! But it's a very fragile solution. Eg. I hardcode the Visual Studio compiler version (the MSC_VER actually.) And I hardcode some random visual studio cmake script paths. The next time VS is upgraded, this will probably fail. Or it'll fail when using an older VS. And it's just bad to write stuff like this, in a build script.
Btw, I've not found any way to retrieve the MSC_VER from the system. There's a few corelated values, that can be pulled:
CMAKE_GENERATOR Visual Studio 16 2019
VSCMD_VER=16.8.4
VCToolsVersion=14.28.29333
But they cannot be converted to the MSC_VER, that I've found.
Also, forget about most of the cmake variables, that you think you know. At this point, they're not set. (The above Windows CMake script sets many of them.)
Anyone have any good advice? Has anyone ever tried to create a alternate toolchain script, when building Windows apps in CMake?
Avoid directory commands add_definitions, add_compile_options, add_link_options, etc. Instead, add these to your toolchain file in the standard _INIT variables.
The suffixes can be overridden using their language-specific variants, too:
set(CMAKE_SYSTEM_NAME win32freertos)
set(CMAKE_CXX_FLAGS_INIT "-DWIN32 -D_WINDOWS /Zi /Od /Ob0 /WX /RTC1 /GR /MDd /EHsc")
set(CMAKE_C_FLAGS_INIT "-DWIN32 -D_WINDOWS /Zi /Od /Ob0 /WX /RTC1 /GR /MDd /EHsc")
set(CMAKE_EXE_LINKER_FLAGS_INIT "/DEBUG /INCREMENTAL")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "/DEBUG /INCREMENTAL")
set(CMAKE_STATIC_LINKER_FLAGS_INIT "/DEBUG /INCREMENTAL")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "/DEBUG /INCREMENTAL")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".exe")
set(CMAKE_EXECUTABLE_SUFFIX_C ".exe")
set(CMAKE_SHARED_LIBRARY_SUFFIX_CXX ".dll")
set(CMAKE_SHARED_LIBRARY_SUFFIX_C ".dll")
set(CMAKE_SHARED_MODULE_SUFFIX_CXX ".dll")
set(CMAKE_SHARED_MODULE_SUFFIX_C ".dll")
set(CMAKE_STATIC_LIBRARY_SUFFIX_CXX ".lib")
set(CMAKE_STATIC_LIBRARY_SUFFIX_C ".lib")
set(CMAKE_IMPORT_LIBRARY_SUFFIX_CXX ".lib")
set(CMAKE_IMPORT_LIBRARY_SUFFIX_C ".lib")
Without an MVE, it's hard to know if this will work.
With the hints above, I've created this toolchain file. It looks promissing so far.
# The name of the target operating system
set(CMAKE_SYSTEM_NAME win32freertos)
set(CMAKE_C_FLAGS_DEBUG_INIT "/MDd /Zi /Ob0 /Od /RTC1")
set(CMAKE_C_FLAGS_INIT "/DWIN32 /D_WINDOWS /W3")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MD /O1 /Ob1 /DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MD /O2 /Ob2 /DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MD /Zi /O2 /Ob1 /DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG_INIT "/MDd /Zi /Ob0 /Od /RTC1")
set(CMAKE_CXX_FLAGS_INIT "/DWIN32 /D_WINDOWS /W3 /GR /EHsc")
set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "/MD /O1 /Ob1 /DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "/MD /O2 /Ob2 /DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "/MD /Zi /O2 /Ob1 /DNDEBUG")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG_INIT "/debug /INCREMENTAL")
set(CMAKE_EXE_LINKER_FLAGS_INIT "/machine:x64")
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL_INIT "/INCREMENTAL:NO")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT "/INCREMENTAL:NO")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO_INIT "/debug /INCREMENTAL")
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG_INIT "/debug /INCREMENTAL")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "/machine:x64")
set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL_INIT "/INCREMENTAL:NO")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT "/INCREMENTAL:NO")
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO_INIT "/debug /INCREMENTAL")
set(CMAKE_STATIC_LINKER_FLAGS_INIT "/machine:x64")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".exe")
set(CMAKE_EXECUTABLE_SUFFIX_C ".exe")
set(CMAKE_SHARED_LIBRARY_SUFFIX_CXX ".dll")
set(CMAKE_SHARED_LIBRARY_SUFFIX_C ".dll")
set(CMAKE_SHARED_MODULE_SUFFIX_CXX ".dll")
set(CMAKE_SHARED_MODULE_SUFFIX_C ".dll")
set(CMAKE_STATIC_LIBRARY_SUFFIX_CXX ".lib")
set(CMAKE_STATIC_LIBRARY_SUFFIX_C ".lib")
set(CMAKE_IMPORT_LIBRARY_SUFFIX_CXX ".lib")
set(CMAKE_IMPORT_LIBRARY_SUFFIX_C ".lib")
I want to do "Edit and Continue" in my Visual studio cmake C++ projects.
I know I have to change /Zi to /ZI, but where?
This is C++
The easiest way is to insert the following commands before the definition of your first target:
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS_DEBUG "/ZI")
set(CMAKE_SHARED_LINKER_FLAGS "/SAFESEH:NO")
set(CMAKE_EXE_LINKER_FLAGS "/SAFESEH:NO")
endif()
If you prefer to set the options for a specific target, use
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(${PROJECT_NAME} PRIVATE "/ZI")
target_link_options(${PROJECT_NAME} PRIVATE "/SAFESEH:NO")
endif()
Warning: as of the latest version of Visual Studio at the time of writing (16.4.2), Visual Studio cannot deal with changes to lambda functions.
The options must be set as follows:
CMAKE_C_FLAGS_DEBUG: /MDd /ZI /Ob0 /Od /RTC1
CMAKE_CXX_FLAGS_DEBUG: /MDd /ZI /Ob0 /Od /RTC1
CMAKE_EXE_LINKER_FLAGS_DEBUG: /debug /INCREMENTAL /LTCG:OFF
CMAKE_MODULE_LINKER_FLAGS_DEBUG: /debug /INCREMENTAL /LTCG:OFF
CMAKE_SHARED_LINKER_FLAGS_DEBUG: /debug /INCREMENTAL /LTCG:OFF
I have a CMakeLists.txt script to compile my libraries.
The problem is I cannot set the compile flags for the libraries.
I've tried
SET(CMAKE_CXX_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
SET(CMAKE_C_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_C_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
SET(CMAKE_EXE_LINKER_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
SET(CMAKE_MODULE_LINKER_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_MODULE_LINKER_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
SET(CMAKE_SHARED_LINKER_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
SET(CMAKE_STATIC_LINKER_FLAGS "/W3 /nologo /EHsc")
SET(CMAKE_STATIC_LINKER_FLAGS_DEBUG "/MTd /Od /Ob0 /Zi /RTC1 /DDEBUG /D_DEBUG")
SET(CMAKE_STATIC_LINKER_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
None of those have effect...
The only way I can change flags for a library is like this.
SET_TARGET_PROPERTIES(MyLib PROPERTIES COMPILE_FLAGS "/W3 /nologo /EHsc")
and this will change the flags for both Debug and Release
How in the world can I set the Release and Debug flags separately for a library using CMake?
Those are globally cached variables you are trying overwrite in your first approach.
Changing those compiler/linker options locally you need config-specific target properties or generator expressions. So taking your second approach and How can I set specific compiler flags for a specific target in a specific build configuration using CMake? I would get:
target_compile_options(
MyLib PRIVATE
/W3 /nologo /EHsc
"$<$<CONFIG:Debug>:/MTd;/Od;/Ob0;/Zi;/RTC1;/DDEBUG;/D_DEBUG>"
"$<$<CONFIG:Release>:/MT;/O2;/Ob2;/DNDEBUG>"
)
or for CMake versions <= 2.8.12 (see also policy CMP0043):
set_target_properties(
MyLib PROPERTIES
COMPILE_FLAGS
"/W4 /nologo /EHsc"
COMPILE_FLAGS_DEBUG
"/MTd /Od /Ob0 /Zi /RTC1"
COMPILE_FLAGS_RELEASE
"/MT /O2 /Ob2"
COMPILE_DEFINITIONS_DEBUG
"DEBUG;_DEBUG"
COMPILE_DEFINITIONS_RELEASE
"NDEBUG"
)
I personally like the "OLD" way of doing it better, because it replaces the default options in Visual Studio's project properties. Whatever is passed via target_compile_options() will end up in Configuration Properties / C/C++ / Command Line / Additional Options.
Some background information why your first approach didn't work:
CMake's generator takes whatever is set for CMAKE_<LANG>_FLAGS at the end of any CMakeLists.txt file and applies it to all library and executable targets in the same CMakeLists.txt file as the default compiler options when generating the build environment.
If you set the linker variables for you main CMakeLists.txt targets in subdirectories CMakeLists.txt it won't help (wrong scope). If I take your above code into the main CMakeLists.txt it does work and I get the following warnings (because you used compiler options for the linker):
1>LINK : warning LNK4044: unrecognized option '/W3'; ignored
1>LINK : warning LNK4044: unrecognized option '/EHsc'; ignored
1>LINK : warning LNK4044: unrecognized option '/MTd'; ignored
1>LINK : warning LNK4044: unrecognized option '/Od'; ignored
1>LINK : warning LNK4044: unrecognized option '/Ob0'; ignored
1>LINK : warning LNK4044: unrecognized option '/Zi'; ignored
1>LINK : warning LNK4044: unrecognized option '/RTC1'; ignored
1>LINK : warning LNK4044: unrecognized option '/DDEBUG'; ignored
1>LINK : warning LNK4044: unrecognized option '/D_DEBUG'; ignored
I'm now hiding the cached variables of the same name set by CMake during the compiler detection. For more details see:
What's the CMake syntax to set and use variables?
CMake: In which order are files parsed (cache, toolchain, etc.)?