Introduction¶
In C++, the workflow and especially the build pipeline is much more complicated than in other languages. Therefore, we start with brief overview of the C++ build pipelines. The following scheme (see the source for links) shows the possible build pipelines for C++, starting from Integrated developemnt tools (IDE) and ending with the linker.

This guide presents mostly the following workflows:
- Clion or Visual Studio IDE
- CMake
- any sequencing tool (these are discribed only briefly as they are configured automaticvally by CMake)
- MSVC and GCC compiler toolchains
Appart from the build pipeline, we also cover the dependency management. For this, we focus on the dependency manager vcpkg.
Compiler Toolchains¶
There are various toolchains available on Windows and Linux, but we limit this guide for only some of them, specifically those which are frequently updated and works great with Clion.
MSYS2 (Windows)¶
- download
- follow the installation guide on the homepage
- install MinGW64 using:
pacman -S mingw-w64-x86_64-gcc
MSVC (Windows)¶
Visual Studio Comunity Edition
Setting up the compiler environment¶
Most of the time, the compiler environment is set up automatically. However, beware that failing to setup the MSVC compiler environment can lead to hard to debug errors or silent selection of another compiler!.
MSVC compiler is automatically set up:
- by Visual Studio and CLion IDEs
- by developer command prompt and developer PowerShell
- anytime when MSBuild is used as build sequencing tool
On the other hand, a typical example of a situation where the compiler environment is not set up is when using a non-developer shell (e.g., PowerShell) and using a non-default build sequencing tool (e.g., Ninja).
To test if the compiler environment is set up correctly, we can use call the cl command. If the compiler environment is not set up correctly, the command will not be found.
To set up the compiler environment manually, you can use the following commands:
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vs = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
Import-Module "$vs\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $vs -DevCmdArguments '-arch=x64 -host_arch=x64'
Common Compiler Flags¶
GCC (Linux/WSL)¶
Installation¶
- If on Windows, Install the WSL first (WSL 2)
- check if GCC is installed by typing
gcc --version
If GCC is not installed:
sudo apt-get updatesudo apt-get upgradesudo apt-get install gcc-<version>
g++¶
g++ is the main executable for the GCC compiler. It is botha compiler and a wrapper for the linker. Typical usage is:
g++ -o <output file> <cpp files and library files>: compilation and linkingg++ -c <cpp files>: compilation onlyg++ -o <output file> <object files and library files>: linking only
Other frequently used flags are:
-g: include debug information-l<library>: link the library namedlib<library>. Unlike direct library specification, here the linkers searches for the library in standard directories.
Build Sequencing Tools¶
Make¶
Most of the time, make scripts should not be written manually, but generated by build script generators like CMake. Therefore, here, we describe the structure of make scripts as they are generated by CMake. The structure is:
Makefile: the main file that contains the rules for building the projectCMakeFiles: a directory containing the files referenced in theMakefile<target name>.dirdirectory for each targetlink.txt: the file containing the linking command
MSBuild¶
The MSBuild is a XML-based build sequencing tool. The scripts are typically generated by CMake or directly by Visual Studio.
The main file for each target is the <target name>.vcxproj file. All properties are stored in the root element <Project>.
The structure inside the project element is as follows (only the most important elements are listed):
<ItemDefinitionGroup Condition="<configuration>">: contains the properties for the given configuration.<ItemGroup><multiple <ClCompile> elements><ItemGroup>: contains the source files that are compiled<ItemGroup><multiple <ProjectReference> elements><ItemGroup>: contains references to other targets (dependencies). Theses targets are built before the current target.
The propertis for each configuration are structured as follows:
<ClCompile>: contains the properties for the compilation of the source files (e.g., language standard, runtime library, etc.)<ResourceCompile>:<PreprocessorDefinitions>: the preprocessor definitions used for the target. These are introduced either by the target itself or by the dependencies.
<Link>: contains the properties for the linking of the target (e.g., the libraries to link, the output file, etc.)
Cmake¶
- Windows: Install CMake from https://cmake.org/download/
- if your CMake is too old (e.g. error: “CMake 3.15 or higher is required”), update CMake (same as new install)
- Linux:
- If cmake is installed already, uninstall it!
- Do not use the cmake from linux repositories!!
- Download CMake
shinstaller from https://cmake.org/download/ - install:
sudo chmod +x <INSTALLER>sudo <INSTALLER>sudo rm <INSTALLER>
- add cmake executable to path
Other details about CMake can be found in the CMake Manual.
vcpkg¶
For detailed information about vcpkg, see the vcpkg Manual.
IDE¶
Clion¶
Configuration¶
Set default layout¶
Window -> Layouts -> Save changes in current layout
Set up the new Nova engine¶
The new Nova engine is a new Clion's engine that is faster and have more features. It is the engine that is used in Visual Studio Resharper C++ plugin. To enable it, go to settings -> Advanced Settings and under Clion check the Use the ReSharper C++ language engine (Clion Nova).
Apart from the new features, the Nova engine can prevent some false errors that are caused by the clangd engine used by the old Clion engine.
Set up new surround with template¶
In Clion, there are two types of surround with templates: surrond with and surround with live template. The first type use simple predefined templates and cannot be modified. However, the second type can be modified and new templates can be added.
Toolchain configuration¶
Go to settings -> Build, Execution, Deployment -> toolchain, add new toolchain and set:
- Name to whatever you want
- The environment should point to your toolchain:
- MSVC:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community - MSYS:
C:\MSYS2 - WSL: From the drop-down list, choose the environment you configured for using with CLion in the previous steps
- MSVC:
- Credentials (WSL) click to the setting button next to the credentials and fill
- host:
localhost - port:
2222 - user and password according to your WSL system credentials
- host:
- Architecture (non WSL): amd64
- CMake:
C:\Program Files\CMake\bin\cmake.exe, for WSL, leave it as it is - other fields should be filled automatically
Multiple WSL toolchains¶
When using multiple WSL toolchains, we need to manually set the compilers. To do so, fill also the C Compiler and C++ Compiler fields in the toolchain settings with the path to the compiler executable.
Project configuration¶
Most project settings resides (hereinafter Project settings) in settings -> Build, Execution, Deployment -> CMake. For each build configuration, add a new template and set:
Nameto whatever you wantBuild typetodebug- To
Cmake options, add:- path to vcpkg toolchain file:
- Linux:
-DCMAKE_TOOLCHAIN_FILE=/opt/vcpkg/scripts/buildsystems/vcpkg.cmake - Windows:
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
- Linux:
- Set the correct vcpkg triplet
- MSVC:
-DVCPKG_TARGET_TRIPLET=x64-windows - MinGW:
-DVCPKG_TARGET_TRIPLET=x64-MinGW - Linux:
-DVCPKG_TARGET_TRIPLET=x64-linux
- MSVC:
- path to vcpkg toolchain file:
WSL extra configuration¶
The CLion does not see the WSL's environment variables (as of 2023-03, see here). To fix it, go to Project settings and set add the necessary environment variables to Environment field.
Configuring only some CMake profiles¶
When we click on the CMake reconiguration button, all profiles are reconfigured. Unfortunately, there is no way how to configure only some profiles. To work around this, we can deactivate the profiles we do not want to configure. To do so:
- go to
settings->Build, Execution, Deployment->CMake - select the profile you want to deactivate
- uncheck the
Enable profilecheckbox located at the top of the profile settings
Troubleshooting¶
Editor reports errors despite the code compiles in all compilers¶
This can be caused by the clangd engine used by the old Clion engine. To fix it, enable the new Nova engine.
Editor actions are not available, Ctrl + click does nothing¶
- check whether the "Go to Reference" action is triggered. If there is a tooltip claiming no references, go to next step. Otherwise, the problem is in the keymap
- check the right side of the status bar (bottom right corner of the editor). There should be an indicator in the format of
C++ | <target name> | <debugging configuration>. If there is another indicator, CLion does not know the context of the project. Probably, the project configuration needs to be fixed
Visual Studio¶
Installation¶
- Install Visual Studio
- Open/Create a CMake project
- Install ReSharper C++
Setting Synchronization¶
- Sign-in in Visual Studio using a Mictosoft account. A lot of settings should be synchronized automatically.
- Apply the layout:
Window->Apply Window Layout-><Layout Name> - Sync ReSharper settings: you can share the file:
%APPDATA%\JetBrains\Shared\vAny\(~\AppData\Roaming\JetBrains\Shared\vAny\).- This does not work good though as the files are changed on both sides constantly.
- unfortunately, as of 01/2023, there is no good way how to share resharper settings
- Install roamed plugins
Basic Configuration¶
- Add 120 char guideline
- install the extension
- add the guideline in command window:
Edit.AddGuideline 120 - if there is an error extension ... did not load properly, you need to install the developer analytic tools package to the Visual Studio:
- Visual Studio Installer ->
modify - Go to the
Individual Componentstab - search for the extension and select it
- proceed with the Visual Studio Modification
- Visual Studio Installer ->
- If you need to use the system CMake, configure it now (described below)
- If you use
*.tppfile, configure a support for them (described below).
installation
Enable template implementation files (.*tpp) syntax highlighting:¶
- Go to
Tools->Options->Text Editor->File Extension - select Microsoft Visual C++
- write
tppto the field and click add - (reopen the file to see changes)
To Change the Build Verbosity¶
- Go to
Tools->Options->Projects and Solutions->Build and Run - Change the value of the MSBuild project build output verbosity.
Project Setting¶
Configure Visual Studio to use system CMake:¶
- Go to
Project->CMake Settings - it should open the
CMakeSettings.jsonfile - Scroll to the bottom and click on
show advanced settings - Set the CMake executable to point to the
cmake.exefile of your system CMake
Build Setting and Enviromental Variables¶
The build configuration is in the file CMakePresets.json, located in the root of the project. The file can be also opened by right clicking on CMakeLists.txt ad selecting Edit CMake presets.
Set the CMake Toolchain File¶
To set the vcpkg toolchain file add the following value to the base configuration cacheVariables dictionary:
"CMAKE_TOOLCHAIN_FILE": {
"value": "C:/vcpkg/scripts/buildsystems/vcpkg.cmake",
"type": "FILEPATH"
}
Set the Compiler¶
The MSVC toolchain has two compiler executables, default one, and clang. The default compiler configuration looks like this:
"cacheVariables": {
...
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
...
},
To change the compiler to clang, replace cl.exe by clang-cl.exe in both rows.
Old Method Using CMakeSettings.json¶
We can open the build setting by right click on CMakeList.txt -> Cmake Settings
To configure configure vcpkg toolchain file: Under General, fill to the Cmake toolchain file the following: C:/vcpkg/scripts/buildsystems/vcpkg.cmake
To configure the enviromental variable, edit the CmakeSettings.json file directly. The global variables can be set in the environments array, the per configuration ones in <config object>/environments (exmaple).
Launch Setting¶
The launch settings determins the launch configuration, most importantly, the run arguments. To modify the run arguments:
1. open the launch.vs.json file:
- use the context menu:
- Right-click on CMakeLists.txt -> Add Debug Configuration
- select default
- or open the file directly, it is stored in <PROJECT DIR>/.vs/
2. in launch.vs.json configure:
- type: default for MSVC or cppgdb for WSL
- projectTarget: the name of the target (executable)
- name: the display name in Visual Studio
- args: json array with arguments as strings
- arguments with spaces have to be quoted with escaped quotes
3. Select the launch configuration in the drop-down menu next to the play button
If the configuration is not visible in the drop-down menu, double-check the launch.vs.json file. The file is not validated, so it is easy to make a typo. If there is any problem, insted of an error, the launch configuration is not available. The following problems are common:
- syntax error in the json (should be marked by red squiggly line)
- typo in the target name
Other launch.vs.json options¶
cwd: the working directory
Microsoft reference for launch.vs.json
WSL Configuration¶
For using GCC 10:
- go to
CmakeSettings.json->CMake variables and cache - select
show advanced variables checkbox - set
CMAKE_CXX_COMPILERvariable to/usr/bin/g++-10
Other Configuration¶
- show white spaces:
Edit->Advanced->View White Space. - configure indentation: described here
Determine Visual Studio version¶
At total, there are 5 different versionigs related to Visual Studio.
The version which the compiler support table refers to is the version of the compiler (cl.exe).
we can find it be examining the compiler executable stored in C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\bin\Hostx64\x64.
Problems & solutions¶
Cannot regenerate Cmake cache¶
go to ./vs and look for file named CmakeWorkspaceSettings. It most likelz contains a line with disable = true. Just delete the file, or the specific line.
Visual Studio Code¶
In VS Code, the C++ support is provided by multiple extensions.
CMake support extension¶
The CMake support extension is provided by the CMake Tools extension.
To set the vcpkg toolchain file, it is best to edit the extension settings, so that the toolchain file is set for all projects:
- In the extension settings, go to
CMake: Configure Args - Add argument
--toolchain - Add argument with the path to the vcpkg toolchain file
To run the configuration or other CMake tasks, open the newly installed CMake view.
By default, the CMake extension uses CMake presets to configure the project. To use CMake withou presets, go to the extension settings and set CMake: Use CMake Presets to never.
Cursor¶
For Cursor, the situation is almost the same as for VS Code. Important differences:
- The main, but important difference is that Cursor does not support the Visual Studio Debugger (cppvsdbg) [source], [source 2]. Therefore, Cursor is hardly usable as the all-in-one IDE for C++, it has to be used in combination with another IDE.
- Cursor does not have a built-in IntelliSense. Instead, it relies on clangd to provide the functionality.
- Cursor does not have the Microsoft C++ Extension enabled. Instead, it has another C++ extension from Anysphere. This is the only C++ extension that needs to be installed manually, as it installs automatically:
- the CMake Tools extension
- the clangd extension
- the CodeLLDB extension
Call the MSVC compiler from Cursor¶
To partially overcome this limitation, we can at least run the program without debugger as a task:
"tasks": [
{
"label": "Run DC 3-minute instance",
"type": "process",
"command": "${command:cmake.launchTargetFilename}",
"options": {
"cwd": "${command:cmake.launchTargetDirectory}"
},
"args": [
"--instance",
"C:/Google Drive AIC/My Drive/AIC Experiment Data/DARP/Instances/DC/instances/start_18-00/duration_01_min/max_delay_03_min"
],
"problemMatcher": []
}
]
Here the executable is defined by:
${command:cmake.launchTargetFilename}: the executable${command:cmake.launchTargetDirectory}: the path to the build bin directory
Both these variables are defined by the CMake Tools extension.
Troubleshooting¶
The Ctrl + click on a function call does not open the definition¶
Unlike in VS Code, Cursor does not have a built-in IntelliSense. Instead, it relies on clangd to provide the functionality. To conclude what is wrong, first try to reset the clangd server:
View->Command Palette- type
clangd: Restart language server
Typical directory structure¶
The typical directory structure for a C++ project is as follows:
data: the directory containing the data files, including test data and default configuration filesinclude: the directory containing public header files- this directory is for the headers that are meant to be included by other projects. Typically, only library projects have this directory
- the projects own headers should be in the
include/<project name>directory to mimic the installation directory structure
install: the directory containing the installation scripts. Only needed for library projects or projects that are meant to be distributedport: the directory containing the vcpkg port files. Only needed for libraries that are meant to be distributed via vcpkgsrc: the directory containing the source files and private headerstest: the directory containing the test files<various build dirs>: the directories containing the build files.
The root directory typically contains the following files:
.gitignore: the git ignore fileCMakeLists.txt: the main cmake fileCTestConfig.cmake: the CTest configuration file
Dependencies¶
Unlike in Python and Java, that have pip and maven, C++ does not have a standard package manager. Instead, there are plenty of ways how to install the dependencies and CMake then tries to find them. In the following table, we list the most common ways how to install the dependencies together with theri pros and cons.
| Method | Platform-independent | Automatic install of the dependency tree | Can install build configuration tools | Support easily determined |
|---|---|---|---|---|
CMake --install |
yes | no | yes | no |
| vcpkg | yes | yes | yes | yes |
CMake FetchContent |
yes | no | no | no |
CMake ExternalProject |
yes |
Vcpkg Libraries¶
- type
vcpkg list, if the library you need is not listed, continue to the next steps - type
vcpkg search <library simple name>and inspect the result to determine the exact name of the package you need- if the library is not listed, check the presence in vcpkg repo
- if the library is in repo, but search does not find it, update vcpkg
- type
vcpkg install <exact name>to install the package - at the end of the installation log, there will be a cmake command needed to integrate the library, put it to the appropriate place to your
CMakeList.txt file
To display the cmake commands for the installed libraries, just run vcpkg install <exact name> again.
Boost¶
With boost, we should install only the necessary components. Then to include boost, we need:
find_package(Boost REQUIRED)- with all compiled components listed
target_include_directories(<YOUR TARGET NAME> PUBLIC ${Boost_INCLUDE_DIRS})
Sometimes, it may be usefull to find out which boost components require linking. The list in the boost documentation, both for Unix and Windows.
JNI¶
for JNI, a JAVA_HOME system property needs to be set to the absolute path to the JDK, e.g., C:\Program Files\Java\jdk-15.0.1
Gurobi¶
- If you don’t have Gurobi installed, do it now, and check that the installation is working
- Windows: just install as usual
- Linux:
- download the archive to
/opt sudo tar xvfz <gurobi archive>- add the the file that introduce environment variables needed for gurobi to
etc/profile.d
- download the archive to
- Linux only: it is necessary to build the C++ library for your version of the compiler. Steps:
1.
cd <GUROBI DIR>/linux64/src/build/2.make3.mv libgurobi_c++.a ../../lib/libgurobi_c++_<some id for you, like version>.a4.cd ../../lib/5.ln -sf ./libgurobi_c++<some id for you, like version>.a libgurobi_c++.a - Follow this guide, specifically:
1. put the attached our custom FindGUROBI script to:
- Windows:
C:\Program Files\CMake\share\cmake-<your cmake version>\Modules/- Linux:/opt/<CMAKNAME>/share/cmake-<VERSION>/Modules2. to yourCMakeLists.txt, add: -find_package(GUROBI REQUIRED)-target_include_directories(<your executable> PRIVATE ${GUROBI_INCLUDE_DIRS})-target_link_libraries(<your executable> PRIVATE ${GUROBI_LIBRARY})-target_link_libraries(<your executable> PRIVATE optimized ${GUROBI_CXX_LIBRARY} debug ${GUROBI_CXX_DEBUG_LIBRARY})3. try to load the cmake projects (i.e., generate the build scripts using cmake). 4. if the C++ library is not found (Gurobi c++ library not found), check whether the correct C++ library is in the gurobi home, the file<library name>.libhas to be in thelibdirectory of the gurobi installation. If the file is not there, it is possible that your gurobi version is too old
Update Gurobi¶
- Updating is done by installing the new version and generating and using new licence key.
- after update, you need to delete your build dir in order to prevent using of cached path to old Gurobi install
- Also, you need to update the library name on line 10 of the
FindGUROBI.cmakescript.
Other Libraries Not Available in vcpkg¶
Test Library linking/inclusion¶
For testing purposes, we can follow this simple pattern:
- build the library
- include the library:
target_include_directories(<target name> PUBLIC <path to include dir>), where include dir is the directory with the main header file of the library. - if the library is not the header only library, we need to:
3.1 link the library:
target_link_libraries(<target name> PUBLIC <path to lib file>), where path to lib file is the path to the dynamic library file used for linking (.soon Linux,.libon Windows). 3.2. add the dynamic library to some path visible for the executable - here the library file is.soon Linux and.dllon Windows - there are plenty options for the visible path, the most common being the systemPATHvariable, or the directory with the executable.
Dependencies with WSL and CLion¶
In WSL, when combined with CLion, some find scripts does not work, because they depend on system variables, that are not correctly passed from CLIon SSH connection to CMake. Therefore, it is necessary to add hints with absolute path to these scripts. Some of them can be downloaded here. Package that require these hints:
- JNI
- Gurobi
Refactoring¶
The refactoring of C++ code is a complex process, so the number of supported refactoring operations is limited. In Visual Studio, the only supported refactoring operation is renaming. In IntelliJ tools (CLion, ReSharper C++), there are more tools available, but still, the refactoring is not as powerful nor reliable as in Java or Python.
Other alternative is to implement the refactoring manually, with a help of some compiler tools like clang Refactoring Engine (example project).
Changing Method Signature¶
As of 2023-10, there is no reliable way how to change the method signature in C++. The most efficient tool is the method signature refactorin in either CLion or ReSharper C++. However, it does not work in all cases, so it is necessary to check and fix the code manually.
Standard Library¶
The strucure and organization of the standard library is different on Windows and Linux.
On Windows (specificly when using MSVC) the library is called MSVC C Runtime (CRT). It is divided between:
VCRuntime<version>.dll: contains the parts that interacts with the OS- new versions are released with new versions of Visual Studio
ucrtbase.dll: contains other parts of the CRT- non-versioned, the same for all versions of Visual Studio
- many other libraries
On Linux, the library is divided between C and C++ standard libraries:
- C++ Standard Library: contains the standard C++ features described in the C++ standard.
- GCC: GNU C++ Library (
libstdc++)- the backward compatibility is maintained by the exported symbols marked as
GLIBCXX_<version>. The max GLIBCXX version per GCC version is listed in the ABI page
- the backward compatibility is maintained by the exported symbols marked as
- LLVM:
libc++
- GCC: GNU C++ Library (
- C Standard Library (
glibc): contains the standard C features described in the C standard.- To get the version, run
ldd --version
- To get the version, run
Linking to the Standard Library¶
On both platforms:
- the standard library is linked automatically
- the standard library is linked dynamically by default
However, Windows and Linux have different attitudes towards static linking of the standard library:
- Windows: static linking is pretty common and easy to setup:
- pros: easy way to distribute the application to older systems, possibly decades old
- cons: even begginers have to check that the executable and all its dependencies link to the same (static vs dynamic) version of the CRT
- Linux: static linking is not common and not recommended:
- pros: the mismatch of the standard library linking between the executable and the dependencies is not a problem
- cons: the executable is typically not portable to other systems, unless the same version of the standard library is installed (
glibcis usually stable, butlibstdc++andlibc++are not)
Setting up the correct CRT with MSVC¶
It is critical to set the runtime library for the executable we are building so that it matches the runtime library used by the dependencies. If the mismatch occurs, it is manifested depending on the situation:
- when we link to a dynamic CRT, but dependencies link to a static CRT, the linker will throw an error, typically for each such dependency
plaintext LNK2038 mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MTd_StaticDebug' in .. - when we link to a staticCRT, but dependencies link to a dynamic CRT, the linker proceeds without errors, but the program crashes at runtime. Typically, some allocation/deallocation error occurs.
The CRT is set by compiler flags (see the table below).
- These flags are typically passed to the compiler by the build system based on the build configuration files (e.g.,
MSBuildfiles). - They affect all parts of the CRT
If we generate thes files by the IDE (Visual Studio), we have to set the CRT in the IDE. If they are generated by CMake, there are three possible situations:
- we use the dynamic CRT (default): nothing has to be done
- the build is handled by vcpkg (libraries installed with
vcpgk install): the runtime library is set by theVCPKG_CRT_LINKAGEvariable in the triplet file. Nothing has to be done. - we use the static CRT: we have to set the
CMAKE_MSVC_RUNTIME_LIBRARYvariable in theCMakeLists.txtfile. if we use vcpgk libraries, we should set it based on the triplet used:cmake if (VCPKG_TARGET_TRIPLET MATCHES "-static") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") endif()
To determine if the type of the runtime library used by the target, we can explore the build script for the target, For MSBuild, the runtime library is set by the RuntimeLibrary property. To determine the runtime library used by a library, we can use the dumpbin tool.
This tool is part of the Visual Studio installation and can be run from the Developer Command Prompt or Developer PowerShell. The following command will display the runtime library used by the library:
dumpbin /directives <path to the library>
The following compiler flags are available:
| Option | Copiler flag | MSBuild name | Description | Linged library |
|---|---|---|---|---|
| Multi-threaded Dynamic | /MD |
MultiThreadedDLL |
The default option. The application uses the dynamic version of the runtime library. | msvcrt.lib |
| Multi-threaded Dynamic Debug | /MDd |
MultiThreadedDebugDLL |
The debug version of the dynamic runtime library. | msvcrtd.lib |
| Multi-threaded Static | /MT |
MultiThreaded |
The application uses the static version of the runtime library. | libcmt.lib |
| Multi-threaded Static Debug | /MTd |
MultiThreadedDebug |
The debug version of the static runtime library. | libcmtd.lib |
| DLL | /LD |
The application is compiled as a DLL. | - | |
| DLL Debug | /LDd |
The debug version of the DLL. | - |
By default, the /MD//MDd flags are used depending on the build type.
Resouces¶
- MSVC CRT on Wikipedia
- CRT reorganization blogpost from 2015
- GitHub repo with the relesed CRT sources, useful information, and links
Exporting symbols for shared libraries¶
When creating a shared library, we have to specify which symbols are exported. These are the only symbols that can be directly used from the client code. This is done using special keywords. Because the keywords are different for different compilers, usually, some macros are used instead. Typically, these macros:
- use the correct keyword for the compiler
- support disabling the keyword for building static libraries or executables
The macros are typically defined in a dedicated header file called export header. This file is then included in every header file that defines a symbol that should be exported.
For the whole export machinery to work, we need to:
- create the export header file and include it in every header file that defines an exported symbol
- mark the symbols that should be exported with the export macro
- use CMake to supply the correct compiler flags used in the export header
Creating the Export Header¶
We can generate the export header file using the GenerateExportHeader module. We can get it by the following code:
add_library(<target name> SHARED <files>)
generate_export_header(<target name>)
This will generate the export header file in the build directory. However, the export file is different for different compilers. Therefore, it is best to copy the file for each compiler and then merge the macros to create a universal export header file. Alternatively, we can use some proven export header file.
Finally, we can store the export header file in the source directory and include it in every header file that defines an exported symbol.
Marking the Symbols¶
Usually, we mark the following symbols for export:
- classes:
class <export macro> MyClass{...} - functions:
<export macro> <return type> my_function(...)
Other symbols does not have to be exported as they are automatically exported by the compiler:
- enums and enum classes
- constants
- templates
Additionaly, only the symbols needed by external code should be exported. The exeption is when the the interface use templates. In this case, all symbols used by the template (but not the template itself) should be exported.
The <export macro> from the GenerateExportHeader is named <target name>_EXPORT, we can check the exact name in the export header file.
CMake Configuration¶
Now the shared library build should work correctly. However, the static library build will be full of warnings, because the export macro is not intended for static libraries. The same is true for executables. Therefore, we need to set a special property for each target that uses the export header and is not a shared library:
target_compile_definitions(<static/executable target name> PUBLIC <static condition macro >)
The static condition macro from the GenerateExportHeader is named <target name>_STATIC_DEFINE. If not clear, we can find the exact macros' names in the export header file.
Resouces¶
Compilation for a specific CPU¶
MSVC¶
MSVC cannot compile for a specific CPU or CPU series. It can, however, use new instructions sets more efficiently if it compiles the code without the support for CPUs thad does not support these instruction sets.
The command for the compiler is: `/arch:
## GCC
In GCC, the march option enables compilation for a specific hardware.
ml) option enables compilation for a specific hardware.
pects that you use vcpkg in a per-project configuration. To make it work, add: -DCMAKE_TOOLCHAIN_FILE=<vcpkg location>/scripts/buildsystems/vcpkg.cmake
- To change build options (option in CMakeLists.txt), run cmake with -D <option name>=<option value> <build dir>. Example: cmake -D BUILD_TESTING=OFF .
Building¶
For building, use:
cmake --build <build dir>
where build dir is the directory containing the build scripts (CmakeFiles folder).
To list the build options:
cmake -L
Specify the build type (Debug, Release)¶
To build in release mode, or any other build mode except for the default, we need to specify the parameters for CMake. Unfortunately, these parameters depends on the build system:
- Single-configuration systems (Unix, MinGW)
- Multi-configuration systems (Visual Studio)
Single-configuration systems¶
Single configuration systems have the build type hardcoded in the build scripts. Therefore, we need to specify the build type for CMake when we generate the build scripts:
cmake ../ -DCMAKE_BUILD_TYPE=Release
By default, the build type is Release.
Multi-configuration systems¶
In multi-configuration systems, the -DCMAKE_BUILD_TYPE parameter is ignored, because the build configuration is supposed to be determined when building the code (i.e., same build scripts for debug and for release). Therefore, we omit it, and instead specify the --config parameter when building the code:
cmake --build . --config Release
Specify the target¶
We can use the --target parameter for that:
cmake --build . --target <TARGET NAME>
Clean the source files¶
Run:
cmake --build . --target clean
Handling Case Insensitivity¶
Windows builds are, in line with the OS, case insensitive. Moreover, the Visual Studio does some magic with names internally, so the build is case insensitive even on VS WSL builds.
The case insensitivity can bring inconsistencies that later breake Unix builds. Therefore, it is desirable to have the build case sensitive even on Windows. Fortunatelly, we can toggle the case sensitivity at the OS level using this PowerShell command:
Get-ChildItem <PROJECT ROOT PATH> -Recurse -Directory | ForEach-Object { fsutil.exe file setCaseSensitiveInfo $_.FullName enable }
Note that this can break the git commits, so it is necessary to also configure git in your case-sensitive repo:
git config core.ignorecase false