openSUSE:Build Service Tutorial

跳转至: 导航, 搜索
这份文档应该会给你一个关于构建服务的概述和一个如何使用这一伟大工具为多个发行版打包的教学。我们力求把所有的操作都在一个案例程序中呈现,这样你可以一步一步的创建你自己的软件包。

前期准备

你应该对于RPM有一般的认识,知道它们是怎么被创造出来的。可以看看openSUSE的打包指南或者另外一种被支持的打包系统dpkg的类似文件。这份文件的目的不是为了替代上述的打包文档。

你应该熟悉你的项目中的软件包所采用的源代码环境。构建服务可以搞定一些一般错误并且在出错时会给你一些指导。我们也有构建服务邮件列表来作为你的帮助和指导。然而,关于应用哪个补丁,用什么编译器参数等等,就完全取决于你自己啦。

要求

为了能够使用构建服务的完整功能,你需要 使用你的openSUSE/Novell账户登录 (同样适用于维基, bugzilla...)。如果你还没有账户,点击页面上方的'注册' 链接来创建一个。

术语

构建服务包含 项目 (你可以在 这里看到它们的列表)。 每个项目都包含用于编译一个或多个 软件包 (i.e., RPM)的资源文件。这些资源文件包括源代码存档,补丁文件,spec规范文件,等等。项目的输出是一个或多个软件源。软件源是我们早就熟悉的概念: 简单说就是一系列RPM组织在一个文件系统层级下,同时还有一些index索引文件/元数据文件来帮助例如 zypper 等工具更加轻松的搜索和解析依赖包。项目输出的软件源对应不同版本的发行版如openSUSE 12.1等等。

只要有项目存在,就会有openSUSE”官方“项目负责编译随标准openSUSE发行版提供的RPM。工厂版项目是一个持续进行的项目,这个项目构成了下一个版本openSUSE。另外还有一些局部项目例如Apache and network:telephony。最后每个用户都可以拥有以 home:username命名的私人车库。

RPM会依赖许多其他RPM,通常这些RPM来自于构建服务的其他项目。有两种重要的实现方法。

首先,如果你的软件包在运行时依赖其他软件包("Requires"),通常它在编译时也依赖它。(i.e, "BuildRequires") 构建服务不会为你自动搜索并找到编译依赖(除非是那些被包含在你为之编译的发行版的标准套装中的包)。所以无论如何你都必须告诉构建服务去哪里找到被要求的包。

其次,对于一个软件源来说一个辉煌的目标就是 有限封闭,即,软件源中的任何软件包的任何依赖都同样在该软件源中(除了包含在标准套装中的那些包)。这使得人们从你的软件源中安装软件包变得方便多了。但是这不是必须的:用户永远可以用 搜索界面 手动找到这些依赖/

构建服务提供了有限的不同方法来实现你的包依赖性。

首先,你可以直接添加依赖包到你的软件源。如果其他软件源没有编译这个依赖包,这个方法显然是你必须用的。然而更普遍的是,这些包已经被其他项目所编译了。考虑使用他们的工作成果。

第二个选项是链入其他项目的软件源到你的软件源。这叫做嫁接。它可以通过编辑你的软件源的元数据来实现。添加另外的项目/软件源作为你的additional path额外路径。这将会把它们简单的添加到构建服务在编译时搜索”编译依赖“的软件源列表中去。这使得你的包的编译能够成功,解决了第一个问题,但是它根本没有达到有限封闭的目标:用户需要自行获取这个依赖包。然而,当你的项目依赖其他项目中的一些包,或者用户很愿意从两个源中同时更新时,这不失为一个好选择。

第三个选项叫做链入,这个方法允许你的项目重新使用已存于其他项目中的包。当你链入一个包到你的项目中时,依赖于它的包能够编译,并且这个包也在你的软件源中出现,因此在不重造轮子的情况下解决了上面两个问题。

有两种链入方式:链接聚合。当你链接时,你只能有选择的修改包编译的方式。你可以添加补丁,开启针对额外软件源的编译。你编译的该软件包的RPM的释出号会和源项目的不一样。注意,这会给用户带来困扰。确实呀,你编译了同名软件的不同版本。

如果你不需要修改那个包的话,你应该选择聚合而不是链接。当你聚合时,你创建的是一个只读链入。这个包不会在你的项目下编译;取而代之的是,已编译好的包会被从源项目复制到你的项目。所以同样的RPM(同样的版本号和释出号)会出现在两个项目中。对于用户来说,困扰会少一些因为RPM都相同。

构建服务会自动检测上游对链入包的修改并触发任何依赖于它们的软件包的重新编译。

工作流程

下面步骤概述了一个通常的流程来创建一个项目和添加一个包到该项目。当然在现实世界的实例中你可能会在一些步骤上失败,需要重载它直到它不再出错。这个概述只是方便带你找找感觉,看看我们想要实现些什么。

如果可能的话,我们会展示给你两种不同的方法:

File:Example.jpg

步骤一 - 登录和一次性本地项目设定

如果你已经有了openSUSE账户,这是最简单的一步。

  • 网页客户端: 打开 http://build.opensuse.org/ 并使用右侧上方的登录链接登入。之后你的私人车库可以通过点击你的用户名实现。
  • 命令行客户端:

首先,你必须在你的机器上安装命令行客户端。你可以找到针对不同发行版的osc包在openSUSE-Tools 软件下载源 (是的: 这也是一个构建服务项目)。使用你喜欢的包管理器来安装osc包。

之后”cd“定位到你想用来装你的项目文件的文件夹。熟悉SVN的人可能会感到宾至如归: 试试用如下命令捡出你的私人车库:

 cd <用来盛放项目根目录的文件夹>
 osc checkout home:<username>
 cd home:<username> 
(请用你的用户名替换 <username>)。会有输入你的用户名和密码的提示窗口 - 在那之后osc会试图捡出你家项目下的包并且创建一个新文件夹叫做home:<username>。你可以修改你的设定通过编辑~/.oscrc

步骤二 - 创建和上传软件包

你可以使用你的家项目作为私人车库来测试软件包,如果一切正常的话,这些包随后会被传送到另外的,对大众更加可见的项目中去。

  • 网页客户端: 点击你的用户名以打开家项目,接着点击”软件包“标签下的”创建新软件包“。你需要填满下面的文本框: "Name" (mandatory), "Title" 和 "Description". 简单的使用软件包名作为"Name",软件包摘要作为"Title",软件包描述作为"Description"。

在包被创建后,前进到“源代码”标签来为你的包添加文件。你需要上传你的包的源代码和至少一个spec规范文件(请见 openSUSE:打包指南)。

  • 命令行客户端
osc meta pkg -e home:<username> <packagename>

osc会在你喜欢的文本编辑器中(基于编辑器环境变量)打开一个xml模板文件。你只需像上面所说的那样添加同样的东西(Name,Title和Description)。

下面祭出:

osc up

接着你会得到一个以你的新软件包为名的新文件夹。在命令行下添加文件,只要‘cd到新文件夹,复制相关文件(典型情况是一个tar.gz后缀的压缩包和一些支持文件,如spec规范文件、补丁、宏变量文件等)。

openSUSE RPM包在一个spec规范配置文件中存放它们的编译指示。

参考 打包指南 来学习如何创建它们。简单点的方式是复制并沿袭一个类似包的规范配置文件,或者从源代码的tar包中获取,如果存在的话。

当文件准备完毕后使用:

osc add *

这会标记该文件夹下的所有文件以便于下次提交。用下面命令提交文件:

osc commit

一次提交会自动触发编译过程。你或许想要延迟提交,直到你成功的在本地编译了这个包,想这么做请看下面。

步骤三 - 选择编译目标

现在你需要选择为哪个发行版(例如openSUSE 12.1, Ubuntu 11.10等等)编译软件包。

  • 网页客户端: 前进到你项目的“软件源”标签,点击添加软件源并选择任意可用的发行版和处理器架构。
  • 命令行客户端:首先获取可用的软件源列表:
osc ls

接着编辑你项目的元数据:

osc meta prj -e home:<username>

使用类似命令添加软件源:

 <repository name="openSUSE_Factory">
   <path project="openSUSE:Factory" repository="standard" />
   <arch>x86_64</arch>
   <arch>i586</arch>
 </repository>

project 项目 可以是 openSUSE:Factory(工厂版), openSUSE:12.1, SUSE:SLE-11:SP1 (企业版SP1)这种。repository="standard" 只用于未来扩展 (分支源)。

步骤四 - 编译你的软件包

你的软件包在被提交或某些文件被修改后会自动计划编译。如果一个依赖包被重新编译了,你的软件包也会被重新编译。

如果你需要的话也可以手动触发一次重新编译:

osc rebuildpac <project> <package> [<repo> [<arch>]]

'通过使用 <repo> 和 <arch> 参数作为选项,重新编译可以被限制在某一特定软件源或处理器架构。'

如果你的项目名为home:username,你现在可以在 http://download.opensuse.org/repositories/home:/username/ 下找到你的项目下载源。

本地编译你的软件包

有的时候与其等待构建服务的返回结果,在你本地计算机上编译你的软件包可能会更快一些。 osc支持本地编译软件包,如果你的本地计算机支援它的话(在x86_64架构的机器上你可以为i586和x86_64架构打包,在i586架构的机器上你只能为i586架构打包)。

 osc build <platform> <arch> <specfile> [--clean|--noinit]

例如:

~/obs/home:user/project # osc build openSUSE_11.4 x86_64 project.spec

如果你是以常规用户身份开始编译的话(这通常是个好主意!),你会被询问本地计算机的根用户密码。如果你添加你的用户名到/etc/sudoers并如下编辑你的~/.oscrc的话可以避免每次都输入密码:

su-wrapper = sudo

并在 visudo 下添加这行(以根用户身份操作):

<你的登录名> ALL = NOPASSWD: /usr/bin/build

到sudo配置里 (当然要去掉 '<' 和 '>')。

osc会连接到源服务器并下载所有需要的RPM到/var/tmp/osbuild-packagecache/<plattform>/<repository>/<arch> 作为缓存。 (所以如果你已经有了一个完整的源,你可以ln -s软链接这些RPM到这个文件夹中以避免庞大的下载流量。)

以openSUSE 12.1源为例,你可以这样使用零售盒装DVD的iso:

 #挂在iso
 mount openSUSE-12.1.iso /mnt/openSUSE-12.1 -o loop
 #建立缓存子文件夹
 mkdir -p /var/tmp/osbuild-packagecache/openSUSE\:12.1/standard
 #做软链接
 ln -s /mnt/openSUSE-12.1/suse/* /var/tmp/osbuild-packagecache/openSUSE:12.1/standard/

上述方法可以同时给你x86和x86_64的源。

软件包现在可以这样进行本地编译:

 osc build openSUSE_12.1 i586 <some-package-name>.spec

osc 会创建一个chroot环境在 /var/tmp/osc-build-root/ 并且开始你的编译。如果你的代码只有少数增减,你可以使用这个选项来避免重新创建编译环境--noinit。如果你怀疑你的chroot环境已损坏,你可以使用这个选项 --clean 来触发一次完整的重新编译。你可以配置chroot文件夹,详见你的 ~/.oscrc 文件中的注释。

osc 会拒绝从你的操作系统不信任的源中安装软件包。这通常是由于你的包链入到了一个开发项目但你的系统没有配置使用那个源。你可以获取必要的GPG密钥通过执行:
sudo rpm --import - <<_END_KEY
$(osc signkey offending-project被警告的项目)
_END_KEY

在你的软件包在这个chroot环境中被编译好后,你可以在/var/tmp/osc-build-root/usr/src/packages/RPMS/中找到输出的RPM包。

如果你的包使用了URL下载服务,你可能需要首先执行这条命令:

#添加下载源并刷新它的元数据
zypper ar -r http://download.opensuse.org/repositories/openSUSE:/Tools/openSUSE_11.3/openSUSE:Tools.repo

本地编译的完整日志文件储存在/var/tmp/osc-build-root/.build.log里。

修正编译过程中的错误

如果你的软件包还没有被针对你的操作系统版本和释出号编译过,那么你需要为openSUSE或其他发行版编译一个新软件包的主要原因是去检查它的兼容性。然而,这样你就会在编译过程中遇到一些需要被修复的新错误。 最简单的排错方法就是chroot到编译环境下去创建一个修复代码片段。你可能也会使用openroot 替代chroot以便于获得X11权限和挂载其他需要的文件夹。

 chroot /var/tmp/build-root/
 cd /usr/src/packages/BUILD/your-package-dir
 ls
 或者:
 openroot /var/tmp/build-root/ 'cd /usr/src/packages/BUILD/your-package-dir; ls; bash'
 ...
 exit
依赖

如果你在编译过程中碰到了依赖错误,添加一行以包含该依赖,就像:

BuildRequires: cmake libkde4-devel

在这个例子中,你的软件包编译前会安装cmake和libkde4-devel包。

安装额外的包到编译根目录

出于调试目的,你可能需要安装额外的包到你的本地编译根目录以便于调试和修复编译相关问题。这可以通过修改~/.oscrc文件,添加变量extra-pkgs来实现。例如:

extra-pkgs = vim gdb strace valgrind
安装权限

如果你收到像这样的错误信息:

error: Bad exit status from /var/tmp/rpm-tmp.qrRAn2 (%install)

这意味着你的 %install 步骤失败了 (这之前的都工作良好)。这可能是由于缺少写入权限,尤其是当你试图往错误的地方安装软件时。这种情况下添加这个make install命令到你的spec规范文件:

make install DESTDIR=%buildroot

补丁

如果你想要给一个文件打补丁,请先复制出它而不是直接到原文件夹中编辑,然后重复你想要的编译过程知道它成功了,接着使用下面命令创建相关补丁。 要让编译过程输出更多的信息你可能需要插入"set -x"参数到你的spec规范配置文件,这会使bash复述所有被执行的命令 (之后用set +x 取消这种复述)。

 diff -Naur /var/tmp/build-root/usr/src/packages/BUILD/your-package-dir/Makefile.orig \
            /var/tmp/build-root/usr/src/packages/BUILD/your-package-dir/Makefile \
     >/osc/home:user/your-package-dir/my.patch

现在添加补丁到spec配置文件,你可以先在头部用"Patch67: my.patch"加入这个补丁,之后在适当的位置(通常是%setup)用"%patch67 -p7"以便于在编译过程中采用它(-p7会略过7层文件夹如果你没有在补丁的头部写入这些文件夹的话)。 你可能会发现使用一个特殊的程序来自动生成补丁会简便一些,那你可以看看Quilt

步骤五 - 检查日志文件

构建服务为每一个软件包的每次编译都生成一个大大的日志文件。

  • 网页客户端:只需要点击软件包视图中的[Build Log]链接。
  • 命令行客户端:根据你需求的不同有几种方法(如果你已经在软件包文件夹中了,那么packagedir就是可有可无的)
osc prjresults [packagedir]

列出了整个项目的汇总编译结果。或者你也可以用:

osc results [packagedir]

列出了单个包的编译结果。

osc buildlog <platform> <arch>

列出一个包的日志文件(你需要在包文件夹中)。

创建路径

路径是包含了一系列软件和它们的使用说明的文件。另外构建服务创建了.ymp文件给每一个生成的软件源路径。这些.ymp文件可以被用户用来 一键安装

简单说,路径当你在要安装一系列软件用于典型用途而不想去手动建立包与包之间的依赖性时非常有用。 提交路径可以使用直接使用api,或者使用osc:

  • 在预设的编辑器 $EDITOR 中打开路径(如果还不存在那么就创建它)
osc meta pattern -e <project> <pattern>
  • 显示已有路径
osc meta pattern <project>
  • 获取一个已有路径
osc meta pattern <project> <pattern>
  • 你也可以这样提交一个本地文件:
osc meta pattern --file <local_file> <project> <pattern>

如何测试:在konqueror征服家中点击.ymp文件会打开安装器,如果你没有安装征服家,你可以以普通用户身份在命令行中开启:

/sbin/yast2 MetaPackageHandler http://download.opensuse.org/repositories/<project>/<SUSE_Factory or openSUSE_10.2>/<pattern>.ymp

下面是一份从KDE:KDE4项目中获取的示例路径文件,你可以从这里查看更新版本。

<pattern
 xmlns="http://novell.com/package/metadata/suse/pattern"
 xmlns:rpm="http://linux.duke.edu/metadata/rpm"
>
    <name>KDE 4 Games</name>
    <summary>KDE 4 Games</summary>
    <description>A number of games for KDE 4.</description>
    <uservisible/>
    <category lang="en">Desktop Functions</category>
    <rpm:recommends>
      <rpm:entry name="kde4-kpat"/>
      <rpm:entry name="kde4-kmahjongg"/>
      <rpm:entry name="kde4-kmines"/>
      <rpm:entry name="kde4-kreversi"/>
      <rpm:entry name="kde4-ksudoku"/>
    </rpm:recommends>
    <rpm:suggests>
      <rpm:entry name="kde4-katomic"/>
      <rpm:entry name="kde4-kbattleship"/>
      <rpm:entry name="kde4-ksquares"/>
      <rpm:entry name="kde4-bovo"/>
      <rpm:entry name="kde4-kiriki"/>
      <rpm:entry name="kde4-kwin4"/>
      <rpm:entry name="kde4-kolf"/>
      <rpm:entry name="kde4-klines"/>
      <rpm:entry name="kde4-ksame"/>
      <rpm:entry name="kde4-lskat"/>
      <rpm:entry name="kde4-kgoldrunner"/>
      <rpm:entry name="kde4-kblackbox"/>
      <rpm:entry name="kde4-kbounce"/>
      <rpm:entry name="kde4-ktuberling"/>
      <rpm:entry name="kde4-knetwalk"/>
      <rpm:entry name="kde4-kjumpingcube"/>
      <rpm:entry name="kde4-kspaceduel"/>
      <rpm:entry name="kde4-konquest"/>
      <rpm:entry name="kde4-kshisen"/>
    </rpm:suggests>
</pattern>

一些标签的描述:

标签 描述
<rpm:requires>
<rpm:entry name="example" />
</rpm:requires>
Requires RPM 一个包的例子: 这个包必须被安装 - 否则路径不满足执行条件。
<rpm:recommends>
<rpm:entry name="example" />
</rpm:recommends>
Recommends RPM 一个包的例子: if available and all dependencies of this package are fulfilled, the package would be installed. 如果这个包存在且它的所有依赖关系都满足,这个包会被安装。如果这个包不存在,不会有错误输出。如果这个包的依赖关系不满足,这个包是可见的但是不会被安装。
<rpm:suggests>
<rpm:entry name="example" />
</rpm:suggests>
Suggests RPM 一个包的例子: 会在路径中显示,但是默认不安装

延伸阅读