Getting Started with CMake: From Hello World to Complex Projects

Hello, today we are going to delve into a topic that many people pay attention to but often do not take seriously—Getting Started with CMake: From Hello World to Complex Multi-Module Project Builds.

Today, we will start from scratch and explore how to use CMake to build projects, making it a powerful assistant for managing your code efficiently.

Getting Started with CMake: From Hello World to Complex Projects

1. Let’s Start with Hello World

1.1. Creating a Simple CMake Project

First, let’s create a simple CMake project. We will start with the simplest Hello World program.

  1. Create a new folder in your working directory, for example, named HelloWorld.

  2. In the HelloWorld folder, create two files:

  • main.cpp
  • CMakeLists.txt
  • In the main.cpp file, write the following code:

  • #include <iostream>
    
    int main() {
        std::cout << "Hello, CMake World!" << std::endl;
        return 0;
    }
    1. Then, open the CMakeLists.txt file and write the following content:
    # Set the minimum required version of CMake
    cmake_minimum_required(VERSION 3.10)
    
    # Set the project name
    project(HelloWorld)
    
    # Specify the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    
    # Add executable file
    add_executable(HelloWorld main.cpp)

    In this CMakeLists.txt file, we have done the following:

    • cmake_minimum_required: Specifies the minimum required version of CMake. The lower the version number, the fewer features CMake has.
    • **project(HelloWorld)**: Sets the project name.
    • **set(CMAKE_CXX_STANDARD 11)**: Sets the C++ standard to C++11.
    • **add_executable(HelloWorld main.cpp)**: Tells CMake to generate an executable named HelloWorld from the source file main.cpp.

    1.2. Running CMake to Build

    1. Open the terminal (or command line tool) and navigate to the HelloWorld folder.
    2. Create a build directory in that folder (it is usually recommended to have a separate directory for build files):
    mkdir build
    cd build
    1. Execute the CMake command to configure the build:
    cmake ..
    1. After generating the build files, we can compile the project:
    make
    1. Finally, run the generated executable:
    ./HelloWorld

    You should see the output:

    Hello, CMake World!

    Thus, we have completed the build and compile process of the simplest CMake project. You can now see that CMake automatically generates a Makefile and executes the compilation process based on the CMakeLists.txt file.

    2. From Single Module to Multi-Module Project Builds

    The true power of CMake lies in its ability to manage complex multi-module projects. Next, we will expand our project by adding multiple modules (i.e., multiple source code files).

    2.1. Multi-Module Project Structure

    In large projects, we usually divide the code into multiple modules, each having its independent source code files. Let’s assume our project has two modules:

    • module1: Contains module1.cpp and module1.h.
    • module2: Contains module2.cpp and module2.h.

    Our project structure is as follows:

    HelloWorld/
    ├── CMakeLists.txt
    ├── main.cpp
    ├── module1/
    │   ├── module1.cpp
    │   └── module1.h
    └── module2/
        ├── module2.cpp
        └── module2.h

    2.2. Modify the CMakeLists.txt Configuration File

    To support multiple modules, we need to modify the CMakeLists.txt file to let CMake know how to find and compile these modules.

    cmake_minimum_required(VERSION 3.10)
    
    # Set the project name
    project(HelloWorld)
    
    # Set the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    
    # Add subdirectories for module1 and module2
    add_subdirectory(module1)
    add_subdirectory(module2)
    
    # Create executable file and link module1 and module2
    add_executable(HelloWorld main.cpp)
    
    # Link the libraries of module1 and module2 to HelloWorld
    target_link_libraries(HelloWorld module1 module2)

    Then, in each module’s folder, add a CMakeLists.txt file to define the build method for the module.

    2.3. Example of module1/CMakeLists.txt:

    # Create a static library for module1
    add_library(module1 STATIC module1.cpp)

    2.4. Example of module2/CMakeLists.txt:

    # Create a static library for module2
    add_library(module2 STATIC module2.cpp)

    2.5. Example of main.cpp:

    #include <iostream>
    #include "module1/module1.h"
    #include "module2/module2.h"
    
    int main() {
        std::cout << "Hello from Module1 and Module2!" << std::endl;
        module1_function();
        module2_function();
        return 0;
    }

    2.6. Example of module1/module1.cpp:

    #include "module1.h"
    #include <iostream>
    
    void module1_function() {
        std::cout << "Module1 function executed!" << std::endl;
    }

    2.7. Example of module2/module2.cpp:

    #include "module2.h"
    #include <iostream>
    
    void module2_function() {
        std::cout << "Module2 function executed!" << std::endl;
    }

    2.8. Build and Run

    Follow the previous steps to create the build directory, run cmake and make, and finally, you will see the following output:

    Hello from Module1 and Module2!
    Module1 function executed!
    Module2 function executed!

    This indicates that our multi-module project has been successfully built and the function calls between the modules can run smoothly.

    3. Advanced CMake Techniques

    3.1. Conditional Builds:
    • You can use the if() statement to decide whether to compile certain files or use specific libraries based on different operating systems, compilers, or other conditions.
    if(WIN32)
        message("Building on Windows")
    elseif(UNIX)
        message("Building on Unix-like OS")
    endif()
    3.2. Custom Build Options:
    • You can define custom compile options in the CMakeLists.txt to use different configurations in different environments.
    option(USE_MY_LIBRARY "Use My Library" ON)
    if(USE_MY_LIBRARY)
        add_subdirectory(MyLibrary)
    endif()

    4. Conclusion

    I hope that through today’s explanation, you can master the basic usage of CMake and adjust the build system according to different project needs.

    In future projects, you will find CMake to be an indispensable build tool that allows you to develop cross-platform more efficiently.

    Leave a Comment