【问题标题】:Makefiles, "configure" files, and other compilation tools -- How do they work? Why do they work the way they do?Makefile、“配置”文件和其他编译工具——它们是如何工作的?为什么他们以他们的方式工作?
【发布时间】:2011-07-20 07:50:36
【问题描述】:

我还是 UNIX/Linux 世界的新手,尤其是 GCC 编译器等相关工具。也就是说,我对 makefile 和类似的东西还是新手(我在 Windows 上使用 MinGW),因为到目前为止,我的大部分开发都是使用 Visual Studio 和 Eclipse 等 IDE。

当我打开一个典型的项目文件夹时,我会看到如下文件:

configure
make
INSTALL
install-sh  (or a variant)

这里有很多我不明白的地方,我的主要问题是:

  1. 这些有什么区别,为什么我们需要每一个? (在 IDE 世界中,您所拥有的只是一个项目文件;仅此而已。所以我很困惑为什么我们这里不只是一个 makefile。)

  2. 这些文件是如何生成的?对于小型项目,您可能可以手动编写它们,但对于像 GCC 这样的大型项目,即使尝试也是荒谬的。我发现编辑这些文件很痛苦,我得出的结论是我不应该手动修改它们。但如果是这样,那么人们通常使用哪些工具来添加和修改这些文件?

  3. 他们为什么不在 makefile 中使用通配符?为什么每个目标文件都有一行?这是因为限制,还是有好处?

  4. 拥有一个对每个文件调用编译器的 shell 脚本与拥有一个执行相同操作的 makefile 之间有什么区别?在 Windows 中,我很想在文件夹中创建一个批处理文件,然后用它编译所有内容——不需要多个文件。有什么不同?为什么没有一个 .sh 文件来编译所有内容?

额外问题:

  • 是否有“标准”makefile 格式?我见过不同的make 工具不接受彼此的格式......我怎么知道该使用什么? GCC 只是这里常用的工具,还是每个人都应该遵循的标准?

随着我更多地了解此类项目的结构,我可能会有更多问题,但就目前而言,这些是我最大的问题。 :)

【问题讨论】:

  • 有诸如 automake 之类的工具可以为您生成其中的一些文件。 configure 通常会查看当前系统并修改程序的构建方式以适应系统。出于几个原因,单独的文件被赋予单独的行。只有文件被修改后才会重新编译。不同的 gcc 实例可以在不同的处理器上运行,甚至可以使用 distcc 等工具在不同的机器上运行。我敢肯定还有其他原因。 Make 只处理编译源文件(通常)与安装(复制到最终目的地)作为一个单独的步骤。

标签: mingw configure makefile


【解决方案1】:
  1. 在您的普通包中,这些文件每个都有各自的用途。这与“每个程序应该做一件事并做好那件事”的unix哲学是一致的。在大多数项目中,您会看到如下文件:

    • configure
    • configure.ac
    • Makefile
    • Makefile.in
    • Makefile.am
    • install-sh
    • INSTALL

    configure(通常)是一个 shell 脚本,用于在构建任何东西之前检查系统的所有必需功能。 Makefile.inMakefile 的模板。将配置测试的结果代入Makefile.in,生成Makefile。这是为了处理在模糊路径中拥有东西(编译器、头文件、库)的人、交叉编译(例如,在 x86 上为 ARM 构建)、可选库支持(某些程序具有可以打开或关闭的附加功能),使用不同的选项进行编译,等等。写一个千篇一律的Makefile 实际上真的很难

    您已经注意到,configure 脚本本身就是一团糟。它不应该被凡人的眼睛看到,也不能被凡人的手编辑。它实际上是使用名为autoconf 的程序编译configure.ac 的结果。 autoconfm4 宏处理器的宏包和包装器,它是当时处理此类事情的唯一好工具(autoconf 确实是相当老的软件,但老化得非常好)。 autoconf 让开发人员可以轻松编写测试来检查构建软件所需的头文件、库或程序(这会因程序而异)。

    如果你再深入一点,你会发现Makefile.in 也往往有点丑陋。这是因为写好的Makefiles 通常是很多样板,这启发了另一个工具automakeautomakeMakefile.am(通常很短且具有声明性)编译成Makefile.in(很大),然后由configure(本质上)编译成Makefile

    install-sh 是与automake 一起分发但复制到其他包中的脚本。如果系统上install 的版本是废话,它作为替代存在(install 将文件复制到安装目录中。一些非常旧的系统已经损坏了install 的版本,而automake 对于丢弃警告非常保守对于旧系统)。其他一些具有类似作用的脚本是compiledepcompylwrap

    INSTALL 只是一个描述如何安装包的文档。它通常是由automake 复制到包中的样板内容。

  2. 我已经在上面回答了这个问题,但这里是摘要:

    • configure.ac ==[autoconf]=> configure
    • Makefile.am ==[automake]=> Makefile.in ==[configure]=> Makefile

    负责的程序在箭头内。要详细了解这一点,我推荐这个autotools tutorial。不要被页数吓到,大部分是一张张出现的图表。

  3. Makefiles 中的通配符有时会被使用。例如,GNU Make 支持$(wildcard) 函数,您可以在其中编写如下内容:

    SOURCES := $(wildcard src/*.c)

    没有使用像$(wildcard) 这样的功能的主要原因是它们是扩展,并且automake 非常努力地生成Makefiles 可以与任何 POSIX 兼容的@ 一起使用987654369@。项目成熟后,要编译的文件列表并不会发生太大变化。

    明确列出文件的第二个原因是程序获得可选功能时。通配符不再适用,您必须列出编译附加功能的条件。

  4. Makefile 跟踪文件之间的依赖关系,而 shell 脚本则不能(无论如何都需要付出很大的努力)。

如果您有 Makefile 规则,例如:

foo.out: foo.in
        generate-foo foo.in

它告诉make,如果foo.in foo.out 更新,则可以通过执行generate-foo foo.in 创建一个新的foo.out。这样可以节省大型项目的大量冗余工作,在这些项目中,您可能只在重新编译之间更改一两个文件。

您的额外问题似乎有点不合时宜。最常见的make 可能是 GNU Make,尽管我猜 BSD make 紧随其后,其次是 Solaris、AIX 等提供的各种专有的make 版本。

这些都接受Makefile 中相同的基本结构(因为 POSIX 是这样说的),但可能具有特定于供应商的语法扩展。

GCC 不是像 make 这样的构建工具。 GCC 是一个命令行编译器,类似于 windows 上的cl.exe

【讨论】:

  • 这对所有事情的解释都非常好,而且很有意义。谢谢! :)
【解决方案2】:

不同的事情有很多原因,但最终归结为可移植性。在 unix 战争期间,每个平台都是不同的,人们试图制作可以在尽可能多的平台上运行的软件。所以必须找到共同点。 sh 就是这样一种通用性,因此编写配置脚本以最大程度地使用可移植性 sh。最大的可移植性意味着使用最少的功能集。 make 的不同实现在它们支持的特性上差别很大,因此编写的 Makefile 尽可能少地使用特性。今天的情况稍微好一些,但便携性问题仍然推动了您所看到的许多事情。

【讨论】:

    【解决方案3】:
    1. configure 是一个设置构建环境并可能生成 makefile 的脚本。使用make 构建项目,使用make installinstall.sh 或类似名称,安装编译文件。如果您想在本地测试某些内容,您可能希望将最后一部分省略,因此它是单独的。
    2. 您可以使用GNU autotools
    3. 你能举出具体的例子吗? Makefile 确实支持通配符,并且不时使用它们。
    4. 如果您更改源文件中的某些内容然后决定重新构建项目,Makefile 会处理依赖关系并仅自动​​重新编译所需文件。使用批处理文件,您将重新编译所有内容,使用 Makefiles,您只需编译已更改的内容。

    【讨论】:

      猜你喜欢
      • 2016-08-18
      • 1970-01-01
      • 2020-06-26
      • 1970-01-01
      • 2018-06-24
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      相关资源
      最近更新 更多