Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101079
  • 博文数量: 22
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-17 11:22
文章分类

全部博文(22)

文章存档

2015年(6)

2014年(16)

我的朋友

分类: LINUX

2015-02-03 18:09:41

原文地址:Makefile 学习笔记 作者:asteriskchina

                                       Makefile 学习笔记
推荐学习资料:
跟我一起学makefile“”,相当全面。
一、基本思想

Make的思想是为每一块模块都设置一个时间标记,然后根据时间标记和依赖关系来决定哪一些文件需要更新。
一旦依赖模块的状态改变了,make就会根据时间标记的新旧执行预先定义的一组命令来生成新的目标。

二、依赖关系
就图1而言,我们可以说可执行程序main依赖于main.o、f1.o和ff1.o。
与此同时,main.o依赖于main.c和def1.h;f1.o依赖于f1.c、def1.h和def2.h;而ff1.o则依赖于ff1.c、def2.h和def3. h。
在makefile中,我们可以用目标名称,加冒号,后跟空格键或tab键,再加上由空格键或tab键分隔的一组用于生产目标模块的文件来描述模块之间的依赖关系。
对于上例来说,可以作以下描述:

main: main.o f1.o f2.o
main.o: main.c def1.h
f1.o: f1.c def1.h def2.h
f2.o: f2.c def2.h def3.h
不难发现,上面的各个源文件跟各模块之间的关系具有一个明显的层次结构,如果def2.h发生了变化,
那么就需要更新f1.o和f2.o,而f1.o和f2.o发生了变化的话,那么main也需要随之重新构建。

默认时,make程序只更新makefile中的第一个目标,如果希望更新多个目标文件的话,可以使用一个特殊的目标all,
假如我们想在一个makefile中更新main和hello这两个程序文件的话,可以加入下列语句达到这个目的:

all: main hello

三、makefile中的规则

格式:目标:[依赖模块][;命令]
习惯上写成2行为:
目标:[依赖模块]
   命令
例如:
test: $(OBJS)
gcc -o test -rdynamic $(OBJS) $(LIBS)
注意:
对于makefile而言,空格字符和tab字符是不同的。所有规则所在的行必须以tab键开头,而不是空格键。
此外,如果在makefile文件中的行尾加上空格键的话,也会导致make命令运行失败。

四、Makefile文件举例
根据图1的规则,写一个简单的makefile如下:
main: main.o f1.o f2.o
gcc -o main main.o f1.o f2.o
main.o: main.c def1.h
gcc -c main.c
f1.o: f1.c def1.h def2.h
gcc -c f1.c
f2.o: f2.c def2.h def3.h
gcc -c f2.c

四、makefile中的常用宏
Make的宏分为两类,一类是用户自己定义的宏,一类是系统内部定义的宏。
用户定义的宏必须在makefile或命令行中明确定义,系统定义的宏不由用户定义

在makefile中定义宏的基本语法是:
宏标识符=值列表
当一个宏定义之后,我们就可以通过$(宏标识符)或者${宏标识符}来访问这个标识符所代表的值了。

举例:
all: main
# 使用的编译器
CC=gcc
#包含文件所在目录
INCLUDE=.
# 在开发过程中使用的选项
CFLAGS=-g -Wall –ansi
# 在发行时使用的选项
# CFLAGS = -O -Wall –ansi
main: main.o f1.o f2.o
$(CC) -o main main.o f1.o f2.o
main.o: main.c def1.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
f1.o: f1.c def1.h def2.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.c
f2.o: f2.c def2.h def3.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c

现在介绍make的内部宏。常用的内部宏有:
$? :比目标的修改时间更晚的那些依赖模块表。
$@ :当前目标的全路径名。可用于用户定义的目标名的相关行中。
$< :比给定的目标文件时间标记更新的依赖文件名。
$* :去掉后缀的当前目标名。例如,若当前目标是pro.o,则$*表示pro。

五、构建多个目录
all: main
# 使用的编译器
CC = gcc
# 安装位置
INSTDIR = /usr/local/bin
# include文件所在位置
INCLUDE = .
# 开发过程中所用的选项
CFLAGS = -g -Wall –ansi
# 发行时用的选项
# CFLAGS = -O -Wall –ansi
main: main.o f1.o f2.o
$(CC) -o main main.o f1.o f2.o
main.o: main.c def1.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
f1.o: f1.c def1.h def2.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.c
f2.o: f2.c def2.h def3.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c
clean:
-rm main.o f1.o f2.o
install: main
@if [ -d $(INSTDIR) ]; \
then \
cp main $(INSTDIR);\
chmod a+x $(INSTDIR)/main;\
chmod og-w $(INSTDIR)/main;\
echo "Installed in $(INSTDIR)";\
else \
echo "Sorry, $(INSTDIR) does not exist";\
fi

六、内部规则
实际上,除了我们显式给出的规则外,make还具有许多内部规则,这些规则是由预先规定的目标、依赖文件及其命令组成的相关行。

七、makefile自动推导功能
只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么 whatever.c,
就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来
上面的makefile可以再优化
objects = main.o f1.o f2.o
main: ${objects}
$(CC) -o main ${objects}
main.o: def1.h
f1.o: def1.h def2.h
f2.o: def2.h def3.h

.PHONY : clean
clean :
-rm main $(objects)

八、常用函数说明

wildcard:  扩展通配符 例如将* 进行匹配

notdir:  去除目录 类似于basename的用法

patsubst: 替换通配符

例子:

在test目录下a.cpp和b.cpp,sub目录下c.cpp和d.cpp


src=$(wildcard *.c ./sub/*.c) 

dir=$(notdir $(src)) 

obj=$(patsubst %.c,%.o,$(dir) )   

all: @echo $(src)

@echo $(dir)

@echo $(obj)

@echo "end"

输出结果:

#wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开

 a.cpp b.cpp ./sub/c.cpp ./sub/d.cpp

 #notdir把展开的文件去除掉路径信息 a.cpp b.cpp c.cpp d.cpp

 #patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o a.o b.o c.o d.o


阅读(1861) | 评论(0) | 转发(0) |
0

上一篇:OpenWrt的主Makefile工作过程

下一篇:没有了

给主人留下些什么吧!~~