- #声明伪目标,防止Makefile去生成all等
 
- .PHONY = all install clean
 
-  
 
- #定义路径变量,所有.c文件和所有非公开的.h应该放在src下,所有需要的.a文件放在lib
 
- #下,所有公开的.h(比如生成库文件的时候)或者多个.c公用的.h放在include文件夹下
 
- #global directory defined
 
- TOPDIR = $(shell pwd)
 
- SRCDIR         = $(TOPDIR)/src
 
- LIBDIR = $(TOPDIR)/lib
 
- OBJECTDIR = $(TOPDIR)/build
 
- INCLUDEDIR = $(TOPDIR)/include
 
-  
 
- #定义交叉编译环境变量,当需要编译arm/mips等平台应用程序/库的时候修改它
 
- #cross compile tools defined 
 
- CROSS_COMPILE = 
 
- AS = $(CROSS_COMPILE)as
 
- LD = $(CROSS_COMPILE)ld
 
- CC = $(CROSS_COMPILE)gcc
 
- CPP = $(CC) -E
 
- AR = $(CROSS_COMPILE)ar
 
- NM = $(CROSS_COMPILE)nm
 
- STRIP = $(CROSS_COMPILE)strip
 
- RANLIB     = $(CROSS_COMPILE)ranlib
 
-  
 
- #本机相关的命令,一般无需修改
 
- #local host tools defined
 
- CP        := cp
 
- RM        := rm
 
- MKDIR    := mkdir
 
- SED        := sed
 
- FIND    := find
 
- MKDIR    := mkdir
 
- XARGS    := xargs
 
-  
 
- #目标名称,这里我们给出了三种常用的目标格式:目标文件,静态库和共享库
 
- #target name
 
- TARGETMAIN     = testmk
 
- TARGETLIBS     = libmk.a
 
- TARGETSLIBS    = libmk.so
 
-  
 
- #所有源码文件的路径被放入SOURCEDIRS,所有.c源码文件(含路径)放入SOURCES
 
- #.c .o and .d files defined
 
- VPATH             = $(shell ls -AxR $(SRCDIR)|grep ":"|grep -v "\.svn"|tr -d ‘:‘)
 
- SOURCEDIRS    = $(VPATH)
 
- SOURCES     = $(foreach subdir,$(SOURCEDIRS),$(wildcard $(subdir)/*.c))
 
-  
 
- #所有目标文件.o(含路径)放入BUILDOBJS,注意它们的路径已经是build了。
 
- SRCOBJS             = $(patsubst %.c,%.o,$(SOURCES))
 
- BUILDOBJS = $(subst $(SRCDIR),$(OBJECTDIR),$(SRCOBJS))
 
-  
 
- #所有.d依赖文件放入DEPS
 
- DEPS            = $(patsubst %.o,%.d,$(BUILDOBJS))
 
-  
 
- #注意-MD,是为了生成.d文件后,构造对.h的依赖
 
- #external include file define
 
- CFLAGS    = -O2 -Wall -MD $(foreach dir,$(INCLUDEDIR),-I$(dir))
 
- ARFLAGS = rc
 
-  
 
- #special parameters for app
 
- CFLAGS    +=
 
-  
 
- #LDFLAGS指明所有-llibxx,libxx.a应该放到lib下,当然也可以添加.so。Xlinker是为了
 
- #在提供多个.a时,未知它们之间的依赖顺序时,自动查找依赖顺序
 
- #c file compile parameters and linked libraries
 
- CPPFLAGS = 
 
- LDFLAGS     =
 
- XLDFLAGS = -Xlinker "-(" $(LDFLAGS) -Xlinker "-)"
 
- LDLIBS         += -L $(LIBDIR) 
 
-  
 
- #如果要生成.a或者.so,那么不要将main函数所在的.c放入src。另外添加$(TARGETLIBS) 
 
- #或$(TARGETSLIBS)到all中
 
- #defaut target:compile the currrent dir file and sub dir 
 
- all: $(TARGETMAIN)
 
-  
 
- #for .h header files dependence
 
- -include $(DEPS)
 
-  
 
- $(TARGETMAIN) :$(BUILDOBJS)
 
-     @$(CC) $(subst $(SRCDIR),$(OBJECTDIR),$^) $(CPPFLAGS) $(CFLAGS) $(XLDFLAGS) -o $@ $(LDLIBS) 
 
-     @$(STRIP) --strip-unneeded $(TARGETMAIN)
 
-  
 
- $(TARGETLIBS) :$(BUILDOBJS)
 
-     @$(AR) $(ARFLAGS) $@ $(BUILDOBJS)
 
-     @$(RANLIB) $@
 
-  
 
- $(TARGETSLIBS) :$(BUILDOBJS)
 
-     @$(CC) -shared $(subst $(SRCDIR),$(OBJECTDIR),$^) $(CPPFLAGS) $(CFLAGS) $(XLDFLAGS) -o $@ $(LDLIBS)
 
-  
 
- #这里是Makefile的核心,根据%中的内容,查找src路径下对应的.c,注意到$@和$<自动
 
- #变量的取值,首先查看路径build/xx是否存在,不存在则创建,然后我们尝试将$@中的src
 
- #替换为build,这样所有的.o和.d都将被创建到对应的build下了。
 
- $(OBJECTDIR)%.o: $(SRCDIR)%.c
 
-     @[ ! -d $(dir $(subst $(SRCDIR),$(OBJECTDIR),$@)) ] & $(MKDIR) -p $(dir $(subst $(SRCDIR),$(OBJECTDIR),$@))
 
-     @$(CC) $(CPPFLAGS) $(CFLAGS) -o $(subst $(SRCDIR),$(OBJECTDIR),$@) -c $<
 
- #添加安装的路径
 
- intall:
 
-  
 
- clean:
 
-     @$(FIND) $(OBJECTDIR) -name "*.o" -o -name "*.d" | $(XARGS) $(RM) -f
 
-     @$(RM) -f $(TARGETMAIN) $(TARGETLIBS) $(TARGETSLIBS)
 
 
经测试,完全满足了要求,虽然不是完美无缺,但是对于通常的工程项目,是绰绰有余了,欢迎大家指正,以便改善。
 
   另外对于Makefile的理解,个人认为可以分成几个部分:依赖关系,自定义变量和自动变量以及Makefile提供的相关函数。理解了它们,对写出结构良好,通用性强的Makefile会有大的帮助,虽然现在有了automake,但是研究一下Makefile的写法还是有收获的。
 
   附件testmk.rar中提供了一个完整的测试程序,目录如下:
 
- |-- build
 
- |-- include
 
- | `-- hello.h
 
- |-- lib
 
- |-- Makefile
 
- `-- src
 
-     |-- a
 
-     | `-- a.c
 
-     |-- b
 
-     | `-- b.c
 
-     `-- main.c
 
-  
 
- 6 directories, 5 files
 
 
 
 
 
  在linux下解压开后运行命令一下命令,可以根据需要定制Makefile。
 
- 
#make            // 当前目录下生成testmk
 
- 
#make libmk.a    // 当前目录下生成libmk.a
 
- 
#make libmk.so    // 当前目录下生成libmk.so
 
 
 
 
   参考资料:
 
- 跟我一起写Makefile(通俗易懂,易于理解)
 
- GNU make中文手册(内容很全,是对GNU makefile手册的完全翻译,做手册参考)
 
 
   思考:
 
    根据Makefile的依赖原理,似乎应该使用一个树类型的结构,a依赖b,c等,b又依赖其他的文件,但是不能反方向依赖(否则为死循环),但是d可以同时依赖b,c,所以不是简单的树形结构,应该是一个图,另外依赖是有方向性的,所以应该是一个有向图结构的实现,不知道对不对?
 
 
# A generic template Makefile
# Author Red_Liu lli_njupt@163.com v0.1
#
# This file is a(part of) free software; you can redistribute it 
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2, or 
# (at your option) any later version.
.PHONY = all install clean
#global directory defined
TOPDIR       = $(shell pwd)
SRCDIR         = $(TOPDIR)/src
LIBDIR      = $(TOPDIR)/lib
OBJECTDIR    = $(TOPDIR)/build
INCLUDEDIR   = $(TOPDIR)/include
#cross compile tools defined 
CROSS_COMPILE ?= 
AS      = $(CROSS_COMPILE)as
LD      = $(CROSS_COMPILE)ld
CC      = $(CROSS_COMPILE)gcc
CPP     = $(CC) -E
AR      = $(CROSS_COMPILE)ar
NM      = $(CROSS_COMPILE)nm
STRIP   = $(CROSS_COMPILE)strip
RANLIB     = $(CROSS_COMPILE)ranlib
#local host tools defined
CP        := cp
RM        := rm
MKDIR    := mkdir
SED        := sed
FIND    := find
MKDIR    := mkdir
XARGS    := xargs
#target name
TARGETMAIN  = testmk
TARGETLIBS     = libmk.a
TARGETSLIBS = libmk.so
#FILE‘ INFOMATION COLLECT
VPATH             = $(shell ls -AxR $(SRCDIR)|grep ":"|grep -v "\.svn"|tr -d ‘:‘)
SOURCEDIRS    = $(VPATH)
#search source file in the current dir
SOURCES      = $(foreach subdir,$(SOURCEDIRS),$(wildcard $(subdir)/*.c))
SRCOBJS             = $(patsubst %.c,%.o,$(SOURCES))
BUILDOBJS = $(subst $(SRCDIR),$(OBJECTDIR),$(SRCOBJS))
DEPS            = $(patsubst %.o,%.d,$(BUILDOBJS))
#external include file define
CFLAGS    = -O2 -Wall -MD $(foreach dir,$(INCLUDEDIR),-I$(dir))
ARFLAGS = rc
#special parameters for apps
CFLAGS    +=
#c file compile parameters and linked libraries
CPPFLAGS    = 
LDFLAGS        =
XLDFLAGS   = -Xlinker "-(" $(LDFLAGS) -Xlinker "-)"
LDLIBS         += -L $(LIBDIR) 
#defaut target:compile the currrent dir file and sub dir 
all:  $(TARGETMAIN)
#for .h header files dependence
-include $(DEPS)
$(TARGETMAIN) :$(BUILDOBJS)
    @$(CC) $(subst $(SRCDIR),$(OBJECTDIR),$^) $(CPPFLAGS) $(CFLAGS) $(XLDFLAGS) -o $@ $(LDLIBS) 
    @$(STRIP)  --strip-unneeded $(TARGETMAIN)
$(TARGETLIBS) :$(BUILDOBJS)
    @$(AR) $(ARFLAGS) $@ $(BUILDOBJS)
    @$(RANLIB) $@
$(TARGETSLIBS) :$(BUILDOBJS)
    @$(CC) -shared $(subst $(SRCDIR),$(OBJECTDIR),$^) $(CPPFLAGS) $(CFLAGS) $(XLDFLAGS) -o $@ $(LDLIBS)
$(OBJECTDIR)%.o: $(SRCDIR)%.c
    @[ ! -d $(dir $(subst $(SRCDIR),$(OBJECTDIR),$@)) ] & $(MKDIR) -p $(dir $(subst $(SRCDIR),$(OBJECTDIR),$@))
    @$(CC) $(CPPFLAGS) $(CFLAGS) -o $(subst $(SRCDIR),$(OBJECTDIR),$@) -c $<
intall:
clean:
    @$(FIND) $(OBJECTDIR) -name "*.o" -o -name "*.d" | $(XARGS) $(RM) -f
    @$(RM) -f $(TARGETMAIN) $(TARGETLIBS) $(TARGETSLIBS)