由于一些原因,学习了一下关于rpm的相关东西,简单的记录一下,初次了解,只是很浅显的东西,如果需要深入了解,建议去官网搜相关文档学习。
1.搭建rpm编包环境
在编rpm包之前需要安装一些工具包,可以直接通过Yum安装
yum -y install rpmdevtools
安装好工具包之后,可以手动建立主目录rpmbuild和次级目录BUILD、BUILDROOT、SOURCES、SPECS、RPMS、SRPMS;也可以使用rpmdev-setuptree自动生成。
2.Rpm编包目录结构
上面已经提到,rpm的编包目录其实是有一个主目录和多个次级目录组成,下面简单介绍一下各次级目录的作用:
BUILD:用于放置原压缩文件解压后的所有文件;
BUILDROOT:用于模拟安装环境下的ROOT目录,举个例子:编好的rpm需要在一个环境下安装,模拟环境下的*/BUILDROOT/etc目录等价于安装环境下的/etc;
SOURCES:用于放置需要编译成rpm的源文件,源文件呈现压缩状态
SPECS:用于放置编包脚本文件*.spec文件
RPMS:放置编包完成的.rpm包
SRPMS:放置源码包
3.spec文件编写
.spec文件是编写每个rpm包必需要自己写的文件,它有自己的一套语法结构,下面详细的介绍一下.spec文件的编写。
.spec文件可以自动生成,也可以自己用vim编写,建议初次写用自动生成方法,
自动生成命令:rpmdev-newspec *.spec
下面给出个字段的含义:
##定义宏,后面用%{_bpath}引用
fine _bpath /opt/product/bin
##软件包的名称,用%{name}引用
Name:product
##简要的描述产品信息
Summary:describe the production a little
##产品的版本号
Version:3.9.0
##产品序列号
Release:*********
##产品分组,通常都是用标准分组
Group:Applications/Production
##产品的授权方式
License:GPL
##源代码,通常是.tar.gz形式的压缩包
Source:%{name}-%{version}.tar.gz
##这是下面make install时是用的虚拟根目录,%{_tpmpath}是系统的宏
BuildRoot:%{_tmppath}/%{name}-%{version}-%{release}
##软件主页地址
URL:http://baidu.com/
##发行商的信息
Vendor:baidu
##产品发行版标识
Distribution:*********
##罗列安装包时需要的依赖包,注:单独一个一个列,如下
Requires: gcc-c++
Requires: systemd
##产品的详细说明
description:********
##打包者信息
Packager:user
--------------------------------------------------------
##打包前的预处理,写脚本语言,%setup是把SOURCES的源代码解压到BUILD中
*****************************************************************
%pre 和 %post 分别定义了RPM包在被安装前后的处理,%preun 和 %postun 则定义了在卸载前后的操作。它们都带有一个参数,具体参数含义如下:
| Install | Upgrade | uninstall |
%pretrans | $1 == 0 | $1 == 0 | (N/A) |
%pre | $1 == 1 | $1 == 2 | (N/A) |
%post | $1 == 1 | $1 == 2 | (N/A) |
%preun | (N/A) | $1 == 1 | $1 == 0 |
%postun | (N/A) | $1 == 1 | $1 == 0 |
%posttrans | $1 == 0 | $1 == 0 | (N/A) |
*****************************************************************
%prep
%setup -n %{name}-%{version}
##开始构建包,编译源代码
%build
make
##将编译好的产品安装到虚拟的根目录,通常先清理,在写安装脚本,此步是模拟执行一遍安装的全过程(实际就是拷贝各种文件),
##拷贝到虚拟目录下,在虚拟目录下建议和真实安装一模一样的目录结构(与%files有极大的关系)
%install
Make install PREFIX=%{prefix} DESTDIR=%{buildroot}
##删除编译过程中的中间文件、目录
%clean
rm -rf $RPM_BUILD_ROOT
##分别是rpm包安装前后、卸载前后执行的脚本
%pre
%post
%preun
%postun
##定义哪些文件、目录将被打进rpm包,并制定安装文件的属性,需要列出所有的,但是需要指明哪些是配置文件等
##如果列出的目录或者文件不存在则会报错
#所有的BUILDROOT下的目录、文件都需要在%file中声明
%files
%attr(644,root,root) %dir /etc/dt.d
%attr(644,root,root) %config(noreplace) /etc/dt.d/*
当.spce文件编写好之后,可以使用rpmbuild -ba *.spec进行编译;
注:通常情况下,.spec文件会和Makefile文件同时使用,.spec文件只是由简单的安装的命令构成,而Makefile充当大批量的脚本语句文件。
4.Makefile文件编写
4.1 概述
Makefile文件由一系列规则(rules)构成。每条规则的形式如下:
:
[tab]
上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。
"目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。
每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。下面就详细讲解,每条规则的这三个组成部分。
4.2 目标(target)
一个目标(target)就构成一条规则。目标通常是文件名,指明Make命令所要构建的对象 。目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。
除了文件名,目标还可以是某个操作的名字,这称为"伪目标"(phony target)。
clean:
rm *.o
上面代码的目标是clean,它不是文件名,而是一个操作的名字,属于"伪目标 ",作用是删除对象文件。
用法:make clean
但是,如果当前目录中,正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令。
为了避免这种情况,可以明确声明clean是"伪目标",写法如下。
.PHONY: clean
clean:
rm *.o temp
声明clean是"伪目标"之后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。像.PHONY这样的内置目标名还有不少,可以查看手册。
如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标。
用法:make
上面代码执行Makefile文件的第一个目标。
4.3 前置条件(prerequisites)
前置条件通常是一组文件名,之间用空格分隔。它指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),"目标"就需要重新构建。
result.txt: source.txt
cp source.txt result.txt
上面代码中,构建 result.txt 的前置条件是 source.txt 。如果当前目录中,source.txt 已经存在,那么make result.txt可以正常运行,否则必须再写一条规则,来生成 source.txt 。
source.txt:
echo "this is the source" > source.txt
上面代码中,source.txt后面没有前置条件,就意味着它跟其他文件都无关,只要这个文件还不存在,每次调用make source.txt,它都会生成。
$ make result.txt
$ make result.txt
上面命令连续执行两次make result.txt。第一次执行会先新建 source.txt,然后再新建 result.txt。第二次执行,Make发现 source.txt 没有变动(时间戳晚于 result.txt),就不会执行任何操作,result.txt 也不会重新生成。
如果需要生成多个文件,往往采用下面的写法。
source: file1 file2 file3
上面代码中,source 是一个伪目标,只有三个前置文件,没有任何对应的命令。
$ make source
执行make source命令后,就会一次性生成 file1,file2,file3 三个文件。这比下面的写法要方便很多。
$ make file1
$ make file2
$ make file3
4.4 命令(commands)
命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建"目标"的具体指令,它的运行结果通常就是生成目标文件。
每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明。
.RECIPEPREFIX = >
all:
> echo Hello, world
上面代码用.RECIPEPREFIX指定,大于号(>)替代tab键。所以,每一行命令的起首变成了大于号,而不是tab键。
需要注意的是,每行命令在一个单独的shell中执行。这些Shell之间没有继承关系。
var-lost:
export foo=bar
echo "foo=[$$foo]"
上面代码执行后(make var-lost),取不到foo的值。因为两行命令在两个不同的进程执行。一个解决办法是将两行命令写在一行,中间用分号分隔。
var-kept:
export foo=bar; echo "foo=[$$foo]"
另一个解决办法是在换行符前加反斜杠转义。
var-kept:
export foo=bar; \
echo "foo=[$$foo]"
最后一个方法是加上.ONESHELL:命令。
.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"
注:详细的Makefile文件请参看文末的参考文献[2]
5.常用宏
一些常用autoconf的宏:
%{_sysconfdir} /etc
%{_prefix} /usr
%{_exec_prefix} %{_prefix}
%{_bindir} %{_exec_prefix}/bin
%{_libdir} %{_exec_prefix}/%{_lib}
%{_libexecdir} %{_exec_prefix}/libexec
%{_sbindir} %{_exec_prefix}/sbin
%{_sharedstatedir} /var/lib
%{_datarootdir} %{_prefix}/share
%{_datadir} %{_datarootdir}
%{_includedir} %{_prefix}/include
%{_infodir} /usr/share/info
%{_mandir} /usr/share/man
%{_localstatedir} /var
%{_initddir} %{_sysconfdir}/rc.d/init.d
路径的其他宏和变量:
These macros should be used for paths that are not covered by the macros mimicking autoconf variables. The %{buildroot} macro or the $RPM_BUILD_ROOT variable is the directory that should be assumed to be the root file system when installing files. It is used as the value for the DESTDIR variable.
%{_var} /var
%{_tmppath} %{_var}/tmp
%{_usr} /usr
%{_usrsrc} %{_usr}/src
%{_lib} lib (lib64 on 64bit multilib systems)
%{_docdir} %{_datadir}/doc
%{buildroot} %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch}
$RPM_BUILD_ROOT %{buildroot
构建flags宏和变量:
These macros should be used as flags for the compiler or linker
%{__global_cflags} -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4
%{optflags} %{__global_cflags} -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables
$RPM_OPT_FLAGS %{optflags}
RPM编包目录的宏:
The macros are usually used with rpmbuild --define to specify which directories rpmbuild should use, it is unusual to use them within SPEC files.
%{_topdir} %{getenv:HOME}/rpmbuild
%{_builddir} %{_topdir}/BUILD
%{_rpmdir} %{_topdir}/RPMS
%{_sourcedir} %{_topdir}/SOURCES
%{_specdir} %{_topdir}/SPECS
%{_srcrpmdir} %{_topdir}/SRPMS
%{_buildrootdir} %{_topdir}/BUILDROOT
参考文献:
[1].https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros#RPM_directory_macros
[2].https://www.kancloud.cn/kancloud/make-command/45593
[3].http://ftp.rpm.org/max-rpm
[4].http://ftp.rpm.org/max-rpm/
[5].http://rpm-guide.readthedocs.io
[6].http://www.rpm.org/wiki/Docs#PackagerDocumentation
[7].https://fedoraproject.org/wiki/How_to_create_an_RPM_package