本章主要讲解如何编写源码和创建软件,对于RPM文件的打包者来说,这些是必备的背景知识。
程序是如何生成的
本地编译代码
解释代码
从源码构建软件
本地编译代码
解释代码
修补软件
安装任意工件
使用install命令
使用make install命令
准备待打包的源码
将源码转换成压缩包
bello
pello
cello
什么是源码?
源码是人类可读的计算机指令,用于描述如何执行一次计算。可以使用一种编程语言来编写源码。
本教程会提供三个版本的Hello World
程序,每个版本都使用一种不同的编程语言来编写。使用这三种不同的语言编写的程序,它们的打包方法也是不同的,涵盖RPM打包者可能会遇到的三种主要的使用场景。
注意:
现在已经有数千种编程语言。本文只会使用三种编程语言,它们足以描述一些基本概念。
- 使用bash编写的
Hello World
程序,文件名为bello
:
#!/bin/bash
printf "Hello World\n"
- 使用Python编写的
Hello World
程序,文件名为pello.py
:
#!/usr/bin/env python
print("Hello World")
- 使用C语言编写的
Hello World
程序,文件名为cello.c
:
#include <stdio.h>
int main(void) {
printf("Hello World\n");
return 0;
}
上述的三个程序都可以将Hello World
信息输出至命令行。
注意:
对于软件打包者来说,了解如何编写程序并不是必需的,但是会很有帮助。
程序是如何生成的
有很多种方法可以将人类可读的源码转换成机器代码 —— 实际上,计算机会遵循这些指令来执行程序。然而,可以将所有方法简化为以下三种:
- 程序是本地编译的。
- 程序是通过原生解释器解释执行的。
- 程序是在编译成字节码之后解释执行的。
本地编译代码
本地编译的软件是使用一种需要编译成机器码的编程语言来编写的,编译之后会得到一个二进制可执行文件。这种软件可以独立运行。
通过这种方式构建的RPM软件包是特定于体系结构的。这就意味着,如果你在一台使用64位(x86_64)AMD或Intel处理器的计算机上编译这种软件,那么得到的可执行文件是不能在32位(x86)AMD或Intel处理器上执行的。编译得到的软件包的文件名会包含体系结构的相关信息。
解释代码
某些编程语言,如bash或Python,不会编译成机器代码。相反,它们的程序源码不用经过事先转换就可以逐步执行,通过某种语言解释器或语言虚拟机解释执行。
完全使用解释型编程语言编写的软件不会特定于体系结构。因此,编译得到的RPM软件包的文件名包含noarch
字符串。
解释型语言有两种:字节码编译型和原生解释型。这两种类型的语言的不同之处在于程序构建过程和软件打包过程。
-
原生解释型的程序
使用原生解释型语言编写的程序根本不需要编译,它们直接由解释器执行。 -
字节码编译型的程序
使用字节码编译型语言编写的程序需要编译成字节码,然后再由语言虚拟机执行。
注意:
某些编程语言会给出选项:它们可以是原生解释型或字节码编译型。
从源码构建软件
本节会解释如何从源码构建软件。
-
对于使用编译型语言编写的软件来说,源码需要经过一个构建的过程才能生成机器代码。这个过程通常称为编译或翻译,因不同语言而异。可以运行或执行构建得到的软件,计算机便会执行由程序员指定的任务。
-
对于使用原生解释型语言编写的软件来说,不需要构建源码,可以直接执行。
-
对于使用字节码编译型语言编写的软件来说,需要将源码编译成字节码,然后再由语言虚拟机执行。
本地编译代码
在这个示例中,你将会把使用C语言编写的cello.c
程序构建成一个可执行程序。cello.c
的源码,如下所示:
#include <stdio.h>
int main(void) {
printf("Hello World\n");
return 0;
}
1. 手动构建
调用GNU编译器集合(GCC)中的C语言编译器,将源码编译成二进制文件:
gcc -o cello cello.c
执行编译输出的二进制文件cello
:
$ ./cello
Hello World
就这些了!你已经将源码本地编译成一个二进制可执行程序,并且成功地运行这个可执行程序。
2. 自动构建
除了手动构建源码之外,你还可以将构建过程自动化。这是大型软件经常使用的一种方法。创建一个名为Makefile
的文件,然后运行GNU的<a href="http://www.gnu.org/software/make/" title="make
“>make
工具,这样便能实现自动化构建了。
为了设置自动化构建,你需要在cello.c
文件所在的相同目录之中,创建一个名为Makefile
的文件。Makefile
文件的内容,如下所示:
cello:
gcc -o cello cello.c
clean:
rm cello
现在构建软件,简单地运行make
工具:
$ make
make: 'cello' is up to date.
这是因为已经存在编译好的二进制文件了,请运行make clean
命令,然后再次运行make
命令:
$ make clean
rm cello
$ make
gcc -o cello cello.c
在另一次构建完成之后,再次尝试构建,此时不会起任何作用:
$ make
make: 'cello' is up to date.
最后,执行程序:
$ ./cello
Hello World
现在,你不仅可以手动编译一个程序,而且还可以使用构建工具实现自动化编译。
解释代码
接下来的两个示例会演示如何运行一个字节码编译型的程序(使用Python编写),以及如何运行一个原生解释型的程序(使用bash编写)。
注意:
在下面的两个示例中,文件顶部的#!
行被称为shebang,它不是编程语言源码的一部分。
shebang会使得系统将一个文本文件看作是一个可执行文件:系统程序加载器会解析包含shebang的行,获取二进制可执行文件的路径,然后会将这个可执行文件作为编程语言的解释器。
1. 字节码编译型的代码
在这个示例中,你将会把使用Python编写的pello.py
程序编译成字节码,然后通过Python的语言虚拟机解释执行。Python源码还可以原生解释执行,但是编译成字节码的执行速度会更快。因此,RPM打包者更倾向于打包编译成字节码的软件,然后分发给终端用户。pello.py
的源码,如下所示:
#!/usr/bin/env python
print("Hello World")
将程序编译成字节码的过程,对于不同的编程语言来说是不同的。编译过程取决于编程语言、语言的虚拟机,以及这种编程语言会使用的工具和流程。
注意:
Python经常编译字节码,但是并不以此处描述的方式执行。以下过程的目标并不是为了符合社区标准,而是为了简单。如果想要了解Python的技术指南,那么请参考软件打包和分发页面。
将pello.py
编译成字节码:
$ python -m compileall pello.py
$ file pello.pyc
pello.pyc: python 2.7 byte-compiled
执行pello.pyc
中的字节码:
$ python pello.pyc
Hello World
2. 原生解释型的代码
在这个示例中,你将会原生解释执行bello
程序,它是用bash内建的语言编写的。bello
的源码,如下所示:
#!/bin/bash
printf "Hello World\n"
使用shell脚本语言编写的程序,如bash,都是原生解释执行的。因此,你只需要将存放源码的文件设置为可执行的,然后运行即可:
$ chmod +x bello
$ ./bello
Hello World
修补软件
补丁是一种源码,用于更新其他源码。补丁采用diff文件格式,因为它能够表示两个不同版本的文本的不同之处。使用diff
工具可以创建diff文件,然后使用<a href="http://savannah.gnu.org/projects/patch/" title="patch
“>patch
工具将其应用至源码。
注意:
软件开发者经常会使用版本控制系统(如git)来管理他们的代码库。这些工具也具备创建diff文件或修补软件的功能。
在以下示例中,我们会使用diff
工具从原始的源码创建一个补丁文件,然后使用patch
工具应用这个补丁文件。当通过SPEC文件创建RPM文件时,将会对源码打补丁,稍后的章节将会描述如何打补丁。
打补丁是如何与RPM打包相关联的呢?在打包时,我们不会简单地编辑/修改原始的源码,而是保持不变,将补丁应用至这些源码。
若要为cello.c
源码创建一个补丁:
Step-1. 保留原始的源码:
$ cp cello.c cello.c.orig
这是保存原始的源码文件的常用方法。
Step-2. 修改cello.c
源码:
#include <stdio.h>
int main(void) {
printf("Hello World from my very first patch!\n");
return 0;
}
Step-3. 使用diff
工具生成补丁文件:
注意:
我们使用了diff
工具的几个常用参数。若要了解这些参数有关的详细信息,请参考diff
的使用手册。
$ diff -Naur cello.c.orig cello.c
--- cello.c.orig 2016-05-26 17:21:30.478523360 -0500
+++ cello.c 2016-05-27 14:53:20.668588245 -0500
@@ -1,6 +1,6 @@
#include<stdio.h>
int main(void){
- printf("Hello World!\n");
+ printf("Hello World from my very first patch!\n");
return 1;
}
\ No newline at end of file
以-
符号开头的行,表示从原始的源码中删除的内容;以+
符号开头的行,表示替换的内容。
Step-4. 将补丁保存至文件:
$ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
Step-5. 恢复原始的cello.c
源码:
$ cp cello.c.orig cello.c
我们保留了原始的cello.c
源码文件,因为当构建RPM文件时,会使用原始的文件,而不是修改后的文件。想要了解更多的信息,请参考SPEC文件的使用手册。
若要使用cello-output-first-patch.patch
文件给cello.c
源码打补丁,则需要将补丁文件重定向至patch
命令:
$ patch < cello-output-first-patch.patch
patching file cello.c
现在,cello.c
文件的内容可以反映补丁的变更:
$ cat cello.c
#include<stdio.h>
int main(void){
printf("Hello World from my very first patch!\n");
return 1;
}
若要构建和运行打过补丁的cello.c
程序,则需要:
$ make clean
rm cello
$ make
gcc -o cello cello.c
$ ./cello
Hello World from my very first patch!
你已经学会如何创建补丁文件、如何给程序打补丁,以及如何构建和运行打过补丁的程序。
安装任意工件
Linux和其他类Unix系统有一个很大的优势,那就是文件系统层次标准(FHS)。它会指定哪些文件应该位于哪些目录之中。RPM软件包安装的文件,应该按照FHS的要求,存放在合适的目录之中。例如,可执行文件应当存放在系统的PATH环境变量所指定的目录之中。
在本指南的上下文中,任意工件是指从RPM软件包安装到系统中的任何文件。对于RPM文件和系统来说,工件可能是一个脚本文件、一个从源码包编译得到的二进制文件、一个预编译的二进制文件或任何其他的文件。
有两种主流的方法可以将任意工件放置在系统之中,我们会探索这两种方法:使用install
命令,以及使用make install
命令。
使用install命令
有时候,使用自动化构建工具(如GNU make)并不是最佳的 —— 例如,如果待打包的程序非常简单,不需要额外的开销。在这些情况下,打包者经常会使用install
命令(由系统的coreutils工具包提供),这个命令会将工件放置在文件系统的指定目录之中,并且会设置指定的权限集合。
以下示例将使用我们以前创建的bello
文件作为我们的安装方法的任意工件。注意,你将需要sudo权限,或者能够以root权限运行这个命令(不包括命令的sudo
部分)。
在这个示例中,install
命令会将bello
文件存放在/usr/bin
目录之中,并且设置可执行脚本的常用权限:
$ sudo install -m 0755 bello /usr/bin/bello
现在,bello
程序已经存放在PATH环境变量列出的某个目录之中。因此,你可以在任意目录中执行bello
程序,而不用指定它的完整路径:
$ cd ~
$ bello
Hello World
使用make install命令
你还可以使用make install
命令将已经构建的软件安装至系统之中,这是一种流行的自动化安装方法。你需要在Makefile
文件中指定如何将任意工件安装至系统之中。
注意:
通常,Makefile
文件是由开发者编写的,而不是由打包者编写的。
向Makefile
文件添加install
节。Makefile
文件的内容,如下所示:
cello:
gcc -o cello cello.c
clean:
rm cello
install:
mkdir -p $(DESTDIR)/usr/bin
install -m 0755 cello $(DESTDIR)/usr/bin/cello
$(DESTDIR)变量是GNU make工具内建的,通常用于指定不同于根目录的安装目录。
现在,你不仅可以使用Makefile
文件来构建软件,还可以通过这个文件将软件安装至目标系统。
为了构建和安装cello.c
程序,你需要:
$ make
gcc -o cello cello.c
$ sudo make install
install -m 0755 cello /usr/bin/cello
现在,cello
程序已经存放在PATH环境变量列出的某个目录之中。因此,你可以在任意目录中执行cello
程序,而不用指定它的完整路径:
$ cd ~
$ cello
Hello World
你已经将一个构建完成的工件安装至系统的指定位置之中了。
准备待打包的源码
注意:
本节创建的代码,可以在此处下载。
开发者通常会以源码压缩包的形式分发软件,然后会用这个压缩包来创建RPM软件包。在本节中,你将会创建这类的压缩包。
注意:
通常,RPM打包者不负责创建源码压缩包,这是开发者负责的工作。打包者会使用现成的源码压缩包。
软件应该使用某种软件许可证进行分发。例如,我们将会使用GPLv3许可证。每个示例程序都会有一个LICENSE
文件,其中包含许可证文本。当打包时,RPM打包者需要处理许可证文件。
若要创建LICENSE
文件,则可以参考以下示例:
$ cat /tmp/LICENSE
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
将源码转换成压缩包
在以下示例中,我们会将三个Hello World
程序分别放入三个用gzip压缩的tar文件之中。软件通常以这种方式发布,稍后将被打包分发。
bello
bello工程使用bash的脚本语言实现Hello World
。这种实现只包含’bello’的shell脚本。因此,除了LICENSE
文件之外,得到的tar.gz
压缩包只包含上述的脚本文件。我们假设程序的版本号是0.1
。
为软件分发准备bello工程:
Step-1. 将文件放入一个单独的目录:
$ mkdir /tmp/bello-0.1
$ mv ~/bello /tmp/bello-0.1/
$ cp /tmp/LICENSE /tmp/bello-0.1/
Step-2. 创建分发的压缩包,然后将这个压缩包移动至~/rpmbuild/SOURCES/
目录:
$ cd /tmp/
$ tar -cvzf bello-0.1.tar.gz bello-0.1
bello-0.1/
bello-0.1/LICENSE
bello-0.1/bello
$ mv /tmp/bello-0.1.tar.gz ~/rpmbuild/SOURCES/
pello
pello工程使用Python语言实现Hello World
。这种实现只包含pello.py
程序。因此,除了LICENSE
文件之外,得到的tar.gz
压缩包只包含上述的源码文件。我们假设程序的版本号是0.1.1
。
为软件分发准备pello工程:
Step-1. 将文件放入一个单独的目录:
$ mkdir /tmp/pello-0.1.1
$ mv ~/pello.py /tmp/pello-0.1.1/
$ cp /tmp/LICENSE /tmp/pello-0.1.1/
Step-2. 创建分发的压缩包,然后将这个压缩包移动至~/rpmbuild/SOURCES/
目录:
$ cd /tmp/
$ tar -cvzf pello-0.1.1.tar.gz pello-0.1.1
pello-0.1.1/
pello-0.1.1/LICENSE
pello-0.1.1/pello.py
$ mv /tmp/pello-0.1.1.tar.gz ~/rpmbuild/SOURCES/
cello
cello工程使用C语言实现Hello World
。这种实现只包含cello.c
和Makefile
文件。因此,除了LICENSE
文件之外,得到的tar.gz
压缩包只包含上述两个文件。我们假设程序的版本号是1.0
。
注意,patch
文件并不会在压缩包中随着程序一起发布。当构建RPM文件时,RPM打包者将会对源码打补丁。补丁文件会和.tar.gz
压缩包一起存放至~/rpmbuild/SOURCES/
目录。
为软件分发准备cello工程:
Step-1. 将文件放入一个单独的目录:
$ mkdir /tmp/cello-1.0
$ mv ~/cello.c /tmp/cello-1.0/
$ mv ~/Makefile /tmp/cello-1.0/
$ cp /tmp/LICENSE /tmp/cello-1.0/
Step-2. 创建分发的压缩包,然后将这个压缩包移动至~/rpmbuild/SOURCES/
目录:
$ cd /tmp/
$ tar -cvzf cello-1.0.tar.gz cello-1.0
cello-1.0/
cello-1.0/Makefile
cello-1.0/cello.c
cello-1.0/LICENSE
$ mv /tmp/cello-1.0.tar.gz ~/rpmbuild/SOURCES/
Step-3. 添加补丁文件:
$ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/
现在,源码已经准备就绪,可以打包RPM文件了。