Detailed explanation of CMake project construction

Table of contents

1. Introduction and installation of CMake

1.1 Install CMake on Ubuntu

1.2 Install CMake in VScode environment

1.3 CMake project construction directory structure

1.4 Compilation and linking of the project

1.5 CMake commonly used predefined variables

1.6 CMake build types

2. Simple use of CMake construction: Helloworld

3. CMake build: multiple source files in the root directory

Elementary: few source files use add_executable()

Advanced: More source files use aux_source_directory() or set()

Four, CMake build: multi-directory multi-source files

5. CMake Construction: Formal Organizational Structure

Method 1: Two CMakeLists.txt

Method 2: a CMakeLists.txt

Six, CMake build: compile dynamic library and static library

Seven, CMake build: link library

Eight, CMake build: add compilation options

Nine, CMake build: add control options

9.1 I originally wanted to generate multiple bin or library files, but now I only want to generate some specified bin or library files

 9.2 For the same bin file, only want to compile part of the code (use macro to control)

10. Summary


1. Introduction and installation of CMake

CMake: It is simple and convenient to use, and can build a project compilation environment across platforms. It is simpler than writing Makefile directly (when compiling a large-scale project, you need to write a large number of file dependencies), you can generate a responsible local Makefile through a simple CMake configuration, and one command will compile the executable file and static library we want to compile. , Dynamic libraries are compiled. Moreover, the configuration file can be directly used on other platforms without modification, which is very convenient.


1.1 Install CMake on Ubuntu

Install CMake using the command:

sudo apt install cmake

After the installation is complete, enter the cmake version in the terminal cmake -version:

fff@ubuntu:~$ cmake -version
cmake version 3.22.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).

1.2 Install CMake in VScode environment

1.2.1 vscode downloads the corresponding plug-in.

1.2.2 Find the location of cmake under the shell terminal, the path on our system is /usr/bin/camke.

fff@ubuntu:~$ whereis cmake
cmake: /usr/bin/cmake /usr/share/cmake /opt/cmake-3.23.0-linux-x86_64/bin/cmake /usr/share/man/man1/cmake.1.gz

1.2.3 Click the extended settings of CMake Tools.

 1.2.4 Select the remote host configuration, find Cmake:Build Environment, and add the corresponding environment variables.


1.3 CMake project construction directory structure

Generally, during actual development, we will clearly distinguish the corresponding directories, so that the project structure looks clearer. For example, the following directory structure:

bin: generated executable file (  .elf file)
lib: generated intermediate library file
include: header file
src: source file
build: temporary intermediate file generated during project compilation
test: test file
3rd: dependent third-party library (such as .a .so .la files, etc.)
CMakeLists.txt: CMake configuration file
autobuild.sh: One-click compilation script

1.4 Compilation and linking of the project

When we usually use commands to compile link files on Linux, the following items may be involved:

 For example, we have a muduo_server.cpp file, the command is as follows:

fff@ubuntu:~/home/Desktop/muduocode$ g++ -o server moduo_server.cpp -lmuduo_net -lmuduo_base -lpthread

We can use the CMake configuration file to accomplish the same function. Create a CMakeList.txt file in the project root directory, and the cmake command will find it from the project root directory.

fff@ubuntu:~/home/Desktop/muduocode$ tree
.
├── CMakeLists.txt
├── muduo_server
├── muduo_server.cpp

 The cmake command will execute the configuration items in the CMakeLists.txt configuration file in the directory. The content of a simple CMakeLists.txt configuration file is as follows:


1.5 CMake commonly used predefined variables

1. PROJECT_NAME : specify the project name through project();
2. PROJECT_SOURCE_DIR : the root directory of the project;
3. PROJECT_BINARY_DIR : the directory where the cmake command is executed;
4. CMAKE_CURRENT_SOURCE_DIR : the directory where the current CMakeList.txt file is located;
5. CMAKE_CURRENT_BIN ARY_DIR : compile directory , can be modified by using add subdirectory;
6. EXECUTABLE_OUTPUT_PATH : binary executable file output location;
7. LIBRARY_OUTPUT_PATH : library file output location;
8. BUILD_SHARED_LIBS : default library compilation method (shared or static), the default is static;
9, CMAKE_C_FLAGS : Set C compilation options;
10, CMAKE_CXX_FLAGS : Set C++ compilation options;
11, CMAKE_CXX_FLAGS_DEBUG: Compilation options when setting the compilation type Debug;
12. CMAKE_CXX_FLAGS_RELEASE : Compilation options when setting the compilation type Release;
13. CMAKE_GENERATOR : Compiler name;
14. CMAKE_COMMAND : The full path of the CMake executable file itself;
15. CMAKE_BUILD_TYPE : Project compilation Generated version, Debug / Release;

1.6 CMake build types

The difference between setting the build type to Release and Debug is mainly reflected in the optimization level of the compiler and the characteristics of the generated executable file.

1. Optimization level :

  • Release mode: In Release mode, the compiler will perform higher-level optimizations to improve code execution efficiency and performance. This includes but is not limited to optimization techniques such as function inlining, loop unrolling, and constant folding to reduce code execution time and memory usage. However, this may increase compilation time and less readable code.
  • Debug mode: In Debug mode, the compiler usually does not perform optimizations to better understand and trace the code during debugging. This means that the resulting executable will be larger, but more convenient for debugging and troubleshooting.

2. Debug information :

  • Release mode: In Release mode, the compiler typically strips debug information to reduce the size of the resulting executable. This means that complete symbol information may not be available when debugging, making the debugging process more difficult.
  • Debug mode: In Debug mode, the compiler will retain complete debugging information so that the code can be accurately located and tracked in the debugger. This makes the debugging process easier and more accurate.

To sum up , the Release mode is suitable for the final released version, focusing on performance and the size of the executable file; while the Debug mode is suitable for the debugging and development phase, focusing on convenient debugging and troubleshooting.

 The build type is Release, written as follows :

SET(CMAKE_BUILD_TYPE "Release")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CXXFLAGS} -Os -s -Wall -std=gnu11  -Werror")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -Os  -s -Wall -std=c++1y -Werror")

The specific parameters are explained as follows:

1. SET(CMAKE_BUILD_TYPE "Release")  Set the build type to Release , which means the code will be compiled in an optimized way to improve performance.

2.SET(CMAKE_C_FLAGS_RELEASE "$ENV{CXXFLAGS} -Os -s -Wall -std=gnu11 -Werror")  Set the compilation parameters of the C compiler in Release mode. The specific parameters are explained as follows:

  • $ENV{CXXFLAGS} is an environment variable to get extra parameters for the C++ compiler so that it can be used with other parameters.
  • -Os Indicates optimizing compilation to reduce the size of the resulting executable.
  • -s Indicates that debug information is stripped to reduce the size of the resulting executable.
  • -Wall Indicates that all warning messages are turned on.
  • -std=gnu11 Indicates compilation using the GNU C11 standard.
  • -Werror Indicates to treat all warnings as errors.

3. Set the compilation parameters of SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -Os -s -Wall -std=c++1y -Werror") the C++ compiler in Release mode. Similar to the above C compiler parameters, except that the C++11 standard is used for compilation.

By setting these parameters, you can perform optimized compilation in Release mode, and treat warnings as errors during compilation to ensure the quality and performance of the generated executable .

The build type is Debug, written as follows:

SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_C_FLAGS_DEBUG "$ENV{CXXFLAGS} -g -Wall -std=gnu11 -Werror")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -g -Wall -std=c++1y -Werror")

The specific parameters are explained as follows:

1.SET(CMAKE_BUILD_TYPE "Debug") Set the build type to Debug to compile in debug mode.

2.SET(CMAKE_C_FLAGS_DEBUG "$ENV{CXXFLAGS} -g -Wall -std=gnu11 -Werror")Set the compilation parameters of  the C compiler in Debug mode. The specific parameters are explained as follows:

  • $ENV{CXXFLAGS} is an environment variable to get extra parameters for the C++ compiler so that it can be used with other parameters.
  • -g Indicates to preserve debug information in the executable for debugging in a debugger.
  • -Wall Indicates that all warning messages are turned on.
  • -std=gnu11 Indicates compilation using the GNU C11 standard.
  • -Werror Indicates to treat all warnings as errors.

3.SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -g -Wall -std=c++1y -Werror")Set the compilation parameters of  the C++ compiler in Debug mode. Similar to the above C compiler parameters, except that the C++11 standard is used for compilation.

By setting these parameters, executable files with debugging information can be generated in Debug mode , and warnings are treated as errors to help debugging and troubleshooting.


2. Simple use of CMake construction: Helloworld

2.1 Write a hello.c:

#include "stdio.h"

int main(void)
{       
    printf("Hello World!\r\n");
    return 0;
}

2.2 Write in the same directory CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
        
project (demo)
        
add_executable(hello hello.c)

2.3 To run CMake in the same directory, you need to enter the command:

fff@ubuntu:~/Desktop/cmake_test$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/liefyuan/Desktop/cmake_test

2.4 will be generated in the same directory:

fff@ubuntu:~/Desktop/cmake_test$ ls -l
总用量 36
-rw-rw-r-- 1 liefyuan liefyuan 11542 Nov  7 14:50 CMakeCache.txt
drwxrwxr-x 5 liefyuan liefyuan  4096 Nov  7 14:50 CMakeFiles
-rw-rw-r-- 1 liefyuan liefyuan  1377 Nov  7 14:50 cmake_install.cmake
-rw-rw-r-- 1 liefyuan liefyuan    84 Nov  7 14:45 CMakeLists.txt
-rw-rw-r-- 1 liefyuan liefyuan    80 Nov  7 14:44 hello.c
-rw-rw-r-- 1 liefyuan liefyuan  4734 Nov  7 14:50 Makefile

2.5 As shown above, the Makefile file has been generated, and there are also files automatically generated by cmake running.
Then you can use makethe command to compile.

fff@ubuntu:~/Desktop/cmake_test$ make
Scanning dependencies of target hello
[ 50%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello

2.6 Look at the generated file again:

fff@ubuntu:~/Desktop/cmake_test$ ls -l
总用量 48
-rw-rw-r-- 1 liefyuan liefyuan 11542 Nov  7 14:50 CMakeCache.txt
drwxrwxr-x 5 liefyuan liefyuan  4096 Nov  7 14:53 CMakeFiles
-rw-rw-r-- 1 liefyuan liefyuan  1377 Nov  7 14:50 cmake_install.cmake
-rw-rw-r-- 1 liefyuan liefyuan    84 Nov  7 14:45 CMakeLists.txt
-rwxrwxr-x 1 liefyuan liefyuan  8600 Nov  7 14:53 hello
-rw-rw-r-- 1 liefyuan liefyuan    80 Nov  7 14:44 hello.c
-rw-rw-r-- 1 liefyuan liefyuan  4734 Nov  7 14:50 Makefile

2.7 You can see that an executable hello file has been generated, enter and ./hellorun the executable file:

fff@ubuntu:~/Desktop/cmake_test$ ./hello
Hello World!

2.8 Run successfully!

PS: If you need to recompile, you can use make cleanthe command to clean (delete the old executable).


3. CMake build: multiple source files in the root directory

Elementary: few source files use add_executable()

3.1 File directory:

fff@ubuntu:~/Desktop/cmake_multi_test$ ls -l
rw-rw-r-- 1   fff fff    93 Nov   7 16:41 CMakeLists.txt
-rw-rw-r-- 1  fff fff    107 Nov  7 16:34 function.c
-rw-rw-r-- 1  fff fff    86 Nov   7 16:38 function.h
-rw-rw-r-- 1  fff fff    85 Nov   7 16:30 main.c

3.2 function.h

#ifndef __FUNCTION_H__
#define __FUNCTION_H__

void func(unsigned int var);

#endif

3.3 function.c

#include <stdio.h>
#include "function.h"

void func(unsigned int var)
{
	printf("para var:%d\r\n", var);
}

3.4 main.c

#include <stdio.h>
#include "function.h"

int main(void)
{
	func(200);
	return 0;
}

CMakeLists.txt

cmake_minimum_required (VERSION 2.8)

project (demo)

add_executable(main main.c function.c)

3.5 Execute cmake .to generate Makefile and run make

fff@ubuntu:~/Desktop/cmake_multi_test$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_multi_test
fff@ubuntu:~/Desktop/cmake_multi_test$ make
Scanning dependencies of target main
[ 33%] Building C object CMakeFiles/main.dir/main.c.o
[ 66%] Building C object CMakeFiles/main.dir/function.c.o
[100%] Linking C executable main
[100%] Built target main

3.6 You can see that an executable main file has been generated, enter and ./mainrun the executable file:

fff@ubuntu:~/Desktop/cmake_multi_test$ ./main
para var:200

3.7 Run successfully!

PS: summary

By analogy, if there are multiple source files in the same directory, then just add all the source files in add_executable.


Advanced: More source files use aux_source_directory() or set()

PS: But if there are a hundred source files, it would be a bit tricky to do this again, and it cannot reflect the superiority of cmake. cmake provides a command to store all source files in a specified directory in a variable. This command is aux_source_directory(dir  var).
The first parameter dir is the specified directory, and the second parameter var is the variable used to store the list of source files.

3.8 Add files againfunction2.c

#include <stdio.h>
#include "function2.h"

void func2(unsigned int var)
{
	printf("para var2:%d\r\n", var);
}

3.9 Add file function2.h

#ifndef __FUNCTION2_H__
#define __FUNCTION2_H__

void func2(unsigned int var);

#endif

3.10 Modify main.c

#include <stdio.h>
#include "function.h"
#include "function2.h"

int main(void)
{
	func(200);
	func2(100);
	
	return 0;
}

3.11 Modify CMakeLists.txt

cmake_minimum_required (VERSION 2.8)

project (demo)

aux_source_directory(. SRC_LIST)

add_executable(main ${SRC_LIST})

3.12 Then run cmake .and make

fff@ubuntu:~/Desktop/cmake_multi_test$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_multi_test
fff@ubuntu:~/Desktop/cmake_multi_test$ make
Scanning dependencies of target main
[ 25%] Building C object CMakeFiles/main.dir/function.c.o
[ 50%] Building C object CMakeFiles/main.dir/function2.c.o
[ 75%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main

3.13 Running executables./main

liefyuan@ubuntu:~/Desktop/cmake_multi_test$ ./main
para var:200
para var2:100

3.14 Run successfully!

PS: To sum up ,
aux_source_directory() also has disadvantages. It will add all the source files in the specified directory, and may add some files we don’t need. At this time, we can use the set command to create variables to store the required source files. ,as follows:

cmake_minimum_required (VERISON 2.8)

project (demo)

set(SRC_LIST 
		./main.c
		./function.c
		./function2.c)

add_executable(main ${SRC_LIST})

Four, CMake build: multi-directory multi-source files

 Generally speaking, when there are many program files, we will manage them by category, and put the codes in different directories according to their functions, so that it is easy to find. So how to write CMakeLists.txt in this case?

4.1 Let's sort out the previous source files (create 2 directories function and function2 ), and the overall file structure is as follows:

fff@ubuntu:~/Desktop/cmake_multi_dir_test$ tree
.
├── CMakeLists.txt
├── function
│   ├── function.c
│   └── function.h
├── function2
│   ├── function2.c
│   └── function2.h
└── main.c

4.2 Modify the CmakeLists.txt file

cmake_minimum_required (VERSION 2.8)

project (demo)

include_directories (function function2)
        
aux_source_directory(function SRC_LIST)
aux_source_directory(function2 SRC_LIST2)

add_executable(main main.c ${SRC_LIST} ${SRC_LIST2})

As above:

  • The aux_source_directory is used 2 times, because the source files are distributed in 2 directories, so add 2 times.

or:

  • Store both additions in the same variable
cmake_minimum_required (VERSION 2.8)

project (demo)

include_directories (function function2)
        
aux_source_directory(function SRC_LIST)
aux_source_directory(function2 SRC_LIST)

add_executable(main main.c ${SRC_LIST})

4.3 Then run cmake .againmake./main

fff@ubuntu:~/Desktop/cmake_multi_dir_test$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_multi_dir_test
fff@ubuntu:~/Desktop/cmake_multi_dir_test$ make
Scanning dependencies of target main
[ 25%] Building C object CMakeFiles/main.dir/main.c.o
[ 50%] Building C object CMakeFiles/main.dir/function/function.c.o
[ 75%] Building C object CMakeFiles/main.dir/function2/function2.c.o
[100%] Linking C executable main
[100%] Built target main
fff@ubuntu:~/Desktop/cmake_multi_dir_test$ ./main
para var:200
para var2:100

normal operation!

PS: new command: include_directories()

Here comes a new command: include_directories() . This command is used to add search paths for multiple specified header files to the project, and the paths are separated by spaces.

 Because function.h and function2.h are included in main.c, if there is no command to specify the location of the header file, it will not be able to compile. Of course, you can also use include to specify the path in main.c, as follows:

#include "function/function.h"
#include "function2/function2.h"

This way of writing is not good-looking!

5. CMake Construction: Formal Organizational Structure

First build a directory tree, as follows:

fff@ubuntu:~/Desktop/cmake_dir$ tree
.
├── bin
├── build
├── include
│   ├── function2.h
│   └── function.h
└── src
    ├── function2.c
    ├── function.c
    └── main.c

Method 1: Two CMakeLists.txt

Create a new file in the root directoryCMakeLists.txt with the following content:

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (src)

You also need to create a new file in the src directoryCMakeLists.txt , the content is as follows:

aux_source_directory (. SRC_LIST)

include_directories (../include)

add_executable (main ${SRC_LIST})

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

A new command set is used here, which is used to define variables, EXECUTABLE_OUT_PATHand is a predefined variablePROJECT_SOURCE_DIR that comes with CMake . Its meaning is as follows,

  • EXECUTABLE_OUTPUT_PATH : The location where the target binary executable is stored
  • PROJECT_SOURCE_DIR : the root directory of the project

Therefore, set here means to set the location where the elf file is stored to the bin directory under the root directory of the project . (cmake has many predefined variables, you can search online for details)

After adding the above two CMakeLists.txt, the overall file structure is as follows,

fff@ubuntu:~/Desktop/cmake_dir$ tree
.
├── bin
├── build
├── CMakeLists.txt
├── include
│   ├── function2.h
│   └── function.h
└── src
    ├── CMakeLists.txt
    ├── function2.c
    ├── function.c
    └── main.c

build, compile, run

  • Step 1: Go to the build folder and run the command:cmake ..
  • Step 2: After the previous step, the Makefile will be generated in the build directory, and then run to compile, and an executable file will be generated in the bin directorymake of the root directorymain
  • Step 3: Enter the bin file in the root directory to run the executable file./main
fff@ubuntu:~/Desktop/cmake_dir/build$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_dir/build
fff@ubuntu:~/Desktop/cmake_dir/build$ make
Scanning dependencies of target main
[ 25%] Building C object src/CMakeFiles/main.dir/function.c.o
[ 50%] Building C object src/CMakeFiles/main.dir/function2.c.o
[ 75%] Building C object src/CMakeFiles/main.dir/main.c.o
[100%] Linking C executable ../../bin/main
[100%] Built target main
fff@ubuntu:~/Desktop/cmake_dir/build$ cd ..
fff@ubuntu:~/Desktop/cmake_dir$ cd bin
fff@ubuntu:~/Desktop/cmake_dir/bin$ ls
main
fff@ubuntu:~/Desktop/cmake_dir/bin$ ./main
para var:200
para var2:100

normal operation!

PS: Why run cmake under the build directory? As can be seen from the previous examples, if this is not done, the accompanying files generated when cmake is running will be mixed with the source code files, which will pollute the directory structure of the program, and run cmake in the build directory, the generated The attached files will only stay in the build directory. If we don't want these files, we can directly clear the build directory, which is very convenient.


Method 2: a CMakeLists.txt

The previous project used 2 CMakeLists.txt, the outermost CMakeLists.txt is used to control the overall situation, and add_subdirectory is used to control the operation of CMakeLists.txt in other directories.

 The above example can also use only one CMakeLists.txt, and change the content of the outermost CMakeLists.txt as follows:

cmake_minimum_required (VERSION 2.8)

project (demo)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

aux_source_directory (src SRC_LIST)

include_directories (include)

add_executable (main ${SRC_LIST})

Then delete the CMakeLists.txt in the src directory . The directory is as follows:

fff@ubuntu:~/Desktop/cmake_dir2$ tree
.
├── bin
├── build
├── CMakeLists.txt
├── include
│   ├── function2.h
│   └── function.h
└── src
    ├── function2.c
    ├── function.c
    └── main.c

build, compile, run

  • Step 1: Enter the build folder first , then run the command:cmake ..
  • Step 2: After the previous step, the Makefile will be generated in the build directory, and then run to compile, and an executable file will be generated in the bin directorymake of the root directorymain
  • Step 3: Enter the bin file in the root directory to run the executable file./main
fff@ubuntu:~/Desktop/cmake_dir2/build$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_dir2/build
liefyuan@ubuntu:~/Desktop/cmake_dir2/build$ ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
fff@ubuntu:~/Desktop/cmake_dir2/build$ make
Scanning dependencies of target main
[ 25%] Building C object CMakeFiles/main.dir/src/function.c.o
[ 50%] Building C object CMakeFiles/main.dir/src/function2.c.o
[ 75%] Building C object CMakeFiles/main.dir/src/main.c.o
[100%] Linking C executable ../bin/main
[100%] Built target main
fff@ubuntu:~/Desktop/cmake_dir2/build$ cd ..
fff@ubuntu:~/Desktop/cmake_dir2$ cd bin
fff@ubuntu:~/Desktop/cmake_dir2/bin$ ls
main
fff@ubuntu:~/Desktop/cmake_dir2/bin$ ./main
para var:200
para var2:100

normal operation!


Six, CMake build: compile dynamic library and static library

6.1 Build a file structure:

fff@ubuntu:~/Desktop/cmake_lib$ tree
.
├── build
├── CMakeLists.txt
├── function
│   ├── function.c
│   └── function.h
└── lib

Run cmake in the build directory, and store the generated library files in the lib directory .

6.2 The content of the newly created CMakeLists.txt is:

cmake_minimum_required (VERSION 2.8)
        
project (demo)
        
set (SRC_LIST ${PROJECT_SOURCE_DIR}/function/function.c)
        
add_library (function_shared SHARED ${SRC_LIST})
add_library (function_static STATIC ${SRC_LIST})
        
set_target_properties (function_shared PROPERTIES OUTPUT_NAME "function")
set_target_properties (function_static PROPERTIES OUTPUT_NAME "function")
        
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

PS: New commands and predefined variables appear here:

  • add_library : Generate a dynamic library or a static library (the first parameter specifies the name of the library; the second parameter determines whether it is dynamic or static, if not, it defaults to static; the third parameter specifies the source file of the generated library)
  • set_target_properties : Set the name of the final generated library, and other functions, such as setting the version number of the library, etc.
  • LIBRARY_OUTPUT_PATH : The default output path of the library file, here is set to the lib directory under the project directory

6.3 Enter the build directory to run the command cmake .., generate the Makefile and then run it make.

fff@ubuntu:~/Desktop/cmake_lib/build$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_lib/build
fff@ubuntu:~/Desktop/cmake_lib/build$ make
Scanning dependencies of target function_static
[ 25%] Building C object CMakeFiles/function_static.dir/function/function.c.o
[ 50%] Linking C static library ../lib/libfunction.a
[ 50%] Built target function_static
Scanning dependencies of target function_shared
[ 75%] Building C object CMakeFiles/function_shared.dir/function/function.c.o
[100%] Linking C shared library ../lib/libfunction.so
[100%] Built target function_shared

6.4 After the compilation is successful, enter the lib directory to check, and find that the dynamic library and static library have been successfully generated.

fff@ubuntu:~/Desktop/cmake_lib/build$ cd ..
fff@ubuntu:~/Desktop/cmake_lib$ cd lib
fff@ubuntu:~/Desktop/cmake_lib/lib$ ls
libfunction.a  libfunction.so

PS: Set_target_properties was used to redefine the output name of the library. If you do not use set_target_properties , then the name of the library is the name defined in add_library , but when you use add_library to specify the library name (the first parameter) twice in a row , the name They cannot be the same, but set_target_properties can set the names to be the same, but the suffixes of the final generated library files are different (one is .so, the other is .a), which will look relatively good.


Seven, CMake build: link library

The library has been generated before, and now the library link is performed.

fff@ubuntu:~/Desktop/cmake_lib_link$ tree
.
├── bin
├── build
├── CMakeLists.txt
├── function
│   ├── inc
│   │   └── function.h
│   └── lib
│       ├── libfunction.a
│       └── libfunction.so
└── src
    └── main.c

7.1 main.c

#include <stdio.h>
#include "function.h"

int main(void)
{       
    func(300);

    return 0;
} 

7.2 CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
        
project (demo)
        
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
        
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)
        
include_directories (${PROJECT_SOURCE_DIR}/function/inc)
        
find_library(FUNCTION_LIB function HINTS ${PROJECT_SOURCE_DIR}/function/lib)
        
add_executable (main ${SRC_LIST})
        
target_link_libraries (main ${FUNCTION_LIB})

Explanation of the new commands:

  • find_library : Find the specified library in the specified directory, and store the absolute path of the library in a variable. The first parameter is the variable name, the second parameter is the library name, the third parameter is HINTS, and the fourth parameter is Path, other usage can refer to cmake documentation
  • target_link_libraries : Link target files with library files

 The advantage of using find_library is that cmake ..it will check whether the library exists during execution, so that errors can be found in advance without waiting until link time.

 cd to the build directory, then run it cmake .. && make, and finally enter the bin directory to check, and found that the main has been generated, just run it.

fff@ubuntu:~/Desktop/cmake_lib_link/build$ cmake ..&& make
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fff/Desktop/cmake_lib_link/build
Scanning dependencies of target main
[ 50%] Building C object CMakeFiles/main.dir/src/main.c.o
[100%] Linking C executable ../bin/main
[100%] Built target main

7.3 Run:

fff@ubuntu:~/Desktop/cmake_lib_link/build$ cd ..
fff@ubuntu:~/Desktop/cmake_lib_link$ cd bin
fff@ubuntu:~/Desktop/cmake_lib_link/bin$ ./main
para var:300

Run successfully! PS:

There are static libraries and dynamic libraries of function in the lib directory, find_library(FUNCTION_LIB function ... is to find the dynamic library by default, if you want to directly specify whether to use the dynamic library or the static library, you can write find_library(FUNCTION_LIB libfunction.so ...or find_library(FUNCTION_LIB libfunction. a…

Eight, CMake build: add compilation options

Sometimes you need to add some compilation options when compiling the program, such as -Wall, -std=c++11etc., you can use it add_compile_optionto operate.

8.1 main.cpp

#include <iostream>

int main(void)
{
	auto data = 100;
	std::cout << "data:" << data << "\n";
	return 0;
}

8.2 CMakeLists.txt

cmake_minimum_required (VERSION 2.8)

project (demo)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

add_compile_options(-std=c++11 -Wall)

add_executable(main main.cpp)

The directory tree is as follows:

fff@ubuntu:~/Desktop/cmake_cpp$ tree
.
├── bin
├── build
├── CMakeLists.txt
└── main.cpp

Then cd to the build directory , execute the cmake ... && make command, you can get the main elf file in the bin directory


Nine, CMake build: add control options

Sometimes when compiling the code, only some specified codes are compiled, you can use the option command of cmake, there are mainly two kinds of situations encountered:

  • I originally wanted to generate multiple bin or library files, but now I only want to generate some specified bin or library files
  • For the same bin file, I only want to compile part of the code (using macros to control)

9.1 I originally wanted to generate multiple bin or library files, but now I only want to generate some specified bin or library files

Assuming that our current project will generate two bin files, main1 and main2, the overall structure is as follows now,

fff@ubuntu:~/Desktop/cmake_2bin$ tree
.
├── bin
├── build
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    ├── main1.c
    └── main2.c

The contents of main1.c and main2.c are as follows,

// main1.c
#include <stdio.h>

int main(void)
{
    printf("hello, this main1\n");
    
    return 0;
}
// main2.c
#include <stdio.h>

int main(void)
{
    printf("hello, this main2\n");
    
    return 0;
}

The content of the outer CMakeLists.txt is as follows,

cmake_minimum_required(VERSION 3.5)

project(demo)

option(MYDEBUG "enable debug compilation" OFF)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

add_subdirectory(src)

The option command is used here , the first parameter is the name of the option, the second parameter is a string, which is used to describe what the option is for, and the third is the value of the option, ON or OFF, you can also Do not write, do not write is the default OFF.

Then write CMakeLists.txt in the src directory , as follows

cmake_minimum_required (VERSION 3.5)

add_executable(main1 main1.c)

if (MYDEBUG)
    add_executable(main2 main2.c)
else()
    message(STATUS "Currently is not in debug mode")    
endif()

Note : if-else is used here to decide whether to compile main2.c according to option

Then cd to the build directory and enter cmake .. && maketo compile only main1. If you want to compile main2, set MYDEBUG to ON and enter again cmake .. && maketo recompile.

PS: Every time you want to change MYDEBUG, you need to modify CMakeLists.txt, which is a bit troublesome. In fact, you can operate it through the cmake command line. For example, if we want to set MYDEBUG to OFF, first cd to the build directory, and then enter cmake .. - DMYDEBUG=ON , so that main1 and main2 can be compiled (in the bin directory )

 9.2 For the same bin file, only want to compile part of the code (use macro to control)

The overall project structure is as follows,

fff@ubuntu:~/Desktop/cmake_1mian_define$ tree
.
├── bin
├── build
├── CMakeLists.txt
└── main.c

Suppose we have a main.c whose content is as follows,

#include <stdio.h>

int main(void)
{
#ifdef WWW1
    printf("hello world1\n");
#endif    

#ifdef WWW2     
    printf("hello world2\n");
#endif

    return 0;
}

You can control the printed information by defining macros. The content of our CMakeLists.txt is as follows,

cmake_minimum_required(VERSION 3.5)

project(demo)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

option(WWW1 "print one message" OFF)
option(WWW2 "print another message" OFF)

if (WWW1)
    add_definitions(-DWWW1)
endif()

if (WWW2)
    add_definitions(-DWWW2)
endif()

add_executable(main main.c)

Here, the name of the option is kept the same as the macro name in main.c, which is more intuitive, and you can also choose a different name. By cooperating with add_definitions() , you can control the printout of a single bin file.

cd to the build directory to execute cmake .. && make, and then to the bin directory to execute ./main, you can see that the print is empty,
then follow the instructions below to execute, and then check the printing effect,

  • cmake .. -DWWW1=ON -DWWW2=OFF && make
  • cmake .. -DWWW1=OFF -DWWW2=ON && make
  • cmake .. -DWWW1=ON -DWWW2=ON && make

Notice:

Here is a small pit to pay attention to: suppose there are two options called A and B, first call cmake to set A, and then call cmake to set B next time, if the cache file generated when cmake was executed last time is not deleted, then this Although A is not set this time, the last option value of A will be used by default.

So if the option changes, either delete the cache file generated when cmake was executed last time, or explicitly specify its value for all options.

10. Summary

The above are some study notes for me to learn CMake . Through simple examples, I can quickly get started with CMake . I also read many blogs of netizens while studying. There are still many knowledge points about CMake, and specific details can be searched online. In short, CMake can save us from writing complex Makefiles , and it is cross-platform. It is a very powerful tool worth learning.

If there is something wrong, I hope you can leave a message to correct me, thank you for reading!

Guess you like

Origin blog.csdn.net/FLM19990626/article/details/132408320