关于Makefile

昨天遇到Makefile的问题。我是个程序盲,也是个linux盲,很多不懂。所以我就搜集一些知识,列举如下:

1. 正如许明彦所说:

Makefile 基本上就是『目标』(target), 『关连』(dependencies) 和『动作』三者所组成的一连串规则。而 make 就会根据 Makefile 的规则来决定如何编译 (compile) 和连结 (link) 程式。

也就是说,拿到源文件,要得到可执行的文件,需要经过下面的几部:

2. 上面提到,如果会有很多源文件,生成很多的o文件。那么一个比较好的办法是,写一个文件告诉编译器改怎么操作。

http://blog.csdn.net/wtz1985/archive/2009/02/04/3862141.aspx (CSDN的文章版权声明:“凡本网站上所有的文字、图片和音频、视频稿件,任何媒体、网站或个人未经本网站协议授权不得转载、链接、转贴或以其他方式复制发表,如有需要,请与本网站联系相关事宜。违者本网站将追究其法律责任。”呃,不知道我引用这句话是不是也违反规定了……OTL)所以我不加链接了,也不引用了。

那篇文章里的内容,总结起来,基本上是
1. 可以把命令写在这个文件里面,写的时候,可以使用事先定义(定义变量,类似宏定义吧)的方式来方便阅读和更改。
2. 各种文件比较多,可能有些依赖关系,需要指定的话,使用 “example.o: 1.cpp 1.h 0.h”。意思是说,冒号左边的目标文件依赖于右边的这些文件。这一句写完了之后,那么下面的跟
3. 关于隐含变量的知识:
$^ 我们上面不是指定了一些依赖关系么,那么这些依赖关系怎么起作用呢?这个符号就是用来代指到这个符号所在位置为止,当前的所有指定的依赖关系中的所有的目标文件。
$< 当目前位置指定的依赖关系中的第一个对象.
$@ 当前目标对象.

举一个不用依赖关系的粒子说:

bin/cmb: $(MATHOBJECTFILES) $(ANISOTROPY)/driver.cc
$(CXX) $(FLAGS) $(INC) $(MATHOBJECTFILES) $(ANISOTROPY)/driver.cc $(LIB) -o bin/cmb

这个里面的意思是:
第一行:那些source生成executable的文件放在bin/cmb。
第二行:使用定义的CXX 来完成这项任务。用到前面定义的INC,我们这里前面定义的是些include的文件,然后把那几个source文件编译成object放在bin/cmb下面。

真 OO无双http://www.cnblogs.com/oomusou/archive/2006/11/29/575838.html这篇文章中,给出了一个标准的c++的makefile的写法:

一个很典型的Linux C/C++的makefile写法,其它的应用可根据此template修改。
(注意每一行的前面若有空格,是tab,不是space)

#Source file

SRC = ThreadQueue.cpp
#Object file
OBJ = $(SRC:.cpp=.o)

#Output execution file
PROGRAM = ThreadQueue

#Compiler
CC = g++

#Include

INCLUDE = -I/usr/include/ncurses -I/usr/include/
#Linker Parameter
LINKPARAM = -lpthread -lncurses
#Options for development

#CFLAGS = -ansi -g -Wall
#Options for release

CFLAGS = -ansi -O -Wall

all: $(PROGRAM)

$(PROGRAM): $(OBJ)

$(CC) -o $(PROGRAM) $(LINKPARAM) $(OBJ)

.SUFFIXES : .cpp

.cpp.o:

$(CC) $(INCLUDE) $(CFLAGS) -c $< 42

clean:

-rm *.o


最后,http://hi.baidu.com/yanweinet/blog/item/24de5b1ff3a51bcba6866967.html有一个unix的例子:

作为Linux或Unix下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便。一般开发情况下,大家都是手工写一个简单Makefile。下面先给一个最简单的示例代码:

#makefile示例
object= main.o function.o

change:$(object)
cc -o change $(object)
mv change $(HOME)/bin
main.o:
cc -c main.c -I$(HOME)/program/date/include
function.o:
cc -c function.c -I$(HOME)/program/date/include

clean:
rm change $(object)

说明:
这里是使用unix下的CC编译器,当然也能换成gcc
最后生成的可执行文件名:change
源程序文件:main.c function.c
.h文件(头文件)所在目录:$(HOME)/program/date/include
中间目标文件名:main.o function.o
当在shall中执行命令:
make clean
将会执行
rm change $(object)
删除文件: change main.o function.o
语句:
mv change $(HOME)/bin
将最终生成的可执行文件移动到
$(HOME)/bin
这一步可以不要,但这样符合目录管理规范,而且$(HOME)/bin一般在环境变量PATH中有设置,这样可以在任意目录下时也能运行可执行文件。

注意:
makefile编辑时另起一行,注意不能用空格,要用TAB键,否则会报错
makefile里面的注释用#,其它都会报错

Makefile简单介绍
Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中纪录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。
Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。

这篇文章中,比较有启发性的是:
I是include文件。
make会决定那些需要重新编译。

还有一个http://my.oschina.net/alphajay/blog/10225

写一个完整的makefile步骤如下:

1 定义可执行文件的名称

TARGET = MyPro

2 指定此编译工程所要使用的头文件所在的目录

INC = -I. -Iheader1 -Iheader2

3 指定cpp文件所在的目录

VPATH = .:src1:src2

4 指定所有的cpp文件

SRC_PATH = $(foreach dir,$(subst :, ,$(VPATH)),$(wildcard $(dir)/,*.cpp))

解释:

foreach,subst,wildcard为makefile的内置函数,其中wildcard函数为字符串连接函数,把函数中的两个参数连接成一个。subst是字符串替换函数,在此句中意思是把VPATH变量中的冒号替换成空格。foreach函数,就是把subst函数的返回值,以空格为分隔符,循环取值赋给dir,然后接受wildcard的处理。

这句的总体意思是:从VPATH中获取每个目录,并分别找出此目录下所有的cpp文件,赋给SRC_PATH变量。

5 定义中间object文件存放的目录。

OBJ_DIR = ./obj

6 所有的object文件要按照和源代码对应的目录存放,定义object文件的目录

OBJ_PATH = $(addprefix $(OBJ_DIR)/, $(subst :, ,$(VPATH) ) )

解释:

addprefix函数是加前缀的函数,词句中是给VPATH中所有已经定义的目录加上./obj/的前缀。

7 定义连接成目标文件所需要的所有的object文件的名称

OBJS = $(addprefix $(OBJ_DIR),$(subst .cpp,.o,$(SRC_PATH)))

8 创建一个object文件的目录

MAKE_OBJECT_DIR := $(shell mkdir -p $(OBJ_DIR) $(OBJ_PATH))

*************************

9 定义实用的编译器

CXX = g++

CXXFLAGS = -g

10 定义目标文件的生成规则

$(TARGET) : $(OBJS)

$(CXX) -o $@ $(OBJS) $(INC) $(CXXFLAGS)

11 定义object文件的生成方式:

$(OBJ_DIR)/%.o : %.cpp %.cpp.d

$(CXX) -o $@ -c $< $(INC) $(CXXFLAGS)

12 定义.cpp.d文件的生成方式

DEPS = $(OBJS:.o=.cpp.d)

$(DEPS) : $(OBJ_DIR)/%.cpp.d : %.cpp

$(CXX) $< -MM $(INC) > $(OBJ_DIR)/$< .d

13 定义清理函数

clean:

@rm -rf ./obj

@rm -rf $(TARGET)

至此,一个通用的makefile完成了,如果增加源代码目录和头文件目录,只需要在INC变量和VPATH变量处加上此目录即可。中间object文件和文件的依赖关系自动生成。

感言:
我是盗窃知识的……这篇文章就是拼凑列举了人家的文章而已~