实验环境】
Ubuntu 8.10发行版、gcc等工具
我们要创建的目录结构如下:
一、创建顶层目录
我们首先在用户目录下创建一个makefileTest的文件夹:
#cd /home/linux/
#mkdir makefileTest
#cd makefileTest
创建好需要用到的文件夹
#mkdir f1 f2 main obj include
进入include文件夹创建一个共用头文件
#cd include
#vim myinclude.h
输入如下内容:
#include <stdio.h>
保存退出
返回顶层目录:
#cd ..
二、创建顶层Makefile文件
#vim Makefile
输入以下内容:
CC = gcc
SUBDIRS = f1 \
f2 \
main \
obj
OBJS = f1.o f2.o main.o
BIN = myapp
OBJS_DIR = obj
BIN_DIR = bin
export CC OBJS BIN OBJS_DIR BIN_DIR
all : CHECK_DIR $(SUBDIRS)
CHECK_DIR :
mkdir -p $(BIN_DIR)
$(SUBDIRS) : ECHO
make -C $@
ECHO:
@echo $(SUBDIRS)
@echo begin compile
CLEAN :
@$(RM) $(OBJS_DIR)/*.o
@rm -rf $(BIN_DIR)
三、进入在f1目录下创建makefile
#cd f1
#vim f1.c
输入如下测试代码:
#include “../include/myinclude.h”
void print1()
{
printf("Message from f1.c...\n");
return;
}
保存退出。
#vim Makefile
输入如下内容:
../$(OBJS_DIR)/f1.o: f1.c
$(CC) -c $^ -o $@
保存退出。
进入f2目录
#cd ../f2
#vim f2.c
输入如下测试代码:
#include “../include/myinclude.h”
void print2()
{
printf("Message from f2.c…\n");
return;
}
保存退出。
#vim Makefile
输入如下内容:
../$(OBJS_DIR)/f2.o: f2.c
$(CC) -c $^ -o $@
保存退出。
进入main目录
#cd ../main
#vim main.c
输入如下内容:
#include <stdio.h>
int main()
{
print1();
print2();
return 0;
}
保存退出。
#vim Makefile
输入如下内容:
../$(OBJS_DIR)/main.o: main.c
$(CC) -c $^ -o $@
保存退出。
进入obj目录
#cd ../obj
#vim Makefile
输入如下内容:
../$(BIN_DIR)/$(BIN) : $(OBJS)
$(CC) -o $@ $^
好了,到此准备工作已经完毕,然我们来测试一下写的makefile是否好用。
进入顶层Makefile所在目录,即makefileTest目录。
#make
会出现如下信息:
目录树结构如下:
我们看到在bin目录下生成了我们的目标文件myapp,在obj目录下生成了.o的中间文件。
让我们运行下myapp看下结果吧。
#bin/myapp
也可以用如下命令清除中间文件和目标文件,恢复make之前的状态:
#make CLEAN
我们可以看到已经变为make之前的目录状态了。
大功告成。最后给大家解释一下顶层makefile中一些命令的的含义吧。
1、我们注意到有一句@echo $(SUBDIRS)
@echo其实是一句显示命令
通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如:
@echo 正在编译XXX模块......
当make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出:
echo 正在编译XXX模块......
正在编译XXX模块......
如果make执行时,带入make参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。
而make参数“-s”或“--slient”则是全面禁止命令的显示。
2、@(RM)并不是我们自己定义的变量,那它是从哪里来的呢?
通常在清除文件的伪目标所定义的命令中“rm”使用选项“–f”(--force)来防止
在缺少删除文件时出错并退出,使“make clean”过程失败。也可以在“rm”之前加
上“-”来防止“rm”错误退出,这种方式时 make 会提示错误信息但不会退出。为了
不看到这些讨厌的信息,需要使用上述的第一种方式。
另外 make存在一个内嵌隐含变量“RM”,它被定义为:“RM = rm –f” 。因此在书
写“clean”规则的命令行时可以使用变量“$(RM)”来代替“rm”,这样可以免出现一
些不必要的麻烦!这是我们推荐的用法。
3、make -C $@
这是一句嵌套makefile的语法,在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。
例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
4. export CC OBJS BIN OBJS_DIR BIN_DIR
我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:
export <variable ...>