初探 ebuild

无论你使用过多少/多久其他的 Linux 发行版,初次接触 Gentoo 时,极有可能会觉得它在软件包的安装方面很神奇。若要在 Gentoo 中安装一个软件包,通常要定义如何进行软件源代码包的下载、解包、打补丁、编译、安装以及合并。为了实现对软件包进行细微的定制,还需要定义一些有用的元数据(即 USE 旗标)、补丁文件以及一些操控软件包编译与安装的过程。Gentoo 是通过 GNU Bash shell 脚本来定义这一切,这种脚本就是所谓的 ebuild 文件。

ebuild 在哪里?

我们在安装 Gentoo 时,一个必须的步骤是下载一个 Portage 树的镜像包,解包后通常安置于 /usr 目录(即 /usr/portage),之后每次执行 emerge --sync 时,便会根据官方远程网站上的 Portage 树来更新你本地的 Portage 树。

粗枝大叶的看,Portage 树有四层结点。根结点便是 /usr/portage 目录,第 2 层结点是软件包所属分类目录,第 3 层结点是软件包的名称目录,叶子结点则是 ebuild 文件以及其他辅助性文件或目录。以 gnome-shell-3.12.2.ebuild 文件为例,它在 Portage 树中的完整路径是 /usr/portage/gnome-base/gnome-shell/gnome-shell-3.12.2.ebuild

对于我们期望的软件包,如果 Portage 树未提供针对它的 ebuild 文件,那么我们需要自己动手丰衣足食。一般是不建议将我们所写的 ebuild 文件放在 Portage 树中的,因为它们可能会在 emerge --sync 期间被冲刷(比如被官方的同名文件替换)。

Portage 树支持一种被称为 Overlay 的技术。简单来说,就是我们可以另行建立一棵新的 Portage 树,这棵树的规模虽然比官方的 Portage 树小很多,但是 Portage 树的管理系统可以将这可新的 Portage 树与官方 Portage 树『合并』。如果新的 Portage 树中某些结点与官方的 Portage 树存在重叠,那么 Portage 树的管理系统会以前者覆盖后者,因此我们新建的 Portage 树通常被直呼为『Overlay』。

建立自己的 Overlay

假设在 /us/local 目录中创建自己的 Overlay,约定俗成的方式是:

# mkdir -p /usr/local/portage

需要将 Overlay 路径告知 Portage 管理系统,即在 /etc/make.conf 文件中添加以下代码:

PORTDIR_OVERLAY="/usr/local/portage"

为了让我们的 Overlay 能够被 Portage 管理系统所接受,需要在 /usr/local/portage 中创建 metadata 子目录,并在该目录内添加内容为 masters = gentoolayout.conf 文件[1],即:

# mkdir  /usr/local/portage/metadata
# echo "masters = gentoo" > /usr/local/portage/metadata/layout.conf

最后,还需要在 `/usr/local/portage 中创建 profiles 子目录,并在该目录内添加 repo_name 文件。我们可以在这份文件中设置 Overlay 名称,只需将 Overlay 名称写入该文件即可。例如,我将我的 Overlay 命名为 garfileo

# echo "garfileo" > /usr/local/portage/profiles/repo_name

今后,就在这个 Overlay 中学习 ebuild 文件的编写。

Hello World!

下面通过写一个非常简单的 ebuild 文件来获取一些直观的认识。假设在 app-misc 这个分类中有一个名为 hello-world 的软件包,现在我们要为这个软件包的 1.0 版的安装写一份 ebuild 文件。

注意:软件包的分类名并不是随意的,它必须要与 /usr/portage 中的某个子目录名一致。

首先在 Overlay 中建立软件包所在的分支:

# mkdir -p /usr/local/portage/app-misc/hello-world

可从 /usr/portage/header.txt 文件中获得 ebuild 文件默认的文件头,即:

# Copyright 1999-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

只不过是一些 Bash 脚本注释形式的文件描述信息而已,但它们是必须的。可以直接将 /usr/portage/header.txt 文件复制为 hello-world-1.0.ebuild 文件,这样便可获得一个含有上述内容的空 ebuild 文件。ebuild 文件的名称必须符合 Portage 所认可的格式,即:软件包名称-版本号.ebuild。这一点很重要。

# cp /usr/portage/header.txt /usr/local/portage/app-misc/hello-world/hello-world-1.0.ebuild

下面,为这份 ebuild 文件增加以下内容:

SLOT="0"

这样,我们便建立了一份最为简单的 ebuild 文件。接下来就是在这份文件上签个字……也就是为之生成一份签名文件,表示这个 ebuild 的是我们做的,出了事我们负责。

# cd /usr/local/portage/app-misc/hello-world
# ebuild ./hello-world-1.0.ebuild manifest

若签名成功,会在 ebuild 文件同一目录中生成一份名为 Manifest 的文件。将来发布这份 ebuild 文件时,需要将数字签名文件一起发出,这样他人便可以验证这份 ebuild 是不是我们做的。因为非常有可能我们在向朋友们发送 ebuild 文件的过程中会被坏人拦截,然后篡改 ebuild 文件。由于 ebuild 是可被系统执行的脚本,因此很有可能变成『病毒』。因此,ebuild 文件的数字签名非常有必要。不过,这里为了简单起见,没有涉及如何用自己的密钥实现对 ebuild 的签名,所得 Manifest 文件仅仅是为了让 ebuild 能够被 Portage 管理系统所认可。

下面,继续向这份 ebuild 加入一些内容,使之变为:

# Copyright 1999-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $
 
EAPI="5"
SLOT="0"
DESCRIPTION="A classical example to use when starting on something new."
HOMEPAGE="http://wiki.gentoo.org/index.php?title=Basic_guide_to_write_Gentoo_Ebuilds"
 
LICENSE="MIT"
KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86"

基本上就是在原来那份最简单的 ebuild 文件的基础上增加了几个变量:

  • EAPI:Portage 系统已经为我们编写了许多有用的 Bash 函数,将 EAPI 的值设为 5 表示我们要用目前最新的 Bash 函数。这个变量必须要在 ebuild 文件头之后进行设定。

  • DESCRIPTION:这个变量存储了 hello-world 这个软件包的简介信息。

  • HOMEPAGE:定义了 hello-world 这个软件包的项目主页。

  • LICENSE:定义了 hello-world 这个软件包所使用的许可证,例如 LGPL,GPL V2,GPL V3,MIT 等。

  • KEYWORDS:如果你期望 hello-world 这个软件包能够安装在你的机器上,那么 KEYWORDS 变量的值必须要包含你在 /etc/make.conf 中所设定的 ACCEPT_KEYWORDS 值。

一旦改动了 ebuild 文件内容,那么必须重新生成 Manifest 文件:

# ebuild ./hello-world-1.0.ebuild manifest

现在,便可以使用 emerge 命令安装这个目前依然是子虚乌有的软件包了。

虽然到现在为止,还是什么也没有做出来,但是看着 emerge 神奇的发现了我写的 ebuild,心底还是蔓生了一些幸福。

emerge 与 ebuild 有什么联系?

简单的说,就是 emerge 这个 Python 脚本会调用 ebuild.sh 这个 Bash 脚本,让后者去执行 ebuild 文件定义的软件包的下载、编译及安装过程。

ebuild.sh 所操控的软件包安装过程是在一个沙箱(Sandbox)中进行的。这一过程结束后,emerge 脚本需要将沙箱中的成果转移到真实世界,即 / 目录。

真实的 Hello World!

在一个遥远的地方,真的存在着 hello-world 的源码包。我们只要通过 ebuild 文件将这个源码包的位置告诉 ebuild.sh 脚本,ebuild.sh 便会不远万里将其擒来。所以,我们需要在 hello-world-1.0.ebuild 文件中添加以下内容:

SRC_URI="http://dev.gentoo.org/~tomwij/files/wiki/hello-world-1.0.tar.gz"

SRC_URI 这个变量便是存储源码包的下载地址的。

在重新生成 Manifest 时,ebuild.sh 便会自动将源码包下载到 /usr/portage/distfiles 目录,并为这个源码包也生成一个数字签名存储在 Manifest 文件中。

既然有了 hello-world 的源码包,那么下一步就该思考如何在 ebuild 文件中定义这个源码包的编译过程了。不过,解开刚才下载的 hello-world-1.0.tar.gz 包看一下,发现包里只有一份 Bash 脚本 hello-world,其内容为:

#!/bin/sh

echo "Hello world!"

所以,这个源码包就没必要编译了,直接安装到系统中即可。从而,我们在 ebuild 文件中获得了第一次编写 ebuild 函数的机会。在现有的 hello-world-1.0.ebuile 的文件中继续添加以下内容:

src_install() {
    dobin hello-world
}

src_installebuild.sh 脚本能够识别并执行的函数名。也就是说,从 ebuild.sh 脚本的角度来看,你若想让我替你将软件包安装至系统中,那么你必须得按照我的习惯来。我的习惯就是在你提供给我的 ebuild 文件中寻找 src_install 这个函数,如果有这个函数,我就执行它,否则我就什么也不做。这就是 ebuild.sh 与 ebuild 文件之间达成的一个约定。

现在我们在 ebuild 文件中向 ebuild.sh 提供了 src_install 这个函数。这个函数只包含一条命令:dobin hello-world 。这个命令的意思是为 hello-world 这个脚本设置可执行权限,然后将其安装至系统默认的可执行文件目录中,即 /usr/bin 目录。

src_install 只是 ebuild.sh 与 ebuild 文件之间众多约定函数中的一个而已,并且这些约定函数是顺次被 ebuild.sh 执行的,如下图所示:

这里存在一个问题,hello-world 这个 Bash 脚本是包含在 hello-world-1.0.tar.gz 这个包内的,而我们只在 hello-world-1.0.ebuild 文件中定义了 src_install 函数,那么 hello-world-1.0.tar.gz 何时被解包的呢?这个问题的答案是,Portage 管理系统中已经为这些约定的函数定义了默认行为。比如,用于为源码包解包的 src_unpack 函数,其默认的定义是:

src_unpack() {
    if [ "${A}" != "" ]; then
        unpack ${A}
    fi
}

如果在 ebuild 文件中没有重新定义 src_unpack 函数,那么 ebuild.sh 便会按照上面图示的管线调用默认的 src_unpack 函数。所以 hello-world 脚本得以从 hello-world-1.0.tar.gz 中解出。

对现在的 hello-world-1.0.ebuild 再次生成 Manifest,然后就可以用它将 hello-world 脚本安装至 /usr/bin 目录中了。

这就是我们用自己写的 ebuild 安装的第一个『软件包』。

温故而知新

由于 ebuild 文件本质上是 Bash 脚本。如果对 Bash 不熟悉,可以通过 Daniel Robbins 写的三篇教程学习一下:

  • Bash by examples, Part 1

  • Bash by examples, Part 2

  • Bash by examples, Part 3

需要特别关注 Part 3,因为在这一篇里,Daniel Robbins 高屋建瓴的描绘出了 Portage 系统初创时期的关键思想。

相关资料

  • 为什么需要在 Overlay 目录中放置 layout.conf 文件?

你可能感兴趣的:(ebuild,gentoo)