嵌入式之行(5):我的Makefile
嵌入式之行(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的确了解不太深入。望诸君见谅!
|
推荐阅读: