C/C++文档阅读笔记-A Simple Makefile Tutorial解析
Makefile文件可以让程序编译更加容易。本博文没有深入讲解Makefile的详细内容和机制,其目的是帮助读者迅速构建自己的Makefile文件,并将它应用于较小规模的项目中。
简单实例
以下包含三个文件:hellomake.c、hellofunc.c和hellomake.h。这些文件构成了一个基本的具有主函数的应用程序。
| hellomake.c | hellofunc.c | hellomake.h |
|---|
| #include <hellomake.h>
int main() {
This code calls a function from another file. The myPrintHelloMake function is invoked. The program returns an exit code of 0. | #include <stdio.h> Include the standard input header. Include the hellomake specific header file.
void myPrintHelloMake(void) {
printf("Hello makefiles!\n");
return;
}| /*
example include file
*/
void myPrintHelloMake(void);
||
通常在不使用makefile时,使用如下的命令编译程序:
gcc -o hellomake hellomake.c hellofunc.c -I.
bash
此命令负责处理两个.c源文件:hellomake.c和hellofunc.c,并生成名为hellomake的可执行程序。通过-I .指明当前目录(.),告诉编译器查找相应的头文件。没有依赖于makefile文件的情况下,默认行为即可完成大部分操作。每次修改后都需要频繁地使用↑键查找对应的命令进行编译操作。如果新增了一个新的.c源文件,则需要相应地调整相关编译指令以确保正确运行。
所以这种方式就存在2个缺点:
当电脑重启发生时,在以下两种情况下需要执行相应的边缘命令:第一种情况是创建了新的.c文件;第二种情况则是电脑重启完成。
如果仅修改了一个未更改的.c文件并运行编译器它会自动处理所有未更改的.c文件从而显著延长编译时间。
创建一个最简单的makefile文件
Makefile1
hellomake: hellomake.c hellofunc.c
gcc -o hellomake hellomake.c hellofunc.c -I.
bash
文件名可以命名为Makefile或makefile,在名称行处输入 make来启动编译过程。这个 make操作无需附加参数。
第一行只是hellomake的一个简单的制表符,随后*.c标记的是目标文件.核心操作位于第二行,程序人员一般会编写一个makefile来配置项目结构.每当在终端执行make命令时,若选定的文件发生更改,系统会自动触发构建过程.
Makefile1应对了频繁使用键盘↑键时操作不便的问题;然而其主要功能是将所有文件进行编译,并未解决效率问题。
注意:在makefile的首行“:”号的前面都要有个tab,这个是必须存在的。
为了提高编译效率,就有了下面这个Makefile2:
Makefile2
CC=gcc
CFLAGS=-I.
hellomake: hellomake.o hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o
bash
为便于后续脚本处理,在此定义了两个宏变量CC和CFLAGS。其主要目的是为了简化makefile脚本的编译指令。其中CC被赋值为gcc编译器,并用于生成hellomake可执行文件。具体来说,在$(CC) -o hellomake hellomake.o hellofunc.o这一系列指令中可以看出,默认情况下选择使用gcc进行编译操作。需要注意的是,在-I .这一选项下编译生成的目标文件hellomake.o将依赖于当前目录下的.h头文件完成构建过程。随后make命令依次对每一个.c源文件进行编译,并最终将构建出一个名为hellomake的可执行程序。
当前的方法足以支持小型项目的开发工作;然而,在修改包含*.h头文件的内容时(例如hellomake.h),现有的make命令不会自动触发对*.c源代码的重新编译过程;因此,在这种情况下(即*.h头文件发生更改),我们需要向make机制传递相关信息以便其重新编译对应的*.c源代码;为此我们引入了一个名为Makefile3的新配置文件来解决这个问题
Makefile3
CC=gcc
CFLAGS=-I .
DEPS=hellomake.h
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: hellomake.o hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o
bash
创建了DEPS宏,并且该宏包含了许多在.c++代码中必须关注的.h文件。需要注意的是,在make命令生成object文件时就必须检查该.h文件是否有更改操作。下面将介绍各个符号的具体含义:
%.o:当前目录匹配到所有.o结尾的文件;
%.c:当前目录匹配到所有.c结尾的文件;
-c:生成对应的object文件;
-o $@:编译时进行输出,输出时文件的名字放到“:”的左边;
$<:依赖的第一个文件;
将Makefile3进行了简化处理,并采用了@和^这两个标记符来实现编译规则的重写部分修改。所有include文件都包含于DEPS宏中,所有object文件也都包含于OBJ宏中。
Makefile4
CC=gcc
CFLAGS=-I .
DEPS=hellomake.h
OBJ=hellomake.o hellofunc.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
bash

对符合解释的补充:
$^:所有依赖文件
为了使.h文件安置在include目录中,
源代码文件应存放在src目录,
静态库应存放在lib目录。
.o目标编译物需妥善安置,
这就需要创建一个新的makefile。
比如考虑这样一个程序依赖m.so库,
其中m代表math模块。
例如一个典型的Makefile结构如下:
Makefile5
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
LIBS=-lm
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
bash

解释下参数:
patsubst:
名称:模式字符串替换函数——patsubst。
功能:搜索给定文本
在这里,
(可以用“\”来转义,以“%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。
示例:
$(patsubst %.c,%.o, a.c b.c)
把字串“a.c b.c”符合模式[%.c]的单词替换成[%.o],返回结果是“a.o b.o”
实例代码打包下载地址:
https://github.com/fengfanchen/CAndCPP/tree/master/MakeFileExample
