Mastering CMake Basics: Practical Compilation from Scratch

Mastering CMake Basics: Practical Compilation from Scratch

1. Introduction to CMake

CMake is a cross-platform installation (compilation) tool that can describe the installation (compilation process) for all platforms using simple statements. It can output various makefiles or project files, and can test the C++ features supported by the compiler, similar to automake under UNIX. The configuration file for CMake is named CMakeLists.txt.

CMake does not directly build the final software but generates standard build files (like Unix’s Makefile or Windows Visual C++’s projects/workspaces), which can then be used in the usual build manner. More information about CMake can be found on the official CMake website.

It can be understood that writing a Makefile is too difficult, and CMake has made a secondary development based on Makefile.

1.1 Features of CMake

  1. Open-source, released under a BSD-like license.

  2. Cross-platform, capable of generating native compilation configuration files; it generates makefiles on Linux/Unix, Xcode on Apple platforms, and MSVC project files on Windows.

  3. Can manage large projects, with KDE4 being the best proof.

  4. Simplifies the compilation and build process. The toolchain for CMake is very simple: cmake + make.

  5. High efficiency, CMake builds KDE4’s kdelibs 40% faster than using autotools to build KDE3.5.6’s kdelibs, mainly because CMake does not use libtool in its toolchain.

  6. Extensible; specific function modules can be written for CMake to expand its functionality.

1.2 Notes

  1. CMake is very simple, but absolutely not as simple as imagined; simple is relative to Makefile.

  2. The process of writing CMake is essentially a programming process, similar to using autotools, but what needs to be written is CMakeLists.txt (one for each directory), using the CMake language and syntax.

  3. CMake’s integration with existing systems is not particularly ideal, such as with pkgconfig.

1.3 Usage Recommendations

  1. If the project only has a few files, writing a Makefile directly is the best choice.

  2. If the language used is not C/C++/Java, it is recommended not to use CMake.

  3. If the language has a very complete build system, such as Java’s Ant, there is no need to learn CMake.

  4. If the project already uses a very complete project management tool and has no maintenance issues, there is no need to migrate to CMake.

  5. If only using Qt programming, there is no need to use CMake, as qmake manages Qt projects with much higher professionalism and automation than CMake.

2. Installing CMake

My current CMake version:

$ cmake --version
cmake version 3.5.1

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

Some projects have minimum version requirements; if the version is too low, the project may not compile, so CMake can be upgraded.

(1) Uninstall the old version of CMake [optional].

sudo apt-get autoremove cmake

(2) Download the CMake compressed package.

wget https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.tar.gz

(3) Unzip the compressed package.

tar zxvf cmake-3.21.3-linux-x86_64.tar.gz

Check the unzipped directory:

cmake-3.21.3-linux-x86_64
├── bin
│   ├── ccmake
│   ├── cmake
│   ├── cmake-gui
│   ├── cpack
│   └── ctest
├── doc
│   └── cmake
├── man
│   ├── man1
│   └── man7
└── share
    ├── aclocal
    ├── applications
    ├── bash-completion
    ├── cmake-3.21
    ├── emacs
    ├── icons
    ├── mime
    └── vim

15 directories, 5 files

Various CMake family product programs are located under the bin directory.

(4) Create a symbolic link.

Note that the file path can be specified, generally choosing under /opt or /usr; here we choose /opt.

sudo mv cmake-3.21.3-Linux-x86_64 /opt/cmake-3.21.3
sudo ln -sf /opt/cmake-3.21.3/bin/*  /usr/bin/

Check the version number:

$ cmake --version
cmake version 3.21.3

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

3. Simple Use of CMake

3.1 Preparation Work

(1) First, create a folder to store the project files.

cd cmake
mkdir t1
cd t1

(2) Create main.c and CMakeLists.txt in the t1 directory (note the case of the filenames):

Contents of main.c:

//main.c
#include <stdio.h>
int main()
{
    printf("Hello World from t1 Main!\n"); 
    return 0;
}

Contents of CMakeLists.txt:

PROJECT(HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_EXECUTABLE(hello2 ${SRC_LIST})

The syntax of the contents of CMakeLists.txt will be explained in detail later.

3.2 Start Building

cmake .

Note that the dot after the command represents the current directory.

Execution result:

CMake Warning (dev) at CMakeLists.txt:4:
  Syntax Warning in cmake code at column 37

  Argument not separated from preceding token by whitespace.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- The C compiler identification is GNU 8.4.0
-- The CXX compiler identification is GNU 8.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir /home/fly/workspace/cmakeProj/t1
-- This is SOURCE dir /home/fly/workspace/cmakeProj/t1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fly/workspace/cmakeProj/t1

The system automatically generated: CMakeFiles, CMakeCache.txt, cmake_install.cmake, etc., and generated a Makefile.

There is no need to care about the function of these files; the key point is that it automatically generated a Makefile. Then perform the actual build of the project by entering the make command in this directory, and you will get the following output:

[ 25%] Building C object CMakeFiles/hello2.dir/main.c.o
[ 50%] Linking C executable hello2
[ 50%] Built target hello2
[ 75%] Building C object CMakeFiles/hello.dir/main.c.o
[100%] Linking C executable hello
[100%] Built target hello

If you need to see the detailed process of the make build, you can use make VERBOSE=1 or VERBOSE=1 make commands to build.

At this point, the required target file hello has been built and is located in the current directory; try running it:

$ ./hello
Hello World from t1 Main

3.3 Explanation of CMakeLists.txt Content

The CMakeLists.txt file is the build definition file for CMake, and the filename is case-sensitive. If the project exists in multiple directories, ensure that each directory to be managed has a CMakeLists.txt. This involves multi-directory builds, which will be explained later.

The contents of the CMakeLists.txt file in the above example are as follows:

PROJECT(HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_EXECUTABLE(hello2 ${SRC_LIST})

(1) PROJECT instruction. The syntax is:

PROJECT(projectname [CXX] [C] [Java])

This instruction can be used to define the project name and specify the supported languages. The list of supported languages can be ignored; by default, it indicates support for all languages. This instruction implicitly defines two CMake variables: < projectname>_BINARY_DIR and < projectname>_SOURCE_DIR, here being HELLO_BINARY_DIR and HELLO_SOURCE_DIR (thus the two MESSAGE instructions in CMakeLists.txt can directly use these two variables). Because internal compilation is used, both variables currently point to the path of the project. Note that in external compilation, the contents referred to by the two will differ.

Meanwhile, the CMake system also helps us predefine the PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR variables, which are consistent with HELLO_BINARY_DIR and HELLO_SOURCE_DIR.

For the sake of consistency, it is recommended to directly use PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR in the future; even if the project name changes, it will not affect these two variables. If < projectname>_SOURCE_DIR is used, after changing the project name, these variables need to be modified accordingly.

(2) SET instruction. The syntax is:

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

The SET instruction can be used to explicitly define variables. For example, using SET(SRC_LIST main.c); if there are multiple source files, it can also be defined as: SET(SRC_LIST main.c t1.c t2.c).

(3) MESSAGE instruction. The syntax is:

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

This instruction is used to output user-defined information to the terminal, and includes three types:

  • SEND_ERROR generates an error and skips the generation process.

  • STATUS outputs information prefixed with –.

  • FATAL_ERROR immediately terminates all CMake processes.

Here, STATUS information output is used, demonstrating the two implicit variables HELLO_BINARY_DIR and HELLO_SOURCE_DIR defined by the PROJECT instruction.

(4) ADD_EXECUTABLE instruction.

ADD_EXECUTABLE(hello ${SRC_LIST})

This defines that this project will generate an executable file named hello, and the related source files are the source file list defined in SRC_LIST; in this example, it can also be directly written as ADD_EXECUTABLE(hello main.c).

In this example, ${} is used to reference variables, which is the variable application method of CMake. However, there are some exceptions, such as in IF control statements, where variables are directly referenced by their names without needing ${}. If ${} is used to apply the variable, IF will check the value of the variable represented by ${}, which of course does not exist.

3.4 Basic Syntax Rules

CMake actually uses “CMake language and syntax” for building. The above content is the so-called “CMake language and syntax”, and the simplest syntax rules are:

(1) Variables are referenced using the ${} method, but in IF control statements, the variable name is used directly.

(2) Instructions (parameter1 parameter2…), parameters are enclosed in parentheses and separated by spaces or semicolons. Taking the ADD_EXECUTABLE instruction as an example, if there is another source file func.c, it should be written as:

ADD_EXECUTABLE(hello main.c func.c)

# or
ADD_EXECUTABLE(hello main.c;func.c)

(3) Instructions are case-insensitive, while parameters and variables are case-sensitive. However, it is recommended to use uppercase instructions. The MESSAGE instruction we used above has already applied this rule:

MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
# It can also be written as:
MESSAGE(STATUS "This is BINARY dir ${HELLO_BINARY_DIR}")

It is important to explain that the project name HELLO and the executable file name hello have no relation.

(4) Project name and executable file. The hello defines the executable file name, and it can also be written as: ADD_EXECUTABLE(t1 main.c), which will generate an executable file named t1 after compilation.

(5) Regarding syntax confusion:

The syntax of CMake is quite flexible and considers various situations, for example:

SET(SRC_LIST main.c)

# It can also be written as 
SET(SRC_LIST "main.c")

There is no difference, but if a source file’s name is fu nc.c (the filename contains spaces), it must use double quotes. If written as SET(SRC_LIST fu nc.c), it will produce an error indicating that fu and nc.c files cannot be found. In this case, it must be written as:

SET(SRC_LIST "fu nc.c")

Additionally, the source file extensions can be omitted from the source file list, for example, it can be written as:

ADD_EXECUTABLE(t1 main)

CMake will automatically look for main.c or main.cpp in the current directory. Of course, it is best not to be lazy about this, to avoid the situation where there are indeed both a main.c and a main.

Parameters can also be separated using semicolons. The following example is also valid:

ADD_EXECUTABLE(t1 main.c t1.c)
# It can be written as
ADD_EXECUTABLE(t1 main.c;t1.c).

Just be consistent in style when writing CMakeLists.txt.

(6) Cleaning the project. Like the classic autotools series of tools, running make clean can clean up the build results.

(7) CMake does not support make distclean. This is clearly explained by the official documentation. CMakeLists.txt can execute scripts and generate some temporary files through scripts, but it cannot track which temporary files are those. Therefore, it cannot provide a reliable make distclean solution.

(8) Internal build. The previous process was an internal build (in-source build), while CMake strongly recommends external builds (out-of-source build). Internal builds may generate more temporary files than your code files, and they generate some intermediate files that cannot be automatically deleted.

(9) External build. The external compilation process is as follows:

  1. First, clear all intermediate files in the t1 directory except for main.c and CMakeLists.txt; the key point is to remove CMakeCache.txt.

  2. Next, create a build directory in the t1 directory; of course, you can also create a build directory anywhere, not necessarily in the project directory.

  3. Then, enter the build directory and runcmake .. (note that.. represents the parent directory, because the parent directory contains the CMakeLists.txt we need; if you create the build directory elsewhere, you need to runcmake <full path of the project> to see the generated Makefile and other intermediate files in the build directory.

  4. Finally, run make to build the project, and you will obtain the target file hello in the current directory (the build directory).

  5. This process is known as out-of-source external compilation, and one of its biggest advantages is that it has no impact on the original project; all actions occur in the compilation directory. This alone is enough to convince us to adopt external compilation for building projects.

It is particularly important to note that when building the project through external compilation, HELLO_SOURCE_DIR still refers to the project path, i.e., /t1; while HELLO_BINARY_DIR refers to the compilation path, i.e., cmake/t1/build.

4. A More Proper CMake Project

In the future, we will adopt out-of-source external builds for all constructions, and the agreed build directory will be the build subdirectory under the project directory.

To make the previous Hello World look more like a project, the following needs to be done:

  1. Add a subdirectory src to place the project source code.

  2. Add a subdirectory doc to place the project documentation hello.txt.

  3. Add text files COPYRIGHT and README in the project directory.

  4. Add a runhello.sh script in the project directory to call the hello binary.

  5. Place the built target files in the bin subdirectory of the build directory.

  6. Finally, install these files: install the hello binary and runhello.sh to/usr/bin, and install the contents of the doc directory along with COPYRIGHT/README to/usr/share/doc/cmake/t2.

4.1 Preparation Work

(1) Create the t2 directory.

mkdir t2

(2) Copy the main.c and CMakeLists.txt of the t1 project to the t2 directory.

cp ./main.c ../t2
cp ./CMakeLists.txt ../t2
# or copy the entire directory
# cp -r ../t1 ../t2

(3) Add the subdirectory src:

cd t2
mkdir src
mv main.c src/

Now the project looks like this: a subdirectory src and a CMakeLists.txt.

(4) As mentioned above, a CMakeLists.txt must be created for any subdirectory. Enter the src subdirectory and write the CMakeLists.txt as follows:

ADD_EXECUTABLE(hello main.c)

(5) Modify the CMakeLists.txt of the t2 project as follows:

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

4.2 Building

Create the build directory and enter the build directory for external compilation.

mkdir build
cd build
cmake  ..
make

After the build is complete, the generated target file hello is located in the build/bin directory.

4.3 Syntax Explanation

(1) ADD_SUBDIRECTORY instruction:

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

This instruction is used to add a subdirectory that stores source files to the current project and can specify the location of intermediate and target binaries. The EXCLUDE_FROM_ALL parameter means to exclude this directory from the compilation process; for example, the project example may need to enter the example directory for separate builds after the project is built (of course, this can also be solved by defining dependencies).

The above example defines that the src subdirectory is added to the project and specifies the compilation output (including intermediate results) path as the bin directory. If the bin directory is not specified, the compilation results (including intermediate results) will all be stored in the build/src directory (this directory corresponds to the original src directory), and specifying the bin directory essentially renames src to bin during compilation, and all intermediate results and target binaries will be stored in the bin directory.

It is worth mentioning the SUBDIRS instruction, which is used as follows:

SUBDIRS(dir1 dir2...)

However, this instruction is no longer recommended. It can add multiple subdirectories at once, and even if external compilation is used, the subdirectory structure will still be preserved.

If in the above example, ADD_SUBDIRECTORY (src bin) is changed to SUBDIRS(src), then a src directory will appear in the build directory, and the generated target code hello will be stored in the src directory.

4.4 Modifying the Location for Saving Target Binaries

Whether using SUBDIRS or ADD_SUBDIRECTORY (regardless of whether the compilation output directory is specified), we can redefine the EXECUTABLE_OUTPUT_PATH and LIBRARY_OUTPUT_PATH variables using the SET instruction to specify the final location of the target binaries (that is, the final generated hello or the final shared library, excluding intermediate files generated during compilation).

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

Earlier, we mentioned the < projectname >_BINARY_DIR and PROJECT_BINARY_DIR variables, which refer to the current directory where the compilation occurs. If it is an internal build, it is equivalent to PROJECT_SOURCE_DIR, which is the directory where the project code resides. If it is an external build, it refers to the external compilation directory, which is build in this example.

Thus, the above two instructions define that the output path for executable binaries is build/bin and the output path for libraries is build/lib.

It is not mentioned here that the construction of shared libraries and static libraries can be ignored for now.

The question is where to write these two instructions in the CMakeLists.txt of the project or in the CMakeLists.txt of the src directory; the simple principle is: wherever ADD_EXECUTABLE or ADD_LIBRARY is used, if the target storage path needs to be changed, it should be added where the definitions are made.

In this example, it is of course the CMakeLists.txt under src.

4.5 How to Install Compiled Software

There are two types of installations: one is to install directly after compiling from code using make install, and the other is to specify the directory during packaging. Therefore, even the simplest manually written Makefile looks like this:

DESTDIR=
install:
mkdir -p $(DESTDIR)/usr/bin install -m 755 hello $(DESTDIR)/usr/bin

At this point, hello can be installed directly to the /usr/bin directory via make install, or it can be installed in the /tmp/test/usr/bin directory by using make install DESTDIR=/tmp/test. This method is often used during packaging.

A slightly more complex case needs to define PREFIX; generally, autotools projects run such commands:

 ./configure –prefix=/usr 
# or
./configure --prefix=/usr/local
# to specify PREFIX

For example, the above Makefile can be rewritten as:

DESTDIR=
PREFIX=/usr
install:
mkdir -p $(DESTDIR)/$(PREFIX)/bin install -m 755 hello $(DESTDIR)/$(PREFIX)/bin

So how should we install the HelloWorld of CMake?

Here, we need to introduce a new CMake instruction INSTALL and a very useful variable: CMAKE_INSTALL_PREFIX.

The CMAKE_INSTALL_PREFIX variable is similar to the –prefix of the configure script, and the common usage is as follows:

cmake -DCMAKE_INSTALL_PREFIX=/usr .

The INSTALL instruction is used to define installation rules, and the content to be installed can include target binaries, dynamic libraries, static libraries, as well as files, directories, scripts, etc.

The INSTALL instruction includes various installation types, and each needs to be explained separately:

(1) Installation of target files:

INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])

The targets after the TARGETS parameter are the target files defined by ADD_EXECUTABLE or ADD_LIBRARY, which can be executable binaries, dynamic libraries, or static libraries.

The target types correspond to three types: ARCHIVE specifically refers to static libraries, LIBRARY refers to dynamic libraries, and RUNTIME refers to executable target binaries.

The DESTINATION defines the installation path. If the path starts with /, it refers to an absolute path; in this case, CMAKE_INSTALL_PREFIX is effectively invalid. If you want to use CMAKE_INSTALL_PREFIX to define the installation path, it should be written as a relative path, i.e., it should not start with /, so the installation path will be ${CMAKE_INSTALL_PREFIX}/<DESTINATION defined path>.

For example:

INSTALL(TARGETS myrun mylib mystaticlib RUNTIME DESTINATION bin 
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)

The above example will install:

  • The executable binary myrun to ${CMAKE_INSTALL_PREFIX}/bin.

  • The dynamic library libmylib to ${CMAKE_INSTALL_PREFIX}/lib.

  • The static library libmystaticlib to ${CMAKE_INSTALL_PREFIX}/libstatic.

  • It is important to note that there is no need to care about the specific paths generated by TARGETS; just write the names of the TARGETS.

(2) Installation of ordinary files:

INSTALL(FILES files... DESTINATION <dir> [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>]
[RENAME <name>] [OPTIONAL])

This can be used to install general files and can specify access permissions. The file names are relative paths from the location of this instruction. If permissions are not explicitly defined, the installed permissions will be:

OWNER_WRITE, OWNER_READ, GROUP_READ, and WORLD_READ, i.e., 644 permissions.

(3) Installation of non-target executable programs (such as scripts):

INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [RENAME <name>] [OPTIONAL])

This works similarly to the FILES instruction, with the only difference being that the installed permissions are:

OWNER_EXECUTE, GROUP_EXECUTE, and WORLD_EXECUTE, i.e., 755 permissions for directories.

(4) Directory installation:

INSTALL(DIRECTORY dirs... DESTINATION <dir> [FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]

[EXCLUDE] [PERMISSIONS permissions...]] [...])

Here, we mainly introduce the DIRECTORY, PATTERN, and PERMISSIONS parameters.

The DIRECTORY parameter connects to the relative path of the source directory, but it is essential to note that abc and abc/ have significant differences. If the directory name does not end with /, this directory will be installed as abc under the target path. If the directory name ends with /, the contents of this directory will be installed to the target path but not the directory itself.

The PATTERN parameter is used for filtering using regular expressions, and the PERMISSIONS parameter is used to specify the permissions of the files after filtering by PATTERN.

For example:

INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE 
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)

The execution result of this instruction is: the icons directory is installed to <prefix>/share/myproj, and the contents of scripts/ are installed to <prefix>/share/myproj, excluding the directory named CVS, and the files of scripts/* are assigned the permissions OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.

(5) Execution of CMake scripts during installation:

INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
# SCRIPT parameter is used to call CMake script files (i.e., <abc>.cmake files) during installation.
# CODE parameter is used to execute CMake instructions, which must be enclosed in double quotes. For example:
INSTALL(CODE "MESSAGE(\"Sample install message.\"")")

There are several instructions marked as outdated during installation, such as INSTALL_FILES, which are no longer recommended for use.

Next, let’s rewrite our project files to support various file installations, and we will use the CMAKE_INSTALL_PREFIX instruction.

4.6 Modifying CMakeLists.txt to Support Installation

(1) First, add the files that were added. Add the doc directory and files:

cd t2
mkdir doc
vi  doc/hello.txt

Fill in some content randomly and save.

(2) Add the runhello.sh script in the project directory, with the content:

hello

(3) Add COPYRIGHT and README in the project directory:

touch COPYRIGHT
touch README

(4) Now modify the CMakeLists.txt files in each directory.

1) To install COPYRIGHT and README, directly modify the main project file CMakeLists.txt and add the following instruction:

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)

2) To install runhello.sh, directly modify the main project file CMakeLists.txt and add the following instruction:

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

3) To install hello.txt in the doc directory, there are two ways: one is to create a CMakeLists.txt in the doc directory and add the doc directory to the project using ADD_SUBDIRECTORY. The other method is to directly use INSTALL(DIRECTORY in the project directory. The former is relatively simple, and you can complete it based on your interest; we will try the latter, while also demonstrating the installation of DIRECTORY. Since hello.txt needs to be installed to /<prefix>/share/doc/cmake/t2, the entire doc directory cannot be installed directly; here we use the method of installing the contents of the doc directory, which is doc/. Add the following line in the project file:

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)

(5) Now enter the build directory for external compilation, noting to use the CMAKE_INSTALL_PREFIX parameter, here we will install it to /tmp/t2 directory:

cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..

(6) Then run:

make
sudo make install

(7) Enter the /tmp/t2 directory to check the installation results:

$ tree -L 5 /tmp/t2/usr/
/tmp/t2/usr/
├── bin
│   └── runhello.sh
└── share
    └── doc
        └── cmakeProj
            └── t2
                ├── COPYRIGHT
                ├── hello.txt
                └── README

5 directories, 4 files

(8) To install directly to the system, you can use the following command:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

Note that the default definition of CMAKE_INSTALL_PREFIX is /usr/local, and if CMAKE_INSTALL_PREFIX is not defined, it will be installed in this location.

Summary

  1. The entire process of using CMake to build the Hello World program has been introduced, along with three simple instructions: PROJECT/MESSAGE/ADD_EXECUTABLE and the method of calling variables, while also mentioning two implicit variables < projectname >_SOURCE_DIR and < projectname >_BINARY_DIR. The method of calling variables has been demonstrated. From this process, one might feel that it is much more complicated than directly writing a Makefile, and it is even possible to generate the required target file directly using gcc -o hello -c main.c. This is because the project only has a few files; writing a Makefile directly is the simplest. However, if there are many project files, then CMake is simpler than Makefile.

  2. Master how to use multiple directories in a project, various installation instructions, and the CMAKE_INSTALL_PREFIX variable.

Welcome to follow my public account

Daily article push

Mastering CMake Basics: Practical Compilation from Scratch

Long press the image above to recognize the QR code to follow

Leave a Comment