Static library linking with CMake and VS:MSVC & CLion:G++

I might be wrong, but I am probably right to say that there is no package manager for C++ as decent as Python, Node.js, .NET, or Ruby's. So, if you are planning to install a library something similar to pip install or npm i, that is not going to happen. Previously, we learned about compiling and linking in C++.

Obtaining 3rd-party libraries

You generally have two ways to obtain a third-party library.

  • By compiling the third-party source and then linking into your project:
  • By obtaining the pre-compiled binary for the specific platform, you are building for, which could be challenging depending on how rare your target device is

Types of libraries

The compiled binary could be of two forms:

  • Static library: you link this binary into your final output or executable. The static library's behavior will not change.
  • Dynamic library: your program uses this type of library during runtime. The dynamic library can be modified/upgraded, or it can introduce new bugs.

The good news with C++ static libraries is that if you build once and keep the libraries somewhere in your local hard drive, you will not have to download the binaries repeatedly across multiple projects.

Build system, compilers and IDEs

There are relatively less scary build systems and IDEs out there which have decent support for adding static libraries into your final executable. We are covering static libraries in this post using CMake as the build system and two different sets of IDEs and compilers on Windows:

Instead of writing commands, we are using the Build/Compile/Run buttons inside the IDEs. It is safe to assume that when Visual Studio 2019 was installed you chose the C++ language support with Visual C++ compiler. We are also assuming that you installed MinGW and CLion is usually good at picking up the installed compilers in the system, so you might have chosen g++ as the default compiler.

fmt, the 3rd-party library we are building

In this example, we are trying to build a popular string processing library namely fmt. When we download from the website, we find two directories of our interest: src and include. Keep note of them for now, but let us open the project with Visual Studio and CLion in any sequence. Use their Build/Run tools. Both will succeed, because they are smart enough to detect the underlying build systems and execute appropriate commands to carry out the process efficiently.

Visual Studio will create an fmtd.lib file, possibly at out\build\x64-Debug (default) and CLion will generate a libfmtd.a file. They are the static libraries we are interested in linking. Actually, the underlying compilers generate these libraries.

Let us copy over the static binaries and header files and create the following directory structure. As it was said earlier that the good thing about this approach is that under src directory you can create as many new projects as you want and still reuse the same static libraries you built once.

[root]
├─includes
│  └─fmt
│      └─*.h
├─lib
│   └─fmt
│       └─fmtd.lib
│       └─libfmtd.a
└─src
    └─hello-fmt
        ├─clion
        │  └─CMakeLists.txt
        │  └─main.cpp
        └─vs
            └─CMakeLists.txt
            └─main.cpp

The program

The program is rather simple. Make use of the relative path and include the necessary header file from the includes directory for us to be able to use the fmt::print function.

#include <iostream>
#include "../../../includes/fmt/core.h"

int main() {
    fmt::print("I'd rather be {1} than {0}.", "right", "happy");
}

The CMake build script

Last, but not least, the CMake build script (CMakeLists.txt) will help us compile the program, link the static library and create an executable to run.

cmake_minimum_required(VERSION 3.17)
project(hello-fmt)

set(CMAKE_CXX_STANDARD 20)
link_directories(${CMAKE_SOURCE_DIR}/../../../lib/fmt)

add_executable(hello-fmt main.cpp)

target_link_libraries(hello-fmt libfmtd.a)
# For Visual Studio:
# target_link_libraries(hello-fmt fmtd.lib)

Conclusion

There you have it. One cross platform build system (CMake), one Windows-specific IDE (Visual Studio 2019) and compiler (MSVC), one cross platform IDE (CLion) and compiler (GNU C++) and as many projects as you want in one single setup. The code of this post can be found here.