C/C++教程

CMAKE学习笔记

本文主要是介绍CMAKE学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

cmake

what's cmake

CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,CMake 的组态档取名为 CMakeLists.txt。也就是在CMakeLists.txt这个文件中写cmake代码。 一句话:cmake就是将多个cpp、hpp文件组合构建为一个大工程的语言。

basic examples

Hello-cmake

Introduction

file tree
    ├── CMakeLists.txt
    ├── main.cpp	
main.cpp
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
   cout << "Hello CMake!" << endl;
   return 0;
}

CMakeLists.txt
cmake_minimum_required(VERSION 3.5) #设置CMake最小版本
project (hello_cmake) #设置工程名
add_executable(hello_cmake main.cpp) #生成可执行文件

解析

命令作用解析
project (hello_cmake) #设置工程名

CMake构建包含一个项目名称,以上命令会自动生成一些变量,在使用多个项目时引用某些变量会更加容易

比如生成了:PROJECT_NAME这个变量

  • PROJECT_NAME 是变量名
  • ${PROJECT_NAME}是变量值,值为hello_camke
add_executable(hello_cmake main.cpp) #生成可执行文件

add_executable()命令指定某些源文件生成可执行文件,本节例子为main.cpp

add_executable()函数的第一个参数是一个可执行文件名,第二个参数是要编译的源文件列表

生成与工程同名的二进制文件
cmake_minimum_required(VERSION 2.6)
project (hello_cmake)
add_executable(${PROJECT_NAME} main.cpp)

project(hello_cmake)函数执行时会生成一个变量,是PROJECT_NAME\${PROJECT_NAME}表示PROJECT_NAME变量的值为hello_cmake,所以把${PROJECT_NAME}用在add_executable()里可以生成可执行文件名字叫hello_cmake

外部构建与内部构建

变量CMAKE_BINARY_DIR指向 cmake命令的根文件夹,所有二进制文件在这个文件夹里产生。

外部构建——推荐

使用外部构建,可以创建一个位于文件系统上任何位置的构建文件夹。所有临时构建和目标文件都位于此目录中,以保持源代码树的整洁。

以本节例子为例

# 新建build构建文件夹,并运行cmake命令
mkdir build
cd build
cmake ..
make
./hello_cmake
输出——Hello CMake

产生的文件树

file tree
    ├── CMakeLists.txt
    ├── main.cpp
    ├── build
    │   ├── CMakeCache.txt
    │   ├── CMakeFiles
    │   │   ├── 2.8.12.2
    │   │   │   ├── CMakeCCompiler.cmake
    │   │   │   ├── CMakeCXXCompiler.cmake
    │   │   │   ├── CMakeDetermineCompilerABI_C.bin
    │   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   │   │   ├── CMakeSystem.cmake
    │   │   │   ├── CompilerIdC
    │   │   │   │   ├── a.out
    │   │   │   │   └── CMakeCCompilerId.c
    │   │   │   └── CompilerIdCXX
    │   │   │       ├── a.out
    │   │   │       └── CMakeCXXCompilerId.cpp
    │   │   ├── cmake.check_cache
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── CMakeOutput.log
    │   │   ├── CMakeTmp
    │   │   ├── hello_cmake.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   └── progress.make
    │   │   ├── Makefile2
    │   │   ├── Makefile.cmake
    │   │   ├── progress.marks
    │   │   └── TargetDirectories.txt
    │   ├── cmake_install.cmake
    │   └── Makefile

可以看到,build文件夹下生成了许多二进制文件,如果要从头开始重新创建cmake环境,只需删除构建目录build,然后重新运行cmake。

内部构建

内部构建将所有临时文件和源文件生成到一起,没有build文件夹,临时文件会和源代码文件混合在一起,比较混乱

Hello-headers

Introduction

file tree
    ├── CMakeLists.txt
    ├── include
    │   └── Hello.h
    └── src
        ├── Hello.cpp
        └── main.cpp
Hello.h
//声明了Hello类,Hello的方法是print()
#ifndef __HELLO_H__
#define __HELLO_H__

class Hello{
public:
	void print();
};

#endif
Hello.cpp
//实现了Hello::print()
#include <iostream>

#include "Hello.h"
using namespace std;
void Hello::print(){
	cout<<"Hello Headers!"<<endl;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)#最低cmake版本

project (Hello_headers)# project name

set(SOURCES
	src/Hello.cpp
	src/main.cpp
	)#创建一个变量,名字叫SOURCE,包含了所有的cpp文件

add_executable(hello_headers ${SOURCES})#用所有的源文件生成一个可执行文件,这里定义
#等价于add_executable(hello_headers src/Hello.cpp src/main.cpp)

target_include_dirctories(hello_headers
	PRIVATE
		${PROJECT_SOURCE_DIR}/include
		)#设置这个可执行文件hello_headers需要包含的库路径
main.cpp
#include "Hello.h"

int main(int agrc, char *argv[]){
        Hello hi;
        hi.print();
        return 0;
}

concepts

Directory Paths

CMake语法指定了许多变量,可用于帮助您在项目或源代码树中找到有用的目录。 其中一些包括:

variable info
CMAKE_SOURCE_DIR The root source directory
CMAKE_CURRENT_SOURCE_DIR The current source directory if using sub-projects and directories
PROJECT_SOURCE_DIR The source directory of the current cmake project
CMAKE_BINARY_DIR he root binary / build directory. This is the directory where you ran the cmake command
CMAKE_CURRENT_BINARY_DIR The build directory you are currently in
PROJECT_BINARY_DIR The build directory for the current project

想仔细体会一下,可以在CMakeLists中,利用message()命令输出一下这些变量。

另外,这些变量不仅可以在CMakeLists中使用,同样可以在源代码.cpp中使用。

源文件变量

创建一个包含源文件的变量,以便于将其轻松添加到多个命令中,例如add_executable()函数。

# Create a sources variable with a link to all cpp files to compile
set(SOURCES
    src/Hello.cpp
    src/main.cpp
)

add_executable(${PROJECT_NAME} ${SOURCES})

在SOURCES变量中设置特定文件名的另一种方法是使用GLOB命令使用通配符模式匹配来查找文件。file(GLOB SOURCES "src/*.cpp")使用*这个通配符,表示所有.cpp结尾的文件都会包含到这个SOURCES变量。

对于modern CMake,不建议对源文件使用变量。 不建议使用glob

相反,通常直接在add_xxx函数中声明源文件。

这对于glob命令尤其重要,如果添加新的源文件,这些命令可能不会始终为您显示正确的结果。在CMake中指定源文件的最佳方法是明确列出它们

包含目录

当您有其他需要包含的文件夹(文件夹里有头文件)时,可以使用以下命令使编译器知道它们: target_include_directories()。 编译此目标时,这将使用-I标志将这些目录添加到编译器中,例如 -I /目录/路径

target_include_directories(target
    PRIVATE #PRIVATE 标识符指定包含的范围
        ${PROJECT_SOURCE_DIR}/include
)
构建运行详细输出

上一节的例子中,使用make命令,输出仅显示构建状态。若想查看用于调试目的的完整输出,可以在运行make时添加参数VERBOSE=1

mkdir build
cd build/
cmake ..
make VERBOSE=1

没有添加VERBOSE=1

#make
[ 33%] Building CXX object CMakeFiles/hello_headers.dir/src/Hello.cpp.o
[ 66%] Building CXX object CMakeFiles/hello_headers.dir/src/main.cpp.o
[100%] Linking CXX executable hello_headers
[100%] Built target hello_headers

添加VERBOSE=1

#make VERBOSE=1
/usr/bin/cmake -S/home/wenboji/cmaketest/hello_headers -B/home/wenboji/cmaketest/hello_headers/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/hello_headers/build/CMakeFiles /home/wenboji/cmaketest/hello_headers/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/wenboji/cmaketest/hello_headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/depend
make[2]: Entering directory '/home/wenboji/cmaketest/hello_headers/build'
cd /home/wenboji/cmaketest/hello_headers/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/wenboji/cmaketest/hello_headers /home/wenboji/cmaketest/hello_headers /home/wenboji/cmaketest/hello_headers/build /home/wenboji/cmaketest/hello_headers/build /home/wenboji/cmaketest/hello_headers/build/CMakeFiles/hello_headers.dir/DependInfo.cmake --color=
Dependee "/home/wenboji/cmaketest/hello_headers/build/CMakeFiles/hello_headers.dir/DependInfo.cmake" is newer than depender "/home/wenboji/cmaketest/hello_headers/build/CMakeFiles/hello_headers.dir/depend.internal".
Dependee "/home/wenboji/cmaketest/hello_headers/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/wenboji/cmaketest/hello_headers/build/CMakeFiles/hello_headers.dir/depend.internal".
Scanning dependencies of target hello_headers
make[2]: Leaving directory '/home/wenboji/cmaketest/hello_headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/build
make[2]: Entering directory '/home/wenboji/cmaketest/hello_headers/build'
[ 33%] Building CXX object CMakeFiles/hello_headers.dir/src/Hello.cpp.o
/usr/bin/c++   -I/home/wenboji/cmaketest/hello_headers/include   -o CMakeFiles/hello_headers.dir/src/Hello.cpp.o -c /home/wenboji/cmaketest/hello_headers/src/Hello.cpp
[ 66%] Building CXX object CMakeFiles/hello_headers.dir/src/main.cpp.o
/usr/bin/c++   -I/home/wenboji/cmaketest/hello_headers/include   -o CMakeFiles/hello_headers.dir/src/main.cpp.o -c /home/wenboji/cmaketest/hello_headers/src/main.cpp
[100%] Linking CXX executable hello_headers
/usr/bin/cmake -E cmake_link_script CMakeFiles/hello_headers.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/hello_headers.dir/src/Hello.cpp.o CMakeFiles/hello_headers.dir/src/main.cpp.o  -o hello_headers 
make[2]: Leaving directory '/home/wenboji/cmaketest/hello_headers/build'
[100%] Built target hello_headers
make[1]: Leaving directory '/home/wenboji/cmaketest/hello_headers/build'
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/hello_headers/build/CMakeFiles 0

Static-library

Introction

file tree
	├── CMakeLists.txt
    ├── include
    │   └── static
    │       └── Hello.h
    └── src
        ├── Hello.cpp
        └── main.cpp
Hello.h
/*声明了Hello类,Hello的方法是print(),*/

#ifndef __HELLO_H__
#define __HELLO_H__

class Hello
{
public:
    void print();
};

#endif
Hello.cpp
/*实现了Hello::print()*/
#include <iostream>

#include "static/Hello.h"
using namespace std;
void Hello::print()
{
    cout << "Hello Static Library!" << endl;
}
main.cpp
#include "static/Hello.h"

int main(int argc, char *argv[])
{
    Hello hi;
    hi.print();
    return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
#库的源文件Hello.cpp生成静态库hello_library
add_library(hello_library STATIC 
    src/Hello.cpp
)
target_include_directories(hello_library
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)
# target_include_directories为一个目标(可能是一个库library也可能是可执行文件)添加头文件路径。
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
#指定用哪个源文件生成可执行文件
add_executable(hello_binary 
    src/main.cpp
)
#链接可执行文件和静态库
target_link_libraries( hello_binary
    PRIVATE 
        hello_library
)
#链接库和包含头文件都有关于scope这三个关键字的用法。

CMake解析

创建静态库

add_library()函数用于从某些源文件创建一个库,默认生成在构建文件夹。 写法如下:

add_library(hello_library STATIC
    src/Hello.cpp
)

在add_library调用中包含了源文件,用于创建名称为libhello_library.a的静态库

建议:将源文件直接传递给add_library调用,而不是先把Hello.cpp赋给一个变量

添加头文件所在的目录

使用target_include_directories()添加了一个目录,这个目录是库所包含的头文件的目录,并设置库属性为PUBLIC。

target_include_directories(hello_library
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

使用这个函数后,这个目录会在以下情况被调用:

  • 编译这个库的时候

    因为这个库hello_library由Hello.cpp生成,Hello.cpp中函数的定义在Hello.h中,Hello.h在这个include目录下,所以显然编译这个库的时候,这个目录会用到

  • 编译链接到这个库hello_library的任何其他目标(库或者可执行文件)

private public interface 的范围详解
  • PRIVATE - 目录被添加到目标(库)的包含路径中。
  • INTERFACE - 目录没有被添加到目标(库)的包含路径中,而是链接了这个库的其他目标(库或者可执行程序)包含路径中
  • PUBLIC - 目录既被添加到目标(库)的包含路径中,同时添加到了链接了这个库的其他目标(库或者可执行程序)的包含路径中

也就是说,根据库是否包含这个路径,以及调用了这个库的其他目标是否包含这个路径,可以分为三种scope。

建议:

对于公共的头文件,最好在include文件夹下建立子目录。

传递给函数target_include_directories()的目录,应该是所有包含目录的根目录,然后在这个根目录下建立不同的文件夹,分别写头文件。

这样使用的时候,不需要写${PROJECT_SOURCE_DIR}/include,而是直接选择对应的文件夹里对应头文件。下面是例子:#include "static/Hello.h"而不是#include "Hello.h"使用此方法意味着在项目中使用多个库时,头文件名冲突的可能性较小。

链接库

创建将使用这个库的可执行文件时,必须告知编译器需要用到这个库。 可以使用target_link_library()函数完成此操作。add_executable()连接源文件,target_link_libraries()连接库文件。

add_executable(hello_binary
    src/main.cpp
)

target_link_libraries( hello_binary
    PRIVATE
        hello_library
)

这告诉CMake在链接期间将hello_library链接到hello_binary可执行文件。 同时,这个被链接的库如果有INTERFACE或者PUBLIC属性的包含目录,那么,这个包含目录也会被传递( propagate )给这个可执行文件。

官方解释:https://cmake.org/cmake/help/v3.0/command/target_include_directories.html

对于

target_link_libraries( hello_binary    
				PRIVATE											        					hello_library )

这个命令中的scope关键字,private,public以及interface可以举例理解:

public是说,你的这个工程如果被link了,那你的target_link_libraries指定的lib也会被link

private是说,你link的这些libs不会被暴露出去。

比如你的工程B是个dll,public连接了C, D 这个时候你的A.exe要链接B,那么它也会链接C和D 如果B是private链接了C, D 那么A链B的时候,不会链C和D

那么,A.exe链接B的时候,其实也有public和private的选项,但是因为没有其他东西链接A,所以不起作用。 这个主要是针对其它工程链自己的设置

对于hello_binary,它不是库,所以不会被链接。直接private自己用这个库就行。

构建运行

#mkdir build
#cd 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
-- Configuring done
-- Generating done
-- Build files have been written to: /home/wenboji/cmaketest/static_library/build
#make
Scanning dependencies of target hello_library
[ 25%] Building CXX object CMakeFiles/hello_library.dir/src/Hello.cpp.o
[ 50%] Linking CXX static library libhello_library.a
[ 50%] Built target hello_library
Scanning dependencies of target hello_binary
[ 75%] Building CXX object CMakeFiles/hello_binary.dir/src/main.cpp.o
[100%] Linking CXX executable hello_binary
[100%] Built target hello_binary
#ls
CMakeCache.txt  cmake_install.cmake  libhello_library.a
CMakeFiles      hello_binary         Makefile
#./hello_binary
Hello static library

Shared-library

Introduction

file tree
    ├── CMakeLists.txt
    ├── include
    │   └── shared
    │       └── Hello.h
    └── src
        ├── Hello.cpp
        └── main.cpp
Hello.h
/*声明了Hello类,Hello的方法是print(),*/
#ifndef __HELLO_H__
#define __HELLO_H__

class Hello
{
public:
    void print();
};

#endif
Hello.cpp
/*实现了Hello::print()*/
#include <iostream>

#include "shared/Hello.h"

void Hello::print()
{
    std::cout << "Hello Shared Library!" << std::endl;
}
main.cpp
#include "shared/Hello.h"

int main(int argc, char *argv[])
{
    Hello hi;
    hi.print();
    return 0;
}  
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
#根据Hello.cpp生成动态库
add_library(hello_library SHARED 
    src/Hello.cpp
)
#给动态库hello_library起一个别的名字hello::library
add_library(hello::library ALIAS hello_library)
#为这个库目标,添加头文件路径,PUBLIC表示包含了这个库的目标也会包含这个路径
target_include_directories(hello_library
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
#根据main.cpp生成可执行文件
add_executable(hello_binary
    src/main.cpp
)
#链接库和可执行文件,使用的是这个库的别名。PRIVATE 表示
target_link_libraries( hello_binary
    PRIVATE 
        hello::library
)

CMake解析

创建动态库

add_library()函数用于从某些源文件创建一个动态库,默认生成在构建文件夹

add_library(hello_library
			SHARED
				src/Hello.cpp
				)

在add_library调用中包含了源文件,用于创建名称为libhello_library.so的动态库

注:静态库后缀为libhello_library.a

创建别名目标

别名目标是在只读上下文中可以代替真实目标名称的替代名称

add_library(hello::library 
				ALIAS 
					hello_library
					)

如下所示,当将目标链接到其他目标时,使用别名可以引用目标

链接共享库和链接静态库相同,创建可执行文件时,请使用target_link_library()函数指向库

add_executable(hello_binary
    src/main.cpp
)

target_link_libraries(hello_binary
    PRIVATE
        hello::library
)

这告诉CMake使用别名目标名称将hello_library链接到hello_binary可执行文件

构建运行

# 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/wenboji/cmaketest/shared_library/build
#make
Scanning dependencies of target hello_library
[ 25%] Building CXX object CMakeFiles/hello_library.dir/src/Hello.cpp.o
[ 50%] Linking CXX shared library libhello_library.so
[ 50%] Built target hello_library
Scanning dependencies of target hello_binary
[ 75%] Building CXX object CMakeFiles/hello_binary.dir/src/main.cpp.o
[100%] Linking CXX executable hello_binary
[100%] Built target hello_binary
#./hello_binary
Hello shared library!

Installing

Build-type

Introduction

file tree
    ├── CMakeLists.txt
    ├── main.cpp
main.cpp
#include <iostream>

int main(int argc, char *argv[])
{
   std::cout << "Hello Build Type!" << std::endl;
   return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
#如果没有指定则设置默认编译方式
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  #在命令行中输出message里的信息
  message("Setting build type to 'RelWithDebInfo' as none was specified.")
  #不管CACHE里有没有设置过CMAKE_BUILD_TYPE这个变量,都强制赋值这个值为RelWithDebInfo
  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)

  # 当使用cmake-gui的时候,设置构建级别的四个可选项
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
    "MinSizeRel" "RelWithDebInfo")
endif()


project (build_type)
add_executable(cmake_examples_build_type main.cpp)

#命令的具体解释在二  CMake解析中,这里的注释只说明注释后每一句的作用

CMake解析

构建级别

CMake具有许多内置的构建配置,可用于编译工程。 这些配置指定了代码优化的级别,以及调试信息是否包含在二进制文件中。

这些优化级别,主要有:

  • Release —— 不可以打断点调试,程序开发完成后发行使用的版本,占的体积小。 它对代码做了优化,因此速度会非常快,

    在编译器中使用命令: -O3 -DNDEBUG 可选择此版本。

  • Debug ——调试的版本,体积大。

    在编译器中使用命令: -g 可选择此版本。

  • MinSizeRel—— 最小体积版本

    在编译器中使用命令:-Os -DNDEBUG可选择此版本。

  • RelWithDebInfo—— 既优化又能调试。

    在编译器中使用命令:-O2 -g -DNDEBUG可选择此版本。

设置级别的方式
CMake图形界面

cmake-gui

CMake命令行

在命令行运行CMake的时候, 使用cmake命令行的-D选项配置编译类型

cmake .. -DCMAKE_BUILD_TYPE=Release
CMake中设置默认的构建级别

CMake提供的默认构建类型是不进行优化的构建级别。 对于某些项目,需要自己设置默认的构建类型,以便不必记住进行设置。

  • set()命令

    该命令可以为普通变量、CACHE变量、环境变量赋值。

    set()可以设置零个或多个参数。多个参数将以分号分隔的列表形式加入,以形成要设置的实际变量值。零参数将导致未设置普通变量。见unset() 命令显式取消设置变量。

    所以此处学习SET命令需要分为设置普通变量,CACHE变量以及环境变量三种类别来学习

    • 正常变量

      • set(<variable> <value>... [PARENT_SCOPE])
        

        设置的变量值 作用域属于整个 CMakeLists.txt 文件。(一个工程可能有多个CMakeLists.txt)

        当这个语句中加入PARENT_SCOPE后,表示要设置的变量是父目录中的CMakeLists.txt设置的变量。

        比如有如下目录树:

        ├── CMakeLists.txt
        └── src
            └── CMakeLists.txt
        

        并且在 顶层的CMakeLists.txt中包含了src目录:add_subdirectory(src)

        那么,顶层的CMakeLists.txt就是父目录,

        如果父目录中有变量Bang,在子目录中可以直接使用(比如用message输出Bang,值是父目录中设置的值)并且利用set()修改该变量Bang的值,但是如果希望在出去该子CMakeLists.txt对该变量做出的修改能够得到保留,那么就需要在set()命令中加入Parent scope这个变量。当然,如果父目录中本身没有这个变量,子目录中仍然使用了parent scope,那么出了这个作用域后,该变量仍然不会存在。

    • CACHE变量

      • set(<variable> <value>... CACHE <type> <docstring> [FORCE])
        
        • 首先什么是CACHE变量,就是在运行cmake的时候,变量的值可能会被缓存到一份文件里即build命令下的CMakeCache.txt,当你重新运行cmake的时候,那些变量会默认使用这个缓存里的值。这个变量是全局变量,整个CMake工程都可以使用该变量。
        • 在这个文件里,只要运行cmake ..命令,自动会出现一些值,比如 CMAKE_INSTALL_PREFIX ,如果设置 set(CMAKE_INSTALL_PREFIX "/usr") ,虽然CACHE缓存文件里还有这个CMAKE_INSTALL_PREFIX 变量,但是因为我们显示得设置了一个名为CMAKE_INSTALL_PREFIX 的正常变量,所以之后使用CMAKE_INSTALL_PREFIX ,值是我们设置的正常变量的值。
        • 如果加上CACHE关键字,则设置的这个变量会被写入缓存文件中(但如果本身缓存文件中有这个变量,则不会覆盖缓存中的变量)。只有加上FORCE关键字,这个被写入文件的值会覆盖之前文件中存在的同名变量。
        • 加上CACHE关键字,和是必需的。

        被 CMake GUI 用来选择一个窗口,让用户设置值。可以有5种选项。其中一个是STRING ,弹出提示消息

        • 为BOOL,则为布尔ON/OFF值。 cmake-gui(1) 提供一个复选框。
        • 为FILEPATH,则为磁盘上文件的路径。 cmake-gui(1) 提供一个文件对话框。
        • 为 PATH ,则为磁盘上目录的路径。 cmake-gui(1) 提供一个文件对话框。
        • 为 STRING ,则为一行文字。 cmake-gui(1) 提供文本字段或下拉选择(如果 STRINGS 设置了缓存条目属性。)
        • 为 INTERNAL ,则为一行文字。 cmake-gui(1)不显示内部条目。它们可用于在运行之间持久存储变量。使用此类型暗含FORCE

        比如

        set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
        

        这句话,就是强制在缓存文件中覆盖CMAKE_BUILD_TYPE这个变量,将这个变量设置为RelWithDebInfo。而STRING "Choose the type of build."参数在使用cmake-gui的时候起作用,在界面上会出现一个下拉框供给用户选择来设置CMAKE_BUILD_TYPE变量。里的一行文字作为提示。

        但是这个下拉框里的内容,需要使用随后的set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")这个命令来设置。也就是所谓的设置string缓存条目属性。

        cmake-gui

        官方文档: https://cmake.org/cmake/help/latest/command/set.html

    • 环境变量

      • set(ENV{<variable>} [<value>])
        

        设置一个 Environment Variable 到给定值。随后的调用$ENV{<varible>}将返回此新值。

        此命令仅影响当前的CMake进程,不影响调用CMake的进程,也不影响整个系统环境,也不影响后续构建或测试过程的环境。

        如果在空字符串之后ENV{}或如果没有参数,则此命令将清除环境变量的任何现有值。之后的参数将被忽略。如果发现其他参数,则会发出警告。

构建运行

#cmake ..
setting build type to 'ReiWithDebInfo' as none was specified
-- 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/wenboji/cmaketest/build-type/build
#make VERBOSE=1
/usr/bin/cmake -S/home/wenboji/cmaketest/build-type -B/home/wenboji/cmaketest/build-type/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/build-type/build/CMakeFiles /home/wenboji/cmaketest/build-type/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/wenboji/cmaketest/build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/depend
make[2]: Entering directory '/home/wenboji/cmaketest/build-type/build'
cd /home/wenboji/cmaketest/build-type/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/wenboji/cmaketest/build-type /home/wenboji/cmaketest/build-type /home/wenboji/cmaketest/build-type/build /home/wenboji/cmaketest/build-type/build /home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake --color=
Dependee "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake" is newer than depender "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Dependee "/home/wenboji/cmaketest/build-type/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Scanning dependencies of target cmake_examples_build_type
make[2]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/build
make[2]: Entering directory '/home/wenboji/cmaketest/build-type/build'
[ 50%] Building CXX object CMakeFiles/cmake_examples_build_type.dir/main.cpp.o
/usr/bin/c++     -o CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -c /home/wenboji/cmaketest/build-type/main.cpp
[100%] Linking CXX executable cmake_examples_build_type
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmake_examples_build_type.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/cmake_examples_build_type.dir/main.cpp.o  -o cmake_examples_build_type 
make[2]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
[100%] Built target cmake_examples_build_type
make[1]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/build-type/build/CMakeFiles 0


#指定构建级别
#cmake .. -DCMAKE_BUILD_TYPE=Release
-- 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/wenboji/cmaketest/build-type/build
#make VERBOSE=1
/usr/bin/cmake -S/home/wenboji/cmaketest/build-type -B/home/wenboji/cmaketest/build-type/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/build-type/build/CMakeFiles /home/wenboji/cmaketest/build-type/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/wenboji/cmaketest/build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/depend
make[2]: Entering directory '/home/wenboji/cmaketest/build-type/build'
cd /home/wenboji/cmaketest/build-type/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/wenboji/cmaketest/build-type /home/wenboji/cmaketest/build-type /home/wenboji/cmaketest/build-type/build /home/wenboji/cmaketest/build-type/build /home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake --color=
Dependee "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake" is newer than depender "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Dependee "/home/wenboji/cmaketest/build-type/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/wenboji/cmaketest/build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Scanning dependencies of target cmake_examples_build_type
make[2]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/build
make[2]: Entering directory '/home/wenboji/cmaketest/build-type/build'
[ 50%] Building CXX object CMakeFiles/cmake_examples_build_type.dir/main.cpp.o
/usr/bin/c++    -O3 -DNDEBUG   -o CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -c /home/wenboji/cmaketest/build-type/main.cpp
[100%] Linking CXX executable cmake_examples_build_type
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmake_examples_build_type.dir/link.txt --verbose=1
/usr/bin/c++  -O3 -DNDEBUG   CMakeFiles/cmake_examples_build_type.dir/main.cpp.o  -o cmake_examples_build_type 
make[2]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
[100%] Built target cmake_examples_build_type
make[1]: Leaving directory '/home/wenboji/cmaketest/build-type/build'
/usr/bin/cmake -E cmake_progress_start /home/wenboji/cmaketest/build-type/build/CMakeFiles 0

Complie-flags

Introduction

file tree
├── CMakeLists.txt
├── main.cpp
main.cpp
#include <iostream>

int main(int argc, char *argv[])
{
   std::cout << "Hello Compile Flags!" << std::endl;

   // only print if compile flag set
#ifdef EX2
  std::cout << "Hello Compile Flag EX2!" << std::endl;
#endif

#ifdef EX3
  std::cout << "Hello Compile Flag EX3!" << std::endl;
#endif

   return 0;
}

CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
#强制设置默认C++编译标志变量为缓存变量,如CMake(五) build type所说,该缓存变量被定义在文件中,相当于全局变量,源文件中也可以使用这个变量
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)

project (compile_flags)

add_executable(cmake_examples_compile_flags main.cpp)
#为可执行文件添加私有编译定义
target_compile_definitions(cmake_examples_compile_flags 
    PRIVATE EX3
)
#命令的具体解释在二  CMake解析中,这里的注释只说明注释后每一句的作用

CMake解析

设置每个目标编译标志

在现代CMake中设置C ++标志的推荐方法是专门针对某个目标(target)设置标志,可以通过target_compile_definitions()函数设置某个目标的编译标志。

target_compile_definitions(cmake_examples_compile_flags
    PRIVATE EX3
)

如果这个目标是一个库(cmake_examples_compile_flags),编译器在编译目标时添加定义-DEX3 ,并且选择了范围PUBLICINTERFACE,该定义-DEX3也将包含在链接此目标(cmake_examples_compile_flags)的所有可执行文件中。 注意,本语句使用了PRIVATE,所以编译选项不会传递。

对于编译器选项,还可以使用target_compile_options()函数。

target_compile_definitions(<target>
		<INTERFACE|PUBLIC|PRIVATE> [items1...]
		[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

是给 target 添加编译选项, target 指的是由 add_executable()产生的可执行文件或 add_library()添加进来的库。<INTERFACE|PUBLIC|PRIVATE>指的是[items...] 选项可以传播的范围, PUBLIC and INTERFACE会传播 <target>的 INTERFACE_COMPILE_DEFINITIONS 属性, PRIVATE and PUBLIC 会传播 target 的 COMPILE_DEFINITIONS 属性。

设置默认编译标志

默认的CMAKE_CXX_FLAGS为空或包含适用于构建类型的标志。 要设置其他默认编译标志,如下使用:

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)

强制设置默认C++编译标志变量为缓存变量,如CMake(五) build type所说,该缓存变量被定义在文件中,相当于全局变量,源文件中也可以使用这个变量。这个变量原本包含的参数仍然存在,只是添加了EX2。

CACHE STRING "Set C++ Compiler Flags" FORCE命令是为了强制将CMAKE_CXX_FLAGS变量 放到CMakeCache.txt文件中

"${CMAKE_CXX_FLAGS} -DEX2"这个字符串可以保留原有的CMAKE_CXX_FLAGS中的参数,额外添加了一个EX2参数。注意写法:空格,并且参数前加了-D

类似设置CMAKE_CXX_FLAGS,还可以设置其他选项:

  • 设置C编译标志: CMAKE_C_FLAGS
  • 设置链接标志:CMAKE_LINKER_FLAGS.
设置CMake标志

与构建类型类似,可以使用以下方法设置全局C 编译器标志。

  1. 利用ccmake或者gui

  2. 在cmake命令行中

     cmake .. -DCMAKE_CXX_FLAGS="-DEX3"
    
区别

设置CMAKE_C_FLAGS和CMAKE_CXX_FLAGS将为该目录或所有包含的子目录中的所有目标全局设置一个编译器标志。 现在不建议使用该方法,首选使用target_compile_definitions函数。因此建议为每个目标设置编译标志

构建运行

#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/wenboji/cmaketest/compile_flags/build
#make
Scanning dependencies of target cmake_examples_compile_flags
[ 50%] Building CXX object CMakeFiles/cmake_examples_compile_flags.dir/main.cpp.o
[100%] Linking CXX executable cmake_examples_compile_flags
[100%] Built target cmake_examples_compile_flags

Third-party-library

Compiling-with-clang

Building-with-ninja

Imported-targets

Cpp-standard

这篇关于CMAKE学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!