Linux里Makefile是什么?它是如何工作的

发布时间:2023-11-19 点击:141
用这个方便的工具来更有效的运行和编译你的程序
makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接—–但是不是所有的文件都需要重新编译,makefile能够纪录文件的信息,决定在链接的时候需要重新编译哪些文件!
当你需要在一些源文件改变后运行或更新一个任务时,通常会用到 make 工具。make 工具需要读取一个 makefile(或 makefile)文件,在该文件中定义了一系列需要执行的任务。你可以使用 make 来将源代码编译为可执行程序。大部分开源项目会使用 make 来实现最终的二进制文件的编译,然后使用 make install 命令来执行安装。
本文将通过一些基础和进阶的示例来展示 make 和 makefile 的使用方法。在开始前,请确保你的系统中安装了 make。
基础示例
依然从打印 “hello world” 开始。首先创建一个名字为 myproject 的目录,目录下新建 makefile 文件,文件内容为:
say_hello: echo hello world在 myproject 目录下执行 make,会有如下输出:
$ make echo hello world hello world在上面的例子中,“say_hello” 类似于其他编程语言中的函数名。这被称之为 目标(target)。在该目标之后的是预置条件或依赖。为了简单起见,我们在这个示例中没有定义预置条件。echo ‘hello world’ 命令被称为 步骤(recipe)。这些步骤基于预置条件来实现目标。目标、预置条件和步骤共同构成一个规则。
总结一下,一个典型的规则的语法为:
目标: 预置条件
<tab> 步骤
作为示例,目标可以是一个基于预置条件(源代码)的二进制文件。另一方面,预置条件也可以是依赖其他预置条件的目标。
final_target: sub_target final_target.c recipe_to_create_final_target sub_target: sub_target.c recipe_to_create_sub_target目标并不要求是一个文件,也可以只是步骤的名字,就如我们的例子中一样。我们称之为“伪目标”
再回到上面的示例中,当 make 被执行时,整条指令 echo “hello world” 都被显示出来,之后才是真正的执行结果。如果不希望指令本身被打印处理,需要在 echo 前添加 @
say_hello: @echo hello world 重新运行 make,将会只有如下输出:
$ make hello world接下来在 makefile 中添加如下伪目标:generate 和 clean:
say_hello: @echo hello world generate: @echo creating empty text files... touch file-{1..10}.txt clean: @echo cleaning up... rm *.txt 随后当我们运行 make 时,只有 say_hello 这个目标被执行。这是因为makefile 中的第一个目标为默认目标。通常情况下会调用默认目标,这就是你在大多数项目中看到 all 作为第一个目标而出现。all 负责来调用它他的目标。我们可以通过 .default_goal 这个特殊的伪目标来覆盖掉默认的行为。
在 makefile 文件开头增加 .default_goal:
.default_goal := generate
make 会将 generate 作为默认目标:
$ make creating empty text files... touch file-{1..10}.txt 顾名思义,.default_goal 伪目标仅能定义一个目标。这就是为什么很多 makefile 会包括 all 这个目标,这样可以调用多个目标。
下面删除掉 .default_goal,增加 all 目标:
all: say_hello generate say_hello: @echo hello world generate: @echo creating empty text files... touch file-{1..10}.txt clean: @echo cleaning up... rm *.txt 运行之前,我们再增加一些特殊的伪目标。.phony 用来定义这些不是文件的目标。make 会默认调用这些伪目标下的步骤,而不去检查文件名是否存在或最后修改日期。完整的 makefile 如下:
.phony: all say_hello generate clean all: say_hello generate say_hello: @echo hello world generate: @echo creating empty text files... touch file-{1..10}.txt clean: @echo cleaning up... rm *.txt make 命令会调用 say_hello 和 generate:
$ make hello world creating empty text files... touch file-{1..10}.txt clean 不应该被放入 all 中,或者被放入第一个目标中。clean 应当在需要清理时手动调用,调用方法为 make clean
$ make clean cleaning up... rm *.txt 现在你应该已经对 makefile 有了基础的了解,接下来我们看一些进阶的示例。
进阶示例
变量
在之前的实例中,大部分目标和预置条件是已经固定了的,但在实际项目中,它们通常用变量和模式来代替。
定义变量最简单的方式是使用 = 操作符。例如,将命令 gcc 赋值给变量 cc:
cc = gcc这被称为递归扩展变量,用于如下所示的规则中:
hello: hello.c ${cc} hello.c -o hello你可能已经想到了,这些步骤将会在传递给终端时展开为:
gcc hello.c -o hello
${cc} 和 $(cc) 都能对 gcc 进行引用。但如果一个变量尝试将它本身赋值给自己,将会造成死循环。让我们验证一下:
cc = gcc cc = ${cc} all: @echo ${cc} 此时运行 make 会导致:
$ make makefile:8: * recursive variable 'cc' references itself (eventually). stop. 为了避免这种情况发生,可以使用 := 操作符(这被称为简单扩展变量)。以下代码不会造成上述问题:
cc := gcc cc := ${cc} all: @echo ${cc} 模式和函数
下面的 makefile 使用了变量、模式和函数来实现所有 c 代码的编译。我们来逐行分析下:
# usage: # make # compile all binary # make clean # remove all binaries and objects .phony = all clean cc = gcc # compiler to use linkerflag = -lm srcs := $(wildcard *.c) bins := $(srcs:%.c=%) all: ${bins} %: %.o @echo checking.. ${cc} ${linkerflag} $< -o $@ %.o: %.c @echo creating object.. ${cc} -c $< clean: @echo cleaning up... rm -rvf *.o ${bins}以 # 开头的行是评论
.phony = all clean 行定义了 all 和 clean 两个伪目标。
变量 linkerflag 定义了在步骤中 gcc 命令需要用到的参数。
srcs := $(wildcard *.c):$(wildcard pattern) 是与文件名相关的一个函数。在本示例中,所有 “.c”后缀的文件会被存入 srcs 变量。
bins := $(srcs:%.c=%):这被称为替代引用。本例中,如果 srcs 的值为 ‘foo.c bar.c’,则 bins的值为 ‘foo bar’。
all: ${bins} 行:伪目标 all 调用 ${bins} 变量中的所有值作为子目标。
规则:
%: %.o @echo checking.. ${cc} ${linkerflag} $< -o $@ 下面通过一个示例来理解这条规则。假定 foo 是变量 ${bins} 中的一个值。% 会匹配到 foo(%匹配任意一个目标)。下面是规则展开后的内容:
foo: foo.o @echo checking.. gcc -lm foo.o -o foo 如上所示,% 被 foo 替换掉了。$< 被 foo.o 替换掉。$<用于匹配预置条件,$@ 匹配目标。对 ${bins} 中的每个值,这条规则都会被调用一遍。
规则:
%.o: %.c @echo creating object.. ${cc} -c $< 之前规则中的每个预置条件在这条规则中都会都被作为一个目标。下面是展开后的内容:
foo.o: foo.c @echo creating object.. gcc -c foo.c 最后,在 clean 目标中,所有的二进制文件和编译文件将被删除。
下面是重写后的 makefile,该文件应该被放置在一个有 foo.c 文件的目录下:
# usage: # make # compile all binary # make clean # remove all binaries and objects .phony = all clean cc = gcc # compiler to use linkerflag = -lm srcs := foo.c bins := foo all: foo foo: foo.o @echo checking.. gcc -lm foo.o -o foo foo.o: foo.c @echo creating object.. gcc -c foo.c clean: @echo cleaning up... rm -rvf foo.o foo

该如何查找网站存在的文件上传漏洞?
国家顶级域名有哪些优势?
鸿星尔克火了之后:有人抢鞋,有人抢域名
不小心把微信聊天记录删掉了怎么恢复?恢复微信聊天记录的方法
腾讯云服务器怎么挂mt4
很多企业听过网络SEO推广,但对于如何做仍很迷茫
数据库的未来在云端
怎样注册一个免费域名 为什么要注册网络域名