Makefile 编写指南

Makefile 编写指南

杨豪迈

Makefile的编写规则概述

target ... : prerequisites...
    command...

每一条Makefile的编译规则由三部分组成,分别是target:目标,prerequisites先决条件和command具体生成的命令

  • target是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)
  • prerequisites是生成target的所有依赖文件。所以如果有一个或多个prerequisites中定义的文件比target文件要新的时候,就会重新执行command命令
  • command就是make时需要执行的命令

Makefile是如何工作的

  1. 读入所有的Makefile。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。
final_app : main.o utils.o
    gcc -o final_app main.o utils.o

在默认的方式下,也就是我们只输入make命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“final_app”这个文件,并把这个文件作为最终的目标文件。

  3. 如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。

  4. 如果final_app所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)

  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件final_app了。

make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。

Makefile编写综述

1. Makefile基本书写规则

规则举例

foo.o : foo.c def.h
    gcc -g -o foo.o foo.c

规则语法

target : prerequisites ;command
    command
  • targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。

  • command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上)

  • prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。

  • 如果命令太长,你可以使用反斜框(‘/’)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。

  • 一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

2. 在Makefile中使用通配符

make支持三各通配符:* ? 和 [...]。这是和Unix的B-Shell是相同的。

举例子:

target : *.o
    command

clean:
    rm -f *.o

3. 在Makefile中的文件搜索

有两种方法可以达到文件控制文件搜索范围的目的

  1. 通过改变VPATH环境变量
VPATH=src:../include

我们可以通过更改VPATH变量来指定依赖文件和目标文件的目录,其中VPATH当中的不同路径通过:分隔开

  1. 通过使用make命令的vpath关键字
vpath <pattern> <directories>

我们通过vpath关键字为符合pattern模式匹配的文件名称指定搜索目录directories

举例

vpath %.h ../headers
#其中%表示有0或者若干个字符

vpath %.c foo
vpath %   blish
vpath %.c foo:bar
#我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make会按照vpath语句的先后顺序来执行搜索。

4. Makefile当中的伪目标

“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

  • 伪目标通过.PHONEY关键字修饰

举例

#示例1

#clean并不会真正的生成相应的clean文件,所以clean是一个伪目标
clean:
    rm *.o temp
.PHONEY: clean


#示例2

#通过运行make可以生成多个可执行文件,其中all代表那三个生成的可执行文件,但是all本身并不会生成,所以将all定义为伪目标
all : prog1 prog2 prog3
.PHONEY : all
prog1 : prog1.o utils.o
    cc -o prog1 prog1.o utils.o
prog2 : prog2.o
    cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o

5. Makefile中的静态模式

targets : target-pattern : prereq-patterns
    command
  • targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。

  • target-parrtern是指明了targets的模式,也就是的目标集模式。

  • prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

下面通过举例说明静态模式的用法

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $(INCLUDE_FILE) $< -o $@

这其中指明了我们的目标从 ( o b j e c t ) 当 中 选 取 。 ‘ ‘ ‘ (object)当中选取。``` (object)<$@```都是自动化变量,分别代表依赖集和目标集

所以上述式子等价于

foo.o : foo.c
    $(CC) -c $(CFLAGS) $(INCLUDE_FILE) foo.c -o foo.o
bar.o : bar.c
    $(CC) -c $(CFLAGS) $(INCLUDE_FILE) bar.c -o bar.o

6. 在Makefile中自动生成依赖

在Makefile中引用其他Makefile


这里我们穿插一下引用其他Makefile的方法

include a.mk b.mk

通过内建的include语句就可以将其他Makefile里面的内容放在对应include的位置

如果你希望Makefile不理睬那些无法读取的Makefile,可以添加一个-

例如

-include a.mk b.mk

如何自动生成依赖

这一部分我个人暂时还没有完全理解透彻,等我理解了再给大家补充,详细资料可以暂时参考自动依赖生成的官方文档,如下

https://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites

7. Makefile中的命令规范

  1. 显示命令

可以通过使用 echo show something来在显示信息

但是默认情况下Makefile会输出echo命令的内容,所以通常使用

show_cmd: 
    @echo show something

在命令开始添加@来防止echo内容的输出

  1. 执行命令

test1:
    cd test
    pwd
test2:
    cd test;pwd

这里通过两个命令的执行说明Makefile执行命令的原理。

  • Makefile在执行每一个目标命令时,将每一行开启子shell执行,所以如果两个命令不在同一行,前一个命令就不会对后一个命令造成影响
  1. 命令出错

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。

但是命令出错并不一定意味着就是错误的,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

#例如:
-include $(INCL_DIR)

clean:
    -mkdir $(SOME_DIR)
    -rm -f *.o
#这样子就可以忽略命令的错误
  1. 镶套执行Makefile

有时候,如果我们的工程量之分庞大的时候,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中

假如,我们的工程目录有一个子文件夹subdir,这个目录下有一个子Makefile,那么我可以这样子执行Makefile

subpro:
    mkdir subdir && make
#或者
subpro:
    make -C subdir

如果你想向下一级Makefile传递变量,可以通过export的方式将变量传递给下一级

如果你在export变量之后又不想让变量在下一个Makefile中传递到下一级,可以使用unexport方法

subpro:
    my_var=something
    export my_var
    make -C subdir
    unexport my_var
    make -C another_subdir

但是在Makefile当中,有两个变量,无论你是否export,都会传递到下一级,那就是SHELLMAKEFLAGS这时候你可以通过显试的改变他达到阻止传递到下一层

subpro:
    mkdir subdir && make MAKEFLAGS=
  1. 定义命令包

我们可以通过定义命令包,达到化简命令的方式,下面举例说明命令包的作用

define run-gcc
gcc -c $^ -o $@
endef

foo.o : foo.c
    $(run-gcc)

其中 $(run-gcc)会被替换成为gcc -c foo.c -o foo.o$@代表的是target,$^代表的是依赖项

8. 在Makefile中使用变量

  1. 理解变量的含义

在Makefile当中,变量默认是通过宏的方式做替换使用的,变量取值时需要通过$符号与小括号$()括起来。

例如:

a=$(b)
b=1
test:
    @echo $(a)

在需要调用$(a)的地方,会通过宏替换的方式,将$(a)替换成$(b),然后再将$(b)替换成1,所以最终的输出就是1

但是这样会造成一些奇怪的现象,例如:

b=2
a=$(b)
b=1
c=$(b)
test:
    @echo the value of a is ${a},the value of b is ${c}

这样子程序的输出结果并不是我们想象中的2和1,而是a和c会同时输出1,这就是宏替换的原因。

如果想要阻止这种情况的发生,可以通过:=来代替=的方式达到防止这种情况发生的目的。

如果是通过:=进行赋值,make就会通过直接递归替换掉所有的宏,直到赋值成为最原始的值。这样子就可以防止上述情况的发生。例如:

b=2
a:=$(b)
b=1
c:=$(b)
test:
    @echo the value of a is ${a},the value of b is ${c}

这样最终程序输出的结果就会2和1了

  1. 变量的高级用法

  • 变量的结尾替换
foo:=a.o b.o c.o
bar=$(foo:%.o=%.c)

通过给变量foo提供模式匹配,直接替换变量中每一项的后缀名

  • 镶套取值
x = $(y)
y = z
z = Hello
a := $($(x))

变量的取值符号可以进行镶套,最终a的值是"Hello"

  • 追加变量值
#方法1:
foo=a.o b.o
foo:=$(foo) c.o
#这里必须使用:=,因为上面解释过,不然会出现宏镶套的情况,产生编译错误

#方法2:
foo=a.o b.o
foo+=c.o
  • 通过变量生成变量
a=b
$(a)_value=c

我们同样可以通过变量的值生成变量,这样子就可以生成一个名为b_value的变量

  1. 局部变量

在Makefile当中,默认的变量都是全局的,但是针对一条特定的规则,我们也可以为他定制局部变量,这样在执行这条规则和这条规则推导下的所有规则时候,都可以使用相应的局部变量了

局部变量示例如图所示:

#定义Makefile的全局变量
var=a

#为test1规则声明一条局部变量
test1: var=b

#定义test1规则
test1: test2
    @echo test1 $(var)

test2:
    @echo test2 $(var)

test3:
    @echo test2 $(var)
  • 如果运行make test1,就会输出
test1 b
test2 b
  • 如果运行make test2,输出
test2 a
  • 运行make test3,输出
test3 a
  1. 模式变量

模式变量也是一种局部变量,在符合target模式的规则中使用

例如:

%.o : CFLAGS+=-o

通过这种模式变量的方式,在所有的target包含.o的规则当中,CFLAGS中都添加了-o选项

9. 在Makefile中使用条件判断

我们可以在Makefile当中使用条件判断使我们的Makefile更加灵活

使用示例:

a=a

foo:
ifeq ($(a),a)
    echo a is a
else
    echo a is not a
endif

这样子规则foo可以根据不同的$(a)来编译生成不同的规则

  1. 相等条件判断

语法:

#是否相同
ifeq ($(a),b)
do something
else
do something
endif

#是否不同
ifneq ($(a),b)
do something
else
do something
endif

示例:

#如果a值为空,那么a=b
ifeq ($(a),)
a=b
endif
  1. 是否有值条件判断

语法:

#如果有值
ifdef a
do something
else 
do something
endif

#如果没有值
ifndef a
do something
else 
do something
endif

注意,ifdef只会判断变量当前是否有值,并不会将变量的值扩展过来。未来方便理解,下面考虑几种情况

#情况一
b=
a=$(b)

#情况二
b=
a:=$(b)

ifdef a
a=defined
else
a=undefined
endif

test:
    @echo $(a)
  • 针对情况一,a相当于一个宏,虽然b是没有值的,但是a是有值的,所以输出defined

  • 针对情况二,a被递归复制成b的值,也就是没有值,所以输出是undefined

10. 在Makefile中使用函数

在Makefile中可以使用函数,用法规则如下:

$(function arguement)
#其中arguement中变量通过逗号分割

下面,我简单介绍一些在Ccodebase里面出现过的函数

  1. strip

用法:

$(strp <text>)

strip 函数可以去掉字符串text中开头和结尾的空隔,然后返回开头结尾没有空格的字符串

  1. foreach

用法:

$(foreach <var>,<list>,<text>)

功能:

这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。

示例:

ames := a b c d
files := $(foreach n,$(names),$(n).o)

最终的返回值是a.o b.o c.o d.o

  1. filter-out

用法:

$(filter-out <pattern...>,<text>)

功能:

<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。

返回:

返回不符合模式<pattern>的字串。

示例:

objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects)) 

返回值是foo.o bar.o

  1. wildcard

用法:

$(wildcard <pattern>)

功能:

wildcard函数用来获取当前make目录下所有的符合<pattern>模式的所有文件的名称组成的字符串,通过空格分开

这里需要强调注意的是,wildcard函数支持的是Unix通配符,和%等Makefile通配符相区分

示例:

$(wildcard *.c)

返回当前目录下的所有.c结尾的文件名称列表

  1. patsubst

用法:

$(patsubst <pattern>,<replacement>,<text>)

功能:

查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“/”来转义,以“/%”来表示真实含义的“%”字符)

返回:

函数返回被替换过后的字符串。

举例:

$(patsubst %.c,%.o,x.c.c bar.c)

返回值是x.c.o bar.o

  1. shell

用法:

$(shell <command>)

shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量。

示例:

files := $(shell echo *.c)

files就是当前目录下的所有.c文件了

11.运行Makefile

  1. 指定Makefile

我们可以通过-f选项选择当前目录下需要运行的Makefile文件

make -f my_make.mk
  1. 指定Makefile的目标

每一次输入make命令,make都会执行一个目标。

  • 如果不输入参数,就是默认第一个规则作为执行的目标。
  • 也可以指定执行的目标,例如make clean就会把clean规则作为本次执行的目标。

在执行make命令是,make命令的参数可能是以-打头(例如make -f等make命令的命令行参数),也可能是包含=的环境变量(例如
make DEBUG=1这一类初始化的环境变量)。剩下的就是make命令的目标了。

例如:

make -f my_make.mk DEBUG=1 prog1 prog2

这其中prog1和prog2就是make命令的目标

在Makefile运行时,通过一个环境变量MAKECMDGOALS储存make命令的目标列表

即然make可以指定所有makefile中的目标,那么也包括“伪目标”。在Unix世界中,软件发布时,特别是GNU这种开源软件的发布时,其makefile都包含了编译、安装、打包等功能。我们可以参照这种规则来书写我们的makefile中的目标。

  • all这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
  • clean这个伪目标功能是删除所有被make创建的文件。
  • install这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
  • print这个伪目标的功能是例出改变过的源文件。
  • tar这个伪目标功能是把源程序打包备份。也就是一个tar文件。
  • dist这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
  • TAGS这个伪目标功能是更新所有的目标,以备完整地重编译使用。
  • checktest这两个伪目标一般用来测试makefile的流程。

12.自动化变量

我们前文中提到过很多的自动化变量,这些变量在规则中扮演着不同的含义

下面我们总汇一下常用的自动化变量的含义

变量含义
$@表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$<依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?所有比目标新的依赖目标的集合。以空格分隔。
$^所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。

13. 隐晦规则

隐晦规则是Makefile之所以强大的原因之一

“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。

“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。

我们还可以通过“模式规则”的方式写下自己的隐含规则。用“后缀规则”来定义隐含规则会有许多的限制。使用“模式规则”会更回得智能和清楚,但“后缀规则”可以用来保证我们Makefile的兼容性。
我们了解了“隐含规则”,可以让其为我们更好的服务,也会让我们知道一些“约定俗成”了的东西,而不至于使得我们在运行Makefile时出现一些我们觉得莫名其妙的东西。当然,任何事物都是矛盾的,水能载舟,亦可覆舟,所以,有时候“隐含规则”也会给我们造成不小的麻烦。只有了解了它,我们才能更好地使用它。

例如,我们有下面的一个Makefile:

foo : foo.o bar.o
    cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把[.o]的目标的依赖文件置成[.c],并使用C的编译命令cc –c $(CFLAGS) [.c]来生成[.o]的目标。也就是说,我们完全没有必要写下下面的两条规则:

foo.o : foo.c
    cc –c foo.c $(CFLAGS)
bar.o : bar.c
    cc –c bar.c $(CFLAGS)

下面我们来看看常用的隐藏规则

  • 编译C程序的隐含规则

<n>.o的目标的依赖目标会自动推导为<n>.c,并且其生成命令是$(CC) –c $(CPPFLAGS) $(CFLAGS)

  • 编译C++程序的隐含规则

<n>.o的目标的依赖目标会自动推导为<n>.cc或是<n>.C,并且其生成命令是$(CXX) –c $(CPPFLAGS) $(CFLAGS)

  • 链接Object文件的隐含规则

<n>目标依赖于<n>.o,通过运行C的编译器来运行链接程序生成(一般是ld),其生成命令是:$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)

重载隐晦规则

你可以这样重载.o文件生成的隐晦规则:

%.o : %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)

也可以这样取消重载

%.o : %.s

在后面不写命令就行

附录

gcc命令参数说明

gcc 与 g++ 分别是 gnu 的 c & c++ 编译器 gcc/g++ 在执行编译工作的时候,总共需要4步:

  1. 预处理,生成 .i 的文件 【预处理器cpp】
  2. 将预处理后的文件不转换成汇编语言, 生成文件 .s 【编译器egcs】
  3. 汇编变为目标代码(机器代码)生成 .o 的文件【汇编器as】
  4. 连接目标代码, 生成可执行程序 【链接器ld】

常用参数详解

  • -x language filename

定文件所使用的语言, 使后缀名无效, 对以后的多个有效。

以使用的参数吗有下面的这些:'c', 'objective-c', 'c-header', 'c++', 'cpp-output', 'assembler', 与 'assembler-with-cpp'

举例:

gcc -x c hello.whatever
  • -c

只激活预处理,编译,和汇编,也就是他只把程序做成obj文件

举例:

gcc -c hello.c

最终生成hello.o

  • -S

只激活预处理和编译,就是指把文件编译成为汇编代码。

举例:

gcc -S hello.c 

最终生成hello.s

  • -E

只激活预处理,这个不生成文件, 你需要把它重定向到一个输出文件里面。

举例:

gcc -E hello.c > test.txt 
gcc -E hello.c | more 

最终生成预处理过后的文件

  • -o

制定目标名称, 默认的时候, gcc 编译出来的文件是 a.out, 很难听, 如果你和我有同感,改掉它, 哈哈。

举例:

gcc -o hello.exe hello.c
gcc -o hello.asm -S hello.c
  • -Dmacro

相当于在代码中#define macro

  • -Dmacro=defn

相当于在代码中#define macro defn

  • -Umacro

相当于在代码中#undef macro

  • -g

在编译的时候加入调试信息

  • -O0 -O1 -O2 -O3

编译器优化的四个等级,-O0不优化,-O3优化等级最高

  • -shared

生成动态链接库

举例:

gcc -shared -o s.so s.c

根据s.c生成动态链接库s.so

  • -ansi

关闭gcc中所有与ansi标准不兼容的特性,并且激活ansi的专有特性

  • -include file

相当于在代码中添加了#include"file"

举例:

gcc hello.c -include /somewhere/something.h
  • -imacros file

将 file 文件的宏, 扩展到 gcc/g++ 的输入文件, 宏定义本身并不出现在输入文件中。

  • -I dir

通过-I参数指定头文件的首选寻找路径

  • -L dir

指定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然编译器将只在标准库的目录找。这个dir就是目录的名称。

  • -lname

这个选项是一个-l加上一个name,表示在库文件的目录下搜索名为libname.so的动态链接库,如果找不到,就搜索名为libname.a的静态链接库。如果有-static,则直接搜索名为libname.a的静态链接库

举例:

#假设此时在当前目录下面的test目录下有一个名为foo.a的静态链接库,我们现在需要在链接时链接该库

gcc -L test -ltest -o main main.c
  • -static

此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么动态连接库,就可以运行。

  • -share

这个命令和上面的恰好相反

此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库。

  • -pthread

-pthread是gcc一个较新的特性,用来取代-lpthread

使用-pthread选项会在程序链接时候链接pthread动态链接库,并且声明-D_REENTRANT,这个宏为其他的一些c语言标准库提供了线程安全的特性

所以在编写基于pthread的多线程程序是多使用这个链接选项

  • -W -w -Wall

这是编译是的警告选项

-w选项表示忽略所有的警告

-W表示只显示编译器认为会出错的选项

-Wall表示显示所有的警告

-Wname-W后面加上警告的名称,表示开启相关警告的提示

  • -M -MM

-M自动生成文件的依赖信息,参见Makefile自动生成依赖的方式

-MM也是生成文件的依赖信息,但是不会生成标准库之类的冗杂的依赖,只生成必要的依赖(也就是忽略#include<file>尖括号的依赖)

示例:

gcc -M hello.c > hello.d
gcc -MM hello.c > hello.d
  • -MD

通过-M生成依赖并且自动保存到文件当中,文件名称可以通过-o指定,或者跟.c文件同名,后缀名为.d

  • -MMD

通过-MM生成依赖并且自动保存到文件当中,文件名称可以通过-o指定,或者跟.c文件同名,后缀名为.d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值