如何构建一个RPM包

构建一个RPM包

1. rpmdevtools工具及SPEC文件

一些基础点 可以直接从下一章节
网上找了好多相关的教程,RedHat的教程应该是最官方的。
目前没找到如何生成压缩包的,本文使用setuptools生成
还有说使用pyinstaller生成可执行文件然后获取其依赖并压缩的,没有测试这个

  1. rpmdevtools工具

    构建主要用的就是SPEC文件和源文件,将其放到对应的文件夹下执行构建命令
    首先创建RPM的工作环境,便捷的就是安装rpmdevtools工具
    执行rpmdev-setuptree命令后结果如下

    1
    2
    3
    4
    5
    6
    7
    8
    # rpmdev-setuptree
    # tree ~/rpmbuild/
    /root/rpmbuild/
    ├── BUILD
    ├── RPMS
    ├── SOURCES
    ├── SPECS
    └── SRPMS

    工作环境目录用途如下

    目录 用途
    BUILD 构建时生成的各种信息都在这里
    RPMS 构建出的二进制包
    SOURCES 构建用的源文件放这里,如压缩包、补丁文件等
    SPECS 构建用的.spec文件放这里
    SRPMS 构建出的源码包
    BUILDROOT 构建过程中创建,保存%install阶段安装的文件
  2. SPEC文件

    SPEC文件可以分为Preamble和Body两部分

    Preamble:

    SPEC指令 用途
    Name package name
    Version version number
    Release NA
    Summary NA
    License NA
    URL 该包的上游网址
    Source0 源文件
    Patch0 数量可变
    BuildArch 构建依赖的环境
    BuildRequires 构建所需的依赖包
    Requires 运行所需的依赖包

    Body:

    SPEC指令 用途
    %description NA
    %prep 构建前的一些命令
    %build 构建时执行的命令
    %install 最终安装目录
    %check 测试软件命令
    %files 最终会安装在系统的文件
    %changelog NA

2.构建一个简单的hello world rpm包

  1. 编写py文件

    还是最通用的hello world

    1
    2
    3
    4
    5
    6
    7
    8
    #!/usr/bin/python
    # -*- coding: utf-8 -*-

    def my_print():
    print('hello world')

    if __name__ == '__main__':
    my_print()
  2. 编写setup.py文件

    前面是license 命令是setuptools.setup
    setup中哪些可以不需要还没有完全搞清楚,当前这样没有问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    # Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    # implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT

    import setuptools

    setuptools.setup(
    name='myrpm',
    version='0.1.1',
    license='Apache License 2.0',
    classifiers=[
    'Development Status :: 5 - Production/Stable',
    'Intended Audience :: Developers',
    'License :: OSI Approved :: Apache Software License',
    'Programming Language :: Python :: 3.7',
    'Programming Language :: Python :: 3.8',
    'Programming Language :: Python :: 3.9',
    'Programming Language :: Python :: Implementation :: PyPy',
    'Topic :: Software Development :: Libraries :: Python Modules',
    ],
    packages=['myrpm'],
    include_package_data=True,
    zip_safe=False,
    platforms='any',
    )

  3. 目录结构

    1
    2
    3
    4
    5
    python-myrpm/
    ├── myrpm
    │   ├── __init__.py
    │   └── myrpm.py
    └── setup.py
  4. 生成压缩包

    在python-myrpm/目录下执行

    1
    python setup.py sdist

    执行后目录如下 生成了myrpm-0.1.1.tar.gz压缩文件
    感兴趣可以解压看看 我们就使用这个压缩文件作为源码构建RPM包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    python-myrpm/
    ├── dist
    │   └── myrpm-0.1.1.tar.gz
    ├── myrpm
    │   ├── __init__.py
    │   └── myrpm.py
    ├── myrpm.egg-info
    │   ├── dependency_links.txt
    │   ├── not-zip-safe
    │   ├── PKG-INFO
    │   ├── SOURCES.txt
    │   └── top_level.txt
    └── setup.py

    对应的还有一些其他命令,install是使用源码安装、build是构建、
    clean是清理build和bdist生产的临时目录、bdist是生成二进制压缩包、
    sdist是源码压缩包,–formats可以指定压缩包格式

    1
    2
    3
    4
    python setup.py build
    python setup.py install
    python setup.py clean
    python setup.py bdist
  5. 制作SPEC文件

    优先使用工具创建 在SPECS目录下执行如下命令 生成myrpm.spec文件

    1
    rpmdev-newspec  myrpm.spec

    修改SPEC文件如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    %global debug_package %{nil}
    Name: myrpm
    Version: 0.1.1
    Release: 1
    Summary: simple python rpm

    License: Apache-2.0
    URL: https://docs.openstack.org/tooz/latest/
    Source0: myrpm-0.1.1.tar.gz
    BuildArch: noarch
    BuildRequires: python3-setuptools

    %description
    A simple python rpm

    %prep
    %autosetup -n %{name}-%{version}

    %build
    %py3_build

    %install
    %py3_install

    pushd %{buildroot}
    if [ -d usr/lib ]; then
    find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
    fi
    popd
    mv %{buildroot}/filelist.lst .

    %files -n %{name} -f filelist.lst
    %dir %{python3_sitelib}/*

    %changelog
    * Thu Sep 21 2023 XXX <XXX@gmail.com>
    - init

    其中
    %global debug_package %{nil} 是缺少debug相关文件 当前不关注使用该命令忽略其报错
    Source0填充包名
    %prep 填充%autosetup -n %{name}-%{version}
    %build 填充%py3_build
    %install 填充%py3_install
    pushd 这里是把最终会安装在系统的文件写入到filelist.lst中
    %file 是指明哪些文件会安装在系统,如果有遗漏,构建是会提示
    ‘error: Installed (but unpackaged) file(s) found’的报错
    %license 和 %doc 当前不关注忽略

    对SPEC文件中更高级的修改需要进一步探索

  6. 构建RPM包

    把第4步和第5步中的源码压缩包、SPEC文件分别放到SOURCE和SPECS目录下

    1
    2
    3
    4
    5
    6
    7
    8
    ~/rpmbuild
    ├── BUILD
    ├── RPMS
    ├── SOURCES
    │   └── myrpm-0.1.1.tar.gz
    ├── SPECS
    │   └── myrpm.spec
    └── SRPMS

    在SPECS目录下执行rpmbuild命令

    1
    rpmbuild -ba myrpm.spec

    构建成功后使用如下命令查询结果

    1
    2
    3
    4
    5
    6
    tree /root/rpmbuild/*RPMS
    /root/rpmbuild/RPMS
    └── noarch
    └── myrpm-0.1.1-1.noarch.rpm
    /root/rpmbuild/SRPMS
    └── myrpm-0.1.1-1.src.rpm
  7. RPM包使用

    使用rpm -ivh 安装rpm包,安装后就可以import了
    使用rpm -qa | grep 查询安装的包
    使用rpm -e 卸载安装的包
    当前发现卸载是会在/usr/lib/python3.9/site-packages/中残留__pycache__文件夹
    暂时无法解决,试了下别的包也有这个问题

3.增量构建RPM包

增量构建可能描述的不准确,实际上是以git.patch的形式添加自己对已有RPM包的修改,然后构建

这种构建要使用源码包
RPM包一般分为二进制和源码包(后缀为.src.rpm)
包名中noarch字段表示无环境依赖

源码包可以通过rpm2cpio命令解压.src.rpm的包得到
也可以找到相关的SPEC文件,通过URL获取对应的压缩包

以上一章节中构建的RPM包为例,更改my_print的打印内容

  1. 解压rpm包获取源码压缩包

    1
    2
    3
    4
    5
    6
    # rpm2cpio myrpm-0.1.1-1.src.rpm | cpio -div -D srcrpm
    # tree
    .
    └── srcrpm
    ├── myrpm-0.1.1.tar.gz
    └── myrpm.spec

    其中 -D 参数为指定解压的目录

  2. 解压源码

    解压后进入到myrpm目录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # tar -vxf myrpm-0.1.1.tar.gz
    # tree
    srcrpm/
    ├── myrpm-0.1.1
    │   ├── myrpm
    │   │   ├── __init__.py
    │   │   └── myrpm.py
    │   ├── myrpm.egg-info
    │   │   ├── dependency_links.txt
    │   │   ├── not-zip-safe
    │   │   ├── PKG-INFO
    │   │   ├── SOURCES.txt
    │   │   └── top_level.txt
    │   ├── PKG-INFO
    │   ├── setup.cfg
    │   └── setup.py
    ├── myrpm-0.1.1.tar.gz
    └── myrpm.spec
  3. 生成git patch文件

    当前在srcrpm/myrpm-0.1.1/myrpm目录,示例如下

    1. 选择要修改的文件初始化git仓
    2. 完成你的修改,且不要add
    3. git diff生成补丁文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # git init
      # git add myrpm.py
      # git commit -m "init"
      # vi myrpm.py
      # git diff myrpm.py > myrpm.patch
      # tree
      .
      ├── __init__.py
      ├── myrpm.patch
      └── myrpm.py
  4. 修改SPEC文件

    需要修改或者添加的字段如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    index 5869db2..cb7fcb8 100644
    --- a/myrpm.spec
    +++ b/myrpm.spec
    @@ -1,12 +1,13 @@
    Name: myrpm
    Version: 0.1.1
    -Release: 1
    +Release: 2
    Summary: simple python rpm

    License: Apache-2.0
    URL: https://docs.openstack.org/tooz/latest/
    Source0: myrpm-0.1.1.tar.gz
    +Patch01: myrpm.patch
    BuildArch: noarch
    BuildRequires: python3-setuptools

    @@ -15,7 +16,7 @@ BuildRequires: python3-setuptools
    A simple python rpm

    %prep
    -%autosetup -n %{name}-%{version}
    +%autosetup -n %{name}-%{version} -p1


    %build
    @@ -38,5 +39,8 @@ mv %{buildroot}/filelist.lst .


    %changelog
    +* Fri Sep 22 2023 xxx <xxx@gmail.com> - 0.1.1-2
    +- change print content
    +
    * Thu Sep 21 2023 xxx <xxx@gmail.com> - 0.1.1-1
  5. 构建

    构建时将patch文件补充到SOURCES目录中,其他与上一章节相同
    如果遇到’No file to patch. Skipping patch.’的报错
    大概率是patch文件中的目录有问题
    如当前示例中,patch文件中的目录为a/myrpm.py和b/myrpm.py
    并且构建是会报错,将目录改为a/myrpm/myrpm.py和b/myrpm/myrpm.py即可

4.部分宏的对应值

%{_sysconfdir} /etc
%{_prefix} /usr
%{_exec_prefix} %{_prefix}
%{_bindir} %{_exec_prefix}/bin
%{_lib} lib (lib64 on 64bit systems)
%{_libdir} %{_exec_prefix}/%{_lib}
%{_libexecdir} %{_exec_prefix}/libexec
%{_sbindir} %{_exec_prefix}/sbin
%{_sharedstatedir} /var/lib
%{_datadir} %{_prefix}/share
%{_includedir} %{_prefix}/include
%{_oldincludedir} /usr/include
%{_infodir} /usr/share/info
%{_mandir} /usr/share/man
%{_localstatedir} /var
%{_topdir} %{getenv:HOME}/rpmbuild
%{_builddir} %{_topdir}/BUILD
%{_rpmdir} %{_topdir}/RPMS
%{_sourcedir} %{_topdir}/SOURCES
%{_specdir} %{_topdir}/SPECS
%{_srcrpmdir} %{_topdir}/SRPMS
%{_buildrootdir} %{_topdir}/BUILDROOT
%{_var} /var
%{_tmppath} %{_var}/tmp
%{_usr} /usr
%{_usrsrc} %{_usr}/src
%{_docdir} %{_datadir}/doc

Comments