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_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()的作用异曲同工