cmake 学习记录

背景:一直他妈的不会cmake,然后很多时候建立新环境运行不起来,很烦

终于有一小段时间,算是闲里偷忙来学一下

就是根据官方的文档来学的,它不是有很多的练习嘛,挨个做了一遍

各个地址:

cmake的总链接:https://cmake.org/cmake/help/latest/

练习题的内容:https://cmake.org/cmake/help/latest/guide/tutorial/index.html

开始吧:

1. 首先熟悉最基本的几个命令:

cmake <dir> 与 CMakeLists.txt配套,通过 cmake 命令将 CMakeLinks.txt翻译为能被make命令使用的makefile.txt文件

因此,cmake在执行命令的时候,需要 <dir> 为 包含CMakeLists.txt 的路径

make 与 makefile.txt文件配套,是用于编译链接各个.c .o .h 文件的

因此,make在执行命令的时候,需要与makefile.txt 在同一路径中

补充:

(1)cmake 也有通过makefile.txt编译链接的能力,执行 cmake --build <dir>  命令就可以,<dir>就是makefile.txt的路径

(2)cmake -S . -B ./build  指令
    -S 就是在指定 CMakeLists.txt文件和要build的源代码文件在哪里 
        【我猜测】:就是add_executable(Tutorial tutorial.cxx)中的 tutorial.cxx 文件的位置
    -B 的含义是指定将 buildtree 的路径放在哪里, 
        build tree 含义是:包括 CMakeCache.txt文件的位值,若 不使用 -B 特殊指定那么就会生成在当前pwd的目录下
        若 -B 后的路径不存在,那么会先新建路经然后再向该路径中存放对应得内容
        (若 build目录存在就直接使用build中的CMakeCache.txt文件链,因此,为了避免出错,建议直接 rm -rf build)

若在指令中不显示地指定 -S -B ,直接写为  `cmake ..` 
                           那么,这个命令的含义就是: source tree 的路径是 `..` 
                           因为,当 -S -B 全省略时,指令退化为 ‵cmake src‵

接着,可以使用 `make`命令 或 `cmake --build <dir>`命令 to build the project      
    若使用 `cmake --build <dir>` 命令,那么,‵--build‵ 命令必须放在参数的最开始


(3)可以并行编译:`cmake --build <dir> -j [<jobs>]`        
                    例如: `cmake --build . -j 4 `

当我看不懂一个东西的时候,我应该继续,还是应该速速看别人的博客来回理解呢?

我打算采取的策略是,看别人的博客吧

还有一个疑问是:一些关键字的含义都他妈的是看明白的阿,以及,别说是看明白了,我他嘛有时候都不知道这些关键字的存在,不是我靠,这些关键字都从哪里列出来的阿????

 引入指定的头文件:

使用 target_include_directories(Tutorial PUBLIC "<dir>") 函数 引入要链接的 .h文件

使用方法:

先add_executable(Tutorial tutorial.cxx)

再 target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

注意 :

target_include_directories(<目标可执行文件名字> <PUBLIC | INTERFACE | PRIVATE> "<dir>")

这个用法:第三个参数可以有多种写法:

目前的感受是:第三个参数的内容应该是 包含.h文件所在的路径

由于我们在build文件夹下执行的各个命令,因此,生成的二进制文件的位置在不使用set(PROJECT_BINARY_DIR "<dir>") 指明的时候,就会存放在当前文件夹位置处

而可行的2个变量 ${PROJECT_BINARY_DIR}" ${CMAKE_CURRENT_BINARY_DIR}的含义分别是:该文件二进制内容所在路径 或 当前执行CMAKE后生成的二进制文件的位置,因此都是可行的

担心的话,可以使用 message("  ${XXXXXXXXXX}  ") 输出一下

存在几个问题:

1.在add_executable()函数上不能直接用嘛:

目前看来是可以的:下述代码被允许,看来可以用相对路径来制定资源

【目前的感受是,所有的 文件都可以改写为】

add_executable(Tutorial tutorial.cxx "../testPath/lalal.h" )

2. 和 add_dirctionary区别?

PUBLIC PRIVATE INTERFACE 之间的区别:cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE - 知乎 

Step 1 的 3 个实验:

我汇总在一起说了:

build一个最简单的项目需要 源码,头文件,链接库 , 指导CMake进行编译链接的CMakeLinks.txt等等

先说下我们的项目路径构成:(左图是build之前,右图是build之后)

其中,我们把头文件放在lib文件夹中,把源文件放到src文件夹中,新建build目录用于存放cmake make 指令生成的内容

注:在tutorial.cxx中用到了 lalal.h 这个头文件中涉及的变量

    

lalal.h内容  

#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 56

tutorial.cxx

// A simple program that computes the square root of a number
#include <cmath>
#include <iostream>
#include <string>
#include <lalal.h>


int main(int argc, char* argv[])
{
  if (argc < 2) {
    std::cout << "Usage: " << argv[0] << " number" << std::endl;
    // Create a print statement using Tutorial_VERSION_MAJOR
    // and Tutorial_VERSION_MINOR
    std::cout << argv[0] << "Version " << Tutorial_VERSION_MAJOR 
              << Tutorial_VERSION_MINOR << std::endl;
    return 1;
  }

  // convert input to double
  const double inputValue = atof(argv[1]);  // 这个语句使用了C++11语法,因此编译的
                                            // 时候需要指明

  // calculate square root
  const double outputValue = sqrt(inputValue);
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;
  return 0;
}

CMakeLists.txt 

# 对于cmake版本的指定必须放在第一行
cmake_minimum_required(VERSION 3.10)

# 通过project()指定该项目的名称和版本号 
#    而项目的名字具体是啥其实目前来看没啥重要的,就只是一个名字
#    "VERSION" 是个关键字,后接该project的版本号
project(Tutorial VERSION 1.56)

# 设置一下编译链接该项目的C++语法标准:
#    注意,设置编译要求需要在 add_executable()函数之前调用
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)


# 生成可执行文件 :add_executable(<target> <source 1> [source 2] [source 3] .. )
#    这里注意,既然是source文件(即 源文件),其实就是 以 .c .cpp .cxx 为结尾的文件
#    以.h 为结尾的文件叫头文件(header file)
add_executable(Tutorial ./src/tutorial.cxx)


# 由于我们使用了头文件,因此,需要手动指定 要链接的头文件的路径,
#    基于的路径就是CMakeLists.txt的位置
#    [注意] target_include_directories() 函数应该写在add_executable()函数之后
target_include_directories(Tutorial PUBLIC "./lib") 

 有几个容易迷惑的地方,我重点说一下:

(1)路径到底用不用加 " " 括起来 ?

[回答]:用 " " 括起来 或者 不括起来 在语法上都对,但是为了我的身心健康和强迫症,我建议自己括起来。

(2)在写相对路径时,表示当前位置的 ` . ` 到底需要不需要?

 [回答]:我的建议时应写尽写。因为我在调用 target_include_directories() 函数时,由于第三个参数 " ./lib " 写成了 " /lib " 就导致cmake找不到头文件lalal.h报错了,,,所以写上吧

(3)在CMakeLists.txt 中写的这些指令会涉及到一些文件的位置,要指定这些文件的路径,需要根据一个相对路径来指定。例如,要告知cmake 链接所需的 lalal.h 头文件 在哪里,那么,我需要给出 lalal.h 相对于当前位置的路径。那么,当前位置是谁的位置呢?

[回答]:当前位置是 CMakeLists.txt 文件所处的文件夹的位置

Step 1 的这三个练习有一些补充:

(1)指定编译时采用的C++标准时,可以使用上文 set  CMAKE_CXX_STANDARD 和CMAKE_CXX_STANDARD_REQUIRED 两个变量指定值的方法实现。

也可以采用下述方法,且这条语句放在 add_executable()函数后也是可以的

target_compile_features(Tutorial PUBLIC cxx_std_11)    

但是这种指定编译器编译语法规则在文档中不是很建议,那就算了,用 set 变量的方法指定吧

【没写完】!!                                                                                      

可以通过在project()函数下方通过 下述语句

set(PROJECT_BINARY_DIR "../testPath")     # 对于路径的设置必须要在project()命令之后

的方式指定通过CMake生成的 二进制文件 的目录

(cmake 通过 configure_file() 生成 .h 文件就会被存放到 PROJECT_BINARY_DIR 这个位置)

且 若 PROJECT_BINARY_DIR 不特意指定,就会是当前执行命令的位置(应该是?存疑问)

有个关于 target_include_dircetionaries() 的解释很好你看看:

cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE - 知乎

=================================================

下述的代码应该时暂存代码

=============

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.56)
 
message("PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
message("PROJECT_SOURCE_DIR,: ${PROJECT_SOURCE_DIR}")

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 首先 .h文件 是可以写在这里的,也是可以不写的!!  即:add_executable(Tutorial tutorial.cxx )  也可以完全正常的执行
# 其次 .h头文件可以被"" 包裹,也可以不被包裹,建议包裹,写作 "XXXX.h"
# 最后 .h文件所在的路径不仅要在 add_exectuable()中指定,
#                   还要在   target_include_directories()中指定,【且两个路径要保证一致】
add_executable(Tutorial tutorial.cxx lalal.h )  
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}/..")

# 对于cmake版本的指定必须放在第一行
cmake_minimum_required(VERSION 3.10)

# 通过project()指定该项目的名称和版本号 
#    而项目的名字具体是啥其实目前来看没啥重要的,就只是一个名字
#    “VERSION" 是个关键字,后接该project的版本号
project(Tutorial VERSION 1.56)
set(PROJECT_BINARY_DIR "../testPath")  
# 对于路径的设置必须要在project()命令之后
# set(PROJECT_BINARY_DIR "../testPath")     
message("PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
message("PROJECT_SOURCE_DIR,: ${PROJECT_SOURCE_DIR}")

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# add_executable(Tutorial tutorial.cxx "../testPath/lalal.h")   #【配合target_include_directories是可以的】
add_executable(Tutorial tutorial.cxx "lalal.h" )#【配合target_include_directories是可以的】
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

# 对于cmake版本的指定必须放在第一行
cmake_minimum_required(VERSION 3.10)

# 通过project()指定该项目的名称和版本号 
#    而项目的名字具体是啥其实目前来看没啥重要的,就只是一个名字
#    “VERSION" 是个关键字,后接该project的版本号
project(Tutorial VERSION 1.56)

set(PROJECT_BINARY_DIR "../testPath")     # 对于路径的设置必须要在project()命令之后
message("set the dir ${PROJECT_BINARY_DIR}")

# output the message of something , 【BUT I DO *NOT* know the synax is right or not】
# message("< ${CMAKE_PROJECT_NAME} / ${PROJECT_NAME}")


# configure_file(TutorialConfig.h.in TutorialConfig.h)
configure_file(TutorialConfig.h.in "../../testPath/lalal.h")


set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# tell CMake to create an executable using specified source code files
# [ASK] how can i create executable file by several source files  
add_executable(Tutorial tutorial.cxx "../testPath/lalal.h" )
# target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
# target_include_directories(Tutorial PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
# 尝试表明:第三个参数的内容应该是 包含.h文件所在的路径  

# 由于我们在build文件夹下执行的各个命令,
# 因此,生成的二进制文件的位置在不使用set(PROJECT_BINARY_DIR "<dir>") 指明的时候,
# 就会存放在当前文件夹位置处

# 而可行的2个变量 ${PROJECT_BINARY_DIR}" ${CMAKE_CURRENT_BINARY_DIR}的含义分别是:
# 该文件二进制内容
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
# target_include_directories(Tutorial PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")  

message("fk---${Project_BINARY_DIR}=====${CMAKE_CURRENT_BINARY_DIR}-------")



# target_compile_features(Tutorial PUBLIC cxx_std_11)    //和set()的作用异曲同工