在打包者的角色中,更新包是一项经常性的任务。 对于某些项目,打包者参与上游维护,或者编写良好的发行说明可以很容易地找出版本之间的变化。 情况并非总是如此,例如由 github 上某处的一两个人维护的一些小项目,验证究竟发生了什么变化可能很有用。 衍射镜 可以帮助确定软件包版本之间的更改。
衍射镜 是一个“智能二进制差异”工具,诞生于 可重现的构建 Debian 中的项目,也可以在 Fedora. 它“了解”各种类型的文本和二进制格式,并将尝试递归解包和比较两个 blob。 特别是它知道在比较之前需要解压缩一些对象,需要解压缩档案,以及如何解构二进制对象,如 ELF 程序和库、Java .jar 文件、Windows .cab 文件等。
就在今天,我收到了一个错误报告,指出有一个新版本的 python-libarchive-c 可用(3.2,而 3.1 是当前打包的)。 它是一个简单的 Python 包。 但即使是一个简单的 Python 包也有一些二进制文件,所以对未打包的 rpm 进行直接比较并不能真正起作用。 让我们看看如何 衍射镜 可用于详细显示二进制包之间的差异。
比较上游档案
第一步是对比上游档案:
$ diffoscope python-libarchive-c-3.{1,2}.tar.gz +++ python-libarchive-c-3.2.tar.gz │ --- python-libarchive-c-3.1.tar ❶ ├── +++ python-libarchive-c-3.2.tar │ ├── file list │ │ @@ -1,46 +1,46 @@ ❷ │ │ -drwxrwxr-x 0 root (0) root (0) 0 2021-06-01 07:32:24.000000 python-libarchive-c-3.1/ │ │ --rw-rw-r-- 0 root (0) root (0) 25 2021-06-01 07:32:24.000000 python-libarchive-c-3.1/.gitattributes │ │ -drwxrwxr-x 0 root (0) root (0) 0 2021-06-01 07:32:24.000000 python-libarchive-c-3.1/.github/ │ │ --rw-rw-r-- 0 root (0) root (0) 20 2021-06-01 07:32:24.000000 python-libarchive-c-3.1/.github/FUNDING.yml ... │ │ --rw-rw-r-- 0 root (0) root (0) 1331 2021-06-01 07:32:24.000000 python-libarchive-c-3.1/version.py │ │ +drwxrwxr-x 0 root (0) root (0) 0 2021-10-06 12:40:03.000000 python-libarchive-c-3.2/ │ │ +-rw-rw-r-- 0 root (0) root (0) 25 2021-10-06 12:40:03.000000 python-libarchive-c-3.2/.gitattributes │ │ +drwxrwxr-x 0 root (0) root (0) 0 2021-10-06 12:40:03.000000 python-libarchive-c-3.2/.github/ │ │ +-rw-rw-r-- 0 root (0) root (0) 20 2021-10-06 12:40:03.000000 python-libarchive-c-3.2/.github/FUNDING.yml ... │ │ +-rw-rw-r-- 0 root (0) root (0) 1331 2021-10-06 12:40:03.000000 python-libarchive-c-3.2/version.py ... │ │ --- python-libarchive-c-3.1/libarchive/ffi.py │ ├── +++ python-libarchive-c-3.2/libarchive/ffi.py │ │┄ Files 0% similar despite different names │ │ @@ -43,15 +43,15 @@ │ │ SEEK_CALLBACK = CFUNCTYPE( │ │ - c_longlong, c_int, c_void_p, c_longlong, c_int ❸ │ │ + c_longlong, c_void_p, c_void_p, c_longlong, c_int │ │ ) │ │ --- python-libarchive-c-3.1/libarchive/read.py │ ├── +++ python-libarchive-c-3.2/libarchive/read.py │ │┄ Files 2% similar despite different names │ │ @@ -61,17 +61,18 @@ │ │ close_cb = CLOSE_CALLBACK(close_func) if close_func else NO_CLOSE_CB │ │ + seek_cb = SEEK_CALLBACK(seek_func) │ │ with new_archive_read(format_name, filter_name, passphrase) as archive_p: │ │ if seek_func: │ │ - ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) │ │ + ffi.read_set_seek_callback(archive_p, seek_cb) │ │ ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) │ │ yield archive_read_class(archive_p) ... │ │ --- python-libarchive-c-3.1/libarchive/write.py │ ├── +++ python-libarchive-c-3.2/libarchive/write.py │ │┄ Files identical despite different names │ │ --- python-libarchive-c-3.1/setup.py ❹ │ ├── +++ python-libarchive-c-3.2/setup.py │ │┄ Files identical despite different names ... │ │ --- python-libarchive-c-3.1/version.py │ ├── +++ python-libarchive-c-3.2/version.py │ │┄ Files 1% similar despite different names │ │ @@ -9,15 +9,15 @@ │ │ def get_version(): │ │ # Return the version if it has been injected into the file by git-archive │ │ - version = tag_re.search('HEAD -> master, tag: 3.1') │ │ + version = tag_re.search('HEAD -> master, tag: 3.2') ❺ │ │ if version: │ │ return version.group(1)
在❶处,我们看到我们正在比较两个档案。 (注意:为了便于阅读,此差异输出已被大量修剪。)
在❷处,我们看到两个档案的列表不同,但差异是意料之中的:档案中顶级目录的名称中包含版本号,因此所有路径都不同。 我们还看到,这两个档案的创建日期不同(分别为 2021-06-01 07:32:24 和 2021-10-06 12:40:03)。 如果上游意外添加或删除文件,此列表将提醒我们。
在❸处,我们看到 SEEK_CALLBACK 函数中有一些代码更改。
上游发行说明 关于它说“这个版本修复了由 custom_reader 和 stream_reader 函数传递给 libarchive 的 seek 回调”,所以这个变化看起来很合理。
大多数文件都没有改变,所以在 ❹ diffoscope 尽职尽责地报告它们是相同的,尽管文件名发生了变化……而在 ❺ 我们得到了另一个与版本相关的更改。
就是这样 – 上游 tarball 不再有任何变化。
然后让我们构建包并将其与之前的构建进行比较。
比较二进制包
为新版本调整好spec文件后,第二步是构建包并与旧版本进行比较。 fedpkg mockbuild 将生成的包放在以版本命名的子目录中:
$ fedpkg mockbuild ... INFO: Results and/or logs in: ~/fedora/python-libarchive-c/results_python-libarchive-c/3.2/1.fc36 $ diffoscope results_python-libarchive-c/3.1/3.fc36/python3-libarchive-c-3.1-3.fc36.noarch.rpm results_python-libarchive-c/3.2/1.fc36/python3-libarchive-c-3.2-1.fc36.noarch.rpm +++ results_python-libarchive-c/3.2/1.fc36/python3-libarchive-c-3.2-1.fc36.noarch.rpm ├── header │ @@ -1,79 +1,79 @@ ❶ │ -HEADERIMMUTABLE: 0000003d00001ed50000003f...1000003e8000000060000 │ +HEADERIMMUTABLE: 0000003d00001e8d0000003f...1000003e8000000060000
在二进制包的情况下,与上游 tarball 相比,存在更多差异。 但是,让我们尝试通过它们。
│ HEADERI18NTABLE: │ - C │ -SIGSIZE: 26733 ❷ │ -SIGMD5: 1aa148ac91484fe8cb55fe3334aae10b │ -SHA1HEADER: 1659a1431af930a0a824c193780e27f28fc2d03e │ -SHA256HEADER: 60e4f84e905bd42693cabe88e63542916a7dfffef052f3e7499cb80a1770c736 │ +SIGSIZE: 26678 │ +SIGMD5: e35e3157e01b6ec26b8e1981f0ba38af │ +SHA1HEADER: faab0b7ee86b23f753b2c49a52d6a8f3deefc7ca │ +SHA256HEADER: 66d39a70dc9e081ba5cc4243e72f434e953d68c9bce8be1127b2b87fa1923d06 │ NAME: python3-libarchive-c │ -VERSION: 3.1 ❸ │ -RELEASE: 3.fc36 │ +VERSION: 3.2 │ +RELEASE: 1.fc36 │ SUMMARY: Python interface to libarchive │ DESCRIPTION: The libarchive library provides a flexible interface for reading and writing archives in various │ formats such as tar and cpio. libarchive also supports reading and writing archives compressed using │ various compression filters such as gzip and bzip2. A Python interface to libarchive. It uses the │ standard ctypes module to dynamically load and access the C library. │ -BUILDTIME: 1638015932 │ +BUILDTIME: 1638015863 ❹ │ BUILDHOST: spora.local │ -SIZE: 68979 │ +SIZE: 69052 │ LICENSE: CC0 │ GROUP: Unspecified │ URL: https://github.com/Changaco/python-libarchive-c │ OS: linux │ ARCH: noarch
我们可以看到 衍射镜 在两个档案中执行类似于 rpmdiff 的操作,但具有更多细节。
在 ❶ 我们看到 rpm 标头发生了变化,这并不奇怪 ? 在 ❷ 我们得到了签名的详细信息,以及在 ❸ 的版本信息。 构建时间戳和 rpm 大小在 ❹ 处也可能很有趣。 衍射镜 然后打印 rpm 标头中的 FILESIZES、FILEMTIMES、FILEMD5S 表的比较。 如果我们试图找出包之间的一些意外差异,这将很有用。
│ CHANGELOGTIME: │ + - 1638014400 │ - 1627387200 ... │ - - 1570104000 │ - - 1566216000 │ CHANGELOGNAME: │ + - Zbigniew Jędrzejewski-Szmek 3.2-1 │ - Fedora Release Engineering - 3.1-2 ... │ - - Miro Hrončok - 2.8-10 │ - - Miro Hrončok - 2.8-9 │ CHANGELOGTEXT: │ + - - Version 3.2 (fixes #2027027) │ - - Second attempt - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild ... │ - - - Rebuilt for Python 3.8.0rc1 (#1748018) │ - - - Rebuilt for Python 3.8
在这里,我们看到更改日志被修剪(添加了我的条目,删除了来自 Miro 的两个条目)。 在 Fedora 我们将 %_changelog_trimage 设置为 2 年,因此即使规范文件定义了更长的变更日志,在构建的包中最旧的条目也会被修剪掉。
然后我们得到一些预期但重要的区别:
│ PROVIDEVERSION: │ - - 3.1-3.fc36 │ + - 3.2-1.fc36 ❶ │ OBSOLETEVERSION: │ - - 3.1-3.fc36 │ + - 3.2-1.fc36 ... │ DIRNAMES: ❷ │ - - /usr/lib/python3.10/site-packages/libarchive_c-3.1-py3.10.egg-info/ │ + - /usr/lib/python3.10/site-packages/libarchive_c-3.2-py3.10.egg-info/ ... │ @@ -1 +1 @@ │ -RPM v3.0 bin i386/x86_64 python3-libarchive-c-3.1-3.fc36 │ +RPM v3.0 bin i386/x86_64 python3-libarchive-c-3.2-1.fc36 ├── content │ ├── file list │ │ @@ -1,35 +1,35 @@ │ │ -drwxr-xr-x 1 0 0 0 2021-10-27 12:20:33.000000 ./usr/lib/python3.10/site-packages/libarchive ❸ │ │ --rw-r--r-- 1 0 0 601 2021-06-01 07:32:24.000000 ./usr/lib/python3.10/site-packages/libarchive/__init__.py │ │ -drwxr-xr-x 1 0 0 0 2021-10-27 12:20:34.000000 ./usr/lib/python3.10/site-packages/libarchive/__pycache__ ... │ │ +drwxr-xr-x 1 0 0 0 2021-11-27 12:24:23.000000 ./usr/lib/python3.10/site-packages/libarchive ❹ │ │ +-rw-r--r-- 1 0 0 601 2021-10-06 12:40:03.000000 ./usr/lib/python3.10/site-packages/libarchive/__init__.py │ │ +drwxr-xr-x 1 0 0 0 2021-11-27 12:24:24.000000 ./usr/lib/python3.10/site-packages/libarchive/__pycache__ ... │ ├── ./usr/lib/python3.10/site-packages/libarchive/ffi.py │ │ @@ -43,15 +43,15 @@ │ │ SEEK_CALLBACK = CFUNCTYPE( │ │ - c_longlong, c_int, c_void_p, c_longlong, c_int ❺ │ │ + c_longlong, c_void_p, c_void_p, c_longlong, c_int │ │ ) │ │ @@ -61,17 +61,18 @@ │ │ close_cb = CLOSE_CALLBACK(close_func) if close_func else NO_CLOSE_CB │ │ + seek_cb = SEEK_CALLBACK(seek_func) │ │ with new_archive_read(format_name, filter_name, passphrase) as archive_p: │ │ if seek_func: │ │ - ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) │ │ + ffi.read_set_seek_callback(archive_p, seek_cb) │ │ ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) ...
在❶处,我们看到包版本更改反映在 rpm 标头中的 PROVIDEVERSION 和 OBSOLETEVERSION 表中。 还有 PROVIDENAME 和 OBSOLETENAME 表,但在这种情况下这些表没有变化。 这很好:我们预计不会对提供或过时的产品进行任何更改,除了版本冲突。
在❷处,我们再次看到版本反映在路径中。
您可以看到 ❸ 和 ❹ 是两个包中经过大量修剪的文件列表。 由于修改时间更改,所有文件都报告为不同。 但请仔细查看时间戳: __init__.py 是上游提供的文件,并且 mtime 在安装过程中被保留,因此我们看到与上游 tarball 比较的第一个列表中相同的时间戳。 但是 __pycache__ 目录是在构建期间创建的,并且有一个时间戳,显示构建完成的时间。 对于构建期间生成的其他文件,我们会看到相同的结果。
现在是无聊的部分:
│ ├── ./usr/lib/python3.10/site-packages/libarchive/__pycache__/__init__.cpython-310.pyc │ │ @@ -1,8 +1,8 @@ │ │ -00000000: 6f0d 0d0a 0000 0000 88e2 b560 5902 0000 o..........`Y... ❶ │ │ +00000000: 6f0d 0d0a 0000 0000 2399 5d61 5902 0000 o.......#.]aY... │ ├── ./usr/lib/python3.10/site-packages/libarchive/__pycache__/entry.cpython-310.pyc │ │ @@ -1,8 +1,8 @@ │ │ -00000000: 6f0d 0d0a 0000 0000 88e2 b560 0c14 0000 o..........`.... ❷ │ │ +00000000: 6f0d 0d0a 0000 0000 2399 5d61 0c14 0000 o.......#.]a.... ... │ │ --- ./usr/lib/python3.10/site-packages/libarchive_c-3.1-py3.10.egg-info/PKG-INFO │ ├── +++ ./usr/lib/python3.10/site-packages/libarchive_c-3.2-py3.10.egg-info/PKG-INFO │ │┄ Files 0% similar despite different names │ │ @@ -1,10 +1,10 @@ │ │ Name: libarchive-c │ │ -Version: 3.1 ❸ │ │ +Version: 3.2 │ │ Summary: Python interface to libarchive │ │ Home-page: https://github.com/Changaco/python-libarchive-c
… 是的, 扩散镜 显示 .pyc 文件的 hexdump 列表之间的差异(❶ 和 ❷ 处的 Python 字节码)。 这些文件是在构建过程中创建的,因此我们知道这些更改对应于对前面清单中显示的源的更改。 最后,我们再次看到 PKG-INFO 文件中的版本更改 (❸)。
目前,diffoscope 不知道如何以更好的方式显示 Python 字节码。 但有可能在未来它将能够将字节码反汇编成更易读的形式,并在其上显示差异。 新的解析器会定期添加到 diffoscope。 对于已编译的程序,它已经在反汇编的机器代码上显示了差异。
结论
在查看了所有这些差异之后,我认为可以肯定地说从 python-libarchive-c-3.1 升级到 python-libarchive-c-3.2 是安全的。 特别是,它甚至适用于稳定版本,因为它只有一个错误修复。
向 Chris Lamb 和 diffoscope 的其他维护者大声疾呼。