• 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 小结

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

  • 相关阅读:
    ubuntu密码正确,却不能登录图形界面
    【转】ubuntu右键在当前位置打开终端
    一些值得学习的Unity教程 (很实用的包括源码)
    Git 报错:git
    Unity3D面试——真实的面试,unity3d面试
    拖拽以及常用的鼠标事件
    白话经典算法系列之一 冒泡排序的三种实现
    c#封装三维向量,另外也看了下别人的C++封装
    c#面试3(选择题)
    Unity3D中目标相对自身的前后左右方位判断
  • 原文地址:https://www.cnblogs.com/tocy/p/makefile-reference-2.html
Copyright © 2020-2023  润新知