CMake cross compiling Windows to Windows - c++

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")

Related

How to enable static analysis with custom ruleset in MSVC via CMakeLists.txt?

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"
)

C++ compiler flags for profiling in Visual Studio

To accurately profile an application, under linux, it is recommended to have optimizations on and debug symbols on via compile options -O2 -g for gcc or g++. This enables profiling to include the user's C++ code and not point to assembly code instead as possible hotspots, for instance. See for instance, here.
In Visual Studio IDE, for usage of profilers, what are the equivalent compile/linking options?
In Release mode of Visual Studio, under "Whole Program Optimization" property sheet that comes loaded by default, the Debug Information Format gets set to Program Database with command line option /Zi. See image:
Is this the Visual Studio/Windows/MSVC (Cl.exe, MSBuild.exe) equivalent of -O2 -g? Or are there any other equivalent settings?
ETA: Under the default Visual Studio 2019 release mode settings, the following flags are set:
/permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t /I"E:\local\boost_1_72_0" /Zi /Gm- /O2 /sdl /Fd"x64\Release\vc142.pdb" /Zc:inline /D "_MBCS" /errorReport:prompt /WX- /Zc:forScope /Gd /Oi /MD /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /FA /Fp"x64\Release\windows.pch" /diagnostics:column
Have a look at the project properties, under C++ optimization. The IDE tells you everything.

How to create Visual Studio 2015 solution from CMake

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>
)

How do I do "Edit and Continue" builds with cmake projects in Visual Studio 2019?

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

CMAKE - setting compile flags for libraries

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.)?