Skip to content

Commit e007c6c

Browse files
committed
機能追加: IL比較で指定文字列を含む行を無視する設定を導入
- ConfigSettings/config.json に ShouldIgnoreILLinesContainingConfiguredStrings と ILIgnoreLineContainingStrings を追加 - IL比較で MVID 行に加え、設定文字列を含む行を部分一致で除外 - 設定有効時は diff_report.md ヘッダに無視ルールを明記 - 関連テストを追加・更新
1 parent f868060 commit e007c6c

13 files changed

Lines changed: 246 additions & 18 deletions

FolderDiffIL4DotNet.Tests/Models/ConfigSettingsTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public void Constructor_DefaultDiskCacheLimits_Are1000And512()
1313

1414
Assert.Equal(1000, config.ILCacheMaxDiskFileCount);
1515
Assert.Equal(512, config.ILCacheMaxDiskMegabytes);
16+
Assert.False(config.ShouldIgnoreILLinesContainingConfiguredStrings);
17+
Assert.NotNull(config.ILIgnoreLineContainingStrings);
18+
Assert.Empty(config.ILIgnoreLineContainingStrings);
1619
}
1720

1821
[Fact]
@@ -22,6 +25,9 @@ public void JsonDeserialize_MissingDiskCacheLimits_UsesDefaults()
2225
Assert.NotNull(config);
2326
Assert.Equal(1000, config.ILCacheMaxDiskFileCount);
2427
Assert.Equal(512, config.ILCacheMaxDiskMegabytes);
28+
Assert.False(config.ShouldIgnoreILLinesContainingConfiguredStrings);
29+
Assert.NotNull(config.ILIgnoreLineContainingStrings);
30+
Assert.Empty(config.ILIgnoreLineContainingStrings);
2531
}
2632

2733
[Fact]
@@ -33,5 +39,15 @@ public void JsonDeserialize_ExplicitZeroDiskCacheLimits_KeepsZero()
3339
Assert.Equal(0, config.ILCacheMaxDiskFileCount);
3440
Assert.Equal(0, config.ILCacheMaxDiskMegabytes);
3541
}
42+
43+
[Fact]
44+
public void JsonDeserialize_IlIgnoreContainsSettings_AreApplied()
45+
{
46+
var json = "{\"ShouldIgnoreILLinesContainingConfiguredStrings\":true,\"ILIgnoreLineContainingStrings\":[\"buildserver\",\"path\"]}";
47+
var config = JsonSerializer.Deserialize<ConfigSettings>(json);
48+
Assert.NotNull(config);
49+
Assert.True(config.ShouldIgnoreILLinesContainingConfiguredStrings);
50+
Assert.Equal(new[] { "buildserver", "path" }, config.ILIgnoreLineContainingStrings);
51+
}
3652
}
3753
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Collections.Generic;
2+
using System.Reflection;
3+
using FolderDiffIL4DotNet.Models;
4+
using FolderDiffIL4DotNet.Services;
5+
using Xunit;
6+
7+
namespace FolderDiffIL4DotNet.Tests.Services
8+
{
9+
public sealed class ILOutputServiceTests
10+
{
11+
[Fact]
12+
public void ShouldExcludeIlLine_MvidPrefix_IsAlwaysExcluded()
13+
{
14+
var result = InvokeShouldExcludeIlLine("// MVID: 1234", shouldIgnoreContainingStrings: false, new List<string>());
15+
Assert.True(result);
16+
}
17+
18+
[Fact]
19+
public void ShouldExcludeIlLine_ContainsConfiguredString_ExcludedOnlyWhenEnabled()
20+
{
21+
var line = ".custom instance void [buildserver] Foo::Bar()";
22+
var targets = new List<string> { "buildserver" };
23+
24+
Assert.True(InvokeShouldExcludeIlLine(line, shouldIgnoreContainingStrings: true, targets));
25+
Assert.False(InvokeShouldExcludeIlLine(line, shouldIgnoreContainingStrings: false, targets));
26+
}
27+
28+
[Fact]
29+
public void GetNormalizedIlIgnoreContainingStrings_RemovesEmptyTrimAndDuplicates()
30+
{
31+
var config = new ConfigSettings
32+
{
33+
ILIgnoreLineContainingStrings = new List<string> { "buildserver", " buildpath ", "", "buildserver", " " }
34+
};
35+
36+
var result = InvokeGetNormalizedIlIgnoreContainingStrings(config);
37+
38+
Assert.Equal(new[] { "buildserver", "buildpath" }, result);
39+
}
40+
41+
private static bool InvokeShouldExcludeIlLine(string line, bool shouldIgnoreContainingStrings, IReadOnlyCollection<string> ilIgnoreContainingStrings)
42+
{
43+
var method = typeof(ILOutputService).GetMethod("ShouldExcludeIlLine", BindingFlags.Static | BindingFlags.NonPublic);
44+
Assert.NotNull(method);
45+
var result = method.Invoke(null, new object[] { line, shouldIgnoreContainingStrings, ilIgnoreContainingStrings });
46+
return Assert.IsType<bool>(result);
47+
}
48+
49+
private static List<string> InvokeGetNormalizedIlIgnoreContainingStrings(ConfigSettings config)
50+
{
51+
var method = typeof(ILOutputService).GetMethod("GetNormalizedIlIgnoreContainingStrings", BindingFlags.Static | BindingFlags.NonPublic);
52+
Assert.NotNull(method);
53+
var result = method.Invoke(null, new object[] { config });
54+
return Assert.IsType<List<string>>(result);
55+
}
56+
}
57+
}

FolderDiffIL4DotNet.Tests/Services/ReportGenerateServiceTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,64 @@ public void GenerateDiffReport_IlDiffDetailsIncludeDisassemblerLabel()
122122
Assert.Contains("`dotnet-ildasm (version: dotnet ildasm 0.12.0)`", reportText);
123123
}
124124

125+
[Fact]
126+
public void GenerateDiffReport_HeaderShowsIlContainsIgnoreNote_WhenEnabled()
127+
{
128+
var oldDir = Path.Combine(_rootDir, "old-ignore-note");
129+
var newDir = Path.Combine(_rootDir, "new-ignore-note");
130+
var reportDir = Path.Combine(_rootDir, "report-ignore-note");
131+
Directory.CreateDirectory(oldDir);
132+
Directory.CreateDirectory(newDir);
133+
Directory.CreateDirectory(reportDir);
134+
135+
var config = CreateConfig();
136+
config.ShouldIgnoreILLinesContainingConfiguredStrings = true;
137+
config.ILIgnoreLineContainingStrings = new List<string> { "buildserver", " buildPath ", "", "buildserver" };
138+
139+
new FolderDiffIL4DotNet.Services.ReportGenerateService().GenerateDiffReport(
140+
oldDir,
141+
newDir,
142+
reportDir,
143+
appVersion: "test",
144+
elapsedTimeString: "00:00:01.000",
145+
computerName: "test-host",
146+
config);
147+
148+
var reportPath = Path.Combine(reportDir, "diff_report.md");
149+
var reportText = File.ReadAllText(reportPath);
150+
Assert.Contains("lines containing any of the configured strings are ignored", reportText);
151+
Assert.Contains("\"buildserver\"", reportText);
152+
Assert.Contains("\"buildPath\"", reportText);
153+
}
154+
155+
[Fact]
156+
public void GenerateDiffReport_HeaderOmitsIlContainsIgnoreNote_WhenDisabled()
157+
{
158+
var oldDir = Path.Combine(_rootDir, "old-ignore-note-off");
159+
var newDir = Path.Combine(_rootDir, "new-ignore-note-off");
160+
var reportDir = Path.Combine(_rootDir, "report-ignore-note-off");
161+
Directory.CreateDirectory(oldDir);
162+
Directory.CreateDirectory(newDir);
163+
Directory.CreateDirectory(reportDir);
164+
165+
var config = CreateConfig();
166+
config.ShouldIgnoreILLinesContainingConfiguredStrings = false;
167+
config.ILIgnoreLineContainingStrings = new List<string> { "buildserver" };
168+
169+
new FolderDiffIL4DotNet.Services.ReportGenerateService().GenerateDiffReport(
170+
oldDir,
171+
newDir,
172+
reportDir,
173+
appVersion: "test",
174+
elapsedTimeString: "00:00:01.000",
175+
computerName: "test-host",
176+
config);
177+
178+
var reportPath = Path.Combine(reportDir, "diff_report.md");
179+
var reportText = File.ReadAllText(reportPath);
180+
Assert.DoesNotContain("lines containing any of the configured strings are ignored", reportText);
181+
}
182+
125183
private static ConfigSettings CreateConfig() => new()
126184
{
127185
IgnoredExtensions = new List<string>(),

Models/ConfigSettings.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ public sealed class ConfigSettings
3939
/// </summary>
4040
public bool ShouldOutputILText { get; set; }
4141

42+
/// <summary>
43+
/// IL 比較時に、指定文字列を「含む」行を無視するかどうか。
44+
/// </summary>
45+
public bool ShouldIgnoreILLinesContainingConfiguredStrings { get; set; }
46+
47+
/// <summary>
48+
/// IL 比較時に無視対象とする文字列リスト(部分一致、複数指定可)。
49+
/// </summary>
50+
public List<string> ILIgnoreLineContainingStrings { get; set; } = new();
51+
4252
/// <summary>
4353
/// ファイルごとの更新日時をレポートに出力するか否か
4454
/// </summary>

README.en.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# FolderDiffIL4DotNet (English)
22

3-
This repository hosts a .NET console application that compares two folders, classifies the differences, and writes a detailed Markdown report. When both inputs are .NET assemblies, the app ignores build-specific artifacts such as the `// MVID:` line, so assemblies that behave the same are treated as equal even if they were produced at different times.
3+
This repository hosts a .NET console application that compares two folders, classifies the differences, and writes a detailed Markdown report. When both inputs are .NET assemblies, the app ignores build-specific artifacts such as the `// MVID:` line, so assemblies that behave the same are treated as equal even if they were produced at different times. You can also configure additional "contains" filters to ignore lines that include specific strings (for example `buildserver`).
44

55
> Looking for the Japanese version? See [README.md](README.md).
66
@@ -75,12 +75,13 @@ Relation to CI:
7575
- For `ILMatch` / `ILMismatch`, the report also includes the disassembler tool and version used (including cache hits).
7676
- Summarizes counts per bucket in the same report.
7777
- The report header lists only the disassembler labels actually observed during IL comparison (or `N/A` when not used).
78+
- If `ShouldIgnoreILLinesContainingConfiguredStrings` is enabled, the report header explicitly notes that lines containing values from `ILIgnoreLineContainingStrings` are ignored.
7879
- Optionally writes ignored files, unchanged files, timestamps, and warnings when at least one `MD5Mismatch` exists.
7980

8081
## File comparison flow
8182

8283
1. **MD5 hash** – if hashes match, the file is `Unchanged (MD5Match)`.
83-
2. **IL diff** – if the file is a .NET assembly (detected via PE/CLR headers, regardless of extension), the app disassembles both versions with the same disassembler/version identity, strips `// MVID:` lines, and compares them line by line. Matches become `Unchanged (ILMatch)`; mismatches become `Modified (ILMismatch)`.
84+
2. **IL diff** – if the file is a .NET assembly (detected via PE/CLR headers, regardless of extension), the app disassembles both versions with the same disassembler/version identity, strips `// MVID:` lines, and compares them line by line. If `ShouldIgnoreILLinesContainingConfiguredStrings` is enabled, lines containing any entry in `ILIgnoreLineContainingStrings` are also ignored (substring match). Matches become `Unchanged (ILMatch)`; mismatches become `Modified (ILMismatch)`.
8485
3. **Text diff** – if the extension appears in `TextFileExtensions`, a line-based text diff runs. Matches are `Unchanged (TextMatch)`; mismatches are `Modified (TextMismatch)`.
8586
4. **Fallback** – remaining files are treated as `Modified (MD5Mismatch)`.
8687

@@ -169,6 +170,8 @@ Place `config.json` next to the executable. Example:
169170
"ShouldIncludeUnchangedFiles": true,
170171
"ShouldIncludeIgnoredFiles": true,
171172
"ShouldOutputILText": true,
173+
"ShouldIgnoreILLinesContainingConfiguredStrings": false,
174+
"ILIgnoreLineContainingStrings": [],
172175
"ShouldOutputFileTimestamps": true,
173176
"MaxParallelism": 0,
174177
"EnableILCache": true,
@@ -189,6 +192,8 @@ Place `config.json` next to the executable. Example:
189192
| `ShouldIncludeUnchangedFiles` | Whether to list `Unchanged` files inside `Reports/<label>/diff_report.md`. |
190193
| `ShouldIncludeIgnoredFiles` | Whether to output ignored files in the `## [ x ] Ignored Files` section (before `Unchanged`). |
191194
| `ShouldOutputILText` | Writes IL dumps to `Reports/<label>/IL/old` and `.../IL/new`. |
195+
| `ShouldIgnoreILLinesContainingConfiguredStrings` | Whether to ignore IL lines that contain any configured string in `ILIgnoreLineContainingStrings`. |
196+
| `ILIgnoreLineContainingStrings` | List of strings used for IL line-ignore filtering (substring match, multiple values allowed), e.g. `buildserver`. |
192197
| `ShouldOutputFileTimestamps` | Adds last modified timestamps to each file line inside the report. |
193198
| `MaxParallelism` | Degree of parallelism for file comparisons. `0` or omitted uses the logical core count. |
194199
| `EnableILCache` | Caches IL disassembly results (MD5 + tool/version) in memory and optionally on disk. |
@@ -238,6 +243,7 @@ After writing the report, the following files are marked read-only (failures onl
238243
- `Reports/<label>/IL/old/*.txt` – IL dumps (build-specific noise removed) for files from the old folder.
239244
- `Reports/<label>/IL/new/*.txt` – IL dumps for the new folder.
240245
- IL dumps exclude lines that start with `// MVID:`.
246+
- If `ShouldIgnoreILLinesContainingConfiguredStrings` is `true`, IL dumps also exclude lines containing any value from `ILIgnoreLineContainingStrings`.
241247

242248
## Performance optimizations
243249

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# FolderDiffIL4DotNet
22

3-
2つのフォルダの差分をレポート出力するコンソールアプリケーションです。.NET アセンブリに関してはビルド固有情報(例: MVID)が存在する場合はこれを除外して IL 比較するため、ビルド日時が異なっていても実質同じ挙動であれば同一と判定します。
3+
2つのフォルダの差分をレポート出力するコンソールアプリケーションです。.NET アセンブリに関してはビルド固有情報(例: MVID)が存在する場合はこれを除外して IL 比較するため、ビルド日時が異なっていても実質同じ挙動であれば同一と判定します。加えて、設定により「指定文字列を含む行」を IL 比較時に無視できます。
44

55
> Need this document in English? See [README.en.md](README.en.md).
66
@@ -88,6 +88,7 @@ CI との関係:
8888
- IgnoredExtensions対象のファイル一覧は、`config.json`のShouldIncludeIgnoredFilesが `true` の場合に `## [ x ] Ignored Files` として Unchanged の直前に出力されます。
8989
- `MD5Mismatch` が 1 件以上存在する場合は、標準出力と `diff_report.md` の Summary 直下に警告を表示し、MD5 ハッシュ比較しか行えず、かつ不一致と判定されたファイルがある旨を明確に示します。
9090
- レポート冒頭の `IL Disassembler` には、実際に IL 比較で使用された逆アセンブラ(ツール名/バージョン)のみを出力します(未使用時は `N/A`)。
91+
- `ShouldIgnoreILLinesContainingConfiguredStrings``true` の場合、レポート冒頭に「`ILIgnoreLineContainingStrings` のいずれかを含む行を無視している」旨を出力します。
9192

9293
## ファイル比較フロー
9394

@@ -97,7 +98,8 @@ CI との関係:
9798
2) .NET アセンブリであれば(拡張子に依存せず、PE/CLR ヘッダで判定)IL に逆アセンブルして比較します。
9899
- .NET アセンブリの判定は PE32(32bit)とPE32+(64bit)の両方に対応し、DnSpyなどの逆アセンブラで処理可能な全ての.NETファイルを正しく検出します(VB.NET、C#、F#などの言語に関係なく、CLRランタイムヘッダが存在するファイルを判定)
99100
- IL 比較時は old/new を同一逆アセンブラ(同一バージョン識別)で逆アセンブルします。異なるツール/バージョンの組み合わせ比較は行いません。
100-
- 行単位の比較(IL出力中の「`// MVID:`」で始まる行があった場合はこれを無視します。)
101+
- 行単位の比較(IL出力中の「`// MVID:`」で始まる行は常に無視します。)
102+
- `config.json` の `ShouldIgnoreILLinesContainingConfiguredStrings` が `true` の場合、`ILIgnoreLineContainingStrings` のいずれかの文字列を「含む」行も無視します(前方一致ではなく部分一致)。
101103
- ビルド日時などビルド固有情報の差異を無視して比較することで、実質同じ挙動のアセンブリはビルド日時が異なっていても同一と判定できます。
102104
- 逆アセンブルに`dotnet-ildasm`を使用した場合、IL 先頭付近に「// MVID: {GUID}」が出力されることが多い一方、`ilspycmd`を使用した場合は出力されません。
103105
- 一致ならばUnchanged, ILMatch、不一致ならばModified, ILMismatchと判定し次のファイル比較へ
@@ -192,6 +194,8 @@ CI との関係:
192194
"ShouldIncludeUnchangedFiles": true,
193195
"ShouldIncludeIgnoredFiles": true,
194196
"ShouldOutputILText": true,
197+
"ShouldIgnoreILLinesContainingConfiguredStrings": false,
198+
"ILIgnoreLineContainingStrings": [],
195199
"ShouldOutputFileTimestamps": true,
196200
"MaxParallelism": 0,
197201
"EnableILCache": true,
@@ -212,6 +216,8 @@ CI との関係:
212216
| ShouldIncludeUnchangedFiles | `Reports/<コマンドライン第3引数に指定したレポートのラベル>/diff_report.md`にUnchangedのファイル一覧を含めるか否か。 |
213217
| ShouldIncludeIgnoredFiles | IgnoredExtensions に該当して比較対象から除外されたファイルを `diff_report.md``## [ x ] Ignored Files` セクション(Unchanged の直前)に出力するか否か。 |
214218
| ShouldOutputILText | `Reports/<コマンドライン第3引数に指定したレポートのラベル>/IL/old, new`にIL全文を出力するか否か。 |
219+
| ShouldIgnoreILLinesContainingConfiguredStrings | IL 比較時に、`ILIgnoreLineContainingStrings` のいずれかを含む行を無視するか否か。 |
220+
| ILIgnoreLineContainingStrings | IL 比較時に無視したい文字列のリスト(部分一致、複数指定可)。例: `"buildserver"`|
215221
| ShouldOutputFileTimestamps | `diff_report.md` の各ファイル行に最終更新日時を併記するか否か( `true` で併記)。 |
216222
| MaxParallelism | ファイル比較の並列度。0 または未指定で論理コア数、自動判定。1 で逐次実行。 |
217223
| EnableILCache | IL 逆アセンブル結果(MD5 + ツール / バージョン単位)をメモリ & 任意ディスクにキャッシュし再実行時の逆アセンブルをスキップ。 |
@@ -255,6 +261,7 @@ dotnet run "/Users/UserA/workspace/old" "/Users/UserA/workspace/new" "YYYYMMDD"
255261
- `Reports/<コマンドライン第3引数に指定したレポートのラベル>/IL/old/*.txt` … 旧バージョン側(比較元)ファイルのビルド固有情報を除く IL 全文を出力(ファイル名称は相対パスの区切り文字を.に置換したもの)
256262
- `Reports/<コマンドライン第3引数に指定したレポートのラベル>/IL/new/*.txt` … 新バージョン側(比較先)ファイルのビルド固有情報を除く IL 全文を出力(ファイル名称は相対パスの区切り文字を.に置換したもの)
257263
- 出力されるIL 全文は「`// MVID:`」で始まる行を除外しています。
264+
- `ShouldIgnoreILLinesContainingConfiguredStrings` が `true` の場合は、`ILIgnoreLineContainingStrings` のいずれかを含む行も除外します。
258265

259266
## パフォーマンス最適化機能
260267

Services/FileDiffService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public async Task<bool> FilesAreEqualAsync(string fileRelativePath, int maxParal
100100
return true;
101101
}
102102

103-
// 2) .NET アセンブリなら IL: IL 比較は MVID 行の除外などアセンブリ固有処理を伴うため別サービスに委譲
103+
// 2) .NET アセンブリなら IL: IL 比較は行除外(MVID や設定文字列)などアセンブリ固有処理を伴うため別サービスに委譲
104104
if (DotNetDetector.IsDotNetExecutable(file1AbsolutePath))
105105
{
106106
try

0 commit comments

Comments
 (0)