2. ソースから CPython を構築する
2. ソースから CPython を構築する
ソースから CPython をビルドすると、検査、変更、デバッグ、テストできるローカル インタプリタが得られます。これは、内部構造を真剣に読む前の実際的な最初のステップです。
ソース ビルドを使用すると、パッケージ化された Python インストールでは通常隠蔽されることを行うことができます。```text change interpreter code add debug prints inspect object layout run CPython tests use debug-only assertions trace reference counts debug crashes in C compare bytecode across builds
CPython は Git リポジトリに存在します。通常のローカル チェックアウトは次のようになります。```bash
git clone https://github.com/python/cpython.git
cd cpython
```リポジトリには、インタープリタ、標準ライブラリ、テスト、ドキュメント、ビルド ファイル、およびプラットフォーム サポート コードが含まれています。
簡略化したビュー:```text
cpython/
Include/ public and internal C headers
Objects/ core object implementations
Python/ compiler, runtime, interpreter loop
Parser/ tokenizer and parser support
Modules/ built-in and extension modules
Lib/ Python standard library
Lib/test/ regression test suite
Programs/ executable entry points
Tools/ developer tools
Doc/ documentation source
```内部作業にとって最も重要なディレクトリは次のとおりです。`Objects`、`Python`、`Include`、`Modules`、 そして`Lib/test`。
## 2.2 ビルドモードの選択
一般的なビルドは 2 つあります。
|ビルド |目的 |
| ------------- | ------------------------------------ |
|リリースビルド |通常インストールされた Python と同様 |
|デバッグビルド |内部の作業に適しています |
内部を調べるには、最初にデバッグ ビルドを使用します。これにより、追加のアサーション、デバッグ ヘルパー、参照追跡サポート、およびより安全な障害モードが有効になります。
デバッグ ビルドは時間がかかりますが、検査は簡単です。
## 2.3 Linux または macOS 上でのビルド
Unix 系システムでは、CPython は通常のconfigure および make フローを使用します。```bash
./configure --with-pydebug
make -j
```これにより、ソース ツリーに実行可能ファイルが生成され、通常は次のような名前が付けられます。```bash
./python
```実行してください:```bash
./python -V
./python -c "print('hello from local CPython')"
```の`--with-pydebug`オプションはデバッグ ビルドを作成します。これにより、ABI タグが変更され、デバッグ動作が有効になります。
一般的な開発用のconfigureコマンドは次のとおりです。```bash
./configure --with-pydebug --with-trace-refs
make -j
```使用`--with-trace-refs`より詳細な参照追跡が必要な場合のみ。オブジェクトのレイアウトが変更され、インタープリタの速度がさらに遅くなる可能性があります。
## 2.4 ビルドの依存関係
最小限のビルドはオプションの依存関係がなくても成功する可能性がありますが、多くの標準ライブラリ モジュールにはシステム ライブラリが必要です。
一般的な依存関係には次のものがあります。
|特集 |典型的な依存関係 |
| ---------------------- | ------------------------ |
| SSL と HTTPS | OpenSSL |
|圧縮 | zlib、bzip2、xz |
| SQLite | SQLite 開発ヘッダー |
| Readline シェルのサポート | readline または libedit |
|呪い |呪い |
|トキンター | Tcl/Tk |
| UUID のサポート |リブイド |
| FFIサポート |りぶふぃ |
依存関係が欠落している場合でも、CPython はビルドできますが、一部の拡張モジュールはスキップされます。
不足しているモジュールに関するメッセージについては、ビルド出力を確認できます。
## 2.5 ツリー外ビルド
CPython はソース ディレクトリの外でビルドできます。これにより、生成されたファイルが分離された状態に保たれます。```bash
mkdir ../cpython-build-debug
cd ../cpython-build-debug
../cpython/configure --with-pydebug
make -j
```結果の実行可能ファイルはビルド ディレクトリに存在します。```bash
./python
```ツリー外ビルドは、1 つのソース チェックアウトから複数の構成が必要な場合に便利です。```text
cpython/
cpython-build-debug/
cpython-build-release/
cpython-build-asan/
```## 2.6 Windows 上でのビルド
Windows では、CPython は Visual Studio ビルド ファイルを使用します。
開発者コマンド プロンプトから:```bat
PCbuild\build.bat -d
```の`-d`flag はデバッグインタープリタを構築します。
実行可能ファイルは通常、次の場所にあります。```text
PCbuild\amd64\python_d.exe
```リリース ビルドでは以下が使用されます。```bat
PCbuild\build.bat
```Windows ビルドには、独自のプロジェクト ファイル、プラットフォーム コード、および拡張機能のビルド ルールがあります。レイアウトは Unix ビルドとは異なりますが、インタープリターのソースは同じコア コードです。
## 2.7 ビルドの検証
ビルド後、実行可能ファイルを確認します。```bash
./python -V
./python -m sysconfig
```インタプリタがインストールされていると認識している場所を確認します。```bash
./python - <<'PY'
import sys
print(sys.executable)
print(sys.prefix)
print(sys.path)
PY
```ソースツリー開発の場合、`sys.path`ローカルを含める必要があります`Lib`ディレクトリ。
## 2.8 テストスイートの実行
CPython のテスト スイートは次のもので実行されます。```bash
./python -m test
```最初のチェックを迅速に行うには:```bash
./python -m test test_sys test_gc test_dict test_compile
```テストを並行して実行します。```bash
./python -m test -j8
```失敗したテストを再実行します。```bash
./python -m test --fail-env-changed
```1 つのテスト ファイルを実行します。```bash
./python -m test test_dict
```1 つのテスト ケースを実行します`unittest`構文:```bash
./python -m unittest Lib.test.test_dict.DictTest.test_constructor
```テスト スイートは内部ワークフローの一部です。 CPython を変更する場合、テストは言語動作の破壊を防ぐ最初の手段となります。
## 2.9 デバッグビルド動作
デバッグ ビルドにより、CPython の内部動作が変更されます。
これにより、次のような追加のチェックが可能になります。```text
assertions in C code
debug memory allocator checks
extra object consistency checks
reference leak tools
debug ABI marker
stricter failure behavior
```デバッグ ビルドでは、バグが早期に発見されることがよくあります。リリース ビルドでは無害に見えるメモリの誤用が、デバッグ ビルドではすぐに中止される場合があります。
これは便利です。内部動作は大きな音を立てて失敗するはずです。
## 2.10 ビルド構成の検査
CPython はビルド構成を公開します`sysconfig`。```python
import sysconfig
print(sysconfig.get_config_var("Py_DEBUG"))
print(sysconfig.get_config_var("WITH_PYMALLOC"))
print(sysconfig.get_config_var("Py_GIL_DISABLED"))
print(sysconfig.get_config_var("CONFIG_ARGS"))
```直接実行します。```bash
./python - <<'PY'
import sysconfig
for name in ["Py_DEBUG", "WITH_PYMALLOC", "Py_GIL_DISABLED", "CONFIG_ARGS"]:
print(name, "=", sysconfig.get_config_var(name))
PY
```これにより、どのコンパイル時機能がアクティブになっているかがわかります。
## 2.11 便利なビルドターゲット
共通`make`ターゲット:
|ターゲット |目的 |
| ------------------ | -------------------------------------- |
|`make`|インタプリタを構築する |
|`make -j`|並行して構築する |
|`make clean`|生成された多数のファイルを削除する |
|`make distclean`|設定出力も削除します。
|`make test`|テストを実行する |
|`make regen-all`|生成されたファイルを再生成する |
|`make profile-opt`|プロファイルに基づいた最適化を使用して構築する |
通常の内部作業には、次を使用します。```bash
make -j
./python -m test test_name
```生成されたファイルの場合、文法、クリニック定義、オペコード メタデータなどの入力を変更する場合にのみ再生成ターゲットを使用してください。
## 2.12 生成されたファイル
いくつかの CPython ファイルが生成されます。やみくもに編集しないでください。
生成されたアーティファクトは次のものから取得されます。```text
Grammar definitions
Argument Clinic input
opcode metadata
frozen modules
configuration scripts
documentation tools
```Argument Clinic は、C 拡張機能および組み込みメソッド定義で特に一般的です。構造化コメントから解析コードとラッパー コードを生成します。
C ファイルには次のようなブロックが含まれる場合があります。```c
/*[clinic input]
module.function
arg: object
Description here.
[clinic start generated code]*/
```生成されたパーツは手動で編集するのではなく、適切なツールを使用して再生成する必要があります。
## 2.13 変更後の再構築
通常の編集ループ:```bash
vim Objects/listobject.c
make -j
./python -m test test_list
```C コードの小さな変更の場合、通常、増分リビルドは高速です。
Python 標準ライブラリの変更の場合:```bash
vim Lib/pathlib/__init__.py
./python -m test test_pathlib
```純粋な Python の変更には C のリビルドは必要ありません。
パーサー、オペコード、または生成されたコードを変更した場合は、ビルドする前に再生成が必要になる場合があります。
## 2.14 デバッグ印刷の追加
独自のインタープリターを実行していることを確認する簡単な方法は、一時的なデバッグ出力を追加することです。
たとえば、C 関数では次のようになります。```c
fprintf(stderr, "debug: list append called\n");
```次に再構築します。```bash
make -j
```小さなプログラムを実行します。```bash
./python - <<'PY'
x = []
x.append(1)
PY
```一時的なプリントは粗雑ですが効果的です。コミットする前にそれらを削除してください。
## 2.15 使用方法`gdb`または`lldb`デバッグ ビルドはネイティブ デバッガーで適切に動作します。
と`gdb`:
```bash
gdb --args ./python script.py
```内部`gdb`:
```gdb
break PyEval_EvalFrameDefault
run
bt
```と`lldb`:
```bash
lldb -- ./python script.py
```内部`lldb`:
```lldb
breakpoint set --name PyEval_EvalFrameDefault
run
bt
```便利なブレークポイント:```text
Py_Initialize
PyEval_EvalFrameDefault
_PyEval_EvalFrameDefault
PyObject_Malloc
PyObject_Free
PyErr_SetString
_Py_Dealloc
```正確なシンボル名はバージョンやビルド構成によって異なる場合があります。
## 2.16 Python レベルの検査を使用する
すべての内部に関する質問に C デバッガが必要なわけではありません。
便利なモジュール:
|モジュール |使用 |
| ------------- | -------------------------------------- |
|`dis`|バイトコードを検査する |
|`sys`|実行時の状態とインタープリターの設定 |
|`gc`|ガベージコレクターの検査 |
|`inspect`|フレーム、関数、ソース、シグネチャ |
|`types`|ランタイム型オブジェクト |
|`sysconfig`|ビルド構成 |
|`tracemalloc`| Python 割り当てトレース |
例:```python
import dis
import gc
import sys
def f(x):
return x + 1
dis.dis(f)
print(f.__code__)
print(sys.getrefcount(f))
print(gc.is_tracked(f))
```このスタイルは、C に移行する前に役立ちます。
## 2.17 メモリ割り当てのデバッグ
CPython にはメモリ アロケータ用のデバッグ フックがあります。
以下を使用して実行します:```bash
PYTHONMALLOC=debug ./python script.py
```これにより、メモリ割り当てに関する追加のチェックが可能になります。 API の誤用、バッファ オーバーラン、アンダーフロー、および解放後の使用パターンを検出できます。
割り当てトレースの場合:```bash
./python -X tracemalloc script.py
```または Python 内で:```python
import tracemalloc
tracemalloc.start()
data = [bytearray(1024) for _ in range(1000)]
current, peak = tracemalloc.get_traced_memory()
print(current, peak)
tracemallocPython レベルのメモリ割り当てパスをトレースします。ネイティブ ヒープのデバッグには依然として低レベルのツールが必要です。
2.18 サニタイザー ビルド
重大な経営幹部レベルの作業の場合は、サニタイザーを使用して構築します。
AddressSanitizer はメモリ エラーを検出できます。bash ./configure --with-pydebug --with-address-sanitizer make -j UnknownBehaviorSanitizer は、未定義の C 動作を検出できます。```bash
./configure --with-pydebug --with-undefined-behavior-sanitizer
make -j
## 2.19 プロファイルガイド付きリリース ビルド
通常の最適化された CPython リリース ビルドでは、プロファイルに基づく最適化が使用される場合があります。```bash
make profile-opt
```これにより、CPython が構築され、トレーニング ワークロードが実行され、プロファイル データを使用して再構築されます。
パフォーマンスを測定するときに使用します。デバッグ ビルドとリリース Python を比較せず、数値を意味のあるものとして扱います。
内部学習用:```text
debug build for correctness and inspection
release build for speed comparison
PGO build for performance-sensitive measurement
```## 2.20 一般的なビルドの問題
|症状 |考えられる原因 |
| ---------------------------- | ------------------------------------------------ |
|`_ssl`欠落しています | OpenSSL ヘッダーまたはライブラリが利用できない |
|`_sqlite3`欠落しています | SQLite 開発パッケージは利用できません |
|`readline`欠落しています | readline または libedit ヘッダーは使用できません。
|ロケールに関するテストの失敗 |環境ロケールの不一致 |
|ネットワーク周りのテスト障害 |外部ネットワークのテストまたはプラットフォームの制限 |
|デバッグ ビルド インポートの不一致 |間違った実行可能ファイルを実行しているか、間違っています`PYTHONPATH`|
|生成されたファイルが古い |再生が必要 |
|リンカーエラー |システム ライブラリが見つからないか互換性がない |
CPython 自体をデバッグする前に、構築したばかりのインタープリターを実行していることを確認してください。```bash
./python - <<'PY'
import sys
print(sys.executable)
print(sys.version)
PY
```## 2.21 複数のビルドを保持する
実際のセットアップ:```text
cpython/
cpython-build-debug/
cpython-build-release/
cpython-build-asan/
```各ビルドを異なるジョブに使用します。
|ビルド |使用 |
| ------- | -------------------------------- |
|デバッグ |内部の読み取りとアサーション |
|リリース |動作の比較 |
|牙山 |メモリエラー検出 |
| PGO |パフォーマンス測定 |
これにより、1 つのビルド ディレクトリを常に再構成する必要がなくなります。
## 2.22 最小限の内部ワークフロー
最初のワークフローとしては次のものが適しています。```bash
git clone https://github.com/python/cpython.git
cd cpython
./configure --with-pydebug
make -j
./python -V
./python -m test test_sys test_gc test_dict
```次に、バイトコードを検査します。```bash
./python - <<'PY'
import dis
def f(x):
return x + 1
dis.dis(f)
PY
```次に、小さなファイルを変更し、再構築して、対象を絞ったテストを実行します。
## 2.23 このビルドでできること
この章を終えると、本書の残りの部分で使用できるローカル CPython 実行可能ファイルが作成されます。
次のものを検査できるようになりました。```text
how source becomes bytecode
how frames execute
how objects are laid out
how reference counts change
how the garbage collector tracks containers
how built-in types are implemented
how tests protect behavior
how C-level bugs surface
```ソース ビルドは、CPython をブラック ボックスから、ステップ実行、計測、変更できるシステムに変えます。