写在前面
本站已经有很多大佬全面详细地介绍过关于Makefile、cmake的内容,在此就不重复他们的工作,本文主要是为大家提供一个快速上手的方案(基本上可以应对常见的项目构建,尤其是自己做的项目)
引子
相信大部分使用过手动编译的同学都对以下编译命令十分熟悉:
g++ test.cpp -o test
执行完上述命令后,即可得到一个名为test的可执行文件(其中-o test
不是必须的,如果不加这个选项及参数,系统会生成默认的可执行文件a.out
),再执行完以下命令即可运行该可执行文件
./test
然鹅,对于大型工程项目(或者说一个有标准化结构的项目),一般是会将接口相关内容写在头文件,而实现部分则写入源文件,就像下面这样(其中#pragma once
是保证不会重复对头文件进行解析):
//test.h
#pragma once
void fun1();
//test.cpp
#include<test.h>
void fun1(){
std::cout<<"hello world"<<std::endl;
}
那么倘若再执行如下命令
g++ test.cpp -o test
那么迎接你的结果则是:
test.cpp:2:9: fatal error: test.h: 没有那个文件或目录
2 | #include<test.h>
| ^~~~~~~~
根据报错结果,我们可以知道是编译器没有找到对应的头文件,从而导致了编译错误,那么针对这个问题有没有解决办法呢?有!而且很简单!具体命令如下:
g++ test.cpp -o test -I ./
实际上-I
是指定头文件的搜索目录,后面的路径则是当前目录,指定了头文件的搜索目录后自然可以正确地进行编译。那么有的同学要问了,你说了这么多和项目构建有什么关系呢?如果是为了解决这个问题就用工程化的构建方式是不是大炮打蚊子呢?当然,前面的内容只是作为一个引子,是一个问题由简单到复杂的过程,工程化构建不仅能解决以上问题,还能够解决其他方面的问题,比如如果有1000个源文件,他们分别在不同目录,有2000个头文件,他们也在不同目录,那么传统的编译方式并不能解决这样繁杂的问题,因此我们正式介绍一下今天的主角
我常用的构建方法
首先声明,本文并不是讲Makefile和cmake的,只是介绍一下构建方法,保证各位同学在不用深入了解详细内容的情况下能够学会“用”,如果有兴趣可以再查阅本站其他大佬写的关于cmake的文章
第一步 构建项目目录
.
├── bin
├── build
├── include
└── src
bin
:用于存放二进制文件,对于一般的项目是存放可执行文件的build
:用于存放cmake指令生成的系列文件,未来在此目录下执行cmake命令,这样可以将生成的系列文件与其他文件隔离开来,视觉上美观,工程上保证目录文件的纯净性(避免文件污染)include
:用于存放头文件(一般是.h文件)src
:用于存放源文件(一般是.c、.cpp文件)
第二步 编写CMakeLists.txt文件(重中之重)
首先最好在CMakeLists的第一行进行cmake最低版本声明,因为这样可以保证cmake时进行版本检查,若出现版本导致的不兼容问题能够及时定位问题,如果没有这一行则不进行版本检查,可能会出现未定义问题
cmake_minimum_required(VERSION 2.8)
之后,我们要为整个项目起一个名字
project(test)
那有的同学可能要问了,为什么要给项目起一个名字呢?如果不起行不行呢?这里先卖个关子,等下后面会给大家解释这里给项目起名字的作用
之后添加源文件和目录文件的搜索目录
include_directories(include)
aux_source_directory(src SRC_LIST)
include_directories(include)
:这一行语句的功能是添加头文件的搜索目录,而开头我们将include/
作为头文件的存放目录aux_source_directory(src SRC_LIST)
:这一行语句则是将src/
下的源文件全部存到变量SRC_LIST
里面,注意一定要分清这俩不是一个东西,也不是简单的给src/
起一个别名叫SRC_LIST
,而是将源文件存到这个变量中
之后的关键一步是通过以下语句构建可执行文件
add_executable(main ${SRC_LIST})
这一行语句实际上就是将我们刚才保存源文件变量内的源文件(好绕但是应该是这么个道理)编译成可执行文件main
,这里我们通过${xxx}
的方式来访问CMakeLists的变量
最后我们为了让项目更具有结构性,所以将可执行文件存入bin/
目录下,也就是以下语句
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
这里呼应第二步开头的“起名字”,我们这里的项目名字与${PROJECT_SOURCE_DIR}
挂钩,而EXECUTABLE_OUTPUT_PATH
则是可执行文件的输出路径,set(AAA BBB)
则是将AAA设置为BBB,这里将可执行文件的输出路径设置为项目目录下的bin/
目录,从而让项目结构更清晰纯净
第三步 编译执行项目
向目录中添加头文件与源文件,使得项目结构如下
.
├── bin
├── build
├── CMakeLists.txt
├── include
│ └── test.h
└── src
├── main.cpp
└── test.cpp
其中各文件内容为(只是一个简单的demo):
//test.h
#pragma once
void sayHello();
//test.cpp
#include<iostream>
#include<test.h>
using namespace std;
void sayHello(){
cout<<"hello world"<<endl;
}
//main.cpp
#include<test.h>
int main(){
sayHello();
return 0;
}
#CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(test)
include_directories(include)
aux_source_directory(src SRC_LIST)
add_executable(main ${SRC_LIST})
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
然后进入build/
目录下,即执行
cd build/
移动到此目录下的目的是为了将cmake
生成的一系列文件存放在build/
目录下(在哪里执行cmake
命令文件就生成在哪里),而由于CMakeLists.txt
文件在目录之外,所以要在上一级目录中寻找文件,也就是执行如下命令
cmake ..
如果执行成功,则会在终端输出以下内容(版本号不一定和我的一样)
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
Compatibility with CMake < 2.8.12 will be removed from a future version of
CMake.
Update the VERSION argument <min> value or use a ...<max> suffix to tell
CMake that the project does not need compatibility with older versions.
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.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
-- Configuring done (1.0s)
-- Generating done (0.0s)
-- Build files have been written to:(这里是你写入文件的路径)
我们会发现,在build/
目录下生成了一大堆文件(是构建相关的文件,如果想深入了解可以在本站查看其他大佬关于cmake的帖子),其中有个叫Makefile
的文件,接下来的make
命令则需要用到这个文件,因此我们继续在这个目录下执行以下命令
make
有的同学可能要问了,这两个命令会用了,但是他们是干什么的呢?简单来说,cmake
就是生成相关构建文件,而make
则是根据这些文件来构建项目,详细知识还请移步其他大佬的文章。
执行成功会在终端输出以下内容:
[ 33%] Building CXX object CMakeFiles/main.dir/src/main.cpp.o
[ 66%] Building CXX object CMakeFiles/main.dir/src/test.cpp.o
[100%] Linking CXX executable (main文件所在路径)
[100%] Built target main
最后我们会在bin/
目录下看到可执行文件,但是有的同学要问了,如果我对代码进行了修改,是不是还要再执行一次cmake
和make
呢?其实如果不修改CMakeLists.txt
文件,那么只要执行make
命令即可,因为CMakeLists.txt
生成的构建文件中不涉及头文件与源文件的内容,可以简单理解为这些文件只是声明了依赖关系,因此只需要执行make命令即可,而反复切换路径非常麻烦,一会进入build/
目录执行make
一会进入bin/
目录执行可执行文件,因此我建议同学们开两个终端,一个在build/
目录下,一个在bin/
目录下,修改完再次编译即可马上执行。
以上就是本期全部内容,如有问题欢迎大家积极讨论。