文件准备
hello.c1 2 3 4 5 6 7
| #include <stdio.h> #include "message.h"
int main(){ sayHello(); return 0; }
|
message.h
message.c1 2 3 4 5 6
| #include "message.h" #include <stdio.h>
void sayHello(){ printf("Hello world lnpbqc"); }
|
传统方法
1 2
| gcc hello.c message.c -o hello ./hello
|
使用构建工具
只是一些简单的项目和文件结构当然可以这样写,但要是复杂起来就不是那么友好了。
这个时候就需要启用make 和cmake 了。
make的简单使用
make 是一个构建自动化工具,广泛应用于软件开发中,尤其在 Unix 和 Linux 环境下。
它通过读取名为 Makefile(当然可以读取其他文件,后续介绍) 的文件,根据其中定义的规则和依赖关系来编译和链接程序
make: 默认情况下,make 会执行 Makefile 中第一个目标(通常是 all 或主目标)。
make target: 只执行特定目标的规则。例如 make clean 只会执行 clean 规则。
make -j [N]: 并行构建,N 指定并行的任务数,可以加速编译。
使用make -f 指定文件名字 来根据其他Makefile规则编译
使用make -n 只打印命令不执行
使用make -C 目录 指定makefile工作目录
基本的结构:
由此即可编写基本make文件:
makefile1 2
| hello: main.c message.c gcc main.c message.c -o hello
|
这样,只要main.c或者message.c发生改变,使用make命令即可完成编译。如果没有改变文件,则不会重新编译。
部分依赖编译
那么如果文件过多,有更多的依赖需要编译,但你只是改动了其中部分文件,不想全部重新编译,也可以分开编译中间文件,最后链接。
makefile1 2 3 4 5 6 7 8 9 10 11 12 13 14
| .PHONY: clean all //.PHONY 表示后面是一个命令,而不会作为一个文件被make识别
hello: main.o message.o gcc main.o message.o -o hello
main.o: main.c gcc -c main.c
message.o: message.c gcc -c message.c
clean: rm -f *.o hello
|
这样一来,只是改动部分文件,而不需要全部重新编译。
有多个目标程序生成
可以使用按照make的特性,把目标程序作为依赖,来实现多个目标同时编译
Makefile1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| .PHONY: clean all
all: hello world @echo "Done" // 在命令前面添加 @ 只会显示执行结果,而不会显示命令本身,@echo "Done"
hello: main.o message.o gcc main.o message.o -o hello
world: main.o message.o gcc main.o message.o -o world
main.o: main.c gcc -c main.c
message.o: message.c gcc -c message.c
clean: rm -f *.o hello world
|
而进一步地,相同生成规则可以写在一起。
makefile1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .PHONY: clean all
all: hello world @echo "Done"
hello world:main.o message.o gcc main.o message.o -o $@
main.o: main.c gcc -c main.c
message.o: message.c gcc -c message.c
clean: rm -f *.o hello world
|
定义变量
对应重复使用的值,可以通过变量一起管理,方便后续更改。
自动变量
- $@ 表示目标文件
- $< 表示第一个依赖
- $^ 表示所有依赖
makefile1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| .PHONY: clean all
CFLAGS = -Wall -g -O2 targets = hello world sources = main.c message.c objects = main.o message.o
all: $(targets) @echo "Done"
$(targets):$(objects) gcc $(CFLAGS) $(objects) -o $@
main.o: main.c gcc $(CFLAGS) -c main.c
message.o: message.c gcc $(CFLAGS) -c message.c
clean: rm -f *.o hello world
|
对于同样的文件后缀有着同样处理逻辑的,可以进步这样省略。
使用通配符简化
makefile1 2
| %.o: %.c gcc $(CFLAGS) -c $< -o $@
|
完整示例
一个完整的Makefile 如下所示。
makefile1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| CC = gcc CFLAGS = -Wall -g
TARGET = HelloWorld
$(TARGET): main.o message.o $(CC) $(CFLAGS) -o $(TARGET) main.o message.o
main.o: main.c $(CC) $(CFLAGS) -c main.c
message.o: message.c $(CC) $(CFLAGS) -c message.c
clean: rm -f $(TARGET) *.o
|
Cmake的初级使用
CMake 是一个跨平台的构建系统生成工具,广泛用于软件开发中,特别是在需要生成项目的构建系统配置文件(如 Makefile 或 Visual Studio 项目文件)时。
CMake 的主要功能是通过一个简单的脚本语言(CMakeLists.txt)来描述项目结构、依赖关系、编译和链接规则,然后为不同的平台生成相应的构建文件。
一个简单的CmakeLists.txt 示例如下。
1 2 3 4 5 6 7 8 9
| cmake_minimum_required(VERSION 3.10)
project(HelloWorld)
# 定义变量 set(C_SOURCES hello.c message.c) message("C_SOURCES: ${C_SOURCES}")
add_executable(HelloWorld ${C_SOURCES})
|
使用方法
- 使用 cmake -S CmakeLists.txt所在目录 -B 生成的编译目录
- 使用 cmake –build 编译生成目录
当你修改源文件后,你可以直接使用以下命令进行重新编译:
cmake --build build
不需要重新运行 cmake -S . -B build,因为构建系统已经生成好了。
如果你只是在编辑 .c 文件或其他源文件,直接运行 cmake –build build 就会重新编译受影响的部分,并生成更新后的可执行文件。
什么时候需要重新运行 cmake -S . -B build?
你只需要重新运行 cmake -S . -B build 或者执行类似步骤的情况包括:
修改了 CMakeLists.txt 文件:例如添加新的源文件、更改编译选项、添加新的库依赖等。
移动或删除了源文件:比如你更改了文件路径或文件名。
更换了编译工具链:例如从 make 切换到 ninja,或者从 gcc 切换到 clang。
在这些情况下,CMake 需要重新配置构建系统,所以你需要重新执行 cmake -S . -B build。