Makefile入门

作者: jdzhangxin | 来源:发表于2018-03-30 15:27 被阅读85次

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 $^ 所有依赖
3. 预定义变量

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

  • 程序名变量
变量 程序 默认值
CC C语言编译程序 cc
CXX C++编译程序 g++
CPP 带有标准输出的C语言预处理程序 $(CC) -E
  • 程序运行参数的变量
变量 程序参数
CFLAGS 用于C编译器的额外标志
CXXFLAGS 用于C++编译器的额外标志

9. 其他

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

10. 总结

参考

相关文章

  • Makefile入门

    0. 作用 Makefile文件告诉Make怎样编译和连接成一个程序。 1. Makefile基本语法与执行 示例...

  • Makefile入门

    make是由一组一组的规则组成的 规则的书写形式: 目标:依赖 [tab]命令 makefile变量 变量名 = ...

  • Makefile入门

    make是一个自动化构建工具,广泛应用于Unix及其类Unix系统中。make最先应用于编译C语言项目,不仅如此,...

  • makefile入门

    典型的makefile规则如下,参考howto_makefiles target 通常是程序所生成的文件的名字,类...

  • Makefile入门(二):MakeFile介绍

    makefile 介绍 make命令执行时,需要一个 makefile 文件,以告诉make命令如何去编译和链接程...

  • makefile快速入门

    makefile的作用:简化编译的过程,通过脚本来执行编译命令1、注释:‘#’开头:如: #这是一个注释 2、执行...

  • makefile入门三

    九、模式变量 在GNU的make中,还支持模式变量(Pattern-specific Variable),通过上面...

  • makefile入门一

    Linux下Makefile详解 下面就来看如何写Makefile文件:Makefile文件编写规范: 下来看事例...

  • makefile入门二

    Makefile教程(绝对经典,所有问题看这一篇足够了) makefile很重要 0.1 关于程序的编译和链接 在...

  • Makefile简单入门

    最近工作编译程序一直在用别人写的Makefile,但是没有系统的学习过,趁着放假学一波 0x00 Makefile...

网友评论

    本文标题:Makefile入门

    本文链接:https://www.haomeiwen.com/subject/bkdrcftx.html