分享

嵌入式之行(5):我的Makefile

 WUCANADA 2012-04-18
嵌入式之行(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

 

# 这些就是传说中的编译器了,比如CCC编译器、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"

 

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

# 因为一般程序编译安装都是:makemake 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也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label

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

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

 

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

 需要执行的

 

在使用Makefilemake程序时最常见的提示信息就是“Nothing to be done for XXX”很多人看到这个信息很不理解(试一下那个make install测试吧)。我认为在两种情况下会出现这个提示信息,第一种情况,这个程序已经编译过一次了,已经生成了.o和可执行文件了,所有目标均已是最新,不需要再做一次无用功——make是何等讲究效率!如果你想每次make all都从编译一次,可以在Makefileall后添加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的确了解不太深入。望诸君见谅!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多