C/C++文档阅读笔记-A Simple Makefile Tutorial解析

Makefile文件可以使得程序编译变得简单。本博文并不是很系统的讲解makefile,本博文的目标是让读者快速编写自己的makefile文件并能应用到中小项目中。

简单实例

举个例子有下面3个文件,分别是hellomake.c,hellofunc.c,hellomake.h这3个文件构成了一个基本的带有main函数的程序。

hellomake.c hellofunc.c hellomake.h
#include <hellomake.h>

int main() {

  //call a function in another file
  myPrintHelloMake();
  return(0);
}
 
#include <stdio.h>
#include <hellomake.h>

void myPrintHelloMake(void) {

  printf("Hello makefiles!\n");

  return;
}
/*
example include file
*/

void myPrintHelloMake(void);
 

通常在不使用makefile时,使用如下的命令编译程序:

gcc -o hellomake hellomake.c hellofunc.c -I.

此命令会编译2个.c文件,包括hellomake.c和hellofunc.c并生成hellomake这个可执行程序。-I . 是告知gcc去当前目录(.)找.h文件,也就是找hellomake.h。没用makefile文件,程序员对代码进行修改后,就需要不停按键盘的↑键去找对应的命令进行编译,如果新增了一个.c文件,还需要修改对应的命令。

所以这种方式就存在2个缺点:

1.如果电脑重启,或者新增了.c文件,就需要重新敲边缘命令,这样十分不方便;

2.如果仅仅只修改了一个.c文件,使用编译命令就会把项目所有的.c重新编译,这样编译就会变得十分慢。

创建一个最简单的makefile文件

Makefile1

hellomake: hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c -I.

文件名可以叫Makefile或makefile,在命名行中输入make执行编译命令,这个make是不需要带参数的。

第一行的hellomake只是一个tab,后面*.c是文件名。关键的命令是在第2行,程序员通常写的makefile,执行make命令时当选择的文件有一个被修改了,才会执行。

Makefile1解决开发时不停使用键盘↑键带来的操作不方便的问题,但他属于把所有文件都编译,所以没有解决效率问题。

注意:在makefile的首行“:”号的前面都要有个tab,这个是必须存在的。

为了提高编译效率,就有了下面这个Makefile2:

Makefile2

CC=gcc
CFLAGS=-I.

hellomake: hellomake.o hellofunc.o
        $(CC) -o hellomake hellomake.o hellofunc.o 

上面定义了CC和CFLAGS两个宏,目的是方便makefile后面的脚本使用,CC=gcc并且后面的$(CC) -o hellomake hellomake.o hellofunc.o说明这个CC=gcc是使用C编译器,CFLAGS列出来标签的list, -I . 编译成.o文件需要依赖当前目录的.h文件。make命令首先会编译每一个.c文件,最后构建成可执行的hellomake文件。

上面这种方式已经可以用于小项目了,但是如果hellomake.h文件改变了,make命令也不会去重编译.c文件。对于这种情况,需要告诉make,哪个.h文件改变了,需要重新编译,这里就有了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

新建了DEPS宏,这个宏里面包含了.cpp代码中需要关注的.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)

对符合解释的补充:

$^:所有依赖文件

如果要将.h文件放到include目录,source文件放到src目录,库文件放到lib目录。.o文件也要规范位置,这样就需要编写新的makefile文件,举个例子当下面这个程序依赖m.so库,这个m是math的意思,并且还需要编写make clean的规则,这样的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)/*~ 

解释下参数:

patsubst:

名称:模式字符串替换函数——patsubst。

功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。

   这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。

   (可以用“\”来转义,以“\%”来表示真实含义的“%”字符)

返回:函数返回被替换过后的字符串。

示例:

$(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

猜你喜欢

转载自blog.csdn.net/qq78442761/article/details/129850556