GNU make使用(二)

[时间:2017-06] [状态:Open]
[关键词:makefile,gcc,编译,shell命令,目标文件]

0 引言及目标

之前使用Makefile都是把源文件和目标文件放到同一个目录编译。近期看到有些编译工具支持将目标文件放到独立的目录,将源代码和目标文件(*.o)分开,这样查看代码以及目录结构也会相对清晰些。

下面我们开始展开这个过程。

1 准备工作

我们的目录结构是这样的

src
|---- audio
	    |--- audio.h
		|--- audio.cpp 
|---- video
		|--- video.h
		|--- video.cpp
|---- main.cpp
Makefile
obj

obj目录用于存放编译之后的目标文件,这个是自动生成的目录。src目录包含需要编译的源代码。相关代码及Makefile都可以从我的SampleCode-git下载。

常规的情况是把*.o目标文件和源代码放到同一个目录。

2 单Makefile编译整个工程

注意这个Makefile和目标文件输出目录在同一级。

OBJ_FOLDER := objs
vpath %.cpp src/audio src/video
#源文件,自动找所有.cpp文件,并将目标定义为同名.o文件
SOURCE  := $(wildcard src/*.cpp) $(wildcard src/audio/*.cpp) $(wildcard src/video/*.cpp)
OBJS := $(SOURCE:%.cpp=${OBJ_FOLDER}/%.o)

#目标文件名,输入任意你想要的执行文件名
TARGET  := separator

#编译参数
CC      := g++
LIBS    :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS  := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS)

#下面的基本上不需要做任何改动了
.PHONY : everything objs clean veryclean rebuild

everything : $(TARGET)

all : $(TARGET)

${OBJS} : $(OBJ_FOLDER)/%.o : %.cpp
	mkdir -p $(OBJ_FOLDER)/$(<D)
	$(CC) -c $(CXXFLAGS) $< -o $@

rebuild: veryclean everything

clean :
	rm -fr *.so
	rm $(OBJS)

veryclean : clean
	rm -fr $(TARGET)

$(TARGET) : $(OBJS)
	$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)

相比于常规情况,主要修改如下:

  1. OBJS := $(SOURCE:%.cpp=${OBJ_FOLDER}/%.o) 目标文件列表换到单独的目录。
  2. ${OBJS} : $(OBJ_FOLDER)/%.o : %.cpp 设置依赖规则
    mkdir -p \((OBJ_FOLDER)/\)(<D) # 创建对应的目录,\((<D)表示\)<所在目录名。
    $(CC) -c $(CXXFLAGS) $< -o $@ # 编译规则

3 多层嵌套的Makefile

先更新下目录结构:

src
|---- audio
	    |--- audio.h
		|--- audio.cpp 
|---- video
		|--- video.h
		|--- video.cpp
|---- main.cpp
|---- Makefile
Makefile
obj

这里在src目录下添加一个Makefile文件。我们的顶级Makefile相对会比较简单,但是为了避免遇到例如:./../obj/xxx.o的文件系统报错,因为../不是一个有效的目录名称,需要从主Makefile中传到参数到子目录的Makefile中。其实现如下:

SOURCE_DIR := src
export LOCAL_PATH := $(shell pwd)
$(warning  ${LOCAL_PATH})

.PHONY: all everything clean veryclean rebuild
all:
	$(MAKE) -C ${SOURCE_DIR}

everything:
	$(MAKE) -C ${SOURCE_DIR} $@

clean:
	$(MAKE) -C ${SOURCE_DIR} $@

veryclean:
	$(MAKE) -C ${SOURCE_DIR} $@

rebuild:
	$(MAKE) -C ${SOURCE_DIR} $@

子目录的实现相对简单点,就是把第一句OBJ_FOLDER更新下,修改如下:

OBJ_FOLDER := $(LOCAL_PATH)/objs
#目标文件名,输入任意你想要的执行文件名
TARGET  := $(LOCAL_PATH)/separator

这里主要有一个知识点,从主Makefile传递到子文件夹的Makefile中的参数。有三种方式:

  1. 在上层Makefile中使用”export”关键字对需要传递的变量进行声明。比如:
    DEBUG_SYMBOLS = TRUE
    export DEBUG_SYMBOLS

当不希望将一个变量传递给子makefile时,可以使用指示符 unexport来声明这个变量。
2. export一般用法是在定义变量的同时对它进行声明。如下:
export DEBUG_SYMBOLS = TRUE
3. 在命令行上指定变量。比如:
$(MAKE) -C xxx DEBUG_SYMBOLS = TRUE
这样在进入子目录xxx执行make时该变量也有效。

4 小结

至此,最初的问题基本解决了。本文主要参考如下资料:

posted @ 2017-07-31 18:40  Tocy  阅读(491)  评论(0编辑  收藏  举报