工程Makefile的编写,对于初始Makefile的菜鸟们来说,还是比较头疼的一件事情,如何做到整个工程结构清晰,各个模块相对独立,模块的添加删除等都比较方便,各个层次的的Makefile风格一致,便于移植,这些并不容易。这里,就自己学习的一些体会,和大家分享一下。
本例工程,实现几种排序算法的具体代码实现和性能比较。工程结构如下:
sort
|-- Makefile
|-- Makefile.rule
|-- main
| |-- Makefile
| |-- include
| | |-- heap_sort.h
| | |-- quick_sort.h
| | `-- shell_sort.h
| |-- libs
| `-- src
| `-- sort.c
|-- heap_sort
| |-- Makefile
| |-- include
| `-- src
| `-- heap_sort.c
|-- quick_sort
| |-- Makefile
| |-- include
| `-- src
| `-- quick_sort.c
`-- shell_sort
|-- Makefile
|-- include
`-- src
`-- shell_sort.c
工程名:sort,根目录下的一级子目录包括main,heap_sort,quick_sort,shell_sort,每级目录中,有include和 src目录,include中,包含该模块用到的相关头文件,src中是源代码。在main目录中,另有libs文件夹,工程需要引入的库可以放在该文件夹中。
Makefile.rule
该文件为工程Makefile的通用规则定义,在各个模块的Makefile中被include。
该文件具体内容如下:
CC := gcc
HIDE_PREFIX := .
该文件主要定义了工程的编译工具,编译链接的参数等。 EXPORT_ODIR和IMPORT_IDIR是编译之后模块的输入输出,编译生成.i和.o文件夹,.i中为模块需要引入的文件的软链接,.o中是该模块输出的文件的软链接。
工程总Makefile 根目录下的Makefile,内容如下:
TOPDIR := .
MODDIR := .
TARGET := $(notdir $(shell cd $(MODDIR) && pwd))
MODULES += quick_sort \
heap_sort \
shell_sort \
main
-include $(TOPDIR)/Makefile.rule
submodule_make = $(MAKE) -C $(TOPDIR)/$(1);
submodule_clean = $(MAKE) clean -C $(TOPDIR)/$(1);
.PHONY : all deps objs clean cleanall rebuild modules cleanmodules
all : $(TARGET)
deps : $(DEPS)
objs : $(OBJS)
modules :
@ $(foreach n,$(MODULES),$(call submodule_make,$(n)))
cleanmodules :
@ $(foreach n,$(MODULES),$(call submodule_clean,$(n)))
clean :
@ $(RM_F) *.o
@ $(RM_F) *.d
@ $(RM_F) *.a
@ $(RM_F) $(wildcard $(SRC)/*.o)
@ $(RM_F) $(wildcard $(SRC)/*.d)
@ $(RM_F) $(wildcard $(MODIR)/*.d)
@ $(RM_F) $(EXPORT_ODIR)
@ $(RM_F) $(IMPORT_IDIR)
@ echo CLEAN DONE
cleanall: cleanmodules clean
@ $(RM_F) $(wildcard $(TOPDIR)/*.d)
@ $(RM_F) $(TARGET)
@ echo CLEANALL DONE
rebuild: clean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@ $(RM_F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(TARGET) : $(OBJS) modules
@ echo MAKE $(TARGET) START
@ mkdir -p $(EXPORT_ODIR)
@ mkdir -p $(IMPORT_IDIR)
@ echo MAKE $(TARGET) DONE
在这个Makefile中,变量MODULES 定义了各个子模块,quick_sort,heap_sort,shell_sort, main。
main模块中Makefile main模块下的Makefile,内容如下:
TOPDIR := ..
MODDIR := .
MODNAME := $(notdir $(shell cd $(MODDIR) && pwd))
TARGET := $(notdir $(shell cd $(TOPDIR) && pwd))
ALIBS = $(addprefix ../, $(wildcard $(TOPDIR)/$(IMPORT_IDIR)/*.a))
LDFLAGS += $(addprefix -L,$(IMPORT_IDIR))
LDFLAGS += $(foreach n,$(MODULES),$(addprefix -l,$(n)))
MODULES += quick_sort \
heap_sort \
shell_sort
-include $(TOPDIR)/Makefile.rule
submodule_make = $(MAKE) -C $(TOPDIR)/$(1);
submodule_clean = $(MAKE) clean -C $(TOPDIR)/$(1);
.PHONY : all deps objs clean cleanall rebuild modules cleanmodules
all : $(TARGET)
deps : $(DEPS)
objs : $(OBJS)
modules :
@ $(foreach n,$(MODULES),$(call submodule_make,$(n)))
cleanmodules :
@ $(foreach n,$(MODULES),$(call submodule_clean,$(n)))
clean :
@ $(RM_F) *.o
@ $(RM_F) *.d
@ $(RM_F) $(wildcard $(SRC)/*.o)
@ $(RM_F) $(wildcard $(SRC)/*.d)
@ $(RM_F) $(EXPORT_ODIR)
@ $(RM_F) $(IMPORT_IDIR)
@ echo CLEAN DONE
cleanall: cleanmodules clean
@ $(RM_F) $(TARGET)
@ echo CLEANALL DONE
rebuild: clean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@ $(RM_F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(TARGET) : $(OBJS) modules
@ echo MAKE $(TARGET) START
@ mkdir -p $(EXPORT_ODIR)
@ mkdir -p $(IMPORT_IDIR)
@ ln -sft $(IMPORT_IDIR) $(ALIBS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)
@ cp -f $@ $(TOPDIR)
@ echo MAKE $(TARGET) DONE
在此模块中,变量MODULES定义了其依赖的子模块。首先编译子模块,然后编译本模块。
其他模块中Makefile
其他模块下的Makefile,内容如下:
TOPDIR := ..
MODDIR := .
MODNAME := $(notdir $(shell cd $(MODDIR) && pwd))
TARGET := $(addprefix lib,$(addsuffix .a,$(MODNAME)))
EXPORT_HEADERS += $(INCLUDES)/*.h
MODULES +=
-include $(TOPDIR)/Makefile.rule
submodule_make = $(MAKE) -C $(TOPDIR)/$(1);
submodule_clean = $(MAKE) clean -C $(TOPDIR)/$(1);
.PHONY : all deps objs clean cleanall rebuild
all : $(TARGET)
deps : $(DEPS)
objs : $(OBJS)
modules :
@ $(foreach n,$(MODULES),$(call submodule_make,$(n)))
cleanmodules :
@ $(foreach n,$(MODULES),$(call submodule_clean,$(n)))
clean :
@ $(RM_F) *.o
@ $(RM_F) *.d
@ $(RM_F) $(wildcard $(SRC)/*.o)
@ $(RM_F) $(wildcard $(SRC)/*.d)
@ $(RM_F) $(EXPORT_ODIR)
@ $(RM_F) $(IMPORT_IDIR)
@ echo CLEAN DONE
cleanall: cleanmodules clean
@ $(RM_F) $(TARGET)
@ echo CLEANALL DONE
rebuild: clean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@ $(RM_F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(TARGET) : $(OBJS) modules
@ echo MAKE $(TARGET) START
@ mkdir -p $(EXPORT_ODIR)
@ mkdir -p $(IMPORT_IDIR)
@ mkdir -p $(TOPDIR)/$(IMPORT_IDIR)
@ $(AR) $(ARFLAGS) -o $(TARGET) $(OBJS)
@ ln -sft $(TOPDIR)/$(IMPORT_IDIR) $(TOPDIR)/$(MODNAME)/$(TARGET)
@ echo MAKE $(TARGET) DONE
此Makefile不依赖于模块名及模块具体内容,具有较好的移植性。添加新的模块,只需直接将该Makefile复制到新模块目录下就可以了,然后在main模块Makefile和根Makefile中添加新的模块名。直接编译就可以了。
准备好了Makefile,就可以进行编译了。
在sort根目录下运行make:
root@mygirl:/study/sort# make root@mygirl:/study/sort# ls heap_sort sort main Makefile Makefile.rule quick_sort shell_sort
|
看到的新出现的文件sort,就是编译后的可之执行文件。
在sort根目录下运行make cleanall:
root@mygirl:/study/sort# make cleanall root@mygirl:/study/sort# ls
heap_sort main Makefile Makefile.rule quick_sort shell_sort
|
这样,就可以清除编译生成的各个文件了。
至此一切OK! 希望对Makefile初学者有所帮助!