# 機能設計書 51-NuGetパッケージ解決

## 概要

本ドキュメントは、F#スクリプト（.fsx）における`#r "nuget:..."`ディレクティブを介したNuGetパッケージ参照の依存関係解決機能について記述する。

### 本機能の処理概要

F#スクリプトファイル内でNuGetパッケージを直接参照し、必要なアセンブリを自動的にダウンロード・解決する機能を提供する。

**業務上の目的・背景**：F#スクリプトでは外部ライブラリを利用する際、従来はアセンブリファイルへの明示的なパス指定が必要であった。本機能により、`#r "nuget:FSharp.Data, 3.1.2"`のような記法でNuGetパッケージを直接参照可能となり、スクリプトの可搬性と開発効率が大幅に向上する。データ分析やプロトタイピング等の迅速な開発シナリオで特に有効である。

**機能の利用シーン**：
- F# InteractiveでのNuGetパッケージ利用
- .fsxスクリプトファイルでの外部ライブラリ参照
- Jupyter NotebookにおけるF#カーネルでのパッケージ参照
- CI/CDパイプラインでのスクリプト実行時のパッケージ解決

**主要な処理内容**：
1. `#r "nuget:..."` ディレクティブの解析とパッケージ参照情報の抽出
2. MSBuildプロジェクトファイルの動的生成
3. NuGet.configファイルの生成とソース設定
4. `dotnet msbuild`コマンドによるパッケージ復元と解決
5. 解決結果のキャッシュと再利用
6. アセンブリパスとネイティブ依存関係の取得

**関連システム・外部連携**：
- NuGet.org および設定されたNuGetフィード
- MSBuild/dotnet CLIツール
- ローカルキャッシュシステム（~/.packagemanagement/nuget/）

**権限による制御**：特になし（ファイルシステムアクセス権限のみ）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | F# Interactive ツールウィンドウ | 参照画面 | NuGetパッケージ参照ディレクティブの実行 |

## 機能種別

データ連携 / 依存関係解決

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| scriptDirectory | string | Yes | スクリプトファイルのディレクトリパス | 有効なディレクトリパス |
| scriptName | string | Yes | スクリプトファイル名 | - |
| scriptExt | string | Yes | スクリプト拡張子（.fsx/.csx） | .fsx または .csx |
| packageManagerTextLines | (string * string) seq | Yes | パッケージ参照ディレクティブ行のシーケンス | - |
| targetFrameworkMoniker | string | Yes | ターゲットフレームワーク（例：net8.0） | 有効なTFM形式 |
| runtimeIdentifier | string | Yes | ランタイム識別子（例：win-x64） | 有効なRID形式 |
| timeout | int | No | タイムアウト時間（ミリ秒） | 0以上の整数、-1で無制限 |

### 入力データソース

- F#スクリプトファイル内の`#r "nuget:..."` ディレクティブ
- システムのNuGet設定ファイル（nuget.config）
- 環境変数（DOTNET_HOST_PATH等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Success | bool | 解決の成功/失敗 |
| StdOut | string[] | 標準出力ログ |
| StdError | string[] | エラー出力ログ |
| Resolutions | seq<string> | 解決されたアセンブリのフルパス |
| SourceFiles | seq<string> | 生成されたスクリプトファイルパス |
| Roots | seq<string> | パッケージルートディレクトリ |

### 出力先

- メモリ内の解決結果オブジェクト（ResolveDependenciesResult）
- キャッシュディレクトリ（~/.packagemanagement/nuget/Cache/）
- 一時プロジェクトディレクトリ

## 処理フロー

### 処理シーケンス

```
1. パッケージ参照ディレクティブの解析
   └─ parsePackageDirective関数でディレクティブ行を解析
   └─ PackageReference構造体のリストを生成

2. 解決結果のキャッシュチェック
   └─ computeHashForResolutionInputsでハッシュ計算
   └─ キャッシュディレクトリから既存結果を検索

3. MSBuildプロジェクトファイルの生成
   └─ generateProjectFileテンプレートを使用
   └─ ターゲットフレームワーク、RID、パッケージ参照を埋め込み

4. NuGet.configファイルの生成
   └─ dotnet nuget list sourceでソース一覧取得
   └─ generateProjectNugetConfigFileテンプレートを使用

5. パッケージ復元の実行
   └─ dotnet msbuild -restore コマンド実行
   └─ InteractivePackageManagementターゲットの実行

6. 解決結果の収集
   └─ .resolvedReferences.pathsファイルの解析
   └─ アセンブリパス、ネイティブパス、初期化スクリプトの抽出

7. 結果のキャッシュと返却
   └─ ハッシュキーでキャッシュに保存
   └─ ResolveDependenciesResultオブジェクトを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: #r nuget ディレクティブ] --> B[ディレクティブ解析]
    B --> C{キャッシュ存在?}
    C -->|Yes| D[キャッシュから結果取得]
    C -->|No| E[プロジェクトファイル生成]
    E --> F[NuGet.config生成]
    F --> G[dotnet msbuild実行]
    G --> H{成功?}
    H -->|Yes| I[解決結果解析]
    H -->|No| J[エラー結果返却]
    I --> K[キャッシュに保存]
    K --> L[結果返却]
    D --> L
    J --> L
    L --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | 禁止パッケージ | mscorlib、System.ValueTuple、NETStandard.Library、Microsoft.NETFramework.ReferenceAssemblies は参照不可 | 常時 |
| BR-51-02 | FSharp.Core制限 | .fsxスクリプトではFSharp.Coreパッケージの参照は不可 | scriptExt = ".fsx" |
| BR-51-03 | ワイルドカードキャッシュ | バージョンにワイルドカード（*）を含む場合はキャッシュ不可 | バージョン指定時 |
| BR-51-04 | タイムアウト | デフォルトタイムアウトはtimeoutパラメータまたは-1（無制限） | パッケージ解決時 |

### 計算ロジック

**キャッシュハッシュ計算**:
- 入力: TFM、RID、パッケージ参照テキスト、スクリプト拡張子
- アルゴリズム: SHA256
- 出力: 16進数文字列

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

本機能はデータベースを使用しない。ファイルシステムベースのキャッシュを使用する。

### ファイルシステム操作

| 操作 | 対象パス | 操作種別 | 概要 |
|-----|---------|---------|------|
| プロジェクト生成 | {projectDirectory}/Project.fsproj | CREATE | 一時MSBuildプロジェクト |
| NuGet設定生成 | {projectDirectory}/NuGet.config | CREATE | NuGetソース設定 |
| 解決結果 | {projectPath}.resolvedReferences.paths | READ | パッケージ解決結果 |
| キャッシュ保存 | {cacheDirectory}/{hash}.resolvedReferences.paths | CREATE | 解決結果のキャッシュ |
| スクリプト生成 | {cacheDirectory}/{hash}.fsx | CREATE | 参照スクリプト |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentException | 禁止パッケージの参照 | パッケージ名を確認 |
| - | ArgumentException | 必須パラメータ不足 | パラメータを正しく指定 |
| - | TimeoutException | パッケージ解決がタイムアウト | タイムアウト値を延長 |
| - | Exception | ソースディレクトリが存在しない | RestoreSourcesパスを確認 |
| - | InvalidOperationException | 解決結果ファイル形式不正 | キャッシュをクリア |

### リトライ仕様

本機能にリトライ機構はない。エラー時は即座に失敗を返却する。

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

ファイルシステム操作のため、トランザクション管理なし。プロセス終了時に一時ディレクトリを削除（DEBUG以外）。

## パフォーマンス要件

- キャッシュヒット時: 即座に結果返却
- 初回解決時: MSBuildプロセス起動のため数秒〜数十秒
- タイムアウトデフォルト: 呼び出し元で指定

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

- NuGetソースの信頼性は利用者の責任
- パッケージのダウンロードはHTTPS推奨
- 署名付きパッケージの検証はNuGet側で実施
- ローカルキャッシュはユーザープロファイル配下に保存

## 備考

- デバッグビルド時は一時ディレクトリを削除しない
- 環境変数`FSharp_NoStableFileHeuristic`でキャッシュ動作を制御可能

---

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

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

### 推奨読解順序

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

まず、パッケージ参照と解決結果のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | FSharp.DependencyManager.Utilities.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs` | Resolution型（16-26行目）でパッケージ解決結果の構造を理解 |
| 1-2 | FSharp.DependencyManager.ProjectFile.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs` | PackageReference型（9-16行目）でパッケージ参照の構造を理解 |
| 1-3 | FSharp.DependencyManager.Utilities.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs` | PackageBuildResolutionResult型（29-40行目）でビルド結果の構造を理解 |

**読解のコツ**: F#のレコード型は構造体的なデータを表現する。中括弧{}で囲まれたフィールド定義に注目。

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

処理の起点となるFSharpDependencyManagerクラスを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | FSharp.DependencyManager.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs` | FSharpDependencyManagerクラス（306-577行目）が主要なエントリーポイント |

**主要処理フロー**:
1. **502-511行目**: ResolveDependenciesメソッドのシグネチャと入力パラメータ
2. **521-528行目**: computeHashForResolutionInputsでキャッシュキー計算
3. **530-543行目**: キャッシュチェックとprepareDependencyResolutionFilesの呼び出し
4. **545-575行目**: 解決結果の処理とキャッシュ保存

#### Step 3: パッケージ参照解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | FSharp.DependencyManager.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs` | parsePackageReferenceOption関数（91-203行目）でディレクティブ解析ロジック |

**主要処理フロー**:
- **97-99行目**: 禁止パッケージの検証
- **121-130行目**: Includeオプションの処理
- **132-135行目**: バージョンオプションの処理
- **173-190行目**: バイナリログとタイムアウトオプションの処理

#### Step 4: プロジェクト生成とビルドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | FSharp.DependencyManager.ProjectFile.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs` | generateProjectFile（46-199行目）でMSBuildプロジェクトテンプレート |
| 4-2 | FSharp.DependencyManager.Utilities.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs` | buildProject関数（220-284行目）でmsbuild実行ロジック |
| 4-3 | FSharp.DependencyManager.Utilities.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs` | executeTool関数（170-218行目）で外部プロセス実行 |

**主要処理フロー**:
- **46-69行目**: プロジェクトテンプレートのPropertyGroup
- **173-196行目**: InteractivePackageManagementターゲット定義
- **237-242行目**: msbuildコマンドライン構築
- **259-272行目**: 解決結果ファイルの読み取り

#### Step 5: キャッシュ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | FSharp.DependencyManager.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs` | computeHashForResolutionInputs関数（227-272行目）でキャッシュキー生成 |
| 5-2 | FSharp.DependencyManager.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs` | tryGetResultsForResolutionHash関数（444-475行目）でキャッシュ取得 |

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

```
FSharpDependencyManager.ResolveDependencies
    │
    ├─ FSharpDependencyManager.computeHashForResolutionInputs
    │      └─ Utilities.computeSha256HashOfBytes
    │
    ├─ tryGetResultsForResolutionHash (キャッシュチェック)
    │      └─ Utilities.getResolutionsFromFile
    │
    └─ prepareDependencyResolutionFiles
           │
           ├─ FSharpDependencyManager.parsePackageDirective
           │      └─ parsePackageReferenceOption
           │
           ├─ FSharpDependencyManager.formatPackageReference
           │
           ├─ Utilities.generateSourcesFromNugetConfigs
           │      └─ executeTool (dotnet nuget list source)
           │
           └─ Utilities.buildProject
                  │
                  ├─ executeTool (dotnet msbuild)
                  │
                  ├─ getResolutionsFromFile
                  │
                  ├─ findReferencesFromResolutions
                  │
                  ├─ findLoadsFromResolutions
                  │
                  └─ findIncludesFromResolutions
```

### データフロー図

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

#r "nuget:FSharp.Data"  ──▶  parsePackageReferenceOption  ──▶  PackageReference
        │
        ▼
PackageReference list   ──▶  formatPackageReference      ──▶  XML文字列
        │
        ▼
XML + TFM + RID        ──▶  generateProjectFile          ──▶  Project.fsproj
        │
        ▼
Project.fsproj         ──▶  buildProject (msbuild)       ──▶  .resolvedReferences.paths
        │
        ▼
resolvedReferences     ──▶  getResolutionsFromFile       ──▶  Resolution[]
        │
        ▼
Resolution[]           ──▶  findReferencesFromResolutions ──▶  string list (DLLパス)
        │
        ▼
すべての結果           ──▶  ResolveDependenciesResult    ──▶  呼び出し元
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| FSharp.DependencyManager.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs` | ソース | メインロジック、エントリーポイント |
| FSharp.DependencyManager.Utilities.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs` | ソース | ユーティリティ関数、外部プロセス実行 |
| FSharp.DependencyManager.ProjectFile.fs | `src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs` | ソース | プロジェクトファイルテンプレート |
