makefile编译子目录

时间:2021-08-23 12:46:27

make子目录常用方法

一般是

SUB_DIR =  lib_src service

.PHONY: subdirs $(SUB_DIR)

subdirs: $(SUB_DIR)

$(SUB_DIR):
    @+make -C $@
    
foo: baz

或者

subdirs:
    for dir in $(SUB_DIR); do \
      @+make -C $$dir; \
    done

使用循环的方式比较直观,但是会有这样的问题

  • 当submake报错的时候不会停止,其他submake会继续执行
  • 不能体验到make的并行编译 即-j选项
  • 子目录之间的依赖不好表示

所以,一般来说会选择第一种来写.但是,当用第一种书写时,怎么表达make子命令(即:make install)呢?

make子命令书写


SUB_DIR_TEST = $(SUB_DIR:%=%_test)

test: $(SUB_DIR_TEST)
$(SUB_DIR_TEST): 
    @+make  -C $(@:%_test=%) test

.PHONY: test subdirs $(SUB_DIR_TEST)

核心就是对子目录名字进行包裹一下,把包裹后的名字当作新的伪目标进行构建

实例

以下是我从工作项目中拷出来的
总共三个级别的目录

  • 根目录
  • lib_src service
  • 实际编译目录:game gate

所以列举了3个makefile

项目目录大致结构

目录做了一些删减
bin输出目录:deploy/bin

├── deploy
│   └── bin
├── deps
│   └── tinyxml
│       ├── include
│       │   └── tinyxml
│       │       └── tinyxml.h
│       └── lib
│           └── libtinyxml.a
├── lib_src
│   ├── auth
│   │   ├── auth.cpp
│   │   └── auth.h
│   ├── common
│   │   ├── common.cpp
│   │   └── common.h
│   └── lib
└── service
    ├── game
    │   └── game.cpp
    └── gate

项目根目录下的Makefile

MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
SUB_DIR = lib_src service
SUB_DIR_TEST := $(SUB_DIR:%=%_test)
SUB_DIR_CLEAN := $(SUB_DIR:%=%_clean)

all : $(SUB_DIR)
    
lib_src:
    @+make -C $@

service:lib_src 
    @+make -C $@
    
clean:$(SUB_DIR_CLEAN) 
    #$(foreach N, $(SUB_DIR),make clean -C $(N);)

$(SUB_DIR_CLEAN):
    @+make clean -C $(@:%_clean=%)

test:$(SUB_DIR_TEST)
    @echo $(MKFILE_PATH)
    
$(SUB_DIR_TEST):
    @+make test -C $(@:%_test=%)
    
.PHONY: all test clean $(SUB_DIR) $(SUB_DIR_TEST) $(SUB_DIR_CLEAN)

lib_src下的Makefile

MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
SUB_DIR = auth common
SUB_DIR_CLEAN := $(SUB_DIR:%=%_clean)
SUB_DIR_TEST := $(SUB_DIR:%=%_test)

all : |mk_dir $(SUB_DIR)
    
$(SUB_DIR): mk_dir
    @+make -C $@
    
mk_dir:
    @mkdir -p bin
    
clean:$(SUB_DIR_CLEAN)
    #$(foreach N, $(SUB_DIR),make clean -C $(N);)
    
$(SUB_DIR_CLEAN):
    @+make clean -C $(@:%_clean=%) 
    
test:$(SUB_DIR_TEST)
    @echo $(MKFILE_PATH)

    
$(SUB_DIR_TEST):
    @+make test -C $(@:%_test=%)
    
.PHONY: all test clean mk_dir $(SUB_DIR) $(SUB_DIR_TEST) $(SUB_DIR_CLEAN)

game下的Makefile

编译选项这些忽略吧

MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
include ../../global.mk
TARGET := $(BIN_DIR)/game_svr$(BIN_TAG)
CUR_INC_FLAGS := -I$(FRAME_INC) -I./ -I$(MYSQL_INC)
CUR_LINK_FLAGS := $(LDFLAGS) -L$(MYSQL_LIB)
CUR_CPPFLAGS := $(CPPFLAGS) $(CUR_INC_FLAGS) 
SUB_DIR := app bll dal db thread config
ALL_OBJS = $(wildcard $(OBJ_DIR)/*.o)

all : |$(OBJ_DIR) $(TARGET)
    
$(TARGET) : $(TARGET_OBJS)  $(SUB_DIR)
    $(CXX) -o $@ $(ALL_OBJS) $(CUR_LINK_FLAGS)
    @echo "#### compile ok "$(TARGET) "####"
    
$(OBJ_DIR)/%.o : %.cpp
    $(CXX) $(CUR_CPPFLAGS) -c $< -o $@
    
$(OBJ_DIR):
    @mkdir  $@

$(SUB_DIR): 
    @echo "#### compile sub dir start ####" $@
    @+make -C $@
    @echo "#### compile sub dir end ####" $@

clean: 

    @rm -f $(wildcard $(OBJ_DIR)/*.o) $(TARGET)
    
test:
    @echo $(MKFILE_PATH)

.PHONY: all test clean 
.PHONY: $(SUB_DIR) 

简要语法说明

$(SUB_DIR:%=%_test)
表示文件名加后缀,结果为lib_src_test service_test
$(@:%_test=%)
表示去掉后缀,结果为lib_src service
=

在真正执行命令的时候才会对变量求值,所以变量值可能会在中间因为其他引用的其他变量被改变而不是预期的

=:

在赋值的时候直接对变量求值,以后如果不重新赋值是不会变化的