# 4. 创建源代码分发包

备注

这篇文档是历史遗留文档，在 https://setuptools.readthedocs.io/en/latest/setuptools.html 上的 `setuptools` 文档独立涵盖此处包含的所有相关信息之后，将不再单独作为正式文档保留。

如 一个简单的例子 一节所示，可以使用 sdist 命令来创建一个源代码分发包。 在最简单的场景下，:

    python setup.py sdist

（假定你没有在设置脚本或配置文件中指定任何 sdist 选项），sdist 将创建基于当前平台的默认格式的归档。 在 Unix 上默认格式为 gzip 的 tar 文件，而在 Windows 上则为 ZIP 文件。

你可以使用 `--formats` 选项来指定任何你想要的格式，例如:

    python setup.py sdist --formats=gztar,zip

是创建一个 gzip 的 tar 文件和一个 zip 文件。 可用的格式有:

格式 | 描述 | 备注
--- | --- | ---
`zip` | zip 文件 (`.zip`) | (1),(3)
`gztar` | gzip 的 tar 文件 (`.tar.gz`) | (2)
`bztar` | bzip2 的 tar 文件 (`.tar.bz2`) | (5)
`xztar` | xz 的 tar 文件 (`.tar.xz`) | (5)
`ztar` | 带压缩的 tar 文件 (`.tar.Z`) | (4),(5)
`tar` | tar 文件 (`.tar`) | (5)

在 3.5 版本发生变更: 添加了对 `xztar` 格式的支持

注释：

1. 默认Windows

2. 默认 Unix

3. 需要有外部 zip 工具或 `zipfile` 模块（自 Python 1.6 起是标准 Python 库的一部分）

4. 需要有 compress 程序。 请注意此格式计划要弃用并将在未来的 Python 版本中被移除。

5. 已被 PEP 527 弃用; PyPI 只接受 `.zip` 和 `.tar.gz` 文件。

当使用任意 `tar` 格式 (`gztar`, `bztar`, `xztar`, `ztar` 或 `tar`) 时，在 Unix 中你可以指定将为每个归档成员设置的 `owner` 和 `group` 名称。

举例来说，如果你希望归档中的全部文件均为 root 所有:

    python setup.py sdist --owner=root --group=root

## 4.1. 指定要分发的文件

如果你没有显式提供文件列表（或如何生成该列表的说明），则 sdist 命令会将一个最小化默认集加入源代码分发版:

- 通过 `py_modules` 和 `packages` 选项指明的所有 Python 源文件

- 在 `ext_modules` 在 `libraries` 选项提及的所有 C 源代码文件

- 通过 `scripts` 选项标识的脚本，参见 安装脚本。

- 任何看起来像是测试脚本的文件: `test/test*.py` (目前，Distutils 不会对测试脚本做除了将它们包括到源代码分发版以外的任何操作，但是在未来将会有一个测试 Python 模块分发版的标准)

- 任何标准的 README 文件 (`README`, `README.txt` 或 `README.rst`), `setup.py` (或任何你指定的安装脚本) 以及 `setup.cfg`。

- 所有匹配 `package_data` 元数据的文件。 参见 安装软件包数据。

- 所有匹配 `data_files` 元数据的文件。 参见 安装其它文件。

有时这就足够了，但通常你还会希望指定要分发的额外文件。 实现这一点的通常方式是编写一个 声明模板，默认名称为 `MANIFEST.in`。 声明模板就是一个有关如何生成你的声明文件 `MANIFEST` 的指令列表，也就是要实际包括在你的源代码分发版中的文件的列表。 sdist 命令会处理这个模板并基于其指令及其在文件系统中的查找结果生成一个声明文件。

如果你想要制作你自己的声明文件，格式很简单：每行一个文件名，仅限常规文件。 如果你提供你自己的 `MANIFEST`，你必须指明一切：上面描述的默认集将不适用于这种情况。

在 3.1 版本发生变更: 现有的已生成 `MANIFEST` 将被生成而无需 sdist 来将其修改时间与 `MANIFEST.in` 或 `setup.py` 的修改时间进行比较。

在 3.1.3 版本发生变更: `MANIFEST` 文件以一个表明它们已生成的注释开始。 没有这条注释的文件不会被覆盖或移除。

在 3.2.2 版本发生变更: sdist 将在不存在 `MANIFEST.in` 时读取一个 `MANIFEST` 文件，如它以前所做的一样。

在 3.7 版本发生变更: `README.rst` 现在已被包括在 distutils 标准 README 列表中。

声明模板每行都有一个命令，其中每个命令指定了一组源代码发布版中要包括或排除的文件。 举例来说，我们再次关注 Distutils 自己的声明模板:

    include *.txt
    recursive-include examples *.txt *.py
    prune examples/sample?/build

其含义应当很清楚：包括发布在根目录中所有匹配 `*.txt` 的文件，在 `examples` 目录下任何位置上匹配 `*.txt` 或 `*.py` 的所有文件，并排除匹配 `examples/sample?/build` 的所有目录。 所有这些操作都将在标准的包括集 之后 执行，因此你可以通过在声明模板中的显式指令来排除标准集中的文件。 （或者，你也可以使用 `--no-defaults` 选项来完全禁用标准集。） 在声明模板迷你语言中还有一些其他的可用命令；参见 创建源码发行包: sdist 命令 小节。

声明模板中命令的顺序是很重要的：在初始状态下，我们有如上所述的默认文件列表，而模板中的每个命令将在该文件列表中添加或移除条目。 一旦我们完全处理好声明模板，我们将再移除不应包括在源代码发布版中的文件:

- Distutils "构建" 目录树中的所有文件 (默认值 `build/`)

- 目录中所有名为 `RCS`, `CVS`, `.svn`, `.hg`, `.git`, `.bzr` 或 `_darcs` 的文件

现在我们有了完整的文件列表，它已被写入声明以供将来引用，并随后用于构建源代码分发压缩 包。

你可以通过 `--no-defaults` 选项禁用默认的包括文件集，你还可以通过 `--no-prune` 禁用默认的排除文件集。

跟随 Distutils 自己的声明模板，让我们追踪' own manifest template, let's trace how the sdist 命令是如何构建要包括在 Distutils 源代码分发版中的文件列表的:

1. 包括 `distutils` 和 `distutils/command` 子目录中的所有 Python 源代码文件（因为与这两个目录对应的包在 setup 脚本的 `packages` 选项中被提及 --- 参见 编写安装脚本 一节）

2. 包括 `README.txt`, `setup.py` 和 `setup.cfg` (标准文件)

3. 包括 `test/test*.py` (标准文件)

4. 包括分发根目录下的 `*.txt` (这将第二次找到 `README.txt`，但这样的冗余随后会被清除)

5. 包括 `examples` 下所有子目录树中匹配 `*.txt` 或 `*.py` 的任何文件，

6. 排除从匹配 `examples/sample?/build` 的目录开始的子目录树中的所有文件 --- 这可能会排除之前两个步骤所包括的文件，因此重要的一点是声明模板中的 `prune` 命令应放在 `recursive-include` 命令之后

7. 排除整个 `build` 目录树，以及任何 `RCS`, `CVS`, `.svn`, `.hg`, `.git`, `.bzr` and `_darcs` 目录

就像在设置脚本中一样，声明模板中的文件和目录名应当总是以斜杠分隔；Distutils 将会负责把它们转化为你的系统平台上的标准表示形式。 通过这种方式，声明模板将可跨操作系统移植。

## 4.2. 声明相关选项

针对 sdist 命令的正常操作过程如下:

- 如果声明文件 (默认为 `MANIFEST`) 存在且第一行不为表示它是由 `MANIFEST.in` 生成的注释，则它会被不加修改地原样使用

- 如果声明文件不存在或是在此之前被自动生成的，则读取 `MANIFEST.in` 并创建声明文件

- 如果 `MANIFEST` 或 `MANIFEST.in` 均不存在，则使用默认文件集创建声明文件

- 使用当前 `MANIFEST` (刚生成的或读入的) 中的文件列表创建源代码分发压缩包

有数个选项可以修改此行为。 首先，使用 `--no-defaults` 和 `--no-prune` 来禁用标准的 "包括" 和 "排除" 集。

第二，你可能只是想要（重）生成声明文件，但不想要创建源代码分发包:

    python setup.py sdist --manifest-only

`-o` 是 `--manifest-only` 的缩写。