makefile

题记:

在此感谢李云的无私分享。看到这篇博客的人搜一下李云的关于makefile的讲解,会受益匪浅。


我对其讲解的有些细节的思考:

1,

.PHONY=clean

CC=gcc
RM=rm

EXE=simple.exe
OBJS=main.o foo.o

$(EXE):$(OBJS)
       CC -o $@ $^
%.o:%.c#(这句的意思是,gcc从上面第二句开始执行的。然后发现要需要main.o,再到这里去执行)
       CC -o $@ -c $^

clean:
       $(RM) $(EXE) $(OBJS)

2,

李云云:“当一个大工程,增加或者减少一个.c文件的话,就需要修改一次makefile的话,工作量很大的。“

现在的项目中也是遇到了李云说的那个问题。不过因为是维护代码,所以变化不是很大的,所以大家都还是比较习惯于修改makefile~~呵呵~~

.PHONY:clean

CC=gcc
RM=rm

EXE=simple.exe
SRCS=$(wildcard *.c)#这句就是自动搜索当前目录下所有的.c文件
OBJS=$(SRCS:.c=.o)#
$(EXE):$(OBJS)
        $(CC) -o $@ $^
%.o:%.c
        $(CC) -o $@ -c $^
clean:
        $(RM) $(EXE) $(OBJS)


3,

编译环境:

complicated目录下:

foo.h foo.c main.c

把生成的.o文件放在objs目录下(这个目录要在makefile中自己建)

把生成的可执行文件放在exes目录下(这个目录要在makefile中自己建)

我自己写了一个makefile,出现了一个错误。这个错误,有助于理解makefile的执行。

.PHONY:all clean

CC = gcc
MKDIR = mkdir
RM = rm
RM_FLAGS = -rf

OBJS_DIR = objs
EXES_DIR = exes
DIRS = $(OBJS_DIR) $(EXES_DIR)

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(OBJS_DIR)/, $(OBJS))
EXES = complecated.exe
EXES := $(addprefix $(EXES_DIR)/, $(EXES))

all:$(DIRS) $(EXES)

$(DIRS):
	$(MKDIR) $@

$(EXES):$(OBJS)
	$(CC) -o $@ $^
$(OBJS):$(SRCS)#这个地方错了。会报“gcc -o objs/foo.o -c foo.c main.c"的错误。
	$(CC) -o $@ -c $^

clean:
	$(RM) $(RM_FLAGS) $(DIRS)

其实错误的原因是对makefile的目标的理解没做对。虽然,$(OBJS)包含了foo.o main.o两个东东,但是在执行

$(OBJS):$(SRCS)

的时候,是执行了2次。第一次目标是foo.o;第二次的目标是main.o.而,我们的$(SRCS)却一直 包含了main.c foo.c两个文件。所以编译八过。

建议: 

$(OBJS):$(SRCS)
改为

$(OBJS)/%.o:%.c


4,增加对头文件的依赖

对于上面的第3点难道真的没问题了吗?实战的李云说,“我们还没有考虑到:如果头文件变化的话,因为我们的makefile是不依赖头文件的,所以,不会去更新编译的”。

也就是如果你在make之后,改变第3点的头文件(foo.h),再一次的执行make其实还是不会产生编译行为的。

我改善了之后的例子:

.PHONY:all clean

MKDIR = mkdir
RM = rm
RMFLAGS = -fr

CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps

EXE = complicated.exe
EXE := $(addprefix $(DIR_EXES)/, $(EXE))

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

all:$(EXE)

-include $(DEPS)#include要放在all之后才行。不信,你试试

$(DIRS):
	$(MKDIR) $@
$(EXE):$(DIR_EXES) $(OBJS)
	$(CC) -o $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o:$(DIR_OBJS) %.c
	$(CC) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep:$(DIR_DEPS) %.c
	@echo "Making $@..."
	@set -e;\
	$(CC) -E -MM $(filter %.c, $^) > $@.tmp;\
	sed 's\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp>$@;
clean:
	$(RM) $(RMFLAGS) $(DIRS)
解释:

    A,李云的例子和我的不同的地方

    $(DIR_DEPS)/%.dep:$(DIR_DEPS) %.c
    @echo "Making $@..."
    @set -e;\
    $(RM) $(RMFLAGS) $@.tmp;\
    $(CC) -E -MM $(filter %.c, $^) > $@.tmp;\
    sed 's\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp>$@;\
    $(RM) $(RMFLAGS) $@.tmp;

    红色两句我删掉了,不然会死循环。我也不晓得原因啦~~敲打

    

    B,

    makefile中的“;”和set -e

    set -e:只要makefile出错的话就退出不继续执行

    :makefile没执行一条shell语句就开启一个shell,而,“;”保证了命令都是一个shell环境下。(shell环境:比如当前路径)


   C,

	$(CC) -E -MM $(filter %.c, $^) > $@.tmp;\

   -E:表示预编译;

   -MM:查看.c文件所需要的非系统的头文件。(-M,包括了系统的头文件)

   

   D,

	sed 's\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp>$@;
   sed语法走一遍:

   sed ’s,被替换的词,用于替换的词,g'

 

   E,

-include $(DEPS)
   include在make执行之前就包含进来。

   -,表示如果文件不存在的话,不要报错

  include的行为:查找被包含进来的文件,如果有的 话,就查找是否目标中有更新这个文件的规则。有则更新。我们这里就是用了这点。所以,include是先于all目标先执行的。

  include的本质作用,就是把包含进来的文件的内容展开到现在文件中。

   所以,这里include的内容

objs/foo.o: foo.c foo.h#main.o一样的东西,就不赘述了
  所以,include之后,先去执行all目标 (因为all目标在include之前)标,然后发现需要目标foo.o,就这上面那行。


5,增加对生成头文件的dep文件的依赖。

赘述一下,上面4点是在makefile中增加对头文件的依赖,如果头文件改变的话,也要相应的重新编译。一般情况是没有问题啦~~

为什么说“一般情况”呢?主要是因为dep文件中记录了.o文件对头文件,源文件的依赖。但是如果,我们增加了头文件呢?!

比如:(李云的笔记哈~~我只是拜读者啦~~)

define.h

#ifndef __DEFINE_H
#define __DEFINE_H

#define HELLO "Hello"

#endif
foo.h

#ifndef __FOO_H
#define __FOO_H
#include "define.h"


void foo ();


#endif
foo.c

#include <stdio.h>
#include "foo.h"

void foo ()
{
    printf ("%s, this is foo ()!\n", HELLO);
}
main.c

#include "foo.h"

int main ()
{
	foo ();
	return 0;
}

makefile

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr

CC = gcc

DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)

EXE = complicated.exe
EXE := $(addprefix $(DIR_EXES)/, $(EXE))

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

all: $(EXE)

ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif

$(DIRS):
	$(MKDIR) $@
$(EXE): $(DIR_EXES) $(OBJS)
	$(CC) -o $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
	$(CC) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep: $(DIR_DEPS) %.c
	@echo "Making $@ ..."
	@set -e; \
	$(CC) -E -MM $(filter %.c, $^) > $@.tmp ; \
	sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ;
clean:
	$(RM) $(RMFLAGS) $(DIRS)
请一定 按照我的步骤做,否则,看不到效果。

A,make(保证一定生成成功哈)

cat deps/foo.h

objs/foo.o: foo.c foo.h define.h

B,增加一个other.h和修改define.h头文件

other.h

#ifndef __OTHER_H
#define __OTHER_H

#define HELLO "Hello"

#endif

define.h

#ifndef __DEFINE_H
#define __DEFINE_H

#include "other.h"//这句修改了

#endif

C,再次make(一定不要之前make clean啊~~~)

发现重新编译了foo.o main.o.是吧?

D,修改other.h

#ifndef __OTHER_H
#define __OTHER_H

#define HELLO "Hi"

#endif

E,再次make,没反应了。

你cat deps/foo.h

objs/foo.o: foo.c foo.h define.h

这里你发现没有对other.h的依赖。 


所以,我们不仅要对头文件进行依赖,还要对生成头文件的dep文件进行依赖。

	sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ;
这句话改为:

	sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@ ;


6,其实我的机器上,上面的makefile总是每次都执行。

目前我只知道生成时间是:foo.o < objs目录 < main.o,所以每次make之后要么foo.o,要么main.o编译。。。呵呵~~这个问题,我看语法是没问题的。可能是shell的相关吧。。。敲打


7,

如果你想体味现实的makefile的话一定要看李云的最后一节。




在此,可能makefile就告一个段落了。

非常感谢李云的分享。李云上的例子我都实践了一遍。问题就是敲打的两个地方,学习过程中有什么疑惑,我们可以交流下。以文会友。



















  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值