configure、make、make install 如何工作?

linux编译安装软件时执行configure的原理

介绍

我们在 linux 上安装软件时,经常会使用如下命令

1
2
3
./configure
make
make install

这些命令是怎么工作的呢?

这些命令有什么作用?

linux 上使用源码编译安装软件通常有如下三步

  1. 配置软件(Configure the software)

configure 脚本负责完成在指定系统上构建软件的准备工作,它确保后续的构建和安装过程中所有需要的依赖可以使用,并且找出使用这些依赖所需的任何信息。

Unix/Linux 程序经常使用 C 语言编写,所以我们经常需要 C 编译器来构建这些程序。在这个场景下, configure 脚本会确保你的系统中确实存在一个 C 编译器,并且找到这个 C 编译器的名字和位置。

  1. 构建软件(Build the software)

一旦 configure 命令执行完成,我们可以调用 make 命令来构建软件。这个步骤会执行定义在 Makefile 文件中的一系列任务来将软件从源码构建为最终可执行程序。

你下载的软件源码 tar 包通常不包含最终的 Makefile 文件,而是包含名为 Makefile.in 的模板文件。然后利用 configure 脚本针对当前的系统利用 Makefile.in 文件生成的定制化的 Makefile 文件。

  1. 安装软件(Install the software)

现在软件已经构建完成,可以运行了,相关文件可以被拷贝到它们的最终目录。 make install 命令会将构建出的可执行文件和它的依赖库、文档拷贝到正确的位置。

这通常意味着软件的二进制文件会被拷贝到你的 PATH 路径下,软件的操作手册会被拷贝到你的 MANPATH 路径下,同时软件依赖的其他文件也会被安全地保存到相应的位置。

因为安装步骤也是定义在 Makefile 文件中,所以软件的安装位置可以通过某个参数传递给 configure 脚本,或者由 configure 脚本利用某个系统变量来实现指定安装位置。

根据软件的安装位置,您可能需要升级此步骤的权限,以便可以将文件复制到系统目录。 使用 sudo 通常可以达到目的。

这些脚本是怎么产生的?

上述步骤之所以能成功工作,是因为 configure 脚本会检查你的系统,并使用它找到的信息将 Makefile.in 模板转换为 Makefile 文件,但是 configure 脚本和 Makefile.in 模板是怎么产生的呢?

如果你曾经打开过 configure 脚本,或者相关的 Makefile.in 文件,你会发现这些文件里包含几千行的脚本语句。有时候这些支持配置的脚本语句比要安装的程序的源代码还要长。

即使利用已有的 configure 脚本文件开始编写,手动编写完成一个 configure 脚本也是非常艰巨的。不过不用担心:这些脚本不是手工构建的。

以这种方式构建的软件通常使用一个叫做 autotools 的工具集进行打包。这个工具集包含 autoconfautomake 等工具,所有的这些工具使得维护软件生命周期变得很容易。最终用户不需要了解这些工具,同时让软件在不同的 Unix/Linux 系统的安装步骤变得简单。

Hello World 案例

我们以一个简单的 Hello World C 语言程序为例,来看看如何使用 autotools 工具集打包。

下面是程序源代码,在一个名为 main.c 的文件中

1
2
3
4
5
6
7
#include <stdio.h>

int main(int argc, char* argv[])
{
    printf("Hello World\n");
    return 0;
}

创建 configure 脚本

不需要手动编写 configure 脚本,我们需要创建一个使用 m4sh 语言(m4 宏命令和 POSIX shell 脚本的组合)编写的 configure.ac 文件来描述 configure 脚本需要做的事情。

我们需要第一个调用的 m4 宏为 AC_INIT,它会初始化 autoconf 并且设置一些关于打包软件的基本信息。我们的软件名为 helloworld,版本为 0.1,维护者为 george@thoughtbot.com

1
AC_INIT([helloworld], [0.1], [george@thoughtbot.com])

这个项目中我们会使用 automake,所以我们需要使用 AM_INIT_AUTOMAKE 宏命令来初始化 automake

1
AM_INIT_AUTOMAKE

下面,我们需要告诉 autoconfconfigure 脚本需要寻找的相关依赖,在本例中, configure 脚本仅需要使用 C 编译器,我们可以使用 AC_PROG_CC 宏命令设置。

1
AC_PROG_CC

如果有其他的依赖项,那我们可以使用其他的 m4 宏命令来设置,如使用 AC_PATH_PROG 宏表示在用户的 PATH 路径中搜索一个特定的程序。

此时我们已经列出了使用的依赖,前面有提到,configure 脚本会根据用户系统的系统信息和 Makefile.in 模板文件生成 Makefile 文件。

下面一行使用 AC_CONFIG_FILES 宏命令告诉 autoconf 工具 configure 脚本文件需要做这些工作:configure 脚本文件需要找到一个名为 Makefile.in 的文件,将文件内的站位符使用对应的值替换,例如将 @PACKAGE_VERSION@ 替换为 0.1,然后将结果写入到 Makefile 文件。

1
AC_CONFIG_FILES([Makefile])

最后,当我们告诉 autoconf 工具所有 configure 脚本需要做的工作后,可以调用 AC_OUTPUT 宏命令输出脚本内容

1
AC_OUTPUT

下面是 configure.ac 中的所有代码,相比 4737 行的 configure 脚本文件,这些代码好多了。

1
2
3
4
5
AC_INIT([helloworld], [0.1], [george@thoughtbot.com])
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

还差一点我们就可以发布软件了,configure 脚本需要一个 Makefile.in 文件,将系统相关信息填充进去后,生成最终的 Makefile 文件。

创建 Makefile 文件

configure 脚本文件相比,Makefile.in 模板文件非常长且复杂。因此,我们不是手工编写,而是编写一个较短的 Makefile.am 文件,然后利用automake 使用该文件为我们生成 Makefile.in 文件。

首先,我们需要设置一些参数来告诉 automake 工具本项目的结构,因为这里的例子不是标准的 GNU 项目的结构,所以结构声明为 foreign

1
AUTOMAKE_OPTIONS = foreign

接下来告诉 automake 我们希望 Makefile 编译的软件名为 helloworld:

1
bin_PROGRAMS = helloworld

这行包含了很多打包信息,感谢 automake统一命名规则

PROGRAMS 后缀成为 primary 主要信息,它告诉 automake 工具 helloworld 文件具有哪些属性。例如 PROGRAMS 需要被编译,相比 SCRIPTSDATA 文件不需要被编译。

bin 前缀告诉 automake 工具,这里列出的文件应该被安装到 bindir 变量指定的路径下。 autotools 工具还为我们定义了其他目录,包括 bindir , libdir , pkglibdir ,我们也可以自定义自己需要的目录。

如果我们的程序有一部分是 Ruby 脚本,我们可以定义 rubydir 变量并且告诉 automake 安装我们的 ruby 文件到该路径。

1
2
rubydir = $(datadir)/ruby
ruby_DATA = my_script.rb my_other_script.rb

可以在安装目录之前添加其他前缀,以进一步区分 automake 的行为。

因为我们已经定义了 RPOGRAM ,我们需要告诉 automake 在哪里可以找到它的源文件。 在这种情况下,前缀是这些源文件构建的程序的名称,而不是它们将安装的位置:

1
helloworld_SOURCES = main.c

这是我们的 helloworld 程序的整个 Makefile.am 文件。 与 configure.acconfigure 脚本一样,它比它生成的 Makefile.in 要短得多:

1
2
3
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = helloworld
helloworld_SOURCES = main.c

把它们放在一起

现在我们已经编写了配置文件,我们可以运行 autotools 并生成完成的 configure 脚本和 Makefile.in 模板。

首先,我们需要生成一个 m4 环境供 autotools 使用:

1
aclocal

现在我们可以运行 autoconf 将 configure.ac转换为configure脚本,并运行automakeMakefile.am转换为Makefile.in`:

1
2
autoconf
automake --add-missing

分发程序

最终用户不需要查看我们的 autotools 工具的设置,因此我们可以分发 configure 脚本和 Makefile.in,而无需分发用于生成它们的所有文件。

幸运的是,autotools 工具也将帮助我们进行分发。 Makefile 包含各种有趣的目标,包括构建项目 tarball 的目标,其中包含我们需要分发的所有文件:

1
2
./configure
make dist

您甚至可以测试分发 tarball 是否可以在各种条件下安装:

1
make distcheck

总览

现在我们知道编译安装命令从何而来以及它是如何运作的!

在维护者的系统上:

1
2
3
4
5
aclocal # Set up an m4 environment
autoconf # Generate configure from configure.ac
automake --add-missing # Generate Makefile.in from Makefile.am
./configure # Generate Makefile from Makefile.in
make distcheck # Use Makefile to build and test a tarball to distribute

在最终用户的系统上:

1
2
3
./configure # Generate Makefile from Makefile.in
make # Use Makefile to build the program
make install # Use Makefile to install the program

参考:
https://zhuanlan.zhihu.com/p/77813702
https://thoughtbot.com/blog/the-magic-behind-configure-make-make-install

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计