我们先写一个简单的的程序:
#include
int main()
{
std::cout << "Hello Cmake" << std::endl;
return 0;
}
上面程序会在控制台中输出:Hello Cmake
那么如何运行上面的代码呢?你可能会说,在vs(或者其他的IDE)中创建一个项目,创建一个main.cpp,把上面的代码复制进行,然后点击运行就好了呀。是的没错,那么我们思考一下,我们点击运行按钮到控制台输出结果,vs在这个过程中,帮我们“偷偷的”的做了哪些事情?
vs大概帮助我们做了以下内容:预处理、编译、优化、汇编、链接、生成可执行文件、 加载与调试、运行程序、退出与清理
不得不感叹这些IDE的方便性,只要点击一个按钮就做了那么多步骤。但是我们现在就是要“化简为繁”手动的做这些事情。亲自感受一下这个过程,这有利于我们后面对cmake的理解。
上面的内容可以分为两个过程:构建过程、运行过程。我们只讲一下构建的过程:
优化
中间代码可以被优化,例如移除多余的计算、循环展开、常量折叠、消除冗余代码等。这一步可以在中间代码层进行,也可以在生成机器代码后进行。
汇编
touch main.cpp
将上述代码复制到其中并保存g++ -E main.cpp -o main.i
根据前面的讲解,我们在代码中加点内容(加一个宏,和注释),来验证上面的说法
#include
#define NUM 12
int main()
{
//注释
std::cout << "Hello Cmake" << std::endl;
std::cout << NUM << std::endl;
return 0;
}
此时,重新运行上面的命令,你可以再文件夹下面看到一个main.i文件,可以打开mian.i,下拉到文件底部,里面生成了那些东西。你会看到头文件被加载了进来,宏被替换了,注释被去掉了
g++ -S main.i -o main.s
g++ -c main.s -o main.o
g++ main.o -o main
./main
经过上面的步骤,应该就可以成功的生成可执行文件,并运行起来。你可能已经发现了,这样很麻烦,而且现在只是一个main.cpp文件,就要那么多步骤,如果文件很多呢,比如增加add.h、add.cpp、de.h,de.cpp:
文件结构如下:
├── add
│ ├── add.cpp
│ └── add.h
├── de
│ ├── de.cpp
│ └── de.h
├── main.cpp
└── Makefile
add.h:
#ifndef _HEAD_ADD_
#define _HEAD_ADD_
int add(int a,int b);
#endif
add.cpp
#include "add.h"
int add(int a,int b)
{
return a+b;
}
de.h
#ifndef _HEAD_DE_
#define _HEAD_DE_
int de(int a,int b);
#endif
de.cpp
#include "de.h"
int de(int a,int b)
{
return b-a;
}
main.cpp
#include
#include "add.h"
#include "de.h"
#define NUM 12
int main()
{
//注释
std::cout << "Hello Cmake" << std::endl;
std::cout << NUM << std::endl;
std::cout << add(10,2) << " " << de(2,10) << std::endl;
return 0;
}
构建过程:
g++ -c add/add.cpp -o add/add.o
g++ -c de/de.cpp -o de/de.o
g++ -c main.cpp -I add -I de -o main.o
g++ main.o add/add.o de/de.o -o main
./main
随着文件增多,会越来越麻烦,而且容易出错。
使用makefile构建,首先要在自己的电脑上安装make(网上有很多方法,不再赘述),然后编写Makefile文件。编写Makefile文件的过程和上面手动构建的过程类似,具体内容有详细注释:
增加Makefile文件
├── add
│ ├── add.cpp
│ └── add.h
├── de
│ ├── de.cpp
│ └── de.h
├── main.cpp
└── Makefile
Makefile文件:
# 定义编译器
CXX = g++
# 定义编译选项
CXXFLAGS = -Wall -g
# 定义目标文件名
TARGET = main
# 自动寻找当前目录及子目录下的所有.cpp文件
SRCS = $(wildcard */*.cpp) $(wildcard *.cpp)
# 自动生成对应的.o文件
OBJS = $(SRCS:.cpp=.o)
# 包含的头文件目录
INCLUDES = -Iadd -Ide
# 默认目标:生成可执行文件
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(TARGET) $(OBJS)
# 生成对象文件的规则
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
# 清理目标
clean:
rm -f $(OBJS) $(TARGET)
直接make就可以生成对应的文件了,而且比之前更加的通用一点。
相信你通过这个一步一步走下来,肯定发现了它的不足:
有没有一个更好方法,既不用繁琐的写着构建过程,还可以自动处理依赖关系,自动寻找第三方的库?
CMake 是一个跨平台的构建系统生成器,它可以根据简单的配置文件(CMakeLists.txt)自动生成适用于多种构建系统的文件,比如 Unix 系统上的 Makefile,Windows 上的 Visual Studio 项目文件等。
优势:
sudo apt-get install cmake
自行百度如何安装。
# 设定最低版本要求
cmake_minimum_required(VERSION 3.10)
# 定义项目名称
project(HelloCMake)
# 设置编译选项
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 添加源文件
set(SOURCES
main.cpp
add/add.cpp
de/de.cpp
)
# 包含头文件目录
include_directories(add de)
# 生成可执行文件
add_executable(main ${SOURCES})
执行以下命令,就可以生成可执行文件了:
mkdir build
cd build
cmake ..
make
非常的简洁明了,不用我们手动管理依赖,也可以实现跨平台。通过上面的内容,应该对cmake有了基本了解。