那么,为什么还需要make?



似乎所有这些都始于一个简单的问题,它首先使我陷入昏迷-“为什么需要制作?为什么我不能与bash脚本相处呢?”。 我想-真的,为什么我需要做? (也是最重要的)他要解决什么问题?


然后,我决定思考一下-如果没有make,我们将如何收集我们的项目。 假设我们有一个带有源代码的项目。 从它们中,您需要获取一个可执行文件(或库)。 乍一看,任务似乎很简单,但我们走得更远。 假设项目在初始阶段包含一个文件。



要编译它,只需运行一个命令:


$ gcc main.c -o main 

这很简单。 但是随着时间的流逝,项目得以发展,其中出现了一些模块,并且源文件越来越大。



要进行编译,您需要有条件地执行以下命令:


 $ gcc -c src0.c $ gcc -c src1.c $ gcc -c main.c $ gcc -o main main.o src0.o src1.o 

同意,这是一个相当漫长而艰苦的过程。 要手动执行此操作,我不会。 我认为可以简单地通过创建包含这些命令的build.sh脚本来实现此过程的自动化。 好的,这要容易得多:


 $ ./build.sh 

我们继续前进! 该项目正在增长,源文件的数量正在增加,并且它们中的行也越来越多。 我们开始注意到编译时间已明显增加。 在这里,我们看到了脚本中的一个重大缺陷-尽管我们只修改了一个文件,但它使用源代码编译了全部50个文件。



它行不通! 开发人员时间太宝贵了。 好吧,我们可以尝试修改构建脚本,以便在编译之前检查源文件和目标文件的修改时间。 并仅编译已更改的源。 有条件地,它可能看起来像这样:


 #!/bin/bash function modification_time { date -r "$1" '+%s' } function check_time { local name=$1 [ ! -e "$name.o" ] && return $? [ "$(modification_time "$name.c")" -gt "$(modification_time "$name.o")" ] && return $? } check_time src0 && gcc -c src0.c check_time src1 && gcc -c src1.c check_time main && gcc -c main.c gcc -o main main.o src0.o src1.o 

现在,只有那些经过修改的源才会被编译。



但是当项目变成这样的时候会发生什么:



迟早会有一段时间,很难理解项目,而对此类脚本的支持本身将是一个费力的过程。 而且,该脚本不会充分检查所有依赖关系并不是事实。 另外,我们可以有几个项目,每个项目都有自己的汇编脚本。


当然,我们看到可以解决此问题。 该工具将提供一种检查依赖性的机制。 在这里,我们正在慢慢了解make的发明。 现在,在知道我们在构建项目的过程中会遇到什么问题后,最后我将对make提出以下要求:


  • 依赖性和目标时间戳的分析
  • 确保派生文件的相关性所需的最少工作量
  • (好吧,+并行执行命令)

生成文件


Makefile用于描述项目组装规则。 创建一个Makefile,我们以声明的方式描述文件之间的某种关系状态。 状态确定的声明性很方便,因为我们说我们有一个文件列表,我们需要通过执行命令列表从文件中获取一个新文件。 在使用某种命令式语言(例如shell)的情况下,我们将不得不执行大量不同的检查,使输出中的代码变得复杂而令人困惑,而make却为我们做。 最主要的是构建正确的依赖关系树。


 <  > : <  ... > <  > ... ... 

我不会谈论如何编写Makefile。 互联网上有很多与此主题相关的手册 ,如果您愿意,可以参考它们。 此外,很少有人手动编写Makefile。 非常复杂的Makefile可能会带来麻烦,而不是简化构建过程。

Source: https://habr.com/ru/post/zh-CN477656/


All Articles