为SAGE打包第三方代码

Sage项目的座右铭之一是不要重复发明轮子:如果一个算法已经在一个经过良好测试的库中实现,那么考虑将该库合并到Sage中。可用软件包的当前列表是的子目录 SAGE_ROOT/build/pkgs/ 。通过位于以下位置的bash脚本安装包 SAGE_ROOT/build/bin/sage-spkg 。此脚本通常通过发出以下命令来调用:

[alice@localhost sage]$ sage -i <options> <package name>...

选项可以是:

  • -f:即使已安装相同版本,也要安装程序包

  • -S:不要删除临时构建目录

  • -c:安装后,运行spkg的测试套件。这应覆盖的设置 SAGE_CHECKSAGE_CHECK_PACKAGES

  • -d:只下载包

该节 目录结构 中每个单独包的结构。 SAGE_ROOT/build/pkgs 。在部分中 构建包 我们将了解如何安装和测试您或其他人编写的新spkg。最后, 新包和更新包的包含程序 解释如何提交新程序包以包含在Sage源代码中。

套餐类型

并非所有包都是默认构建的,它们分为标准包、可选包和实验包:

  • standard 默认情况下会生成程序包。对于几个包裹, configure 检查它们是否可从系统中使用,在这种情况下,将跳过这些包的生成。标准包有严格的质量要求:它们应该在所有支持的平台上工作。为了让新的标准包被接受,它应该在一段时间内是可选的,请参见 新包和更新包的包含程序

  • optional 程序包遵循相同的要求,它们还应该在所有支持的平台上工作。如果有 optional doctests 在Sage库中,这些测试必须通过。请注意,可选程序包不像标准程序包那样经过测试,因此在实践中它们可能比标准程序包更容易损坏。

  • experimental 套餐的门槛要低得多:即使有一些问题,套餐仍然可以接受。

包源类型

与按包类型划分的正交性不同,包恰好具有以下源类型之一:

  1. A normal 套餐:

    • 来自所需文件中命名的tarball checksums.ini 并寄托在Sage的镜子上;

    • 其版本号由所需文件定义 package-version.txt

    • 可以打补丁;

    • Sage使用构建和安装脚本安装程序包(请参见 构建和安装的脚本 normal 包裹 );

    • Sage记录使用文件安装的程序包的版本号 $SAGE_LOCAL/var/lib/sage/installed/ 并将在以下情况下重新运行安装 package-version.txt 改变。

  2. A wheel 套餐:

    • 来自所需文件中命名的车轮文件 checksums.ini 并寄托在Sage的镜子上;

    • 根据策略,只允许独立于平台的轮子,即, *-none-any.whl 文件;

    • 其版本号由所需文件定义 package-version.txt

    • 无法打补丁;

    • 不需要任何构建和安装脚本(有一个例外:包 spkg_pip 使用自定义安装脚本从轮子上安装自身);

    • Sage记录使用文件安装的程序包的版本号 $SAGE_LOCAL/var/lib/sage/installed/ 并将在以下情况下重新运行安装 package-version.txt 改变。

  3. A pip 套餐:

    • 是直接从https://pypi.org/;获得的

    • 使用所需文件确定要安装的版本 requirements.txt --在其最简单的形式中,该文件只包含包的名称(更多详细信息请参阅https://pip.pypa.io/en/stable/user_guide/#requirements-files);

    • 无法打补丁;

    • Sage使用 pip 包管理器;

    • Sage将记录已安装的包版本号委托给它;

    • 根据政策,没有 standard 包被允许为 pip 包裹。

  4. A script 套餐:

    • 与焦油球无关;

    • 该文件 package-version.txt 是可选的;

    • 可以与储存库中的源树相关联;

    • 安装程序包将运行安装脚本 spkg-installspkg-install.in (见 构建和安装的脚本 normal 包裹 );

    • Sage记录使用文件安装的程序包的版本号 $SAGE_LOCAL/var/lib/sage/installed/ 并将在以下情况下重新运行安装 package-version.txt 改变。

  5. A dummy 套餐:

    • 仅用于记录等价系统包的名称;

    • 没有 spkg-install 脚本,并尝试使用Sage安装该程序包,则会给出一条错误消息。

综上所述:包源类型的确定如下:如果有文件 requirements.txt ,它是一种 pip 包裹。如果不是,那么如果有 checksums.ini 文件,它是 normalwheel 。否则,如果它有一个 spkg-installspkg-install.in 脚本,它是一个 script 包,如果不是,则它是一个 dummy 包裹。

目录结构

Sage中的第三方包由两部分组成:

  1. 第三方分发的焦油球,或尽可能接近的焦油球。修改tarball的正当理由包括删除不必要的文件以保持下载大小易于管理、重新生成自动生成的文件或在必要时更改目录结构。在某些情况下,您可能需要(另外)更改tarball的文件名。在任何情况下,实际代码都不能修改:如果需要更改源代码,请添加一个 patch 取而代之的是。

  2. 生成脚本和关联文件位于子目录中 SAGE_ROOT/build/pkgs/<package> ,在那里您可以替换 <package> 使用上游项目名称的小写版本。如果项目名称包含非字母数字且不是下划线的字符,则应删除这些字符或将其替换为下划线。例如,项目 FFLAS-FFPACK 名为 fflas_ffpack 在塞奇。

作为一个例子,让我们考虑一个假设的Foo项目。他们(在上游)分发了一个玻璃球 FoO-1.3.tar.gz (它将自动放置在 SAGE_ROOT/upstream 在安装过程中)。为了将其打包到Sage中,我们创建了一个子目录,其中至少包含以下文件:

SAGE_ROOT/build/pkgs/foo
|-- checksums.ini
|-- dependencies
|-- package-version.txt
|-- spkg-install.in
|-- SPKG.rst
`-- type

以下是一些可以添加的附加文件:

SAGE_ROOT/build/pkgs/foo
|-- distros
|   |-- platform1.txt
|   `-- platform2.txt
|-- has_nonfree_dependencies
|-- huge
|-- patches
|   |-- bar.patch
|   `-- baz.patch
|-- spkg-check.in
|-- spkg-configure.m4
|-- spkg-src
`-- trees.txt

我们将在以下各节中讨论各个文件。

套餐类型

档案 type 应包含单个单词,该单词为 standardoptionalexperimental 。看见 套餐类型 了解这些类型的含义。

构建和安装的脚本 normal 包裹

这个 spkg-build.inspkg-install.in 文件是的模板 bash 脚本 spkg-buildspkg-install ,构建和/或安装程序包。

这个 *.in 脚本模板应 not 以一行横线作为前缀 (#!... ),并且不应在其权限中设置可执行位。这些是在生成脚本时自动添加的,在安装包时还会添加一些额外的样板文件。

这个 spkg-build.inspkg-install.in Sage源代码树中的文件只需关注构建和安装该包的特定步骤。如果没有 spkg-build.in 存在,则 spkg-install.in 负责这两个步骤,尽管在可能的情况下鼓励将它们分开。

还可以包括名为的类似脚本模板 spkg-preinst.inspkg-postinst.in 在将程序包安装到之前或之后运行其他步骤 $SAGE_LOCAL 。建议将修改已安装文件的步骤放在单独的 spkg-postinst.in 脚本模板,而不是将它们与 spkg-install.in 。这是因为自从 :issue:`24106`spkg-install 不一定直接将程序包安装到 $SAGE_LOCAL 。然而,到了那个时候 spkg-postinst 正在运行,则安装到 $SAGE_LOCAL 已经完成了。

在最好的情况下,只需执行常见的配置/制作/制作安装步骤即可安装上游项目。在这种情况下, spkg-build.in 脚本模板将简单地包括:

cd src
sdh_configure
sdh_make

看见 帮助器函数 有关帮助器函数的更多信息 sdh_configuresdh_make

这个 spkg-install.in 脚本模板将包括:

cd src
sdh_make_install

请注意,tarball内的顶级目录已重命名为 src 在调用 spkg-buildspkg-install 脚本,因此您可以只使用 cd src 而不是 cd foo-1.3

如果包含但未安装任何有意义的文档 sdh_make_install (它调用 make install ),则可以添加类似以下内容的内容来安装它:

if [ "$SAGE_SPKG_INSTALL_DOCS" = yes ] ; then
    sdh_make doc
    sdh_install doc/ "$SAGE_SHARE"/doc/PACKAGE_NAME
fi

在构建时 CFLAGSCXXFLAGSFCFLAGS ,以及 F77FLAGS 通常设置为 -g -O2 -march=native (根据 debugging options 以及是否正在建造 fat binaries )。

有略微修改的版本可用:

# No ``-march=native``.
export CFLAGS=$CFLAGS_NON_NATIVE

# ``-O3`` instead of ``-O2``.
export CFLAGS=$CFLAGS_O3

# Use flags as set by the user, possibly empty.
export CFLAGS=$ORIGINAL_CFLAGS

同样,对于 CXXFLAGSFCFLAGS ,以及 F77FLAGS

备注

在Sage 9.1之前,脚本模板被称为 spkg-buildspkg-install 等,不带扩展名 .in

在Sage 8.1之前,包括了Shebang行,并且脚本被标记为可执行。然而,情况不再是这样,从 :issue:`23179` 。现在,源代码树中的脚本被故意编写为不直接执行,并且只有在将它们复制到包的构建目录中时才被制成可执行脚本。

构建/安装脚本仍然可以用Python编写,但Python代码应该放在一个单独的文件中(例如 spkg-install.py ),然后可以从实数执行 spkg-install.in 比如:

exec sage-bootstrap-python spkg-install.py

exec python3 spkg-install.py

更详细地说: sage-bootstrap-python 运行计算机上预安装的Python版本,这是Sage的构建先决条件。请注意 sage-bootstrap-python 可接受多种版本的Python,请参阅 SAGE_ROOT/build/tox.ini 了解更多细节。您应该只使用 sage-bootstrap-python 对于必须能够在Sage执行之前运行的安装任务 python3 可用。它不能用于运行 pipsetup.py 任何包裹。

python3 运行由Sage管理的Python3版本(Sage自己从SPKG安装Python3或在系统python3上安装venv)。如果您要安装一个Python包,以确保库安装在正确的位置,则应该使用此选项。

顺便说一句,还有一个剧本 sage-python 。这应该在运行时使用,例如,在 SAGE_LOCAL/bin 预计赛奇的 Python 已经建成了。

许多包目前没有分开构建和安装步骤,只提供了一个 spkg-install.in 同时执行这两种操作的文件。这种分离对于超级用户拥有的安装层次结构特别有用,其中类似于 sudo 必须用于安装文件。为此,Sage使用环境变量 $SAGE_SUDO ,它的值可以由开发人员在生成时提供,它应该提供给适当的系统特定 sudo -LIKE命令(如果有)。然后遵守以下规则:

  • 如果 spkg-build.in EXISTS,生成的脚本 spkg-build 首先被调用,然后是 $SAGE_SUDO spkg-install

  • 否则,仅 spkg-install 被调用(不带 $SAGE_SUDO )。这样的包应该在所有命令前面加上 spkg-install.in 写入到安装层次结构中的 $SAGE_SUDO

如果一个 spkg-src 文件,则表示该tarball不是未经修改的第三方tarball(请参见 修改第三方代码 )。它记录了tarball是如何生成的(通过修改上游tarball或从存储库生成它)。因为理想情况下我们的tarball不会被修改,对于大多数包来说没有 spkg-src 文件。

安装和编写的脚本 script 包裹

script 包,也可以使用名为 spkg-install 。它需要是一个可执行的Shell脚本;它不受上一节中描述的模板的约束,并且将直接从构建目录执行。

我们的大多数人 script 包与存储库中包含的源树相关联,位于的子目录中 $SAGE_ROOT/pkgs/ 。在本例中,有一个符号链接 src 指向源树和脚本的 spkg-src 这将为包裹构建一个tarball。

帮助器函数

spkg-buildspkg-install ,以及 spkg-check 脚本,可以使用以下函数。它们在文件中定义 $SAGE_ROOT/build/bin/sage-dist-helpers ,如果你想看源代码。应该使用它们来确保设置了适当的变量,并避免代码重复。这些函数名称以 sdh_ ,代表“Sage分发帮手”。

  • sdh_die MESSAGE :如果上一个命令的错误代码为非零,则退出构建脚本,否则返回1,并打印一条错误消息。这通常使用如下所示:

    command || sdh_die "Command failed"
    

    此函数还可以(如果没有给出任何参数)从标准输入读取错误消息。特别是,这在与here文档一起编写多行错误消息时非常有用:

    command || sdh_die << _EOF_
    Command failed.
    Reason given.
    _EOF_
    

    备注

    其他帮助器函数调用 sdh_die ,所以不要使用(例如) sdh_make || sdh_die :后面的那部分 || 永远达不到。

  • sdh_check_vars [VARIABLE ...] :检查一个或多个变量是否已定义且非空,如果有任何变量未定义或为空,则退出并返回错误。变量名称应该不带‘$’,以防止不需要的扩展。

  • sdh_configure [...] :运行 ./configure 带着争论 --prefix="$SAGE_LOCAL"--libdir="$SAGE_LOCAL/lib"--disable-static--disable-maintainer-mode ,以及 --disable-dependency-tracking 。的其他参数 ./configure 可以作为论据给出。

  • sdh_make [...] :运行 $MAKE 使用默认目标。的其他参数 $MAKE 可以作为论据给出。

  • sdh_make_install [...] :运行 $MAKE install 将DESTDIR正确设置为临时安装目录,用于分段安装。的其他参数 $MAKE 可以作为论据给出。如果 $SAGE_DESTDIR 如果未设置,则运行该命令时 $SAGE_SUDO ,如果设置的话。

  • sdh_setup_bdist_wheel [...] :运行 setup.py bdist_wheel 以及用于将包安装到Sage中的其他默认参数。

  • sdh_pip_install [...] :相当于跑步 pip install with the given arguments, as well as additional default arguments used for installing packages into Sage with pip. The last argument must be . 以指示从当前目录进行安装。

    sdh_pip_install 实际上通过以下方式进行安装 pip wheel ,在中创建控制盘文件 dist/ ,然后是 sdh_store_and_pip_install_wheel (见下文)。

  • sdh_pip_editable_install [...] :相当于跑步 pip install -e with the given arguments, as well as additional default arguments used for installing packages into Sage with pip. The last argument must be . 以指示从当前目录进行安装。看见 pip documentation 有关可编辑安装的更多详细信息,请参阅。

  • sdh_pip_uninstall [...] :运行 pip uninstall 带着给定的论点。如果不成功,则会显示警告。

  • sdh_store_and_pip_install_wheel . :当前目录,由必需的参数指示 . ,必须有一个子目录 dist 包含唯一的控制盘文件 (*.whl )。

    This command (1) moves this wheel file to the directory $SAGE_SPKG_WHEELS ($SAGE_LOCAL/var/lib/sage/wheels) and then (2) installs the wheel in $SAGE_LOCAL.

    这两个步骤,而不是直接写入 $SAGE_LOCAL ,请使用临时目录 $SAGE_DESTDIR 如果设置,则使用 $SAGE_SUDO (如果已设置)。

  • sdh_install [-T] SRC [SRC...] DEST :复制一个或多个文件或目录 SRC (在目录的情况下递归)到目标目录 DEST ,同时确保 DEST 并且它的所有父目录都存在。 DEST 应该是下面的路径 $SAGE_LOCAL ,大体上。为 DESTDIR 安装、 $SAGE_DESTDIR 路径会自动优先于目的地。

    The -T option treats DEST as a normal file instead (e.g. for copying a file to a different filename). All directory components are still created in this case.

以下内容会自动添加到每个安装脚本中,因此您不需要自己添加。

  • sdh_guard :包装器 sdh_check_vars 这将检查一些公共变量,如果没有这些变量,许多/大多数包将无法正确构建 (SAGE_ROOTSAGE_LOCALSAGE_SHARE )。这对于防止安装到意外位置非常重要。

以下内容也可用,但很少使用。

  • sdh_cmake [...] :运行 cmake 在当前目录中使用给定的参数,以及传递给cmake的附加参数(假设包使用GNUInstallDir模块),以便 CMAKE_INSTALL_PREFIXCMAKE_INSTALL_LIBDIR 都设置正确。

  • sdh_preload_lib EXECUTABLE SONAME :(仅限Linux--在其他平台上不运行。)检查由加载的共享库 EXECUTABLE (可以是程序或其他库),用于以 SONAME ,如果找到,则追加 SONAME 发送到 LD_PRELOAD 环境变量。看见 :issue:`24885`

允许使用系统包

对于许多Sage包,可以使用已经安装的系统版本,并且Sage的顶层 ./configure 脚本确定何时可以实现这一点。要实现这一点,程序包需要有一个名为 spkg-configure.m4 ,例如,它可以确定安装的软件是否足够新(有时不是太新),以供Sage使用。此脚本由 GNU M4 macro processor

此外,如果Sage包的软件是由系统包提供的, ./configure 脚本可以提供该信息。为此,必须有一个目录 build/pkgs/PACKAGE/distros 包含名称如下的文件::

arch.txt
conda.txt
debian.txt
fedora.txt
homebrew.txt
...

对应于不同的包装系统。每个系统包应显示在单独的行上。如果Shell样式变量引用 ${PYTHON_MINOR} 出现时,它将被次要版本的Python所取代,例如,对于Python3.12.x,它是12。每件事都在一条线上 # 字符被忽略,因此可以在文件中包括注释。

例如,如果 ./configure 检测到Homebrew打包系统正在使用,如果当前程序包可以由名为“foo”的Homebrew程序包提供,则该文件 build/pkgs/PACKAGE/distros/homebrew.txt 应该包含单行“foo”。如果 foo 当前已卸载,则 ./configure 将打印一条消息,建议用户运行 brew install foo 。看见 使用Sage的等效分发包数据库 了解更多有关这方面的信息。

重要

所有新的标准包在可能的情况下都应该包括 spkg-configure.m4 脚本和已填充的 distros 目录。中有很多例子 build/pkgs ,包括 build/pkgs/python3build/pkgs/suitesparse ,仅举几个例子。

请注意,对于某些包(在撰写本文时),这可能是不可能的,例如通过pip安装的包,以便在运行Sage时使用,如 matplotlibscipy 。如果通过pip安装程序包以在单独的进程中使用,如 tox ,那么这应该是可能的。

自我测试

这个 spkg-check.in 文件是一个可选的脚本模板,但强烈建议您运行包的自检。的格式。 spkg-check 与之相同 spkg-buildspkg-install 。在生成和安装之后运行,如果 SAGE_CHECK 环境变量已设置,请参阅《Sage安装指南》。理想情况下,上游有某种类型的测试套件,可以用标准的 make check 目标。在这种情况下, spkg-check.in 脚本模板将简单地包含:

cd src
$MAKE check

基于Python的包

基于Python的包应该声明 $(PYTHON) 作为依赖项,并且大多数基于Python的包还将具有 $(PYTHON_TOOLCHAIN) 作为仅限订单的依赖项,这将确保基本包,如 pipsetuptools 在生成包时都是可用的。

安装的最佳方式 normal 基于Python的包是用来 pip ,在这种情况下, spkg-install.in 脚本模板可能只由以下部分组成

cd src && sdh_pip_install .

哪里 sdh_pip_install 是由提供的函数 sage-dist-helpers 这指向了正确的 pip ,并包括正确安装到Sage中所需的一些默认标志。

如果 pip 不适用于包,而适用于如下命令 python3 setup.py install 威尔,你可以用 sdh_setup_bdist_wheel ,然后是 sdh_store_and_pip_install_wheel .

spkg-check.in 脚本模板,使用 python3 而不是仅仅 python 。这些路径是由Sage构建系统设置的,这样就可以运行正确版本的Python。例如, scipy spkg-check.in 文件包含行

exec python3 spkg-check.py

抽象需求: install-requires.txt 文件

normal Python包和所有 wheel 包必须有一个文件 install-requires.txt 。为 pip 包,则该文件是可选的;如果该文件丢失,则 requirements.txt 而是使用文件。

如果在PyPI上有可用的Python包,则 install-requires.txt 文件必须包含程序包的名称,因为它是PyPI所知道的。

可选地, install-requires.txt 可以对版本约束(如下限和上限)进行编码。约束的格式为 install_requires 的关键字 setup.cfgsetup.py

Sage将这些版本约束用于两个目的:

  • 作为生成中的Python分发包元数据的来源 SAGE_ROOT/pkgs/ ,见 依赖项和分发包

  • 当实验选项 configure --enable-system-site-packages 被使用,则 configure 脚本检查这些约束,以确定是否接受在系统Python中安装此程序包。

强烈建议包含备注(以 # ),该文件解释了为什么需要一个特定的下限或上限,或者为什么我们希望包含或拒绝某些版本。

例如:

$ cat build/pkgs/sphinx/package-version.txt
3.1.2.p0
$ cat build/pkgs/sphinx/install-requires.txt
# gentoo uses 3.2.1
sphinx >=3, <3.3

评论可能包括指向GitHub问题/PR的链接,如下例所示:

$ cat build/pkgs/packaging/install-requires.txt
packaging >=18.0
# Issue #30975: packaging 20.5 is known to work
# but we have to silence "DeprecationWarning: Creating a LegacyVersion"

当前编码的版本约束只是一个起点。邀请开发人员和下游打包人员根据他们的经验和测试来细化版本约束。当进行包更新以从较新版本中获取关键错误修复时,则应调整下限。设置上限以防止不兼容的未来更改是一个复杂的主题;请参见 :issue:`33520`

混凝土(销钉)要求 normalwheelscript 套餐: package-version.txt 文件

喜欢 normal 非Python包,全部 normal Python包和所有 wheel 包必须有一个文件 package-version.txt 。为 script 该文件是可选的。

Sage将此版本用于两个目的:

  • 这是Sage发行版提供的版本。

  • 作为生成 requirements.txt 中的Python分发包的文件 SAGE_ROOT/pkgs/ ,见 依赖项和分发包

    以供使用生成的 requirements.txt 文件,请参阅 pip User Guide

的具体要求 pip 套餐: requirements.txt 文件

与之形成鲜明对比的是 normalwheel ,以及 script 包裹、 pip 包不使用 package-version.txt 文件。

相反,具体要求设置在 requirements.txt 文件,该文件被直接传递到 pip 在安装时。

这个 requirements.txt 文件使用非常灵活的格式,该格式在 pip User Guide 。通过这种格式,可以将具体需求固定到特定版本,或设置可接受的版本范围,或者完全不受约束。这种格式甚至足够灵活,可以同时安装几个分发包,并可以根据操作系统或Python版本进行调整。

固定版本具有潜在的稳定性优势,因为它可以避免新的、不兼容的版本对Sage分发版本的追溯破坏,还可以帮助实现计算的重复性。

成本是更新版本需要至少两名Sage开发人员的工作:一名准备公关,另一名负责审查。此外,当该包没有得到升级它的开发人员的注意时,就有可能错过较新版本中的错误修复,或者错过主要新版本中的功能。

不固定版本有一个明显的潜在好处,即始终保持最新,因为 pip 联系索引服务器(PYPI)以获取并安装程序包。(请注意 normalwheel 在构建和安装程序包时,程序包总是固定的,甚至无法访问索引服务器。)

但这种动态也带来了不稳定的风险,要么是因为包本身受到新版本中的错误的影响,要么是因为破坏了与Sage的兼容性。

什么策略对包最好取决于各种因素,包括上游项目使用的开发速度和质量控制、Sage开发人员对包的兴趣、在Sage中的集成深度、是否影响数学等。

有关依赖关系的说明 pip 包裹

对象的依赖项 pip 包不需要在Sage发行版中作为包提供,因为包可以直接从PyPI中拉出它的一些构建时和运行时依赖项。这对开发人员来说是一种适度的便利,如果想要保持广泛的版本范围,这可能是重要的。

但是,如果依赖项也是Sage发行版的包,那么我们必须声明该依赖项。否则,在生成或升级时可能会出现各种错误。当新版本的 pip 包添加了碰巧是Sage包的依赖项,这是不稳定的另一个来源。

SPKG.rst文件

这个 SPKG.rst 文件应遵循以下模式:

PACKAGE_NAME: One line description
==================================

Description
-----------

What does the package do?

License
-------

What is the license? If non-standard, is it GPLv3+ compatible?

Upstream Contact
----------------

Provide information for upstream contact.  Usually just an URL.

Dependencies
------------

Only put special dependencies here that are not captured by the
``dependencies`` file. Otherwise omit this section.

Special Update/Build Instructions
---------------------------------

If the tarball was modified by hand and not via an ``spkg-src``
script, describe what was changed. Otherwise omit this section.

使用 PACKAGE_NAME 替换为SPKG名称(=中的目录名 build/pkgs )。

中不包括Changelog SPKG.rst 文件。我们只在GitHub上的提交消息和Pull请求讨论中跟踪此信息。

包依赖项

许多包依赖于其他包。例如,考虑一下 eclib 用于椭圆曲线的软件包。该程序包使用库pari、ntl和flint。因此,以下是 dependencies 文件用于 eclib

pari ntl flint

----------
All lines of this file are ignored except the first.

对于Python包,常见的依赖项包括 pipsetuptools ,以及 future 。如果您的包依赖于其中任何一个,请使用 $(PYTHON_TOOLCHAIN) 取而代之的是。例如,下面是 dependencies 文件用于 configparser

$(PYTHON) | $(PYTHON_TOOLCHAIN)

(请参阅下文以了解 | 。)

如果没有依赖项,则可以使用

# no dependencies

----------
All lines of this file are ignored except the first.

实际上存在两种依赖关系:正常依赖关系和仅订单依赖关系,这两种依赖关系较弱。的语法。 dependencies 文件是

normal dependencies | order-only dependencies

如果没有 | ,则所有依赖项都是正常的。

  • 如果包裹A有一个 order-only dependency 在B上,这仅仅意味着在A可以建造之前,B必须被建造。B的版本并不重要,重要的是安装了B。如果依赖项纯粹是构建时依赖项(例如,对pip的依赖项仅仅是因为 spkg-install 文件使用pip)。

    或者,您可以将仅限订单的依赖项放在单独的文件中 dependencies_order_only

  • 如果A有一个 normal dependency 在B上,这还意味着每次B更新时都应该重建A。这是最常见的一种依赖。正常的依赖关系是库所需要的:如果我们升级NTL,我们应该重新构建所有使用NTL的东西。

有些程序包仅用于程序包的自检 (spkg-check )。这些依赖项应在单独的文件中声明 dependencies_check

某些依赖项是可选的,因为如果将它们配置为安装,则它们只是一种依赖项。这些依赖项应在单独的文件中声明 dependencies_optional

为了检查包的依赖关系是否正确,以下命令应该可以正常工作:

[alice@localhost sage]$ make distclean && make base && make PACKAGE_NAME

最后,请注意,标准包应该只依赖于标准包,而可选包应该只依赖于标准或可选包。

包装标签

我们使用以下“标签”来组织我们的 index of packages in the Reference Manual

  • 放置一个名为的空文件 math 在程序包目录中,使程序包出现在标准程序包、可选程序包和实验程序包索引的“数学”子节中。

  • 放置空文件名 front-end 在程序包目录中,使程序包出现在“前端、图形、文件编写”小节中。

  • 没有这些标签的包出现在“其他依赖项”小节中。

我们在持续集成脚本中使用以下标记来过滤掉不能或不应该自动测试的包。

  • 您可以通过放置一个名为的空文件来将包标记为“巨型” huge 在程序包目录中。例如,包 polytopes_db_4d 是一个大型数据库,其压缩的tarball大小为9 GB。

  • 对于其他一些包,我们放置了一个名为的空文件 has_nonfree_dependencies 在程序包目录中。这表示安装了该程序包的Sage不能被重新分发,而且该程序包只能在安装了其他非免费程序包后才能安装。

安装程序包的位置

Sage发行版有几个安装树的概念。

  • $SAGE_VENV 是所有Python包的默认安装树,即具有 install-requires.txt 、轮子包和PIP包,并使用 requirements.txt

  • $SAGE_LOCAL 是所有非Python包的默认安装树。

  • $SAGE_DOCS (仅在构建时设置)是HTML和PDF文档的安装树。

通过放置一个文件 trees.txt 在程序包目录中,可以覆盖安装树。例如, build/pkgs/python3/trees.txt 包含单词 SAGE_VENV ,以及 build/pkgs/sagemath_doc_html/trees.txt 包含单词 SAGE_DOCS

包版本控制

这个 package-version.txt 文件只包含版本。所以如果上游是 FoO-1.3.tar.gz 则包版本文件将仅包含 1.3

如果上游包来自某个版本而不是稳定版本,或者如果上游没有版本号,您应该使用进行修订的日期。例如, database_stein_watkins 与版本一起打包 20110713 包含截至2011-07-13的数据库。请注意,该日期应引用 contents 而不是在为Sage包装的那天。此特定的SAGE包适用于 database_stein_watkins 创建于2014年,但其中包含的数据最后一次更新是在2011年。

如果您应用任何补丁程序,或者如果您对上游tarball进行了更改(请参见 修改第三方代码 下面),则应附加一个 .p0 设置为该版本,以指示它不是未经修改的包。

此外,每当对包进行更改时 without 更改上游tarball(例如,添加额外的补丁程序或修复 spkg-install 文件),您还应该添加或提高补丁级别。所以不同的版本应该是 1.31.3.p01.3.p1 ..版本号或补丁级别的更改将触发程序包的重新安装,以便将更改考虑在内。

校验和和tarball名称

这个 checksums.ini 文件包含上游tarball的文件名模式(没有实际版本)及其校验和。所以如果上游是 $SAGE_ROOT/upstream/FoO-1.3.tar.gz ,创建新文件 $SAGE_ROOT/build/pkgs/foo/checksums.ini 仅包含:

tarball=FoO-VERSION.tar.gz

Sage在内部取代了 VERSION 内容为的子字符串 package-version.txt

上游URL

除了中的这些字段之外 checksums.ini ,可选字段 upstream_url 包含指向上游程序包存档的URL。

发布经理使用中的信息 upstream_url 下载上游包存档,并在准备新版本时使其在Sage镜像上可用。在GitHub PR升级包时,PR描述不应再包含上游URL,以避免信息重复。

请注意,与 tarball 字段,即 upstream_url 是模板;子字符串 VERSION 替换为实际版本。它也可以写成 ${VERSION} ,并且可以通过以下方式引用版本的点分隔组件 VERSION_MAJORVERSION_MINOR ,以及 VERSION_MICRO

对于可从PyPI获得的Python包,您应该使用 upstream_url 从… pypi.io ,它遵循以下格式

upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz

希望在归档文件在Sage镜像上可用之前测试公关分支程序包更新的开发人员。Sage退回到从 upstream_url 在尝试了所有的Sage镜子之后。(这可以通过使用以下命令来禁用 ./configure --disable-download-from-upstream-url 。)要加快这一过程,您可以修剪 upstream/mirror_list 到更少的镜子。

用于创建和维护包的实用程序脚本

该命令 sage --package 提供了一系列用于创建和维护Sage分发包的功能。

正在创建包

假设您已经下载了 $SAGE_ROOT/upstream/FoO-1.3.tar.gz ,您可以使用::

[alice@localhost sage]$ sage --package create foo                     \
                                         --version 1.3                \
                                         --tarball FoO-VERSION.tar.gz \
                                         --type experimental

创造 $SAGE_ROOT/build/pkgs/foo/package-version.txtchecksums.ini ,以及 type 一步到位。

您可以使用附加参数跳过手动下载上游tarball --upstream-url 。此命令还将设置 upstream_url 中的字段 checksums.ini 如上所述。

对于可从PyPI获得的Python包,您可以使用:

[alice@localhost sage]$ sage --package create scikit_spatial --pypi   \
                                         --type optional

这将自动从PYPI下载最新版本,并通过查询PYPI获取大多数必要信息。尤其是, SPKG.rst 文件创建为程序包的自述文件的副本。

这个 dependencies 文件可能需要编辑(注意有关以下方面的警告 --no-deps 该Sage在安装程序包期间发出!)。

此外,您可能希望在文件中为可接受的包版本设置下限和上限 install-requires.txt 。(请确保中的版本 package-version.txt 属于此可接受的版本范围!)

默认情况下,当包作为独立于平台的滚轮可用时, sage --package 创建车轮套装。要创建普通包(例如,当包需要打补丁时),您可以使用:

[alice@localhost sage]$ sage --package create scikit_spatial --pypi   \
                                         --source normal              \
                                         --type optional

要创建PIP包而不是普通或车轮包,可以使用:

[alice@localhost sage]$ sage --package create scikit_spatial --pypi   \
                                         --source pip                 \
                                         --type optional

当包已经存在时, sage --package create 覆盖它。

将包更新到新版本

一个具有 upstream_url 只需键入::即可更新信息

[alice@localhost sage]$ sage --package update numpy 3.14.59

它将自动下载存档并更新中的信息 build/pkgs/numpy/

对于可从PyPI获得的Python包,还有另一个快捷方式::

[alice@localhost sage]$ sage --package update-latest matplotlib
Updating matplotlib: 3.3.0 -> 3.3.1
Downloading tarball to ...matplotlib-3.3.1.tar.bz2
[...............................................................]

准备更新时,检查文件中可能声明的可接受包版本的任何下限和上限 install-requires.txt 仍然正确,并根据需要更新它们。中的版本 package-version.txt 始终需要落在版本范围内!

如果你通过了开关 --commit ,则该脚本将运行 git commit 为了你。

如果您希望更新程序包 foo 通过手动更改中的文件 build/pkgs/foo ,您将需要运行::

[alice@localhost sage]$ sage --package fix-checksum foo

这将修改 checksums.ini 文件中包含正确的校验和。

获取程序包指标

该命令 sage --package metrics 计算Sage分发或给定程序包列表中所有程序包的机器可读聚合指标:

[alice@localhost sage]$ sage --package metrics
has_file_distros_arch_txt=181
has_file_distros_conda_txt=289
has_file_distros_debian_txt=172
has_file_distros_fedora_txt=183
has_file_distros_gentoo_txt=211
has_file_distros_homebrew_txt=95
has_file_distros_macports_txt=173
has_file_distros_nix_txt=72
has_file_distros_opensuse_txt=206
has_file_distros_slackware_txt=32
has_file_distros_void_txt=221
has_file_patches=63
has_file_spkg_check=106
has_file_spkg_configure_m4=262
has_file_spkg_install=322
has_tarball_upstream_url=291
line_count_file_patches=31904
line_count_file_spkg_check=585
line_count_file_spkg_configure_m4=3337
line_count_file_spkg_install=4342
packages=442
type_base=1
type_experimental=18
type_optional=151
type_standard=272

开发人员可以使用这些指标来监控Sage发行版的复杂性和质量。以下是一些例子:

  • has_file_patches 指示有多少个包不为空 patches/ 目录,以及 line_count_file_patches 给出修补程序文件中的总行数。

    理想情况下,我们不必为包裹携带补丁。例如,在发布新的上游版本时更新补丁程序可能是一种维护负担。

    开发人员可以通过与包的上游维护人员合作来准备一个新版本,该版本需要更少或更小的补丁,或者根本不需要补丁。

  • line_count_spkg_install 中的总行数。 spkg-installspkg-install.in 文件;请参见 构建和安装的脚本 normal 包裹

    当我们带着复杂的东西 spkg-install.in 对于普通程序包,可能表示上游程序包的构建和安装脚本应该得到改进。

    开发人员可以通过与包的上游维护人员合作来准备改进的版本,从而提供帮助。

  • has_file_spkg_check 指示有多少个包具有 spkg-checkspkg-check.in 文件;请参见 自我测试

  • has_file_spkg_configure_m4 指示准备了多少个程序包来检查等效的系统程序包,以及 has_file_distros_arch_txthas_file_distros_conda_txt 等统计有多少个包提供了相应的系统包信息。

构建包

在此阶段,您有一个尚未随Sage分发的新tarball (FoO-1.3.tar.gz 在部分的示例中 目录结构 )。

现在,您可以使用以下命令安装程序包:

[alice@localhost sage]$ sage -i package_name

或:

[alice@localhost sage]$ sage -f package_name

以强制重新安装。如果您的包包含 spkg-check 脚本(请参见 自我测试 )它可以通过::运行

[alice@localhost sage]$ sage -i -c package_name

或:

[alice@localhost sage]$ sage -f -c package_name

如果一切顺利,打开一个公关,代码在下面 SAGE_ROOT/build/pkgs

修改第三方代码

在Sage发行版中,我们尽可能使用稳定版本的第三方包的未打补丁的原始上游tarball。然而,有时修改是必要的,要么是为了修复错误,要么是为了使包构建在Sage支持的平台上。

仅限 normal 包可以打补丁;请参见 包源类型 。如果某个Python包当前是 wheel 包,您需要对其进行修补,将其更改为 normal 请先打包。

何时修补、何时重新打包、何时自动没收

  • 首先,检查是否已经有更新的稳定版本的包可以修复该问题。在这种情况下,请尝试升级包。

  • 检查Debian或其他发行版是否已经为上游提供了补丁。如果可能的话,使用它们,不要重新发明轮子。

  • 如果上游项目在GitHub上维护,请检查是否有可以导入的拉取请求;请参见 通过从GitHub导入拉取请求来准备补丁 下面。

  • 有时,您似乎需要打补丁(手写) Makefile 因为它对一些路径或编译器标志进行了“硬编码”:

    --- a/Makefile
    +++ b/Makefile
    @@ -77,7 +77,7 @@
     # This is a Makefile.
     # Handwritten.
    
    -DESTDIR = /usr/local
    +DESTDIR = $(SAGE_ROOT)/local
     BINDIR   = $(DESTDIR)/bin
     INCDIR   = $(DESTDIR)/include
     LIBDIR   = $(DESTDIR)/lib
    

    不要为此使用补丁。可以从命令行覆盖Makefile变量。只需在中使用以下内容 spkg-install

    $(MAKE) DESTDIR="$SAGE_ROOT/local"
    
  • 如果上游Makefile没有构建共享库,那么就不必费心去修补它了。

    取而代之的是自动没收包裹,并使用Automake和LibTool的标准设施。这确保了共享库构建可以在Linux和MacOS之间移植。

  • 如果您必须更改 configure.ac 或AutoTools生成系统的其他源文件(或者如果您正在自动没收包),则不能使用修补; modified tarball 取而代之的是。

  • 如果补丁很大,不要使用补丁。vt.制造一个 modified tarball 取而代之的是。

  • 否则, maintain a set of patches

通过从GitHub导入拉取请求来准备补丁

在最简单、最常见的情况下,在上游包的GitHub库中已经有了一个Pull请求。

例如,如果https://github.com/discopt/cmr/pull/64是我们希望使用的PR,则将url更改为https://github.com/discopt/cmr/pull/64.patch并将此文件保存在 patches/ 包目录的子目录(如果子目录尚不存在,则创建子目录)。确保它具有 .patch 文件扩展名;如果您的浏览器使用 .patch.txt 扩展名,将其重命名。

修改 package-version.txt 文件以指示更改的修补程序级别;请参见 包版本控制 。这确保了程序包将被重新生成,即使其上游版本没有更改。当其他人测试您添加的补丁时,这一点尤其重要。

接下来,使用补丁测试构建包,例如使用 make build 。您应该看到如下所示的消息 Applying 64.patch 。消息,如 Hunk #1 succeeded at 144 with fuzz 1 (offset 9 lines) 都是可以忽略的。当您准备补丁程序的PR所基于的版本不同于Sage程序包使用的版本时,或者当有其他补丁程序对同一文件进行更改时,它们会出现。

确保使用以下命令将修补程序文件添加到分支 git add 。提交时,请使用提交消息,如 build/pkgs/cmr: Add https://github.com/discopt/cmr/pull/64 as a patch 。当您从该分支打开您的PR时,我们在GitHub操作上运行的自动测试将自动重建打补丁的包。

手动准备补丁程序

补丁必须在其标头中包含文档(在第一个diff块之前),并且路径中必须只有一个“前缀”级别(即,只有一个路径级别高于要打补丁的上游源代码的根)。因此,典型的补丁文件应该如下所示:

Add autodoc_builtin_argspec config option

Following the title line you can add a multi-line description of
what the patch does, where you got it from if you did not write it
yourself, if they are platform specific, if they should be pushed
upstream, etc...

diff -dru Sphinx-1.2.2/sphinx/ext/autodoc.py.orig Sphinx-1.2.2/sphinx/ext/autodoc.py
--- Sphinx-1.2.2/sphinx/ext/autodoc.py.orig  2014-03-02 20:38:09.000000000 +1300
+++ Sphinx-1.2.2/sphinx/ext/autodoc.py  2014-10-19 23:02:09.000000000 +1300
@@ -1452,6 +1462,7 @@

     app.add_config_value('autoclass_content', 'class', True)
     app.add_config_value('autodoc_member_order', 'alphabetic', True)
+    app.add_config_value('autodoc_builtin_argspec', None, True)
     app.add_config_value('autodoc_default_flags', [], True)
     app.add_config_value('autodoc_docstring_signature', True, True)
     app.add_event('autodoc-process-docstring')

直接位于 patches/ 直接应用,然后在运行 spkg-install 脚本(只要他们有 .patch 分机)。如果您需要有条件地应用补丁程序(例如仅在特定平台上),您可以将这些补丁程序放置在 patches/ 并手动应用它们。 sage-apply-patches 剧本。例如,考虑布局:

SAGE_ROOT/build/pkgs/foo
|-- patches
|   |-- solaris
|   |   |-- solaris.patch
|   |-- bar.patch
|   `-- baz.patch

补丁 bar.patchbaz.patch 应用于未打包的上游源 src/ 在运行之前 spkg-install 。要有条件地为Solaris应用修补程序, spkg-install 应包含类似以下内容的部分:

if [ $UNAME == "SunOS" ]; then
    sage-apply-patches -d solaris
fi

凡. -d 标志将应用 solaris/ 主目录的子目录 patches/ 目录。

如何维护一组补丁

我们建议使用以下工作流程来维护一组补丁程序。

  • 派生程序包并将其放在公共Git存储库中。

    如果上游有一个公共版本控制存储库,请从那里导入它。如果上游没有公共版本控制存储库,请从上游tarball导入当前源代码。我们给支行打个电话吧 upstream

  • 为Sage所需的更改创建一个分支,我们称其为 sage_package_VERSION ,在哪里 version 是上游版本号。

  • 进行更改并将其提交给分支机构。

  • 生成补丁程序 upstream 分支机构:

    rm -Rf SAGE_ROOT/build/pkgs/PACKAGE/patches
    mkdir SAGE_ROOT/build/pkgs/PACKAGE/patches
    git format-patch -o SAGE_ROOT/build/pkgs/PACKAGE/patches/ upstream
    
  • 或者,创建一个 spkg-src 使用上述命令重新生成补丁目录的Sage包目录中的文件。

  • 当新的上游版本可用时,将其合并(或导入)到 upstream ,然后创建一个新的分支,并将其重新设置为更新的上游的基础:

    git checkout sage_package_OLDVERSION
    git checkout -b sage_package_NEWVERSION
    git rebase upstream
    

    然后重新生成面片。

改性焦油球

如果您确实必须修改上游tarball,那么建议您编写一个名为 spkg-src ,这就造成了变化。这不仅可以作为文档,还可以更容易地将相同的修改应用于未来的版本。

新包和更新包的包含程序

不是Sage一部分的包将首先成为可选的或试验性的(如果它们不是构建在所有支持的系统上,则是后者)。当它们在可选中一段时间内没有问题后,可以建议将它们作为标准包包含在Sage中。

要推荐包含可选/试验性内容的包,请打开添加了标签的GitHub PR c: packages: experimentalc: packages: optional 。以下各节介绍了相关的代码要求。

在审查并包括PR之后,可选套餐将保持至少一年的状态,之后可以建议将其作为标准套餐包含在SAGE中。为此,使用标签打开GitHub PR c: packages: standard 。然后在谷歌小组中提出一个建议 sage-devel

如上所述,将包升级到新的上游版本或使用其他补丁也包括在相应类别中打开PR。

许可证信息

包的许可信息需要放在它的 SPKG.rst 文件和文件中 SAGE_ROOT/COPYING.txt 。每次升级程序包时,请检查许可证是否在不同版本之间更改。

如果由于许可原因而无法重新分发包的上游tarball,请将其重命名为包含字符串 do-not-distribute 。这将防止发布管理脚本将其上载到Sage镜像。

有时,上游tarball包含一些使用自由软件许可证的可分发部分和一些非自由部分。在这种情况下,制作一个只包含自由部分的定制tarball是一个很好的解决方案;请参见 改性焦油球 以及 giac 以包为例。

新标准包的必备条件

要使程序包成为Sage标准分发的一部分,它必须满足以下要求:

  • License 。对于标准包,许可证必须与GNU通用公共许可证版本3兼容。 licenses and comments about them

  • Build Support 。代码必须构建在所有完全支持的平台(Linux、MacOS)上;请参阅 在多个平台上测试 。它必须作为普通程序包或Python(独立于平台的)轮程序包从源代码安装,请参见 包源类型

  • Quality 。代码应该比任何其他可用代码“更好”(通过上述两个标准),作者需要证明这一点。应该将其与Python和其他软件进行比较。通过质量测试的标准包括:

    • 速度

    • 文档

    • 可用性

    • 没有内存泄漏

    • 可维护性

    • 可移植性

    • 合理的构建时间、大小、依赖项

  • Previously an optional package 。新的标准套餐必须花一些时间作为可选套餐。或者有一个很好的理由来解释这是不可能的。

  • Refereeing 。代码必须参照,如中所述 GitHub上的SAGE存储库