[时间: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 小结

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

GNU make使用(二)的更多相关文章

  1. GNU make 总结 (二)

    规则描述了在何种情况下使用什么命令来创建或者更新一个目标.如果在makefile中第一个规则有多个目标的话,那么多个目标中的第一个将会作为make的“终极目标”. 3.1 规则语法 TARGETS : ...

  2. 从零开始之uboot、移植uboot2017.01(二、从入口分析流程)

    原创: To_run_away 从零开始学linux 本节的开始之前,先看一下uboot的链接脚本. 一.链接脚本 /* * Copyright (c) 2004-2008 Texas Instrum ...

  3. 查看centos版本号

    --写在开始-- 玩Linux,不同的版本会有一些细微区别: so,经常需要查看服务器版本号: --正文-- 有以下命令可以查看linux服务器版本号: # lsb_release -a LSB Ve ...

  4. 在Linux下怎么确定哪个网卡对应哪个接口?

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  5. [Linux Kernel]查看CentOS版本方法

    查看CentOS版本方法  有以下命令可以查看:   # lsb_release -a LSB Version:    :core-3.1-ia32:core-3.1-noarch:graphics- ...

  6. buildroot构建项目(三)--- u-boot 2017.11 适配开发板修改 1

    当前虽然编译成功了,但是对于我们自己的目标板并不太适用.还得做一系列得修改. 一.lds 文件分析 u-boot 中最重要得链接文件即是,u-boot.lds.我们可以查看我们编译出来得 u-boot ...

  7. npm 版本问题

    STF之问题篇 https://yq.aliyun.com/articles/221602 装完成后输入stf doctor查看工具依赖是否正确,安装教程可以参考我之前写的,这里不再多说,直接说问题. ...

  8. 给广大码农分享福利:一个业界良心的github仓库,中文计算机资料

    我今天查资料时无意发现的,https://github.com/CyC2018/CS-Notes 这个仓库包含了下列几个维度的计算机学习资料: 深受国内程序员喜爱,已经有超过3万多star了. 1. ...

  9. Yum三方仓库——RPMForge

    参考:How to Enable RPMForge Repository in RHEL/CentOS 7.x/6.x/5.x RPMForge / RepoForge这两个项目已经死亡,不应该使用 ...

随机推荐

  1. hdu1269 有向图强连通 【Tarjan】(模板)

    <题目链接> 题目大意: 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称 ...

  2. 一个基于C++11的定时器队列(timerfd,poll实现)

    目录 前言 优点 test 源代码 @ 前言 最近小程序要用到定时器,找了一圈也没找到合适的,最后还是绕回来选择了muduo里面的TimerQueue,整理了下它的代码,独立了出来,因为实在懒得从头写 ...

  3. Spring Boot 入门详细分析

    推荐阅读: 我们为什么要学习 Spring Boot 我们搭建 Spring Boot 项目,可以使用 Spring 为我们提供的初始化网站,那个可能不太方便,今天呢,我们就来说说如何使用 IDEA ...

  4. 进程间通信(IPC)

    1.什么是进程间通信 通俗来讲,进程间通信就是:多个进程之间的数据交互 进程都有自己独立的虚拟地址空间,导致进程之间的数据交互变得十分困难,通信复杂了,但是安全性提高了: 进程间通信的本质:多个进程之 ...

  5. odoo 模型继承

    在odoo中有两种模型的继承机制(传统方式和委托继承方式) 重点:在__manifest__.py中找到depends,加上要继承的模块 'depends': ['account'] 注意继承的模型所 ...

  6. SSID 已经一个路由器设多个SSID

    SSID(Service Set Identifier)   SSID,AP唯一的ID码,许多人认为可以将SSID写成ESSID,其实不然,SSID是个笼统的概念,包含了ESSID和BSSID,用来区 ...

  7. [CF490F]Treeland Tour(线段树合并)

    树上LIS:树上找一条简单路径的子序列使点权严格单增,最大化长度. 原题数据过小,用线段树合并可以做到$O(n\log n)$. 每个点用一棵线段树维护以每个权值为结尾的LIS最长长度,线段树合并时更 ...

  8. ab,qps 并发连接数

    并发连接数 = pv / 统计时间 * 页面衍生连接次数 * http响应时间 * 因数 / 其他web服务器 数量 pv = 并发连接数 * 统计时间 * 其他web服务器 数量/ 页面衍生连接次数 ...

  9. config、option、setting辨析

    作为一个编程新手,在软件目录中常常会看到这几个词,尤其 config . setting 翻译成中文区别不是很大,总让人有点区分不了他们的使用场景.在知乎上看到了关于这个问题的讨论觉得受益挺大的,自己 ...

  10. [HackerRank]Choosing White Balls

    [HackerRank]Choosing White Balls 题目大意: 有\(n(n\le30)\)个球排成一行,每个球的颜色为黑或白. 执行\(k\)次操作,第\(i\)次操作形式如下: 从\ ...