| |
你可以点击这里获取源代码
在test文件夹中有三个子目录,分别存放目标文件、源文件、头文件;我们假设在src目录中有main.c yuer.c 在include目录中yuer.h。然后我们把我们的Makefile放置在test目录下(即根目录下),当然我们也可以放置在源目录下,只需作一点点修改;
我们这个makefile的目标是为了达到简洁,直观,并且易于维护,实现在集中目标文件的功能,所以我们将建立多个子makefile,共同来完成我们的目标;
注解:
environment.mak 定义全局变量,命令等
buildir.mak 检测编译目录,在目标目录中调用make
Makefile 简洁,直观,易于维护的makefile
内容:
environment.mak
#---------------------------------------------------------
# 设置命令变量
#---------------------------------------------------------
CC = gcc
RM = rm
ECHO = echo
MKDIR = mkdir
RMDIR = rmdir
# 定义一个当前编译目录变量
BUILDING_DIR := $(shell pwd)
builddir.mak
#---------------------------------------------------------
# 包含environment.mak
#---------------------------------------------------------
include /home/yuqiang/桌面/test/environment.mak
# 这个宏变量用于删除目标文件
define TARGET_CLEAN
$(MAKE) -C $(BUILDING_DIR)/objs -f $(BUILD_DIR)/Makefile IN_OBJECT_DIR=1 $@
endef
#---------------------------------------------------------
# 默认目标
#---------------------------------------------------------
default:check_dir
$(MAKE) -C $(BUILDING_DIR)/objs -f $(BUILDING_DIR)/Makefile IN_OBJECT_DIR=1
# 检查目标目录是否存在
check_dir:
ifeq "$(wildcard objs)" ""
@$(MKDIR) -p objs
endif
#---------------------------------------------------------
# 删除目标文件和objs目录
#---------------------------------------------------------
clean:
$(TARGET_CLEAN)
@$(ECHO) Remove dir: objs
-@$(RMDIR) objs
Makefile
ifdef IN_OBJECT_DIR
#---------------------------------------------------------
# 包含environment.mak
#---------------------------------------------------------
include /home/yuqiang/桌面/test/environment.mak
# 定义源文件搜索路径
vpath %.c /home/yuqiang/桌面/test/src
TARGETS := myapp
OBJS := yuer.o main.o
#---------------------------------------------------------
# 设置编译参数变量
#---------------------------------------------------------
INCLUDES := -I/home/yuqiang/桌面/test/include
CFLAGS += $(INCLUDES)
default:$(TARGETS)
$(TARGETS):$(OBJS)
@$(ECHO) Creating $@ ...
@$(CC) $^ -o $@
%.o:%.c
@$(ECHO) Compiling $@ ...
@$(CC) $(CFLAGS) -c $< -o $@
clean:
@$(ECHO) Cleaning $(OBJS) ...
@$(ECHO) Cleaning $(TARGETS) ...
-@$(RM) -f *.o
-@$(RM) -f $(TARGETS)
else
include /home/yuqiang/桌面/test/builddir.mak
endif
注意:
1,此makefile在linux下面的shell运行,如果是在windows下面,要稍微修改一点点,理解原理后可自行修改
2,因为此makefile使用了vpath关键字,所以在gcc命令中,请务必使用自动变量$^ $@ $<,而不要直接使用依赖文件。具体原因清看GNU makefile 3.8
3,在environment.mak中的BUILDING_DIR变量,在不同的时候值是不一样的。在builddir.mak中时为/home/yuqiang/桌面/test/ 在Makefile中是为/home/yuqiang/桌面/test/objs
4,你可能会发现在Makefile和builddir.mak中都一个default目标,会不会产生冲突。不会的,因为他们不可能同时存在。
解释:
为了让你更好的理解此makefile工作的原理,我在这里解释一下,它的工作流程,以便你更快的读懂。
当我们做完所有的工作之后,我们就可以在终端执行make了,当我们输入make的时候,由于Makefile中的IN_OBJECT_DIR未定义的,所以不包含ifdef IN_OBJECT_DIR到else中间的代码,而是else到endif之间的代码,相当于,Makefile仅由一句话include /home/yuqiang/桌面/test/builddir.mak组成。
所以当你输入make的时候,首先执行builddir.mak中的内容,检查objs目录是否存在,不存在则创建,然后在builddir.mak中自动调用make命令,而此时的make命令把一个宏IN_OBJECT_DIR=1传递到了他要调用的makfile中,所以这次调用make时Makefile的内容为ifdef IN_OBJECT_DIR到else之间的内容,创建目标文件和可执行文件。
删除目标文件和目录原理相似
建议:
你已经看到了,我们这样写include /home/yuqiang/桌面/test/builddir.mak其实很不方便的,如果我的test换在了其他的目录下,我们每次都要修改这些地方,不方便,也不简洁,还容易出错,所以我建议,我们可以创建一个shell文件(set.sh)来设置一些变量。把这个set.sh加到test文件夹中。
set.sh
#! /bin/sh
# 不打印目录
MAKEFLAGS:=--no-print-directory
# 清除环境变量
unset YUER_ROOT
unset YUER_INCLUDE
unset YUER_MAKE
# 导出环境变量
export YUER_ROOT=`pwd`
export YUER_INCLUDE=$(YUER_ROOT)/include
export YUER_MAKE=$(YUER_ROOT)/make
if [ -z YUER_ROOT ];then
echo "环境配置成功!"
else
echo "环境配置失败"
fi
这样之后,我们的makefile中的include /home/yuqiang/桌面/test/builddir.mak ... 就可以写成
include $(YUER_ROOT)/environment.mak
include $(YUER_ROOT)/builddir.mak
这样做的好处是,可以更好的移植我们的Make系统,我们还可以把通用文件(%.mak)通过一个变量YUER_MAKE放置到一个目录中,同一管理,多次使用。在你输入make之前,在test目录下运行 source set.sh然后在运行make就可以了。非常方便使用。还有很多的好处,我就不再赘述,等待你自己去发现了。
运行:
$ make
Compiling yuer.o ...
Compiling main.o ...
Creating myapp ...
$ make clean
Cleaning function.o main.o ...
Cleaning myapp ...
Remove dir: objs