Makefile 浅析 (3)

2017-12-14  本文已影响0人  heyzqq

今天, 做了个比较复杂的项目, 先来看看项目结构:

├── build  // 编译目录
│   ├── exes
│   │   └── huge.exe
│   ├── libs
│   │   ├── libbar.a
│   │   └── libfoo.a
│   ├── Makefile
│   └── make.rule
└── source  // 源码目录
    ├── bar  // 小项目 1
    │   ├── inc
    │   │   └── bar.h
    │   └── src
    │       ├── bar.c
    │       ├── deps
    │       │   └── bar.dep
    │       ├── makefile
    │       └── objs
    │           └── bar.o
    ├── foo  // 小项目 2
    │   ├── inc
    │   │   └── foo.h
    │   └── src
    │       ├── deps
    │       │   └── foo.dep
    │       ├── foo.c
    │       ├── makefile
    │       └── objs
    │           └── foo.o
    └── huge  // 小项目 3
        └── src
            ├── deps
            │   └── main.dep
            ├── main.c
            ├── makefile
            └── objs
                └── main.o

1. 首先, 可以直接创建好目录

[root@ test]# mkdir -p build source/foo/src source/foo/inc source/huge/src

2. 然后把资源文件都放到相应的文件夹

├── build
│   ├── Makefile
│   └── make.rule
└── source
    ├── bar
    │   ├── inc
    │   │   └── bar.h
    │   └── src
    │       ├── bar.c
    │       ├── makefile
    ├── foo
    │   ├── inc
    │   │   └── foo.h
    │   └── src
    │       ├── foo.c
    │       ├── makefile
    └── huge
        └── src
            ├── main.c
            └──  makefile

3. 从 build 目录开始看

  1. make.rule: 这是 make 的主要规则, 即把各个 makefile 的共同点合并, 到时候直接 include 就可以了.
  2. Makefile: 编译整个工程的 Makefile, 它只是便利每个小项目, 然后执行该项目自己的 makefile.
# 文件: make.rule

.PHONY: all test clean

CC          = gcc 
AR          = ar
ARFLAGS     = crs 
MKDIR       = mkdir
RM          = rm
RMFLAGS     = -rf 

ROOT_DIR   ?= /home/XXX
DIR_OBJS    = objs
DIR_EXES    = $(ROOT)/build/exes
DIR_DEPS    = deps
DIR_LIBS    = $(ROOT)/build/libs
ifeq ("$(wildcard $(DIR_DEPS))", "") 
    DIR_DEPS_PRE := $(DIR_DEPS)
endif
ifeq ("$(wildcard $(DIR_OBJS))", "") 
    DIR_OBJS_PRE := $(DIR_OBJS)
endif
DIRS        = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) $(DIR_LIBS)
RMS         = $(DIR_OBJS) $(DIR_DEPS)

# EXE       =
ifneq ($(EXE), "") 
EXE        := $(addprefix $(DIR_EXES)/, $(EXE))
RMS        += $(EXE)
endif

# LIB       = libfoo.a
ifneq ($(LIB), "") 
LIB        := $(addprefix $(DIR_LIBS)/, $(LIB))
RMS        += $(LIB)
endif

SRCS        = $(wildcard *.c)
OBJS        = $(patsubst %.c, %.o, $(SRCS))
OBJS       := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS        = $(SRCS:.c=.dep)
DEPS       := $(addprefix $(DIR_DEPS)/, $(DEPS))

ifneq ($(EXE), "") 
all: $(EXE)
endif

ifneq ($(LIB), "") 
all: $(LIB)
endif

ifneq ($(MAKECMDGOALS), clean)
include $(DEPS)
endif

# 添加 include 目录
ifneq ($(INC_DIRS), "") 
INC_DIRS := $(strip $(INC_DIRS))
INC_DIRS := $(addprefix -I, $(INC_DIRS))
endif

ifneq ($(LINK_LIBS), "")
LINK_LIBS := $(strip $(LINK_LIBS))
LINK_LIBS := $(addprefix -l, $(LINK_LIBS))
endif

$(DIRS):
    $(MKDIR) $@


$(EXE): $(DIR_EXES) $(OBJS)
    $(CC) -L$(DIR_LIBS) -o $@ $(filter %.o, $^) $(LINK_LIBS)

$(LIB): $(DIR_LIBS) $(OBJS)
    $(AR) $(ARFLAGS) $@ $(filter %.o, $^)

$(DIR_OBJS)/%.o: $(DIR_OBJS_PRE) %.c
    $(CC) $(INC_DIRS) -o $@ -c $(filter %.c, $^)

$(DIR_DEPS)/%.dep: $(DIR_DEPS_PRE) %.c
    @echo "Making $@ ..."
    @set -e; \
    $(CC) $(INC_DIRS) -E -MM $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]* ,$(DIR_OBJS)/\1.o $@: ,g' > $@

clean:
    $(RM) $(RMFLAGS) $(RMS)

# 文件: Makefile

.PHONY: all clean

DIRS    = $(ROOT_DIR)/source/foo/src \
          $(ROOT_DIR)/source/bar/src \
          $(ROOT_DIR)/source/huge/src

RM      = rm
RMFLAGS = -rf 
RMS     = $(ROOT_DIR)/build/exes \
          $(ROOT_DIR)/build/libs

all:
    @set -e; \
    for dir in $(DIRS); \
    do \
        cd $$dir && $(MAKE); \
    done

    @echo ""
    @echo ":-) Completed !"
    @echo ""

clean:
    @set -e; \
    for dir in $(DIRS); \
    do \
        cd $$dir && $(MAKE) clean; \
    done
    $(RM) $(RMFLAGS) $(RMS)
    @echo ""
    @echo ":-) Completed !"
    @echo ""

4. 然后就是 source 里面个目录的 Makefile

这部分很简单, 只是 include make.rule, 然后配置自己独有/不同的变量:

# 文件: bar/src/makefile
EXE         =
LIB         = libbar.a

INC_DIRS    = $(ROOT_DIR)/source/bar/inc

LINK_LIBS   = 

include $(ROOT_DIR)/build/make.rule
# 文件: foo/src/makefile
EXE         =
LIB         = libfoo.a

# 头文件路径
INC_DIRS    = $(ROOT_DIR)/source/foo/inc

LINK_LIBS   =

# ROOT 为 buid 的绝对路径.
include $(ROOT_DIR)/build/make.rule
# 文件: huge/src/makefile
EXE         = huge.exe
LIB         = 

INC_DIRS = $(ROOT_DIR)/source/foo/inc \
           $(ROOT_DIR)/source/bar/inc

LINK_LIBS = foo bar

include $(ROOT_DIR)/build/make.rule

5. 最后, 在 build 目录 make, 将执行 build/Makefile 里面的规则, 编译 source 里面所有的源文件

过程大概如下:

make[1]: Entering directory '/home/XXX/source/foo/src'
mkdir deps
Making deps/foo.dep ...
mkdir /home/XXX/build/libs
mkdir objs
gcc -I/home/XXX/source/foo/inc -o objs/foo.o -c foo.c
ar crs /home/XXX/build/libs/libfoo.a objs/foo.o
make[1]: Leaving directory '/home/XXX/source/foo/src'
make[1]: Entering directory '/home/XXX/source/bar/src'
/home/XXX/build/make.rule:52: deps/bar.dep: 没有那个文件或目录
mkdir deps
Making deps/bar.dep ...
mkdir objs
gcc -I/home/XXX/source/bar/inc -o objs/bar.o -c bar.c
ar crs /home/XXX/build/libs/libbar.a objs/bar.o
make[1]: Leaving directory '/home/XXX/source/bar/src'
make[1]: Entering directory '/home/XXX/source/huge/src'
/home/XXX/build/make.rule:52: deps/main.dep: 没有那个文件或目录
mkdir deps
Making deps/main.dep ...
mkdir /home/XXX/build/exes
mkdir objs
gcc -I/home/XXX/source/foo/inc -I/home/XXX/source/bar/inc -o objs/main.o -c main.c
gcc -L/home/XXX/build/libs -o /home/XXX/build/exes/huge.exe objs/main.o -lfoo -lbar
make[1]: Leaving directory '/home/XXX/source/huge/src'

:-) Completed !

[1] 至简李云. 驾驭Makefile(准完整版) 2009.08.25

上一篇下一篇

猜你喜欢

热点阅读