嵌入式之行(5):我的Makefile
嵌入式Linux
作者:李迟
2011-01-15 20:11

说明:

1、文中多处出现Makefile,它可以认为是一个具体的文件——即文件名就是「Makefile」,也可以认为它是抽象的「Makefile」,比如下文说到的「两个Makefile」,它们的名称肯定是不同的,但它们都是「Makefile」。——不知这样说,阁下能不能明白,我也没有好的文字表达了。

2、本文以小笑自己从网路、书籍总结的Makefile模板来讲一下有关的Makefile知识、技巧,当然,不可能很完整,不过能正常使用。

本文不打算讲述Makefile的来源、好处以及其它一些理论的知识,有关Makefile的知识可以写成一本书。网路上的《跟我一起写 Makefile》是一篇很好的文章,建议看一下。此处给出小笑的一个Makefile例子,它能应付基本的项目管理。小笑的毕业设计程序就是在这个Makefile基础上修改而来的。闲话不说,进入主题。

先来看一下具体的Makefile模板文件。其中的红色为小笑写的注释。

######################################

# my Makefile template

#

# Uage:

# compile:"make all" or just "make"

# clean:"make clean"

#

# ChangeLog:

# 2010-4-21:

# add some info

# 2010-4-20:

# new for DEBUG

# another way to change SRCS to OBJECTS

# another way to generate .o file

########################################

### 宏定义DEBUG,我没有找到好的办法,只好出此下策。

DEBUG = y

# 这些就是传说中的编译器了,比如CC是C编译器、CPP是C++编译器,其它们都是宏来著。

# 也可以定义其它一些编译器,比如交叉编译器CROSS_COMPILER=arm-linux-gcc等等。

CC = gcc

CPP = g++

CROSS_COMPILER = arm-linux-gcc

###=====================================

#####>>>>>!!!!! C编译的一些标志,如打开警告,调试标志等 !!!!!!<<<<<#####

### C

CFLAGS = -Wall

##! 这里就是添加调试或优化标志,当然,也可以在上述宏中使用,不必这样麻烦。

ifeq ($(DEBUG), y)

CFLAGS += -g

else

CFLAGS += -O2

endif

#####>>>>>!!!!! 这是C++语言编译的一些标志,同C !!!!!!<<<<<#####

### C++

CPPFLAGS = -Wall

##! 一样的

ifeq ($(DEBUG), y)

CPPFLAGS += -g

else

CPPFLAGS += -O2

endif

###=====================================

#####>>>>>!!!!! 这些是别的一些宏,如链接库位置、名称等(我不知如何移称呼这个宏,百度吧) !!!!!!<<<<<#####

### 如-lpthread or -lncurses or -lpanel or -lmenu or -lm,等。

### 注意:有些库不是Linux默认的,比如多线程的pthread,如果编译时不加上的话,编译是不会通过的,此外,还有ncurses库、SDL库等等,要注意一下。

### 在此处添加

LDFLAGS =

LDFLAGS +=

# 这个是删除使用到的宏

RM = rm -rf

###=====================================

### 此处添加目标名称

#####>>>>>!!!!! 最好起一个有意义的名称,比如采集视频数据的,可以是capture,显示用的,可以是display,等等 !!!!!!<<<<<#####

target =

### 此处添加目标文件(即.o文件)

OBJECTS = .o

OBJECTS += .o

### 这是生成.o文件的另外一种方法,有点麻烦,但也可以。

# 此处添加源文件

#SRCS =

#SRCS +=

# 生成相应的.o文件

#OBJECTS = $(SRCS:.c=.o)

#OBJECTS = $(SRCS:.cpp=.o)

###=====================================

### 真正的编译开始,all是一个伪目标,编译时的「make all」中的「all」就是它。

all: $(target)

##############################

# 这是另外一种方法:

# foo.o:foo.c foo.h(可以不使用.h文件的!)

# (tab) $(CC) $(CPPFLAGS) -c $< -o $@

#

# thread.o: thread.cpp thread.h

# $(CC) $(CPPFLAGS) -c $< -o $@

# main.o: main.cpp thread.cpp

# $(CC) $(CPPFLAGS) -c $< -o $@

#

# 这种方法就是实打实的,需要什么,添加什么,我也用过,也不麻烦,不妨一试。

##############################

# 这种方法简便一些

# 上述列出了所有用到的.o文件,都是依赖条件。

# $^:所有的依赖文件,$<:第一个依赖文件,$@:目标文件(可执行的程序)

$(target): $(OBJECTS)

$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@

# 清除,比如中间文件,目标文件。

clean:

@echo "Cleaning..."

$(RM) $(OBJECTS) $(target)

@echo "Done"

# 安装。其实这个命令除了显示一些信息外,什么事也没做。

# 因为一般程序编译安装都是:make;make install;这样做,以防万一。

# 也可试试删除这几行,执行一下make install,看看效果。

install:

@echo " Note:"

@echo "To install or not install,that is the question"

@echo

# 声明了三个伪目标

.PHONY:all clean install

### end of the Makefile

Makefile的格式如下所示:

target:prerequisites

command

下面是《跟我一起写 Makefile》中的介绍:

target也就是一个目标文件,可以是ObjectFile,也可以是执行文件。还可以是一个标签(Label)。

prerequisites就是要生成那个target所需要的文件。

command就是make需要执行的命令。(任意的Shell命令)

下文所讲的「目标文件」,可能是指生成的「可执行文件」,也可能是指这里的「target」,假设读者应当能区别出来。

需要执行的

在使用Makefile来make程序时,最常见的提示信息就是「Nothing to be done for XXX」,很多人看到这个信息,很不理解(试一下那个make install测试吧)。我认为在两种情况下会出现这个提示信息,第一种情况,这个程序已经编译过一次了,已经生成了.o和可执行文件了,所有目标均已是最新,不需要再做一次「无用功」了——make是何等讲究效率!如果你想每次make all都从编译一次,可以在Makefile的all后添加clean,这个clean必须放在第一位置才能在每次编译前都清除一些中间生成的文件(.o文件)和目标文件。如下:

all: clean $(target)

不要担心clean在后面才出现,Makefile不管先后的。

另一种情况是声明了一个「伪目标」,但又make它,就会出现上面的提示信息,比如上述的Makefile模板中,如果去掉与目标install相关的几行语句,但最后却声明它是「伪目标」的话,当执行「make install」后,就会出现「Nothing to be done for install」。

当make一个不存在的目标时,会提示:

Make: *** No rule to make target 『XXX』. Stop.

因为make确实找不到XXX,当然也不会去执行了。

make程序时,也不一定是「make all」,只要是一个在Makefile文件中出现的目标文件(target)即可。如果你将「all」改为「love」的话,你输入「make love」,照样能顺利通过编译,阁下不妨一试。

此外,还有一些make的技巧,比如一个程序要应用于两个平台(我写的视频采集程序要在PC上执行,也要在ARM上执行),程序是不用改多少的。最关键的就是编译器,当应用于ARM平台时,只要修改Makefile中的编译器就可以了。所以我的工程目录下有两个Makefile,比如PC平台中的为Makefile,而ARM平台的为arm-Makefile。这样,在编译PC平台的程序时,可以直接「make all」,因为make首先找到的是Makefile,就不会执行到arm-Makefile了。那么,交叉编译怎么办呢?make有一个-f选项,可以选择自定义的Makefile。不过在编译过程中,出现了一些问题,我明明在Makefile中指明了编译器为arm-linux-gcc了,但编译过程中有些文件还是被gcc所编译,造成链接的失败,我实在没有办法,只能显式指定CC选项了。这样,在交叉编译时,简单的「make all」,就变成了「make –f arm-Makefile CC=arm-linux-gcc」,由于同时要处理两个平台,不得不出此下策了。(其实也没有多么麻烦。)

我还发现gcc的-M选项的好处。此选项是在编译时指定某一个宏,在条件编译中特别有用。在PC机上,摄像头的设备文件为/dev/video,但在ARM开发板上却为/dev/video1,我也不知是怎么搞的,在开发板中,video不是video1的链接文件,我试了几次,结果还是一样。没办法,只好在程序显式使用宏定义来指定设备文件名称了。不过,为了方便,我使用了条件编译。如下:

#ifdef __ARM__

#define device 「/dev/video1」

#else

#define device 「/dev/video」

#endif

即使用__ARM__来选择设备文件的名称。

这时,gcc的-D选项就派上用场了,在arm-Makefile文件中的CFLAGS中添加了-D__ARM__,在Makefile文件中不添加,这样就很好解决了两个平台的设备文件名称问题了。当然,如果仅仅只是针对ARM平台的话,这些也用不著,但不失为一种方法。(更正:以前写成「-M」选项是错误的!正确的选项为「-D」,特此说明并致歉!)

本著「够用即可」的原则,不再详细介绍Makefile了。毕竟,小笑也不太懂。

上述的Makefile可以认为是一个Makefile的模板,可以在它的基础上修改,成为适合自己的工程的「Makefile」,再总结出一个自己的Makefile模板。

再:写完这篇文章后,我读了几次,感觉不像我写作风格,总是表达不出想要表达的意思。自己文笔水平是一个问题,二来对Makefile的确了解不太深入。望诸君见谅!

推荐阅读:

查看原文 >>
相关文章