RPM 打包入门(基于openEuler)

主要参考内容(均为官方文档):

https://rpm-packaging-guide.github.io/#building-rpms
https://docs.redhat.com/zh_hans/documentation/red_hat_enterprise_linux/8/html/packaging_and_distributing_software/introduction-to-rpm_packaging-and-distributing-software#introduction-to-rpm_packaging-and-distributing-software

本文的环境:WSL2 + Qemu + OpenEuler24.03
提到的所有示例,均在该环境下测试

一、快速了解

RPM Package Manager (RPM)是一个运行在 Red Hat Enterprise Linux (RHEL)、CentOS 和 Fedora 上的软件包管理系统。您可以使用 RPM 为任何这些操作系统所创建的软件进行分发、管理和更新。

在 Red Hat Enterprise Linux 中,RPM 完全集成到更高级别的软件包管理软件中,如 YUM 或 PackageKit。虽然 RPM 提供了自己的命令行界面,但大多数用户只需要通过这个软件与 RPM 进行交互。但是,在构建 RPM 软件包时,您必须使用 RPM 工具,如 rpmbuild (8) 。

具体来说,RPM 软件包包含以下部分:

  • GPG 签名:GPG 签名用于验证软件包的完整性。
  • 标头(软件包元数据):RPM 软件包管理器使用此元数据来确定软件包依赖项、安装文件的位置及其他信息。
  • payload:有效负载是一个 cpio 归档,其中包含要安装到系统的文件。
    RPM 软件包有两种类型。这两种类型都共享文件格式和工具,但内容不同,并实现不同的目的:
  • 源 RPM(SRPM):SRPM 包含源代码和 spec 文件,它描述了如何将源代码构建为二进制 RPM。另外,SRPM 可以包含源代码的补丁。
  • 二进制 RPM:一个二进制 RPM 包含了根据源代码和补丁构建的二进制文件。
    安装 rpmdevtools 软件包:

openEuler 中使用 dnf,其他系统中可能使用 yum 安装:

dnf install rpmdevtools*

二、前置知识

这里仅简要罗列,更详细的内容可从文章开头的参考链接中查看
可以选择跳过,直接查看第三章的示例

1、源代码
源代码是人类可读的计算机指令,其描述了如何执行计算。源代码是使用编程语言表达的。

2、机器码
源代码转换为机器码方法:

  • 原生编译软件
    原生编译的软件是独立软件、原生编译的 RPM 软件包是特定于架构的,例如 C 语言
  • 使用语言解释器或语言虚拟机解释软件,可以使用原始解释或字节编译软件
    完全使用解释编程语言编写的软件不是特定于架构的。因此,生成的 RPM 软件包在其名称中有 noarch 字符串
    • 原始解释的软件:不需要编译这类软件,原始解释的软件由解释器直接执行
    • 字节编译的软件:必须首先将这类软件编译成字节码,然后由语言虚拟机执行

3、构建方式
在软件构建过程中,源代码被转换为可以使用 RPM 打包的软件工件

  • 原生编译的代码构建软件,使用以下方法之一将使用编译语言编写的软件构建成可执行文件:
    • 手动构建(使用 gcc 命令)
    • 自动化构建(使用 Makefile 文件与 make 命令)
  • 将解释编程语言编写的源代码转换为机器码:
    • 字节编译
    • 原始解释(例如 Shell 脚本)
      Python 支持原始解释、字节编译两种方式,字节编译的版本速度更快。因此,RPM 软件包程序更喜欢将字节编译的版本打包,来分发给最终用户。

4、修复软件(打补丁)
在打包软件时,可能需要对原始源代码进行某些更改,如修复 bug 或更改配置文件。在 RPM 打包中,可以将原始源代码保持不变,并在其上应用补丁。
补丁是更新源代码文件的一段文本。补丁具有 diff 格式,因为它代表文本的两个版本之间的区别。可以使用 diff 实用程序创建补丁,然后使用 patch 实用程序将补丁应用到源代码。

5、LICENSE
软件许可证文件(LICENSE)告知用户他们可以使用源代码做什么和不能做什么。
没有源代码许可证意味着您保留此代码的所有权限,任何人都不能从源代码复制、分发或创建衍生工作。
GPLv3 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/.

6、源代码存档(归档文件)
归档文件是带有 .tar.gz 或 .tgz 后缀的文件。将源代码放入存档中是发布软件以稍后打包为分发的常用方法。
注意:补丁 文件不随程序一起在存档中分发。构建 RPM 时,RPM 软件包管理器应用补丁。补丁将与 tar.gz 存档一起放在 ~/rpmbuild/SOURCES/ 目录中。
LICENSE 和源代码是一起归档的,而 patch 不和源代码一起归档,即 .tar.gz 归档文件中包含 LICENSE 和源代码,patch 和 .tar.gz 并列存放在 ~/rpmbuild/SOURCES 目录下

# 源代码文件夹
mkdir cello-1.0
mv cello.c cello-1.0/
mv Makefile cello-1.0/
mv LICENSE cello-1.0/

# 进行压缩打包,创建存档
tar -cvzf cello-1.0.tar.gz cello-1.0

# 移动到 ~/rpmbuild/SOURCES/ 目录中, 存储用于构建软件包的源文件的默认目录
# 对于需要 patch 的软件包,将所需的 patch 也移动到 ~/rpmbuild/SOURCES/ 目录中
mv cello-1.0.tar.gz ~/rpmbuild/SOURCES/

三、打包 Hello World 示例

0、设置工作区

# 1. 安装 rpmdevtools 软件包
dnf install rpmdevtools
# 2. 执行 rpmdev-setuptree 程序
rpmdev-setuptree
# 默认在 ~ 目录下创建 rpmbuild 文件夹
tree ~/rpmbuild/

RPM 打包工作区目录:

1、spec 文件

spec 文件是一个文件,其中包含 rpmbuild 实用程序用来构建 RPM 软件包的指令。此文件通过在一系列部分中定义指令,为构建系统提供必要信息。这些部分在 spec 文件的 Preamble 和 Body 部分中定义:

  • Preamble 部分包含一系列在 Body 部分中使用的元数据项。
  • Body 部分代表指令的主要部分。

列举了 Preamble 部分关键的指令:
暂时无法在飞书文档外展示此内容
列举 Body 中部分重要的项目:
暂时无法在飞书文档外展示此内容

2、BuildRoots

在 RPM 打包上下文中,buildroot 是 chroot 环境。构建工件通过使用与最终用户系统中将来层次结构相同的文件系统层次结构放在此,buildroot 充当根目录。构建工件的放置必须遵循最终用户系统的文件系统层次结构标准。
buildroot 中的文件稍后放入 cpio 存档,后者成为 RPM 的主要部分。当在最终用户的系统中安装 RPM 时,这些文件将提取到 root 目录中,保留正确的层次结构。

# 对于不熟悉的宏,可以使用 rpm --eval 查看内容
rpm --eval %{_bindir}
# 输出:/usr/bin

3、使用 spec 文件

要打包新软件,必须创建一个 spec 文件。通过以下任一方法创建 spec 文件:

  • 从头开始手动编写新的 spec 文件。
  • 使用 rpmdev-newspec 工具。这个工具会创建一个未填充的 spec 文件,其中会填写必要的指令和字段。
cd ~/rpmbuild/SPECS

# 创建空的 spec 文件
rpmdev-newspec bello    # hello world     Bash版本
rpmdev-newspec cello    # hello world     C版本
rpmdev-newspec pello    # hello world     python版本

填充 spec 文件流程:

  1. 打开 rpmdev-newspec 创建的 ~/rpmbuild/SPECS/.spec 文件
  2. 填充 spec 文件的 Preamble 部分的指令:
    • Name:已指定为 rpmdev-newspec 的参数
    • Version:设置为与源代码的上游版本匹配
    • Release:自动设置为 1%{?dist},它最初是 1
    • Summary:软件包的单行说明
    • License:与源代码关联的软件许可证
    • URL:上游软件网站的 URL,为实现一致性,请使用 %{name} RPM 宏变量,并使用 https://example.com/%{name} 格式
    • Source:上游软件源代码的 URL,直接链接到被打包的软件版本
    • BuildRequires:指定软件包的构建时依赖项
    • Requires:指定软件包的运行时依赖项
    • BuildArch:指定软件架构
  3. 填充 spec 文件 Body 部分中的以下指令:可以将这些指令视为部分标题,因为这些指令可以定义多行、多结构或脚本化任务
    • %description:软件的完整描述
    • %prep:一系列命令来准备软件以进行构建
    • %build:用于构建软件的一系列命令
    • %install:一系列命令:用于指示 rpmbuild 命令如何将软件安装到 BUILDROOT 目录中
    • %files:指定在系统上安装的 RPM 软件包提供的文件列表
    • %changelog:软件包的每个 Version-Release 的 datetamped 条目列表
      从 %changelog 部分的第一行开始,带有星号(*)字符,后跟 Day-of-Week Month Day Year Name Surname - Version-Release
      对于实际更改条目,请遵循这些规则:
      • 每个更改条目都可以包含多个项目,每个代表一个改变。
      • 每个项目在新行中开始。
      • 每个项目以连字符(-)字符开头。

4、分析 bello 的 spec 文件

Bello 是使用 bash 写的 hello world,spec 文件如下

Name:           bello
Version:        0.1                --> 软件版本
Release:        1%{?dist}          --> 发布版本
Summary:        Hello World example implemented in bash script

License:        GPLv3+
URL:            https://www.example.com/%{name}    --> 软件上游网站 url
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz # 软件的 url

Requires:       bash              --> 运行时依赖 bash

BuildArch:      noarch            --> 与机器的体系结构无关

%description
The long-tail description for our Hello World Example implemented in
bash script.

%prep                               --> 准备阶段:例如解压缩源代码存档
%setup -q

%build                              --> 构建阶段:bash 脚本不需要构建

%install                            --> 安装阶段

mkdir -p %{buildroot}/%{_bindir}

install -m 0755 %{name} %{buildroot}/%{_bindir}/%{name}    --> 安装到指定路径

%files
%license LICENSE                      --> LICENSE
%{_bindir}/%{name}                    --> /usr/bin/bello

%changelog                            --> 改变日志
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1
- First bello package
- Example second item in the changelog for version-release 0.1-1
  • BuildRequires 指令指定软件包的 build-time 依赖项已被删除,因为没有可用于 bello 的构建步骤。Bash 是原始解释编程语言,文件仅安装到其系统上的位置。
  • Requires 指令指定软件包的运行时依赖项,它只包括 bash,因为 bello 脚本只需要 bash shell 环境才能执行。
  • %build 部分指定如何构建软件为空,因为不需要构建 bash 脚本。
  • %setup 对源代码存档进行解压

install:是Linux系统中用来复制文件和设置权限的命令,-m 0755:这个选项指定了安装文件的权限。0755是一个八进制数,表示文件所有者有读写执行权限(7),组用户和其他用户有读取和执行权限(5),没有写权限。
所以,整个 install 命令的作用是将名为%{name}的可执行文件复制到%{buildroot}/%{_bindir}目录下,并设置其权限为0755。在RPM打包过程中,%{buildroot}目录下的内容最终会被打包到 RPM 文件中,当用户安装RPM包时,这些文件会被安装到实际的文件系统路径上。
rpmbuild 常用的参数:
暂时无法在飞书文档外展示此内容

# 进入 SPEC 文件夹中
cd ~/rpmbuild/SPECS

# 以构建 bello 的 RPM 包为例(其他的类似)
rpmbuild -bb bello.spec

可以使用 --noclean 留下构建的过程文件,在 BUILD/BUILDROOT 中可以看到 build 时做了什么
默认在构建好 rpm 包时,BUILD/BUILDROOT 下的内容会被清理

5、分析 pello 的 spec 文件

Pello 是使用 python 写的 hello world 程序

注:以下 spec 文件无法再 python3 上使用,python2 下应该是可以正确使用的【未验证】
在 python3 中,python -m compile 会将生成的pyc字节码文件放到 pycache 文件夹下,并且会修改命名,与该 spec 文件不匹配。而在 python2 中,生成的 pyc 字节码文件,可以按照以下 spec 描述进行。

Name:           pello
Version:        0.1.1
Release:        1%{?dist}
Summary:        Hello World example implemented in Python

License:        GPLv3+
URL:            https://www.example.com/%{name}
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz

BuildRequires:  python        --> 构建生成 pyc 字节码时,需要使用 python 
Requires:       python        --> 运行时需要使用 python
Requires:       bash          --> 运行时需要 bash

BuildArch:      noarch

%description
The long-tail description for our Hello World Example implemented in Python.

%prep                            --> 准备阶段:
%setup -q                        --> 解压源代码存档文件

%build                            --> 构建阶段:

python -m compileall %{name}.py   --> build 阶段, 对 py 进行编译, 得到字节码

%install                                   --> 安装阶段:

mkdir -p %{buildroot}/%{_bindir}               --> 递归创建目录
mkdir -p %{buildroot}/usr/lib/%{name}

cat > %{buildroot}/%{_bindir}/%{name} <<EOF    --> 创建切入bash脚本,命名为 pello
#!/bin/bash
/usr/bin/python /usr/lib/%{name}/%{name}.pyc    --> 使用 python 执行 pyc
EOF

chmod 0755 %{buildroot}/%{_bindir}/%{name}     --> 修改切入bash脚本的权限 

install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/   --> 将源 py 文件安装到 /usr/lib 中

%files
%license LICENSE                    --> LICENSE
%dir /usr/lib/%{name}/              --> /usr/lib/pello 目录
%{_bindir}/%{name}                  --> /usr/bin/pello 下的新建的切入脚本
/usr/lib/%{name}/%{name}.py*        --> /usr/lib/pello 目录下的 pyc 字节码文件

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1.1-1
  - First pello package
  • Requires 指令指定软件包的运行时依赖项,其中包括两个软件包:
    • 在运行时执行字节代码所需的 python 软件包。
    • 执行小入口点脚本所需的 bash 软件包。
  • BuildRequires 指令指定软件包的 build-time 依赖项,它只包括 python 软件包。pello 程序需要 python 执行字节型构建流程。
  • %build 部分指定如何构建软件,创建脚本的字节版本。请注意,在实际打包中,通常会根据所使用的发行版自动执行。
  • %install 部分与这个事实对应,您必须将字节文件安装到系统上的库目录中,以便可以访问它。
    创建打包程序脚本的示例显示了 spec文件本身可以脚本化。

7、分析 cello 的 spec 文件

cello 是使用 C语言编写的 hello world 示例程序,并且使用补丁 patch 进行修复

注:个人实验过程中,%prep 部分使用官方提供的示例,应用补丁会有错误,未进行解决
应用补丁的 cello 版本实验可能应用不成功

Name:           cello
Version:        1.0
Release:        1%{?dist}
Summary:        Hello World example implemented in C

License:        GPLv3+
URL:            https://www.example.com/%{name}
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz

Patch0:         cello-output-first-patch.patch        --> 需要使用的补丁

BuildRequires:  gcc            --> 构建时需要 gcc
BuildRequires:  make           --> 自动化构建需要 make

%description
The long-tail description for our Hello World Example implemented in
C.

%prep                   --> 准备阶段:
%setup -q               --> 解压源码等

%patch0                 --> 应用补丁到源码(属于准备阶段)

%build                  --> 构建阶段:
make %{?_smp_mflags}    --> %{?_smp_mflags} 表示 -j4 等,加速编译

%install
%make_install           --> 调用 make install

%files
%license LICENSE        --> LICENSE
%{_bindir}/%{name}      --> 可执行文件

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 1.0-1
- First cello package

Makefile 文件中指定如何编译、clean,以及 install

# cello 的 Makefile 文件
cello:
        gcc -g -o cello cello.c

clean:
        rm cello

install:
        mkdir -p $(DESTDIR)/usr/bin                       --> 递归创建目录
        install -m 0755 cello $(DESTDIR)/usr/bin/cello    --> 安装到指定路径

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/731823.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Opencv学习项目5——pyzbar,numpy

上一次我们使用pyzbar进行解码二维码并将其内容显示在图像上&#xff0c;使用的是rect barcode.rect来获取图像的坐标&#xff0c;这次我们使用另一种方法来获取坐标进行画框。 Numpy介绍 NumPy 是一个用于科学计算的开源 Python 库&#xff0c;提供了对大量数值数据进行高效操…

Java学习笔记(一)Java内容介绍、程序举例、DOS命令、Java跨平台特性的本质

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java内容介绍、程序举例、DOS命令、Java跨平台特性的本质详细介绍以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 目录 1、内容介绍…

STM32项目分享:家庭环境监测系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.…

数据可视化实验四:Pyecharts数据可视化

目录 一、使用PyEcharts绘制全国肺炎确诊人数分布图 1.1 柱状图 1.1.2 代码实现 1.1.2 绘制结果 1.2 饼状图 1.2.1 代码实现 1.2.2 绘制结果 1.3 使用over lap实现图形叠加 1.3.1 代码实现 1.3.2 绘制结果 1.4 地图绘制-Map 1.4.1 代码实现 1.4.2 绘制结果 1.5 地…

ECharts 词云图案例二:创意蒙版应用

ECharts 词云图案例二&#xff1a;创意蒙版应用 引言 在数据可视化领域&#xff0c;ECharts 以其强大的功能性和灵活性&#xff0c;成为开发者和设计师的首选工具之一。继上一篇关于 ECharts 词云图的详细介绍后&#xff0c;本文将探索词云图的进阶应用——使用蒙版来创造更具…

谷歌手机刷机教学

注意&#xff1a;手机已经解开了oem锁和bl 1、adb基础命令 连接设备adb devices&#xff1a;列出当前连接的所有设备。 adb connect <设备IP>&#xff1a;通过IP地址连接设备&#xff08;用于无线连接&#xff09;。 设备信息adb shell getprop&#xff1a;获取设备的所…

MySQL的DDL语句

文章目录 ☃️概述☃️DDL&#xff08;数据定义语言&#xff09;☃️数据库操作☃️表操作☃️DDL的重要性 ☃️概述 MySQL 通用语法分类 ● DDL: 数据定义语言&#xff0c;用来 定义数据库对象&#xff08;数据库、表、字段&#xff09; ● DML: 数据操作语言&#xff0c;用…

C#修改 EXE 文件图标和 winForm 窗口图标

修改 EXE 文件图标 1.准备好图片&#xff0c;转换为 Icon 图片&#xff1b; 2.右键工程&#xff0c;选择属性&#xff1b; 3.选择 Icon 图标即可&#xff1b; 4.重新生成可执行文件&#xff0c;查看。 修改 winForm 窗口图标 1.选中 winForm &#xff0c;查看属性&#x…

Android提供的LruCache类简介(1)

* If your cached values hold resources that need to be explicitly released, * override {link #entryRemoved}. * 如果你cache的某个值需要明确释放&#xff0c;重写entryRemoved() * If a cache miss should be computed on demand for the corresponding keys, * ov…

CVPR最佳论文:谷歌基于Spectral Volume从单图生成视频

一、摘要&#xff1a; 论文&#xff1a;Generative Image Dynamics&#xff0c;https://arxiv.org/pdf/2309.07906 项目主页&#xff1a;https://generative-dynamics.github.io/ 本文提出了一种新颖的方法来模拟场景运动的图像空间先验。通过从真实视频序列中提取的自然振荡…

调试实战 | 记一次有教益的 vs2022 内存分配失败崩溃分析(续)

前言 前一阵子遇到了 vs2022 卡死的问题&#xff0c;在上一篇文章中重点分析了崩溃的原因 —— 当 vs2022 尝试分配 923MB 的内存时&#xff0c;物理内存页文件大小不足以满足这次分配请求&#xff0c;于是抛出异常。 本篇文章将重点挖掘一下 vs2022 在崩溃之前已经分配的内容…

昇思25天学习打卡营第4天|网络构建|函数式自动微分

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 网络构建 神经网络模型是由神经网络层和Tensor操作构成的&#xff0c;mindspore.nn提供了常见神经网络层的实现&#xff0c;在MindSpore中&#xff0c;Cell类是构建所有网络的基类&#xff0c;也…

借助AI营销类API,实现自动化的营销流程

借助AI营销类API&#xff0c;企业可以实现自动化的营销流程&#xff0c;提高效率和效果&#xff0c;并节省大量的时间和资源。这些API利用人工智能和机器学习的技术&#xff0c;能够自动化地执行各种营销任务和流程。首先&#xff0c;AI营销类API可以帮助企业实现自动化的市场调…

【鸿蒙】创建第⼀个鸿蒙项⽬

点击 Create Project 配置项目 开发工具界面 工程介绍

探索AI前沿:本地部署GPT-4o,打造专属智能助手!

目录 1、获取API_key 2、开始调用 3、openai连接异常 4、解决方法&#xff1a; 5、调用GPT-4o 1、获取API_key 这里就不多赘述了&#xff0c;大家可以参考下面这篇博客 怎么获取OpenAI的api-key【人工智能】https://blog.csdn.net/qq_51625007/article/details/13763274…

大数据与java哪个好找工作?这篇文章帮你做选择!

大数据与java哪个好找工作&#xff1f;这篇文章帮你做选择&#xff01; 还在为选择Java开发还是Java大数据而头疼吗&#xff1f;别担心&#xff0c;本文将从就业前景、学习方向、学习内容以及薪资待遇四个方面&#xff0c;为你揭开Java和Java大数据的神秘面纱&#xff0c;帮你做…

ZW3D二次开发_删除草图中的实体

1.目前草图中的实体不能直接通过id删除&#xff0c;而是通过entityPath实体路径&#xff0c;所以需要将id转化为实体路径。 2.以下示例代码的主要功能为获取草图中的所有实体并删除&#xff1a; int Count;int *idEnts;ZF_CALL(cvxSkInqGeom(&Count, &idEnts));//获取…

《窄门》情不知所起,而一往情深

《窄门》情不知所起&#xff0c;而一往情深 安德烈纪德&#xff08;1869-1951&#xff09;&#xff0c;法国作家。纪德一生著有小说、剧本、论文、散文、日记、书信多种&#xff0c;主要作品有小说《背德者》《窄门》《田园交响曲》《伪币制造者》等&#xff0c;戏剧《康多尔王…

x64汇编fastcall调用约定

x64汇编环境&#xff1a;只需要在x86基础上对项目属性进行设置&#xff0c;将平台设置为所有平台&#xff1b; 以及在将debug改为x64模式即可&#xff1a; 后续写完代码直接生成项目再使用本地调试器进行运行即可。 fastcall调用约定 在x64架构下&#xff0c;fastcall调用约定…

android倒计时封装(活动进入后台,倒计时依然能正常计时)

public class TimeUtils { /倒计时时长 单位&#xff1a;秒/ public static int COUNT 20*60; /当前做/ private static int CURR_COUNT 0; /预计结束的时间/ private static long TIME_END 0; /计时器/ private static Timer countdownTimer; /显示倒计时的textVi…