Linux自动化构建工具make/makefile详解
  PzTaj2xFbKXN 2023年11月02日 37 0

什么是makefile

makefile是一个文件。一个工程中的源文件数量不计其数,其按类型、功能、模块分放在不同的目录中,makefile制定了一系列规则指定哪些文件先编译,哪些文件后编译,哪些文件需要重新编译,以及更加复杂的功能操作。make是一个工具,用来解释makefile中的指令。make和makefile带来的好处就是自动化编译,在完成makefile的编写后,只需要一个make命令整个工程就会完全自动化编译,极大提高软件开发的效率。

本文介绍makefile的编写规则和用法。在更深入了解makefile之前,需要先了解程序的编译和链接过程以及文件的时间属性,这对理解makefile的编写和执行规则很有帮助。

程序的编译和链接

详见浅析C语言预处理Linux平台下的ELF文件结构探索

Linux自动化构建工具make/makefile详解_make/makefile

文件的时间属性

在Linux中,一切皆文件,而文件可以看做两部分:文件内容和文件属性,即文件 = 文件内容 + 文件属性,二者分别占用存储空间。文件属性包括文件类型、文件权限、文件所属和时间属性等。使用 stat 命令可以查看文件状态,包括文件 inode、文件大小和文件时间等。

对于linux文件,有三种时间,分别是access时间、modify时间和change时间

Linux自动化构建工具make/makefile详解_make/makefile_02

  • access时间即文件被访问的时间。这个时间是改变最频繁的时间,几乎对文件的所有操作,包括读文件、增删查改文件都会修改这个时间,因为这些操作都会访问文件。由于access时间最容易改变,为了提高系统效率,access时间往往会在文件被访问超过一定次数或时间后才会被延迟修改
  • modify和change时间都是文件被修改的时间,不同的是modify时间针对的是文件内容的修改,而change时间针对的是对文件属性的修改。对文件内容的增删改都会修改这个时间。
  • change时间针对的是对文件属性的修改,文件属性的变动会修改这个时间。

上述三种时间往往是一同改变的,访问文件并对其内容进行修改后,该文件的属性往往也会发生变化。

makefile规则和用法

makefile的书写有两个关键点:依赖关系和依赖方法。依赖关系可以理解为完成这个工作需要哪些文件;依赖方法可以理解为以这些文件完成工作的详细做法。同时,makefile要与所有所需文件处在同一目录下。依赖关系包含两部分:目标文件(target)和依赖文件(prerequisites),生成target所需的文件包含在prerequisites中,其生成规则定义在依赖方法(command)中:

target... : prerequisites ...
	command

例如要以 def.cmain.c 生成一个 a.out 文件,a.out 即为目标文件,def.c和 main.c 即为依赖文件,写法如下:

a.out:def.c main.c
	gcc def.c main.c -o a.out

需要注意的是,当目标文件为最新时,连续执行make命令进行重复编译是不允许的,只有当依赖文件被修改后,make命令才会进行重新编译,这是为了避免多余的重复编译时间。为了做到这一点,make会先比较目标文件与依赖文件的modify时间,当目标文件的modify时间在依赖文件之后,便会执行makefile中的指令,否则不予执行。

在makefile中,可以使用.PHONY修饰目标文件,此时目标文件被称为伪目标。.PHONY 后跟的目标不是真正的目标文件名称,与真正的目标文件名没有相互关系,对于伪目标,不论目标是否存在、不论目标文件与依赖文件的modify时间关系如何,都会执行相应的指令。例如若要使上述的 a.out 重复生成,可以将a.out用.PHONY修饰:

.PHONY:a.out
a.out:def.c main.c
	gcc def.c main.c -o a.out

在makefile中,往往也包含清理指令,用于对项目进行自动化清理。清理指令与上述编译过程相似,例如若要自动化清理 a.out,则可以写入以下命令:

clean:
	rm -f a.out

清理命令往往需要被多次、强制执行,所以可以用.PHONY对其进行修饰:

.PHONY:clean
clean:
	rm -f a.out

除了上述主要规则之外,makefile还有以下注意事项:

  • makefile中指令的执行顺序与书写顺序无关,make会自动在makefile中寻找相关命令。
  • 单独使用make命令时,make会默认执行makefile中的第一条指令,若需要执行其他指令,则需要在make后面跟上目标名称,例如在执行清理时,需要使用make clean,当然,若将clean命令写在makefile开头,则只需要make即可。
  • makefile中常见的特殊符号有:$^,代表所有依赖文件&@,代表所有目标文件。使用这些符号可以简化makefile的书写。
  • 执行make命令时,会默认将指令在命令行进行回显,可以在指令之前加上@符号以取消回显
  • make的执行规则是,只生成所有目标对象中的第一个,当然,make会根据语法规则,递归生成第一个目标对象的所有依赖对象后再回头生成第一个目标对象,生成后退出。

综上所述,上述例子中的 makefile 完整写法应类似这样:

a.out:def.c main.c
	gcc def.c main.c -o a.out

.PHONY:clean
clean:
	rm -f a.out

一个makefile实例

下面通过一个较复杂的makefile实例来加深对makefile执行规则的理解,这个makefile的内容如下:

test1.out:test1.o	
	@gcc test1.o -o test1.out
test1.o:test1.s
	@gcc -c test1.s -o test1.o
test1.s:test1.i
	@gcc -S test1.i -o test1.s
test1.i:test1.c
	@gcc -E test1.c -o test1.i

test2.out:test2.c #test2.out不会被生成
	@gcc test2.c -o test2.out

.PHONY:clean
clean:
	@rm -f test1.out test1.o test1.s test1.i

上述makefile中对 test1.c 进行分步编译,但是make只会生成makefile中的第一个目标文件,这个目标文件是 test1.out,生成test1.out所需要的依赖文件是test1.o,但是目前不存在这个文件,所以make会向下寻找生成test1.o所需的依赖文件和方法,设法生成test1.o,而生成test1.o又需要新的依赖文件和依赖方法,如此make便会向下递归式地生成所有所需文件,并最后生成test1.out。上述所有递归生成所需文件的目的都是为了最终生成test1.out,即第一个目标文件,这个过程中生成的所有其他文件都是附属品。test2.out并不会被生成,因为它不是makefile中的第一个目标文件。

Linux自动化构建工具make/makefile详解_make/makefile_03


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论