# 機能設計書 61-プリコンパイル

## 概要

本ドキュメントは、Juliaにおけるモジュールのプリコンパイルとキャッシュ生成機能について、その設計と実装を記述するものである。`__precompile__` および `precompile` によるモジュールの事前コンパイルと、パッケージキャッシュの並列生成を対象とする。

### 本機能の処理概要

本機能は、Juliaモジュールのプリコンパイル（事前コンパイル）とキャッシュファイル（`.ji` ファイル）の生成を行う。プリコンパイルにより、パッケージの初回ロード時間を大幅に短縮し、開発者の生産性を向上させる。

**業務上の目的・背景**：Juliaのパッケージは初回ロード時に型推論やコード生成を行うため、プリコンパイルなしでは毎回数秒から数十秒の待ち時間が発生する。本機能はこれらの処理結果をキャッシュファイルとして保存し、2回目以降のロード時間を劇的に短縮する。これはJuliaの「Time to First Plot (TTFP)」問題への主要な対策である。

**機能の利用シーン**：パッケージのインストール後の自動プリコンパイル、`using`/`import` 時の自動プリコンパイル、`Pkg.precompile()` による手動プリコンパイル、および CI/CD パイプラインでのビルド時プリコンパイルにおいて利用される。

**主要な処理内容**：
1. プロジェクト環境（`Project.toml` / `Manifest.toml`）の解析と依存関係グラフの構築
2. 循環依存の検出とスキップ
3. 並列プリコンパイルワーカーの管理（`JULIA_NUM_PRECOMPILE_TASKS` による制御）
4. 各パッケージのキャッシュファイル（`.ji`）生成と検証
5. プログレスバーによる進捗表示
6. 拡張（Extensions）のプリコンパイル対応

**関連システム・外部連携**：Pkg.jl パッケージマネージャとの密接な連携、TOML パーサーによる設定ファイル読み込み、ファイルシステムキャッシュ操作。

**権限による制御**：特になし。ファイルシステムのパーミッションに依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | Pkgモード（pkg>） | 参照画面 | `precompile` コマンドによるプリコンパイル実行 |

## 機能種別

計算処理 / データ連携（キャッシュファイル I/O）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pkgs | `Union{Vector{String}, Vector{PkgId}}` | No | プリコンパイル対象パッケージ（空の場合は全依存パッケージ） | パッケージが存在すること |
| internal_call | `Bool` | No | 自動プリコンパイル呼び出しかどうか | - |
| strict | `Bool` | No | エラー報告範囲の制御 | - |
| warn_loaded | `Bool` | No | ロード済みパッケージの警告表示 | - |
| timing | `Bool` | No | 各パッケージのコンパイル時間を表示 | - |
| configs | `Union{Config, Vector{Config}}` | No | コンパイル設定（Cmd と CacheFlags のペア） | - |
| manifest | `Bool` | No | マニフェスト全体をプリコンパイルするか | - |
| io | `IO` | No | 出力ストリーム | - |

### 入力データソース

- `Project.toml`：プロジェクトの直接依存関係
- `Manifest.toml`：完全な依存関係グラフ
- 環境変数 `JULIA_NUM_PRECOMPILE_TASKS`：並列タスク数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cache_paths | `Vector{String}` | 生成されたキャッシュファイルのパス一覧 |
| nothing | `Nothing` | プリコンパイルをスキップすべき場合 |

### 出力先

- ファイルシステム上のキャッシュディレクトリ（`~/.julia/compiled/`）

## 処理フロー

### 処理シーケンス

```
1. 環境解析
   └─ ExplicitEnv を構築し Project.toml / Manifest.toml をパース
2. パッケージ識別
   └─ 指定パッケージを PkgId に変換し依存グラフを構築
3. 循環依存検出
   └─ scan_deps! で依存グラフを走査し循環依存を検出・警告
4. 並列タスク初期化
   └─ Semaphore で並列度を制御（デフォルト: CPU_THREADS + 1, 最大16）
5. パッケージ毎のプリコンパイル実行
   └─ 依存関係の順に各パッケージをプリコンパイルし .ji キャッシュを生成
6. プログレス表示
   └─ MiniProgressBar / ターミナルスピナーで進捗をリアルタイム表示
7. 結果収集と検証
   └─ キャッシュファイルの存在と整合性を確認
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ExplicitEnv 構築]
    B --> C[依存関係グラフ構築]
    C --> D{循環依存あり?}
    D -->|Yes| E[循環パッケージをスキップ・警告]
    D -->|No| F[並列プリコンパイルタスク起動]
    E --> F
    F --> G[各パッケージのキャッシュ生成]
    G --> H{全パッケージ完了?}
    H -->|No| G
    H -->|Yes| I[結果検証・ロード済みパッケージ警告]
    I --> J[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-61-01 | 循環依存スキップ | 循環依存にあるパッケージはプリコンパイルをスキップする | 依存グラフに循環が存在する場合 |
| BR-61-02 | `__precompile__(false)` | 明示的にプリコンパイルを無効化したパッケージはスキップする | パッケージが `__precompile__(false)` を宣言している場合 |
| BR-61-03 | 並列度制限 | Windows では並列タスク数をデフォルトの半分にする | `Sys.iswindows()` が true の場合 |
| BR-61-04 | 拡張プリコンパイル | 拡張はすべてのトリガーが環境に存在する場合のみプリコンパイルする | 拡張パッケージの処理時 |

### 計算ロジック

並列タスク数の算出：
```
default = Sys.iswindows() ? div(CPU_THREADS, 2) + 1 : CPU_THREADS + 1
default = min(default, 16)
num_tasks = max(1, parse(Int, ENV["JULIA_NUM_PRECOMPILE_TASKS"]) ?? default)
```

## データベース操作仕様

本機能はデータベースを使用しない。キャッシュファイル（`.ji`）をファイルシステムに読み書きする。

### 操作別データベース影響一覧

該当なし（ファイルシステムベースのキャッシュ操作）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | PkgPrecompileError | 不明なパッケージ名が指定された場合 | エラーメッセージを表示 |
| - | PkgPrecompileError | キャッシュファイル生成に失敗した場合 | エラー詳細を表示し他パッケージは続行 |
| - | 循環依存検出 | 依存グラフに循環が存在する場合 | 警告を表示しスキップ |

### リトライ仕様

プリコンパイル自体にリトライ機構はない。ロック取得時にリトライが行われる。

## トランザクション仕様

キャッシュファイルの書き込みはアトミックに行われる（一時ファイルに書き込み後リネーム）。

## パフォーマンス要件

- 並列プリコンパイルにより、シリアル実行と比較して大幅な速度向上を実現する
- プログレスバーの更新頻度は30FPSを上限とする（`PROGRESS_BAR_TIME_GRANULARITY`）

## セキュリティ考慮事項

- キャッシュファイルはユーザーのホームディレクトリに保存され、ファイルパーミッションに従う
- プリコンパイル時に任意のJuliaコードが実行される可能性がある

## 備考

- 環境変数 `JULIA_NUM_PRECOMPILE_TASKS` で並列度を制御可能
- `timing=true` を指定すると各パッケージのコンパイル時間が表示される

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、プリコンパイルシステムの中核データ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | precompilation.jl | `base/precompilation.jl` | `ExplicitEnv` 構造体（9-27行目）: プロジェクト環境を表現するデータ構造。依存関係・弱依存・拡張・ルックアップ戦略を保持 |
| 1-2 | precompilation.jl | `base/precompilation.jl` | `MiniProgressBar` 構造体（271-283行目）: プログレスバーの状態管理 |
| 1-3 | precompilation.jl | `base/precompilation.jl` | `PkgPrecompileError` 例外型（362-364行目）: プリコンパイルエラーの表現 |

**読解のコツ**: `ExplicitEnv` のフィールドは Project.toml / Manifest.toml の構造と対応している。`project_deps` は `[deps]` セクション、`project_weakdeps` は `[weakdeps]` セクションに対応する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | precompilation.jl | `base/precompilation.jl` | `precompilepkgs` 関数（544-561行目）: 公開APIのエントリーポイント。引数の正規化を行い `_precompilepkgs` に委譲 |

**主要処理フロー**:
1. **544-555行目**: キーワード引数の定義とデフォルト値の設定
2. **556行目**: デバッグログ出力
3. **558-561行目**: 引数を正規化して内部実装 `_precompilepkgs` に委譲

#### Step 3: 環境解析処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | precompilation.jl | `base/precompilation.jl` | `ExplicitEnv(envpath::String)` コンストラクタ（42-266行目）: Project.toml / Manifest.toml を解析し依存関係グラフを構築 |

**主要処理フロー**:
- **49行目**: `parsed_toml(envpath)` で Project.toml をパース
- **60-71行目**: `[deps]`, `[weakdeps]`, `[extras]` セクションの解析
- **91-108行目**: `[extensions]` セクションの解析とトリガーUUID解決
- **110-184行目**: Manifest.toml の解析と依存関係の展開
- **186-266行目**: 圧縮形式から展開形式への変換とルックアップ戦略の決定

#### Step 4: 循環依存検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | precompilation.jl | `base/precompilation.jl` | `scan_deps!` / `scan_pkg!` 関数（428-460行目）: DFS ベースの循環依存検出アルゴリズム |

#### Step 5: プリコンパイル実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | precompilation.jl | `base/precompilation.jl` | `_precompilepkgs` 関数（578行目以降）: 並列プリコンパイルの実装本体 |
| 5-2 | loading.jl | `base/loading.jl` | `compilecache` 関数: 個々のパッケージのキャッシュファイル生成 |

### プログラム呼び出し階層図

```
precompilepkgs(pkgs)
    │
    ├─ ExplicitEnv(envpath)
    │      ├─ parsed_toml(envpath)      # Project.toml 解析
    │      └─ parsed_toml(manifest)     # Manifest.toml 解析
    │
    ├─ _precompilepkgs(...)
    │      ├─ ExplicitEnv()             # アクティブプロジェクトの環境解析
    │      ├─ scan_deps!()              # 循環依存検出
    │      │      └─ scan_pkg!()
    │      ├─ collect_all_deps()        # 間接依存の収集
    │      ├─ Base.compilecache()       # 各パッケージのキャッシュ生成
    │      └─ show_progress()           # プログレスバー表示
    │             ├─ start_progress()
    │             ├─ show_progress()
    │             └─ end_progress()
    │
    └─ excluded_circular_deps_explanation()  # 循環依存の説明生成
```

### データフロー図

```
[入力]                          [処理]                          [出力]

Project.toml ──────▶ ExplicitEnv() ──────▶ 依存関係グラフ
Manifest.toml ─────▶                      │
                                          ▼
                                   scan_deps!() ──────▶ 循環依存リスト
                                          │
                                          ▼
                                   _precompilepkgs() ──▶ .ji キャッシュファイル
                                          │
JULIA_NUM_PRECOMPILE_TASKS ──▶ Semaphore  │
                                          ▼
                                   show_progress() ──▶ ターミナル出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| precompilation.jl | `base/precompilation.jl` | ソース | プリコンパイル機能の主実装 |
| loading.jl | `base/loading.jl` | ソース | `compilecache` 等のキャッシュ生成処理 |
| staticdata.c | `src/staticdata.c` | ソース（C） | シリアライズ・デシリアライズのランタイム実装 |
| aotcompile.cpp | `src/aotcompile.cpp` | ソース（C++） | AOTコンパイルの低レベル実装 |
| Project.toml | プロジェクトルート | 設定 | プロジェクト依存関係定義 |
| Manifest.toml | プロジェクトルート | 設定 | 完全な依存関係グラフ |
