** For more posts, please see

Total catalog of romantic carriages for audio and video system learning **

C/C++ automatic build tool Makefile upgrade tool, from Makefile to CMake

The C/C++ automatic build tool Makefile introduces the scripting tool Makefile for building projects, which is much more convenient than manually executing GCC commands. However, if you build a project using only makefiles, there is a problem. This is because the commands in makefiles are platform-specific. For example, in the previous article, the GNU GCC command was used. That’s another Makefile to write, which doesn’t appeal to lazy programmers, so CMake, which supports cross-platform syntax and is much simpler, was born.

This article is a good introduction to CMake. It is recommended to read the first two articles and at least one introduction to THE C/C++ Automatic Build Tool Makefile before you read this article. Otherwise, you will get confused due to the lack of an overall understanding of project construction.

CMake is introduced

CMake CMake CMake CMake CMake

CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice. The suite of CMake tools were created by Kitware in response to the need for a powerful, cross-platform build environment for open-source projects such as ITK and VTK.

CMake is a cross-platform build script that generates makefiles for a specific platform. CMake generates makefiles and builds projects from makefiles. CMake does not build projects.

Here’s how to use CMake, from simple to complex projects, on Ubuntu.

Single directory single file

First create the source file main.cpp in Demo1:

#include <iostream>
  
int main(a) {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

Copy the code

Then create the CMakeLists file (the name cmakelists.txt is fixed according to the specification) :

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo$ cd Demo1/
ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo1$ ls
main.cpp
# create CMakeLists file (name fixed)
ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo1$ vim CMakeLists.txt
Copy the code

Write CMakeLists. TXT:

# specify the version number of cmake
cmake_minimum_required(VERSION 3.16)
# Project Name
project(CmakeDemo)
# specify the C++ version
set(CMAKE_CXX_STANDARD 14)
# specify the name of the build executable and the source file to depend on.
add_executable(CmakeDemo main.cpp)
Copy the code

As you can see, cmake scripts are much cleaner than makefiles, specifying the source and final executable files directly (without having to worry about GCC commands and intermediate assembles, object files, etc.). By saying “add_executable” as if “I made the executable CmakeDemo with main. CPP”, the cmake command generated the corresponding Makfile.

Cmake command format:

Cmake [options] < existing build path >

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo1$ cmake .
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.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/ubuntu/study/projects/CmakeDemo/Demo1
Copy the code

At this point the cmake directive generates a lot of files, notice that the familiar Makefile has been generated:In the case of makefiles, that of course is to execute the make directive to build the project:

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo1$ make 
Scanning dependencies of target CmakeDemo
[ 50%] Building CXX object CMakeFiles/CmakeDemo.dir/main.cpp.o
[100%] Linking CXX executable CmakeDemo
[100%] Built target CmakeDemo
Copy the code

It is nice to see that the executable has been generated:Run:

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo1$ ./CmakeDemo 
Hello, World!
Copy the code

No problem ~ ~

Single directory with multiple files

Now we’ve tweaked the original to add the Dog class:

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo2$ ls
CMakeLists.txt  Dog.cpp  Dog.h  main.cpp
Copy the code

T h:

#ifndef CMAKEDEMO_DOG_H
#define CMAKEDEMO_DOG_H


class Dog {
public:
    void shut(a);

};


#endif //CMAKEDEMO_DOG_H
Copy the code

T the CPP:

#include "Dog.h"
#include <iostream>

void Dog::shut(a) {
    std::cout << "I am dog!" << std::endl;
}
Copy the code

The main. CPP:

#include "Dog.h"

int main(a) {
    Dog dog;
    dog.shut(a);return 0;
}
Copy the code

The corresponding cmakelists. TXT file is very small, just add the Dog class file to the add_executable:

# specify the version number of cmake
cmake_minimum_required(VERSION 3.16)
# Project executable file name
project(CmakeDemo1:)
# specify the C++ version
set(CMAKE_CXX_STANDARD 14)
# specify the source file to generate the executable
add_executable(CmakeDemo main.cpp Dog.cpp Dog.h)
Copy the code

Cmakelists.txt (); CMake (); cmakelists.txt ();

The answer is yes.

# specify the version number of cmake
cmake_minimum_required(VERSION 3.16)
# Project executable file name
project(CmakeDemo1:)
# specify the C++ version
set(CMAKE_CXX_STANDARD 14)


Find all source files in the directory
# And save the name to the DIR_SRCS variable
aux_source_directory(. DIR_SRCS)
# specify the source file to generate the executable
add_executable(CmakeDemo ${DIT_SRCS})
Copy the code

Simply add aux_source_directory to get all the files in the directory and assign all the files to a variable DIR_SRCS that will be passed to add_executable.

Cmake has a number of built-in variables, some to get the current environment information, such as CMAKE_SYSTEM_VERSION for the system version, some to get the current project information, such as PROJECT_SOURCE_DIR for the project directory, and some to change the build process. Such as the option CMAKE_CXX_FLAGS when compiling C++ files.

The official variables documentation describes these built-in variables: cmake-variables

Multiple directories and files

It’s the same project source, but put the Dog class in the lib directory, create a cmakelists. TXT file in the lib directory, package the Dog class as a dynamic link library, and give it to main. CPP to link it to an executable.Cmakelists.txt in lib:

Find all source files in the current directory
Save the name to the DIR_LIB_SRCS variable
aux_source_directory(. DIR_LIB_SRCS)

Animal link library (SHARED
add_library (Animal SHARED ${DIR_LIB_SRCS})
Copy the code

Create a libAnimal. So dynamic link library using add_library.

Cmakelists.txt in Demo3:

# specify the version number of cmake
cmake_minimum_required(VERSION 3.16)
# Project executable file name
project(Demo)
# specify the C++ version
set(CMAKE_CXX_STANDARD 14)
Add a subdirectory and build that subdirectory.
add_subdirectory(lib)
# add the header directory because the current root directory main. CPP needs to reference the dog.h header
include_directories(lib)
Find all source files in the directory
# And save the name to the DIR_SRCS variable
aux_source_directory(. DIR_SRCS)
Print out the DIR_SRCS variable for debugging
Message("DIR_SRCS = ${DIR_SRCS}")
# specify the source file to generate the executable
add_executable(Demo ${DIR_SRCS})
Add link library
target_link_libraries(Demo Animal)
Copy the code

Multiple directories here are three key:

1. Add header directories: Since the current root directory main. CPP needs to reference the dog.h header, it needs to be referenced through include_directories. Note that the CMakeLists directory is relative to the current CMakeLists file.

2. Add_subdirectory

Add a subdirectory to the build.

It is to enable the current CMakeLists to execute to the CMakeLists holding the specified subdirectory and then build the source files in the subdirectory, in this case, the dynamic link library libAnimal. So corresponding to the subdirectory construction.

3. Link operation:

target_link_libraries(Demo Animal)
Copy the code

Link the generated Demo executable to the dynamic link library (not really yet, of course, because the dynamic link library is linked at runtime)

Standardization of multiple directories and files

The above engineering structure is not quite standard. For example, the files generated by CMake are put together with the source files, which leads to the inconvenient unified processing of these files. Now the general standard engineering structure is like this:

A build directory is dedicated to CMake files. The project source files and library source files are stored separately in the SRC and lib directories. The project root directory has cmakelists. TXT for managing global CMakeLists files.

Root directory of cmakelists.txt:

CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
  
PROJECT(Demo)
# import 2 subdirectories
ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(src)
Copy the code

Very simple, just add the SRC and lib directories to the build.

The SRC CMakeLists. TXT:

# specified executable file output path for execution of CMake/SCR/bin directory (/ home/ubuntu/study/projects/CmakeDemo/Demo4 / build/SRC/bin)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
Add the lib subdirectoryinclude_directories(.. /lib)Add a header directoryinclude_directories(.. /lib)Find all source files in the directory
# And save the name to the DIR_SRCS variable
aux_source_directory(. DIR_SRCS)
Message("DIR_SRCS = ${DIR_SRCS}")
Message("PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")
Message("PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}")

# specify the source file to generate the executable
add_executable(Demo ${DIR_SRCS})
Add link library
target_link_libraries(Demo Animal)

Copy the code

The lib CMakeLists. TXT:

Find all source files in the current directory
Save the name to the DIR_LIB_SRCS variable
aux_source_directory(. DIR_LIB_SRCS)
# the output path at the specified library lib (/ home/ubuntu/study/projects/CmakeDemo/Demo4 / build/lib)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
Create Animal link library
add_library (Animal SHARED ${DIR_LIB_SRCS})

Copy the code

Since you need to generate the build file under build, you need to execute CMake in build:

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo4/build$ cmake ..
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.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
DIR_SRCS = ./main.cpp
PROJECT_SOURCE_DIR = /home/ubuntu/study/projects/CmakeDemo/Demo4/src
PROJECT_BINARY_DIR = /home/ubuntu/study/projects/CmakeDemo/Demo4/build/src
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ubuntu/study/projects/CmakeDemo/Demo4/build

Copy the code

At this point, the build file has generated the build file Makefile and the corresponding build result files lib and SRC, and lib and SRC have generated their respective makefiles.Execute make under build:At this point the executable and the dynamic link library have been generated, execute the executable:Perfect ~ ~

To sum up, create a root directory as the main cmakelists. TXT, and modify the original SRC and lib cmakelists. TXT output path, so that the files generated by their CMake are placed in the corresponding directory under build, that is, the project is placed in build directory. SRC and lib are placed in the build/ SRC and build/lib directories respectively.

The final directory structure is:

ubuntu@VM- 207 --ubuntu:~/study/projects/CmakeDemo/Demo4$├─ Build │ ├─ CMakeFiles │ ├─3.16.3│ │ │ ├ ─ ─ CMakeCCompiler. Cmake │ │ │ ├ ─ ─ CMakeCXXCompiler. Cmake │ │ │ ├ ─ ─ CMakeDetermineCompilerABI_C. Bin │ │ │ ├ ─ ─ CMakeDetermineCompilerABI_CXX. Bin │ │ │ ├ ─ ─ CMakeSystem. Cmake │ │ │ ├ ─ ─ CompilerIdC │ │ │ │ ├ ─ ─ a.out │ │ │ │ ├ ─ ─ CMakeCCompilerId. C │ │ │ │ └ ─ ─ TMP │ │ │ └ ─ ─ CompilerIdCXX │ │ │ ├ ─ ─ a.out │ │ │ ├ ─ ─ CMakeCXXCompilerId. CPP │ │ │ └ ─ ─ TMP │ │ ├ ─ ─ cmake. Check_cache │ │ ├ ─ ─ CMakeDirectoryInformation. Cmake │ │ ├ ─ ─ CMakeOutput. Log │ │ ├ ─ ─ CMakeTmp │ │ ├ ─ ─ Makefile2 │ │ ├ ─ ─ a Makefile. Cmake │ │ ├ ─ ─ progress. The marks │ │ └ ─ ─ TargetDirectories. TXT │ ├ ─ ─ cmake_install. Cmake │ ├ ─ ─ Lib │ │ ├ ─ ─ CMakeFiles │ │ │ ├ ─ ─ Animal. Dir │ │ │ │ ├ ─ ─ the build, make │ │ │ │ ├ ─ ─ cmake_clean. Cmake │ │ │ │ ├ ─ ─ Cmake │ │ ├─ Heavy metal metal Metal Metal Metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal metal │ │ │ │ ├ ─ ─ flags. Make │ │ │ │ ├ ─ ─ link. TXT │ │ │ │ └ ─ ─ progress. Make │ │ │ ├ ─ ─ CMakeDirectoryInformation. Cmake │ │ │ └ ─ ─ progress. Marks │ │ ├ ─ ─ cmake_install. Cmake │ │ ├ ─ ─ libAnimal. So │ │ └ ─ ─ a Makefile │ ├ ─ ─ a Makefile │ └ ─ ─ the SRC │ ├ ─ ─ bin │ │ └ ─ ─ Demo │ ├ ─ ─ CMakeFiles │ │ ├ ─ ─ CMakeDirectoryInformation. Cmake │ │ ├ ─ ─ Demo. Dir │ │ │ ├ ─ ─ the build, make │ │ │ ├ ─ ─ Cmake_clean. Cmake │ │ │ ├ ─ ─ CXX. Includecache │ │ │ ├ ─ ─ DependInfo. Cmake │ │ │ ├ ─ ─ depend. The internal │ │ │ ├ ─ ─ depend. The make │ │ │ ├ ─ ─ flags. Make │ │ │ ├ ─ ─ link. TXT │ │ │ ├ ─ ─ main. CPP. O │ │ │ └ ─ ─ progress. Make │ │ └ ─ ─ progress. The marks │ ├ ─ ─ Cmake_install. Cmake │ └ ─ ─ a Makefile ├ ─ ─ CMakeLists. TXT ├ ─ ─ lib │ ├ ─ ─ CMakeLists. TXT │ ├ ─ ─ t CPP │ └ ─ ─ t h └ ─ ─ the SRC ├ ─ ─ CMakeLists. TXT └ ─ ─ the main CPPCopy the code

conclusion

This article is the introduction of CMake, as a kind of role, CMake itself is relatively simple, detailed usage can generally be checked by CMake official documents or Baidu Google to solve, with the basis of CMake, then the next step can steadily march to NDK.

Don’t forget to like this post if you found it helpful