Makefile入门

0. 作用

Makefile文件告诉Make怎样编译和连接成一个程序。

1. Makefile基本语法与执行

示例

编译一个单文件HelloWorld.cpp

  • 编写Makefile
HelloWorld : HelloWorld.cpp
  g++ HelloWorld.cpp -o HelloWorld
clean :
  rm HelloWorld
  • 编译
make
  • 清空
make clean

构成

Makefile主要由多条规则构成,每条规则由三部分构成:目标(target)、依赖(prerequiries)和命令(command)。

格式

按如下格式编写Makefile

目标(target): 依赖(prerequiries)...
  命令(command)
  • 目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’(仅仅表达动作的目标称为假想目标)。
  • 依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。
  • 命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。

注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。

说明

  1. 默认情况下,make最先执行第一条。
  2. 使用make 目标名的方式,执行指定的规则。

2. Makefile多文件编译

示例

  • String.h
#ifndef _STRING_H_
#define _STRING_H_
#include <iostream>
using namespace std;
#include <string.h>
class String{
public:
    String(const char* cstr = NULL);
    String(const String& str);
    String& operator=(const String& str);
    ~String();
    char* c_str() const {
        return m_data;
    }
private:
    char* m_data;
};
ostream& operator<<(ostream& os, const String& str);
#endif // _STRING_H_
  • String.cpp
#include "String.h"
String::String(const char* cstr /*= NULL*/) {
    if (cstr) {
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data, cstr);
    }
    else {
        m_data = new char[1];
        *m_data = '\0';
    }
}
String::String(const String& str) {
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
}
String& String::operator=(const String& str) {
    //检测是否自我赋值
    if (this == &str)
        return *this;
    delete [] m_data;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}
String::~String() {
    delete[] m_data;
}
ostream& operator<<(ostream& os, const String& str) {
    os << str.c_str();
    return os;
}
  • StringTest.cpp
#include "String.h"
int main() {
    String s1;
    String s2("hello");
    String s3(s1);  //拷贝构造函数
    cout << s3 << endl;
    s3 = s2;    //拷贝赋值函数
    cout << s3 << endl;
    return 0;
}
  • makefile
StringTest:StringTest.o String.o
  g++ -o StringTest StringTest.o String.o
StringTest.o:StringTest.cpp String.h
  g++ -c StringTest.cpp
String.o:String.cpp String.h
  g++ -c String.cpp
clean :
  rm StringTest StringTest.o String.o

说明

  1. make执行规则之前,检查依赖是否存在或者是否最新的。如果不是则执行依赖对应的规则,创建或者更新依赖。

3. 使用变量简化makefile

每次增加新的文件,需要在makefile的很多地方增加依赖,容易导致遗漏。可以使用变量可以简化,避免这种出错的可能。

  • 变量定义:变量 = 字符串
  • 变量使用:$(变量名)

示例

  • makefile
OBJS = StringTest.o String.o

StringTest:$(OBJS)
  g++ -o StringTest $(OBJS)
StringTest.o:StringTest.cpp String.h
  g++ -c StringTest.cpp
String.o:String.cpp String.h
  g++ -c String.cpp
clean :
  rm StringTest $(OBJS)

在makefile文件中使用名为objects, OBJECTS, objs, OBJS, obj, 或 OBJ的变量代表所有OBJ文件已是约定成俗。

说明

  1. 变量是定义一个字符串,在多处替代该字符串使用。

4. 命令自动推导

编译.o文件这类非常普遍并且常用,规则也比较简单

文件名.o:文件名.cpp 头文件
  g++ -c 文件名.cpp

make提供一种简化写法,可以自动推导出该规则

文件名.o:头文件

这种简化规则称为隐含规则,非简化规则成为具体规则。

示例

  • makefile
OBJS = StringTest.o String.o

StringTest:$(OBJS)
  g++ -o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h

clean :
  rm StringTest $(OBJS)
  • 小知识
    通常,规则按照目标进行分组。规则也可以按照依赖分组。例如,例子中String.oStringTest.o都依赖String.h。那么,可以这两个合并到一个规则中。
OBJS = StringTest.o String.o

StringTest:$(OBJS)
 g++ -o StringTest $(OBJS)
StringTest.o String.o:String.h

clean :
 rm StringTest $(OBJS)

按照依赖分组规则可以减少规则数量,规则按照目标分组更符合我们日常思维习惯。


5. 假想目标

表达动作的目标称为假想目标。通常规则会生成或者更新与目标的同名文件,但是假想目标不生成文件,只是作为几个命令组成特殊规则的名称。例如例子中的clean,只是执行清理动作。如果,makefile同级目录存在与假象目标同名的文件(例如:clean),那么会导致命令不会被执行。所以需要把目标显示声明为假想目标。

.PHONY 目标

示例

  • makefile
OBJS = StringTest.o String.o

.PHONY: all clean

all:StringTest

StringTest:$(OBJS)
  g++ -o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h

clean :
  rm StringTest $(OBJS)

  • 常用假想目标
No. 假想目标 功能
1 all 这所有目标的目标,一般是编译所有的目标。
2 clean 删除所有被make创建的文件。
3 install 安装已编译好的程序,就是把目标执行文件拷贝到指定的目标中去。
4 print 列出改变过的源文件。
5 tar 源程序打tar包备份
6 dist 创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。

6. 通配符与变量

编译.o文件可以写成更通用的方式,使用我们之前已经定义好的变量$(OBJS),自动推导出需要生成的规则。

  • makefile
OBJS = StringTest.o String.o

.PHONY: all clean

all:StringTest

StringTest:$(OBJS)
  g++ -o StringTest $^
$(OBJS):%.o:%.cpp
  $(CXX) -c $(CXXFLAGS) $< -o $@

.PHONY: clean

clean :
  rm StringTest $(OBJS)

说明

1. 通配符

通配符主要用于匹配文件名,makefile中使用%作为通配符。从匹配目标格式的目标名中依据通配符抽取部分字符串,再按照抽取字符串分配到每一个依赖格式中产生依赖名。例如,使用%.o:%.cpp

2. 自动变量

自动变量是在规则每次执行时都基于目标和依赖产生新值的变量。下面是常用的自动变量。

No. 自动变量 含义
1 $< 表示第一个匹配的依赖
2 $@ 表示目标
3 $^ 所有依赖
4 $? 所有依赖中更新的文件
5 $+ 所有依赖文件不去重
6 $(@D) 目标文件路径
7 $(@F) 目标文件名称
3. 预定义变量

预定义变量是makefile已经定义好的变量,用户可以在makefile文件中改变变量的值。

  • 程序名变量
No. 变量 程序 默认值
1 CC C语言编译程序 cc
2 CXX C++编译程序 g++
3 AR C++打包程序 ar
4 CPP 带有标准输出的C语言预处理程序 $(CC) -E
5 RM 删除命令 rm
  • 程序运行参数的变量
No. 变量 程序参数
1 CFLAGS 用于C编译器的额外标志
2 CXXFLAGS 用于C++编译器的额外标志
3 ARFLAGS 用于C/C++打包器的额外标志
4 LDFLAGS 链接库路径-L
5 LDLIBS 链接库-l

9. 其他

  • 注释#
  • 换行\
  • 回显命令@echo

10. 总结

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 一.makefile格式 1.makefile 文件一般命名 为makefile 即可,放在目录下面,直接调出 t...
    浩林Leon阅读 7,709评论 0 2
  • 来自陈浩的一片老文,但绝对营养。 示例工程:3 个头文件*.h,和 8 个 C 文件*.c。 初 编译过程,源文件...
    周筱鲁阅读 4,677评论 0 17
  • 通常一个大型程序由多个模块文件构成的,按照其功能划分,模块文件会分布在不同的目录中,模块文件之间有包含有头文件,调...
    wayyyy阅读 1,363评论 0 0
  • makefile关系到整个工程的编译规则,一个工程中的源文件不计其数,按其类型、功能、模块分别放在若干的目录当中,...
    Joe_HUST阅读 1,870评论 0 3
  • 洛阳王利华 坚持分享第220(2017.12.18)天: 今天给学生们做了一个“猜猜看”的游戏。猜之前每...
    骄阳567阅读 275评论 0 0