# 機能設計書 91-アマルガメーション

## 概要

本ドキュメントは、mrubyのアマルガメーション（Amalgamation）機能の設計を記述する。アマルガメーション機能は、mrubyの全ソースファイルを単一の`mruby.c`および`mruby.h`に統合するシングルファイルビルド機能を提供する。

### 本機能の処理概要

アマルガメーション機能は、mrubyの全ヘッダファイルと全ソースファイルを依存関係順に結合し、単一のヘッダファイル（`mruby.h`）と単一のソースファイル（`mruby.c`）を生成する。これにより、SQLiteの配布モデルと同様に、たった2つのファイルだけでmrubyを任意のプロジェクトに組み込むことが可能になる。

**業務上の目的・背景**：組み込みシステムやIoTデバイスへのmruby統合において、複雑なビルドシステムの構築は大きな障壁となる。アマルガメーション機能は、ビルドシステムへの依存を排除し、標準Cコンパイラだけでmrubyをコンパイル可能にすることで、この障壁を大幅に低減する。また、単一コンパイル単位にすることでコンパイラ最適化の恩恵を最大化できる。

**機能の利用シーン**：
- 組み込みデバイスへのmruby統合時（ビルドシステムが限定的な環境）
- サードパーティプロジェクトへのmrubyライブラリ組み込み時
- CI/CDパイプラインでの簡易ビルド時
- クロスコンパイル環境での配布用バイナリ生成時

**主要な処理内容**：
1. mrubyのコアヘッダファイルを依存関係順に読み込み、インクルードガードを除去して結合し`mruby.h`を生成
2. gem由来のヘッダファイルを収集・追加
3. コアソースファイルを初期化順序に従って結合し`mruby.c`を生成
4. 生成済みファイル（mrblib.c、gem_init.c等）をソースに追加
5. gemのソースファイルをライブラリgemに限定して追加（バイナリgem除外）
6. マクロ競合の回避処理（#undef による清掃）
7. X-macroヘッダ（ops.h等）のインライン展開
8. ローカルインクルード（.cstubファイル等）のインライン化

**関連システム・外部連携**：Rakeビルドシステムとの連携。`rake amalgam`コマンドで起動される。ビルド構成（build_config.rb）に従い、含めるgemを決定する。presym（事前シンボル生成）タスクへの依存がある。

**権限による制御**：特になし。ビルドタスクとして実行される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | コマンドライン | 主画面 | `rake amalgam`コマンドで実行 |

## 機能種別

コード生成処理 / ファイル統合処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| build | MRuby::Build | Yes | ビルドターゲット情報（gems、ビルドディレクトリ等） | libmruby_enabled?がtrueであること |
| MRUBY_CONFIG | 環境変数(String) | No | ビルド構成ファイルパス | 未指定時はデフォルト構成を使用 |

### 入力データソース

- `include/` ディレクトリ配下のmrubyコアヘッダファイル群
- `src/` ディレクトリ配下のmrubyコアソースファイル群
- `build/<target>/include/` 配下の生成済みヘッダファイル（presymヘッダ等）
- `build/<target>/mrblib/mrblib.c` 生成済みRuby標準ライブラリのCソース
- `build/<target>/mrbgems/gem_init.c` gem初期化ソース
- 各gemの`include/`および`src/`（または`core/`）ディレクトリ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| mruby.h | テキストファイル(C header) | 全ヘッダを統合した単一ヘッダファイル（200-500KB） |
| mruby.c | テキストファイル(C source) | 全ソースを統合した単一ソースファイル（2-4MB） |

### 出力先

`build/<target>/amalgam/` ディレクトリに出力される。

## 処理フロー

### 処理シーケンス

```
1. Rakeタスク :amalgam が呼び出される
   └─ tasks/amalgam.rake がエントリーポイント
2. MRuby::Amalgam.new(build) でインスタンス生成
   └─ gemヘッダ名のプリコレクション (collect_gem_header_names)
3. generate_header(output_path) でmruby.h生成
   ├─ write_header_preamble: プリアンブル出力（ガード、標準ヘッダ、gem定義）
   ├─ write_ordered_headers: HEADER_ORDERに従いヘッダを順次インライン
   │   └─ write_header_content: 各ヘッダの処理
   │       ├─ extract_include_guard: インクルードガード検出
   │       ├─ strip_include_guard: ガード除去
   │       └─ transform_includes: 内部includeを再帰的にインライン
   ├─ write_gem_headers: gemのヘッダを追加
   └─ write_header_postamble: ポストアンブル出力（#endif）
4. generate_source(output_path) でmruby.c生成
   ├─ write_source_preamble: プリアンブル出力
   ├─ write_internal_headers: internal.h、presym/table.h、value_array.hを追加
   │   └─ forward declaration（前方宣言）を出力
   ├─ write_core_sources: CORE_SOURCE_ORDERに従いコアソースを結合
   │   └─ write_source_content: ソースファイル処理
   │       └─ transform_source_includes: includeの変換・コメントアウト
   ├─ write_generated_sources: mrblib.cを追加
   └─ write_gem_sources: gemソースを追加
       ├─ library_gems: バイナリgem(mruby-bin-*)を除外
       ├─ gemのsrc/またはcore/からCソースを収集
       ├─ gem_mrblib.c, gem_init.cを追加
       └─ write_macro_cleanup: マクロ競合回避（#undef）
```

### フローチャート

```mermaid
flowchart TD
    A[rake amalgam 実行] --> B{libmruby_enabled?}
    B -->|No| Z[スキップ]
    B -->|Yes| C[MRuby::Amalgam.new]
    C --> D[collect_gem_header_names]
    D --> E[generate_header - mruby.h生成]
    E --> E1[write_header_preamble]
    E1 --> E2[write_ordered_headers]
    E2 --> E3[write_gem_headers]
    E3 --> E4[write_header_postamble]
    E4 --> F[generate_source - mruby.c生成]
    F --> F1[write_source_preamble]
    F1 --> F2[write_internal_headers + forward decls]
    F2 --> F3[write_core_sources]
    F3 --> F4[write_generated_sources - mrblib.c]
    F4 --> F5[write_gem_sources]
    F5 --> G[完了メッセージ出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-91-01 | バイナリgem除外 | mruby-bin-*で始まるgemはmain()関数を持つため除外する | 常時 |
| BR-91-02 | ヘッダ依存順序 | HEADER_ORDERに定義された順序でヘッダを処理し依存関係を解決する | ヘッダ生成時 |
| BR-91-03 | ソース初期化順序 | CORE_SOURCE_ORDERに定義された順序でソースを結合する | ソース生成時 |
| BR-91-04 | インクルードガード除去 | 結合時にインクルードガードの#ifndef/#endifを除去し#defineは保持する | 各ヘッダ処理時 |
| BR-91-05 | 重複防止 | 処理済みヘッダを@processed_guardsで追跡し二重インクルードを防止する | 各ヘッダ処理時 |
| BR-91-06 | X-macroインライン | ops.h等のX-macroヘッダは毎回インライン展開する（マクロ定義により展開結果が異なるため） | ソース中のinclude処理時 |
| BR-91-07 | マクロ競合回避 | CONFLICTING_MACROSに定義されたマクロはgemソース間で#undefする | gemソース結合時 |
| BR-91-08 | gem定義収集 | MRB_USE_*、MRB_UTF8_*、HAVE_MRUBY_*パターンの定義をヘッダ先頭に追加 | ヘッダ生成時 |
| BR-91-09 | presym依存 | ヘッダ生成は:gensymタスク完了後に実行される | ヘッダ生成時 |

### 計算ロジック

特になし。

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

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

該当なし（データベースを使用しない）。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | FileNotFound | 入力ヘッダ/ソースファイルが存在しない | File.exist?チェックでスキップされる |
| - | DirectoryNotFound | 出力ディレクトリが存在しない | FileUtils.mkdir_pで自動作成 |
| - | 前方宣言不足 | ソース順序の関係で関数が定義前に呼ばれる | write_internal_headersで前方宣言を出力 |

### リトライ仕様

リトライは不要（バッチ的なコード生成処理のため）。

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

該当なし（ファイル生成処理のため）。

## パフォーマンス要件

生成処理は通常数秒以内に完了する。出力ファイルサイズはgemの数に依存し、mruby.hは200-500KB、mruby.cは2-4MBが目安である。

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

- 生成ファイルはビルド成果物であり、機密情報を含まない
- 入力はファイルシステム上のソースコードのみであり、外部入力を受け付けない

## 備考

- SQLiteのアマルガメーション配布モデルに着想を得た機能である
- ボクシングヘッダ（boxing_no.h、boxing_word.h、boxing_nan.h）は条件付きインクルードとして処理される
- ファイル出力はバイナリモード（"w:binary"）で行われ、改行コードの変換を防止する

---

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

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

### 推奨読解順序

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

アマルガメーション機能の中心となるデータ構造は `MRuby::Amalgam` クラスのインスタンス変数群である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | amalgam.rb | `lib/mruby/amalgam.rb` | HEADER_ORDER定数（6-30行目）: ヘッダの依存関係順序を定義 |
| 1-2 | amalgam.rb | `lib/mruby/amalgam.rb` | BOXING_HEADERS定数（33-37行目）: 条件付きボクシングヘッダ |
| 1-3 | amalgam.rb | `lib/mruby/amalgam.rb` | CORE_SOURCE_ORDER定数（40-74行目）: コアソースの結合順序 |
| 1-4 | amalgam.rb | `lib/mruby/amalgam.rb` | CONFLICTING_MACROS定数（353-367行目）: gem間で競合するマクロ一覧 |
| 1-5 | amalgam.rb | `lib/mruby/amalgam.rb` | XMACRO_HEADERS定数（436-438行目）: 毎回インライン展開が必要なヘッダ |

**読解のコツ**: `MRuby::Amalgam`はRubyクラスであり、`@processed_guards`（処理済みインクルードガード）、`@processed_headers`（処理済みヘッダパス）、`@gem_header_names`（gemヘッダ名一覧）の3つの追跡用インスタンス変数が状態管理の鍵である。

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

Rakeタスクがエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | amalgam.rake | `tasks/amalgam.rake` | Rakeタスク定義。:amalgamタスクの依存関係と実行フロー |

**主要処理フロー**:
1. **1行目**: `require_relative '../lib/mruby/amalgam'` でAmalgamクラスを読み込み
2. **3行目**: `MRuby.each_target do` で各ビルドターゲットに対して処理
3. **4行目**: `next unless libmruby_enabled?` でlibmruby無効なターゲットをスキップ
4. **11-14行目**: `file header_file => [:gensym]` でヘッダファイル生成タスク定義（presymに依存）
5. **23-26行目**: `file source_file => source_deps` でソースファイル生成タスク定義
6. **29-33行目**: `:amalgam`タスクがheader_fileとsource_fileに依存

#### Step 3: ヘッダ生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | amalgam.rb | `lib/mruby/amalgam.rb` | generate_header（99-111行目）: ヘッダ生成のメインメソッド |
| 3-2 | amalgam.rb | `lib/mruby/amalgam.rb` | write_header_preamble（147-182行目）: プリアンブル生成とgem定義収集 |
| 3-3 | amalgam.rb | `lib/mruby/amalgam.rb` | write_ordered_headers（201-208行目）: HEADER_ORDER順にヘッダをインライン |
| 3-4 | amalgam.rb | `lib/mruby/amalgam.rb` | write_header_content（247-265行目）: 個別ヘッダの処理（ガード除去・include変換） |
| 3-5 | amalgam.rb | `lib/mruby/amalgam.rb` | inline_header（534-566行目）: ヘッダの再帰的インライン展開 |

**主要処理フロー**:
- **247-258行目**: インクルードガードの検出と重複チェック。処理済みならスキップ
- **263行目**: `transform_includes(content, inline: true)` で内部includeを再帰インライン
- **478-480行目**: `extract_include_guard` - 正規表現で`#ifndef GUARD / #define GUARD`パターンを検出
- **485-495行目**: `strip_include_guard` - ガードの`#ifndef`と末尾`#endif`を除去、`#define`は保持

#### Step 4: ソース生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | amalgam.rb | `lib/mruby/amalgam.rb` | generate_source（113-124行目）: ソース生成のメインメソッド |
| 4-2 | amalgam.rb | `lib/mruby/amalgam.rb` | write_internal_headers（283-328行目）: 前方宣言とinternalヘッダの追加 |
| 4-3 | amalgam.rb | `lib/mruby/amalgam.rb` | write_core_sources（330-341行目）: コアソースの結合 |
| 4-4 | amalgam.rb | `lib/mruby/amalgam.rb` | write_gem_sources（369-410行目）: gemソースの結合（バイナリgem除外） |
| 4-5 | amalgam.rb | `lib/mruby/amalgam.rb` | transform_source_includes（440-474行目）: ソース中のinclude変換処理 |

**主要処理フロー**:
- **289-297行目**: 前方宣言の出力（mrb_irep_free, mrb_class_find_path等）
- **369-410行目**: gemソース処理。`library_gems`でバイナリgem除外後、src/またはcore/配下のCソースを結合
- **440-474行目**: `transform_source_includes` - mruby関連includeをコメントアウト、X-macroをインライン、ローカルincludeをインライン

#### Step 5: include変換ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | amalgam.rb | `lib/mruby/amalgam.rb` | transform_includes（497-515行目）: ヘッダ用のinclude変換 |
| 5-2 | amalgam.rb | `lib/mruby/amalgam.rb` | transform_source_includes（440-474行目）: ソース用のinclude変換 |
| 5-3 | amalgam.rb | `lib/mruby/amalgam.rb` | mruby_header?（528-531行目）: mrubyヘッダの判定 |
| 5-4 | amalgam.rb | `lib/mruby/amalgam.rb` | already_included?（517-526行目）: 処理済みヘッダの判定 |

**読解のコツ**: include変換には2種類ある。ヘッダ用（`transform_includes`）は再帰的にインライン展開し、ソース用（`transform_source_includes`）はコメントアウトまたはローカルファイルをインライン化する。X-macroヘッダは特別扱いで常にインライン展開される。

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

```
rake amalgam (tasks/amalgam.rake:29)
    |
    +-- MRuby::Amalgam.new(build) (lib/mruby/amalgam.rb:76)
    |       +-- collect_gem_header_names (84-97)
    |
    +-- amalgam.generate_header(header_file) (99-111)
    |       +-- write_header_preamble (147-182)
    |       |       +-- collect_gem_defines (185-192)
    |       +-- write_ordered_headers (201-208)
    |       |       +-- write_header_content (247-265) [x HEADER_ORDER.size]
    |       |               +-- extract_include_guard (478-483)
    |       |               +-- strip_include_guard (485-495)
    |       |               +-- transform_includes (497-515)
    |       |                       +-- inline_header (534-566) [再帰]
    |       +-- write_gem_headers (232-245)
    |       |       +-- write_header_content [x gem_headers]
    |       +-- write_header_postamble (194-199)
    |
    +-- amalgam.generate_source(source_file) (113-124)
            +-- write_source_preamble (269-281)
            +-- write_internal_headers (283-328)
            |       +-- transform_source_includes (440-474)
            +-- write_core_sources (330-341)
            |       +-- write_source_content (412-423) [x CORE_SOURCE_ORDER.size]
            |       |       +-- transform_source_includes (440-474)
            |       +-- write_macro_cleanup (425-432)
            +-- write_generated_sources (343-349)
            |       +-- write_source_content
            +-- write_gem_sources (369-410)
                    +-- library_gems (141-143)
                    +-- write_source_content [x gem_sources]
                    +-- write_macro_cleanup [x gems]
```

### データフロー図

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

include/*.h  ─────────────┐
                           ├──▶ write_ordered_headers ──────────┐
build/*/include/*.h ──────┘     + transform_includes            │
                                + inline_header (再帰)          ├──▶ build/*/amalgam/mruby.h
mrbgems/*/include/*.h ────────▶ write_gem_headers ──────────────┘

include/mruby/internal.h ────▶ write_internal_headers ─────────┐
build/*/include/presym/*.h ──┘  + forward declarations          │
                                                                 │
src/*.c ──────────────────────▶ write_core_sources ─────────────┤
                                + transform_source_includes      │
                                + write_macro_cleanup            ├──▶ build/*/amalgam/mruby.c
build/*/mrblib/mrblib.c ─────▶ write_generated_sources ────────┤
                                                                 │
mrbgems/*/src/*.c ────────────▶ write_gem_sources ──────────────┤
mrbgems/*/core/*.c ───────────┘ + library_gems (bin-* 除外)     │
build/*/mrbgems/gem_init.c ───▶ + write_macro_cleanup ──────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| amalgam.rake | `tasks/amalgam.rake` | Rakeタスク | アマルガメーションタスクのエントリーポイント |
| amalgam.rb | `lib/mruby/amalgam.rb` | ソース | アマルガメーション処理の本体（MRuby::Amalgamクラス） |
| amalgamation.md | `doc/guides/amalgamation.md` | ドキュメント | アマルガメーション機能のユーザガイド |
| Rakefile | `Rakefile` | ビルド設定 | amalgam.rakeの読み込み（40行目） |
| mruby.h | `include/mruby.h` | ヘッダ | 統合対象のメインヘッダ |
| internal.h | `include/mruby/internal.h` | ヘッダ | ソース側に統合される内部ヘッダ |
| value_array.h | `src/value_array.h` | ヘッダ | ソース側に統合される内部ヘッダ |
| presym/table.h | `build/*/include/mruby/presym/table.h` | 生成ヘッダ | 事前シンボルテーブル（生成ファイル） |
| ops.h | `include/mruby/ops.h` | ヘッダ | X-macroヘッダ（毎回インライン展開） |
| mrblib.c | `build/*/mrblib/mrblib.c` | 生成ソース | コンパイル済みRuby標準ライブラリ |
| gem_init.c | `build/*/mrbgems/gem_init.c` | 生成ソース | gem登録コード |
