Building And Using Library Files In CMake

Background

CMake

CMake is a cross-platform open-source build tool used to manage and automate the build process of software projects.

Building And Using Library Files In CMake

CMake automatically generates build system files suitable for different compilers and operating systems, such as Makefile and Visual Studio solutions, based on the descriptions in the CMakeLists.txt file.

Compilation Types

Generally, programs can be compiled into three types corresponding to the following forms:

  • Linux System: demo (executable), libcommon.a (static library), libcommon.so (dynamic library)
  • Windows System: demo.exe (executable), libcommon.lib (static library), libcommon.dll (dynamic library)

Building Library Files

Overview

Sometimes we need to generate static or dynamic libraries from the source code we write for third-party use, instead of executable programs.

Preparation Work

Overview

Assuming we want to implement a log library loglib, create a log library project directory in the current directory.

Creating Include Directory

Create the loglib.h header file in loglib/include:

#ifndef LOGLIB_H
#define LOGLIB_H

void printMsg(const char* msg);

#endif 

Creating Src Directory

Create the loglib.cpp source file in loglib/src:

#include "loglib.h"
#include "iostream"

void printMsg(const char *msg)
{
    std::cout << msg << std::endl;
}

Creating CMakeLists.txt

Create a CMakeLists.txt in the loglib directory to build the loglib library, the final directory structure is as follows:

Building And Using Library Files In CMake

Building loglib Library

Overview

To build a static library in CMake, use the add_library command. Based on the directory structure above, write the CMakeLists.txt file.

Writing CMakeLists.txt

Basic Contents

A CMakeLists.txt contains the following basic contents:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

Including Header Files

Since loglib.cpp and loglib.h are not in the same directory, we need to specify the include path for the project. Use the include_directories command to specify the header file search path:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

include_directories(${PROJECT_SOURCE_DIR}/include

Finding Source Files

When there are many source files in the project, manually listing each file can be cumbersome. You can use the file command to find files:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

include_directories(${PROJECT_SOURCE_DIR}/include
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

Adding Source Files

In add_library, use the keywords SHARED and STATIC to specify the generation of dynamic and static library files respectively:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

include_directories(${PROJECT_SOURCE_DIR}/include
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(loglib STATIC ${SRC_LIST})
Specifying Output Directory

Use the LIBRARY_OUTPUT_PATH parameter to specify the generation path of the library file:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

include_directories(${PROJECT_SOURCE_DIR}/include
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_library(loglib STATIC ${SRC_LIST})

Building Library Files

Execute the build command in the terminal and then check the generation result:

cmake -B build -S .
cmake --build ./build
tree

The execution result is as follows:

Building And Using Library Files In CMake

If executed on Windows, a .lib file will be generated:

Building And Using Library Files In CMake

Controlling Library Generation Type

The variable BUILD_SHARED_LIBS can set the generation type of the add_library library file. When the value is ON, a dynamic library is generated; when OFF, a static library is generated.

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

project(loglib)

option(BUILD_SHARED_LIBS "Build shared libraries" ON)

include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_library(loglib  ${SRC_LIST})

Execute the build command, the running result is as follows:

Building And Using Library Files In CMake

Building loglib Library on Windows

Overview

Dynamic link libraries on Windows usually generate two files: one is the .dll file and the other is the .lib file.

Symbol Marking

On the Windows platform, symbols defined in the executable file exporting the DLL must be marked as __declspec(dllexport), and symbols defined in the executable file exporting the DLL must also be marked as __declspec(dllexport).

Modifying loglib.h

Modify the loglib.h file to adapt to Windows definitions:

#ifndef LOGLIB_H
#define LOGLIB_H

#ifdef _WIN32
    #ifdef LOGLIB_EXPORTS
        #define LOGLIB_API __declspec(dllexport)
    #else
        #define LOGLIB_API __declspec(dllimport)
    #endif
#else
    #define LOGLIB_API
#endif

LOGLIB_API void  printMsg(const char* msg);

#endif 

Modifying CMakeLists.txt

Use the target_compile_definitions command to add the predefined macro LOGLIB_EXPORTS to the target loglib:

cmake_minimum_required(VERSION 3.0)
project(loglib)

option(BUILD_SHARED_LIBS "Build shared libraries" ON)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(loglib  ${SRC_LIST})

# Add LOGLIB_EXPORTS macro
target_compile_definitions(loglib PRIVATE LOGLIB_EXPORTS)

target_link_libraries(loglib interfacelib)

install(TARGETS loglib DESTINATION bin)

Building Project

Execute the build command, and you can see that loglib.dll and the corresponding loglib.lib files are generated correctly:

Building And Using Library Files In CMake

Adding Subprojects

Overview

Typically, a project has multiple modules or subprojects. If each module is built separately, it can be cumbersome. In CMake, use add_subdirectory to add a subdirectory to the current project. Thus, a project can be decomposed into multiple subprojects, each with its own CMakeLists.txt file to manage the build process.

Adding Main CMakeLists.txt

Add the main project’s CMakeLists.txt in the root directory, the directory structure is as follows:

Building And Using Library Files In CMake

Writing CMakeLists.txt

Use the add_subdirectory command to add subprojects:

cmake_minimum_required(VERSION 3.0)
project(mainProject)# Main project

add_subdirectory(loglib)# Add subproject

Building Project

Execute the build command in the terminal and then check the generation result:

cmake -B build -S .
cmake --build ./build
tree loglib

The execution result is as shown in the figure above, indicating that the subproject was correctly built when building the main project.

Referencing Library Files

Overview

Generally, in the process of implementing a project, some third-party dynamic or static library files will be needed.

Preparation Work

Add a subproject test in the current main project directory to reference the loglib library generated above. The directory structure is as follows:

Building And Using Library Files In CMake

The code in test.cpp is as follows:

#include "loglib.h"

int main() 
{
    printMsg("hello world");
    return 0;
}

Using Static Libraries

Overview

In CMake, to build a static library, use the link_libraries command to link one or more static libraries.

Writing CMakeLists.txt

Basic Contents

A CMakeLists.txt contains the following basic contents:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})
Adding Header File Paths

Use the include_directories command to specify the loglib library header file search path:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})
Adding Library File Paths

Use the link_directories command to specify the loglib library file search path:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

Linking Static Libraries

Use the link_libraries command to link the static library loglib:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)
# Link static library
link_libraries(loglib)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

Specifying Generation Path

Use the EXECUTABLE_OUTPUT_PATH parameter to specify the generation path of the executable program:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)
# Link static library
link_libraries(loglib)
# Specify generation path
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

Adding Test Subproject

Add the test subproject in the main CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(mainProject)# Main project

# Add loglib subproject
add_subdirectory(loglib)
# Add test subproject
add_subdirectory(test)

Building and Running Program

Execute the build command in the terminal and then check the generation result:

cmake -B build -S .
cmake --build ./build
./test/bin/test

The execution result is as follows:

Building And Using Library Files In CMake

Using Dynamic Libraries

Overview

In CMake, to build a static library, use the target_link_libraries command to link one or more dynamic libraries.

Writing CMakeLists.txt

Basic Contents

A CMakeLists.txt contains the following basic contents:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})
Adding Header File Paths

Use the include_directories command to specify the loglib library header file search path:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})
Adding Dynamic Library File Paths

Use the link_directories command to specify the loglib library file search path:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

Linking Dynamic Libraries

Unlike static libraries, target_link_libraries should be placed after add_executable because the dynamic library will only be loaded into memory when the executable program calls the functions in the dynamic library.

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

# Link dynamic library
target_link_libraries(test loglib)

Specifying Generation Path

Use the EXECUTABLE_OUTPUT_PATH parameter to specify the generation path of the executable program:

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
project(test)

# Header file search path
include_directories(${PROJECT_SOURCE_DIR}/../loglib/include) 
# Library file search path
link_directories(${PROJECT_SOURCE_DIR}/../loglib/bin)

# Specify generation path
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(test ${SRC_LIST})

# Link dynamic library
target_link_libraries(test loglib)

Adding Test Subproject

Add the test subproject in the main CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(mainProject)# Main project

# Add loglib subproject
add_subdirectory(loglib)
# Add test subproject
add_subdirectory(test)

Building and Running Program

Execute the build command in the terminal and then check the generation result:

cmake -B build -S .
cmake --build ./build
./test/bin/test

The execution result is as follows:

Building And Using Library Files In CMake

Installation and Packaging

Library File Installation

Overview

Installation means copying the compiled files (executable files, static libraries, dynamic libraries, header files, etc.) to the corresponding directories.

Setting Installation Path

In CMake, use the parameter CMAKE_INSTALL_PREFIX to set the installation path. To customize the installation path, just set this parameter.

Default Path on Linux

On Linux, printing the value of the CMAKE_INSTALL_PREFIX parameter results in:

Building And Using Library Files In CMake

Default Path on Windows

On Windows, printing the value of the CMAKE_INSTALL_PREFIX parameter results in:

Building And Using Library Files In CMake

Modifying Default Path

Since the default installation path is in the system’s file directory, it requires administrative permissions to operate. You can modify the installation path in the main module:

cmake_minimum_required(VERSION 3.0)
project(mainProject)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)

set(CMAKE_INSTALL_PREFIX "/data/loglib")

add_subdirectory(loglib)
add_subdirectory(test)
add_subdirectory(interfacelib)

Installing Library Files

Overview

Use the install command to perform installation operations.

Target Installation

Use the TARGETS parameter to install target files, adding the installation command in the loglib module:

cmake_minimum_required(VERSION 3.0)
project(loglib)

option(BUILD_SHARED_LIBS "Build shared libraries" ON)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(loglib  ${SRC_LIST})

target_link_libraries(loglib interfacelib)

install(TARGETS loglib interfacelib DESTINATION lib)

File Installation

Use the FILES parameter to install target files, adding the installation command in the loglib module:

cmake_minimum_required(VERSION 3.0)
project(loglib)

option(BUILD_SHARED_LIBS "Build shared libraries" ON)

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(loglib  ${SRC_LIST})

target_link_libraries(loglib interfacelib)

install(TARGETS loglib interfacelib DESTINATION lib)
install(FILES ${PROJECT_SOURCE_DIR}/include/loglib.h DESTINATION include)

Installation Command

Use the –install command to execute the installation operation:

cmake -B build -S .
cmake --build ./build
cmake --install ./build

cd /data/loglib
tree

The execution result is as follows:

Building And Using Library Files In CMake

Library File Packaging

Overview

CPack is a component of CMake used for automating the creation and packaging of software packages. It can generate various software package formats, such as ZIP, TGZ, RPM, DEB, etc.

Enabling CPack

Enable CPack in the main module’s CMakeLists.txt, usually placed at the end of the file:

cmake_minimum_required(VERSION 3.0)
project(mainProject)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output/bin)

set(CMAKE_INSTALL_PREFIX "/data/loglib")

add_subdirectory(interfacelib)
add_subdirectory(loglib)
add_subdirectory(test)

include(InstallRequiredSystemLibraries)
include(CPack)

Publishing Package

Execute the following command for the packaging operation:

cmake -S .. -B .
cpack

The execution result is as follows:

Building And Using Library Files In CMake

You can see the packaging results in the build directory:

Building And Using Library Files In CMake

Leave a Comment