Makefile基础知识

1、 Makefile简单示例

hello: hello.cpp
    g++ hello.cpp -o hello # 开头必须为一个Tab,不能为空格

 但通常需要将编译与链接分开写,分为如下两步:

hello: hello.o
    g++ hello.o -o hello
hello.o: hello.cpp
    g++ -c hello.cpp

2、Makefile文件的命名与指定

Make会自动查找makefile文件,查找顺序为GNUmakefile -> makefile -> Makefile

GNUmakefile:不建议使用,因为只有GNU make会识别,其他版本的make(如BSD make, Windows nmake等)不会识别,如果只给GNU make使用的情况

makefile:可以使用,GNU make和其他版本make识别

Makefile:最常用,强烈建议使用

如果运行make的时候没有找到以上名字的文件,则会报错,这时候可以手动指定文件名

make -f mkfile  # make -f <filename>
make --file=mkfile # make --file=<filename>

注意:手动指定之后,make就会使用指定的文件,即使有Makefile或者makefile不会再自动使用

3、Makefile文件内容组成

一个Makefile文件通常由五种类型的内容组成:显式规则、隐式规则、变量定义、指令和注释

显式规则(explicit rules):显式指明何时以及如何生成或更新目标文件,显式规则包括目标、依赖和更新方法三个部分

隐式规则(implicit rules):根据文件自动推导如何从依赖生成或更新目标文件。

变量定义(variable definitions):定义变量并指定值,值都是字符串,类似C语言中的宏定义(#define),在使用时将值展开到引用位置

指令(directives):在make读取Makefile的过程中做一些特别的操作,包括:

  1. 读取(包含)另一个makefile文件(类似C语言中的#include)

  2. 确定是否使用或略过makefile文件中的一部分内容(类似C语言中的#if)

  3. 定义多行变量

注释(comments):一行当中 # 后面的内容都是注释,不会被make执行。make当中只有单行注释。如果需要用到#而不是注释,用\#。

4、make使用流程

1、准备好需要编译的源代码

2、编写Makefile文件

3、在命令行执行make命令

稍微复杂的Makefile

sudoku: block.o command.o input.o main.o scene.o test.o
    g++ -o sudoku block.o command.o input.o main.o scene.o test.o

block.o: block.cpp common.h block.h color.h
    g++ -c block.cpp

command.o: command.cpp scene.h common.h block.h command.h
    g++ -c command.cpp

input.o: input.cpp common.h utility.inl
    g++ -c input.cpp

main.o: main.cpp scene.h common.h block.h command.h input.h
    g++ -c main.cpp

scene.o: scene.cpp common.h scene.h block.h command.h utility.inl
    g++ -c scene.cpp

test.o: test.cpp test.h scene.h common.h block.h command.h
    g++ -c test.cpp

hello.o: hello.cpp
    g++ -c hello.cpp


clean:
    rm block.o command.o input.o main.o scene.o test.o
    rm sudoku.exe

规则(Rules):一个Makefile文件由一条一条的规则构成,一条规则结构如下

target … (目标): prerequisites …(依赖)
        recipe(方法)
        …
        …

第二种写法:

target … (目标): prerequisites …(依赖); recipe(方法) ;…

         Make主要用于处理C和C++的编译工作,但不只能处理C和C++,所有编译器/解释器能在命令行终端运行的编程语言都可以处理(例如Java、Python、 Golang....)。Make也不只能用来处理编程语言,所有基于一些文件(依赖)的改变去更新另一些文件(目标)的工作都可以做。

(1)目标

  1. Makefile中会有很多目标,但最终目标只有一个,其他所有内容都是为这个最终目标服务的,写Makefile的时候先写出最终目标,再依次解决总目标的依赖

  2. 一般情况第一条规则中的目标会被确立为最终目标,第一条规则默认会被make执行

  3. 通常来说目标是一个文件,一条规则的目的就是生成或更新目标文件。

  4. make会根据目标文件和依赖文件最后修改时间判断是否需要执行更新目标文件的方法。如果目标文件不存在或者目标文件最后修改时间早于其中一个依赖文件最后修改时间,则重新执行更新目标文件的方法。否则不会执行。

  5. 除了最终目标对应的更新方法默认会执行外,如果Makefile中一个目标不是其他目标的依赖,那么这个目标对应的规则不会自动执行。需要手动指定,方法为

    make <target>  # 如 make clean , make hello.o
  6. 可以使用.DEFAULT_GOAL来修改默认最终目标

    .DEFAULT_GOAL = main
    
    all: 
        @echo all
    
    main:
        @echo main

伪目标

如果一个目标并不是一个文件,则这个目标就是伪目标。例如前面的clean目标。如果说在当前目录下有一个文件名称和这个目标名称冲突了,则这个目标就没法执行。这时候需要用到一个特殊的目标 .PHONY,将上面的clean目标改写如下:

.PHONY: clean
clean:
    rm block.o command.o input.o main.o scene.o test.o
    rm sudoku

这样即使当前目录下存在与目标同名的文件,该目标也能正常执行。

伪目标的其他应用方式

如果一条规则的依赖文件没有改动,则不会执行对应的更新方法。如果需要每次不论有没有改动都执行某一目标的更新方法,可以把对应的目标添加到.PHONY的依赖中,例如下面这种方式,则每次执行make都会更新test.o,不管其依赖文件有没有改动

test.o: test.cpp test.h
        g++ -c test.cpp

.PHONY: clean test.o

(2)依赖

1)依赖类型:

①普通依赖

前面说过的这种形式都是普通依赖。直接列在目标后面。普通依赖有两个特点:

  1. 如果这一依赖是由其他规则生成的文件,那么执行到这一目标前会先执行生成依赖的那一规则
  2. 如果任何一个依赖文件修改时间比目标晚,那么就重新生成目标文件

查找相关依赖

g++ -MM test.cpp

②order-only依赖

依赖文件不存在时,会执行对应的方法生成,但依赖文件更新并不会导致目标文件的更新;

如果目标文件已存在,order-only依赖中的文件即使修改时间比目标文件晚,目标文件也不会更新。

定义方法如下:

targets : normal-prerequisites | order-only-prerequisites

normal-prerequisites部分可以为空

2)指定依赖搜索路径

make默认在Makefile文件所在的目录下查找依赖文件,如果找不到,就会报错。这时候就需要手动指定搜索路径,用VPATH变量或vpath指令。

①VPATH用法如下:

VPATH = <dir1>:<dir2>:<dir3>...
# 例如
VPATH = include:src

多个目录之间冒号隔开,这时make会在VPATH指定的这些目录里面查找依赖文件。

②vpath指令用法:

vpath比VPATH使用更灵活,可以指定某个类型的文件在哪个目录搜索。

用法如下:

vpath <pattern> <directories>

vpath %.h include  # .h文件在include目录下查找
vpath %.h include:headers  # .h文件在include或headers文件下查找

vpath % src   # 所有文件都在src下查找

vpath hello.cpp src  # hello.cpp文件在src查找

(3)更新方法

target … (目标): prerequisites …(依赖)
        recipe(方法)
        …
        …

1)关于执行终端

更新方法实际上是一些Shell指令,通常以Tab开头,或直接放在目标-依赖列表后面,这些指令都需要交给Shell执行,所以需要符合Shell语法。默认使用的Shell是sh。

可以通过SHELL变量手动指定Shell:

SHELL = C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe
SHELL = cmd.exe

默认的执行方式为一条指令重新调用一个Shell进程来执行。有时为了提高性能或其他原因,想让这个目标的所有指令都在同一进程中执行,可以在Makefile中添加 .ONESHELL。

 .ONESHELL:

这样所有指令都会在同一次Shell调用中执行。

2)Shell语句回显问题

通常make在执行一条Shell语句前都会先打印这条语句,如果不想打印可以在语句开头在@

@echo hello
@g++ -o hello hello.cpp

也可以使用.SILENT来指定哪些目标的更新方法指令不用打印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值