Skip to content

Commit c025a9f

Browse files
authored
feat: added build target for dotnet clean, which now removes the generated files. (#10)
1 parent 3f0a287 commit c025a9f

6 files changed

Lines changed: 199 additions & 11 deletions

File tree

QUICKSTART.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,6 @@ dotnet build -v detailed > build.log 2>&1
272272
**Quick Fix:**
273273
```bash
274274
dotnet clean
275-
rmdir /s /q obj\efcpt
276275
dotnet build
277276
```
278277

@@ -298,7 +297,7 @@ dotnet build path\to\Database.sqlproj
298297
**Quick Fix:**
299298
```bash
300299
# Force full regeneration
301-
rmdir /s /q obj\efcpt
300+
dotnet clean
302301
dotnet build
303302
```
304303

@@ -339,12 +338,9 @@ dotnet build
339338
## Command Cheat Sheet
340339

341340
```bash
342-
# Clean build
341+
# Clean build and force regeneration
343342
dotnet clean && dotnet build
344343

345-
# Force regeneration
346-
rmdir /s /q obj\efcpt && dotnet build
347-
348344
# Detailed logging
349345
dotnet build -v detailed
350346

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ Generated models appear in `obj/efcpt/Generated/` automatically!
762762
**Solution:** Delete intermediate folder to force regeneration:
763763

764764
```bash
765-
rmdir /s /q obj\efcpt
765+
dotnet clean
766766
dotnet build
767767
```
768768

docs/user-guide/troubleshooting.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ When `EfcptDumpResolvedInputs` is `true`, check `obj/efcpt/resolved-inputs.json`
6969

7070
4. **Force regeneration:**
7171
```bash
72-
rmdir /s /q obj\efcpt
72+
dotnet clean
7373
dotnet build
7474
```
7575

@@ -177,9 +177,9 @@ When `EfcptDumpResolvedInputs` is `true`, check `obj/efcpt/resolved-inputs.json`
177177

178178
**Solutions:**
179179

180-
1. **Delete fingerprint cache:**
180+
1. **Clean and rebuild:**
181181
```bash
182-
rmdir /s /q obj\efcpt
182+
dotnet clean
183183
dotnet build
184184
```
185185

@@ -247,7 +247,7 @@ When `EfcptDumpResolvedInputs` is `true`, check `obj/efcpt/resolved-inputs.json`
247247

248248
3. **Force regeneration:**
249249
```bash
250-
rmdir /s /q obj\efcpt
250+
dotnet clean
251251
dotnet build
252252
```
253253

src/JD.Efcpt.Build/build/JD.Efcpt.Build.targets

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,12 @@
180180
</ItemGroup>
181181
</Target>
182182

183+
<!-- Clean target: remove efcpt output directory when 'dotnet clean' is run -->
184+
<Target Name="EfcptClean"
185+
AfterTargets="Clean"
186+
Condition="'$(EfcptEnabled)' == 'true'">
187+
<Message Text="Cleaning efcpt output: $(EfcptOutput)" Importance="normal" />
188+
<RemoveDir Directories="$(EfcptOutput)" Condition="Exists('$(EfcptOutput)')" />
189+
</Target>
190+
183191
</Project>

src/JD.Efcpt.Build/buildTransitive/JD.Efcpt.Build.targets

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,17 @@
262262
</ItemGroup>
263263
</Target>
264264

265+
<!--
266+
Clean target: remove efcpt output directory when 'dotnet clean' is run.
267+
268+
This integrates with the standard clean/restore/build/pack pipeline by ensuring
269+
that efcpt artifacts are properly cleaned alongside other intermediate outputs.
270+
-->
271+
<Target Name="EfcptClean"
272+
AfterTargets="Clean"
273+
Condition="'$(EfcptEnabled)' == 'true'">
274+
<Message Text="Cleaning efcpt output: $(EfcptOutput)" Importance="normal" />
275+
<RemoveDir Directories="$(EfcptOutput)" Condition="Exists('$(EfcptOutput)')" />
276+
</Target>
277+
265278
</Project>
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using JD.Efcpt.Build.Tests.Infrastructure;
2+
using TinyBDD;
3+
using TinyBDD.Xunit;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
using Task = System.Threading.Tasks.Task;
7+
8+
namespace JD.Efcpt.Build.Tests;
9+
10+
[Feature("Clean target: dotnet clean removes efcpt output directory")]
11+
[Collection(nameof(AssemblySetup))]
12+
public sealed class CleanTargetTests(ITestOutputHelper output) : TinyBddXunitBase(output)
13+
{
14+
private sealed record CleanTestContext(
15+
TestFolder Folder,
16+
string AppDir,
17+
string EfcptOutputDir) : IDisposable
18+
{
19+
public void Dispose() => Folder.Dispose();
20+
}
21+
22+
private sealed record CleanResult(
23+
CleanTestContext Context,
24+
int ExitCode,
25+
string Output,
26+
bool EfcptDirExistedBefore,
27+
bool EfcptDirExistsAfter);
28+
29+
private static CleanTestContext SetupProjectWithEfcptOutput()
30+
{
31+
var folder = new TestFolder();
32+
var appDir = folder.CreateDir("TestApp");
33+
34+
// Get the absolute path to the JD.Efcpt.Build source directory
35+
var efcptBuildRoot = Path.Combine(TestPaths.RepoRoot, "src", "JD.Efcpt.Build");
36+
37+
// Create a minimal project file that imports our targets with absolute paths
38+
var csproj = $"""
39+
<Project Sdk="Microsoft.NET.Sdk">
40+
<PropertyGroup>
41+
<TargetFramework>net8.0</TargetFramework>
42+
<Nullable>enable</Nullable>
43+
</PropertyGroup>
44+
45+
<Import Project="{efcptBuildRoot}/build/JD.Efcpt.Build.props" />
46+
47+
<PropertyGroup>
48+
<EfcptEnabled>true</EfcptEnabled>
49+
</PropertyGroup>
50+
51+
<Import Project="{efcptBuildRoot}/build/JD.Efcpt.Build.targets" />
52+
</Project>
53+
""";
54+
55+
File.WriteAllText(Path.Combine(appDir, "TestApp.csproj"), csproj);
56+
57+
// Create the efcpt output directory with sample content (simulating a previous build)
58+
var efcptOutputDir = Path.Combine(appDir, "obj", "efcpt");
59+
Directory.CreateDirectory(efcptOutputDir);
60+
61+
// Add sample files that would exist after a build
62+
File.WriteAllText(Path.Combine(efcptOutputDir, "fingerprint.txt"), "sample-fingerprint-hash");
63+
File.WriteAllText(Path.Combine(efcptOutputDir, "efcpt.stamp"), "stamp");
64+
65+
var generatedDir = Path.Combine(efcptOutputDir, "Generated");
66+
Directory.CreateDirectory(generatedDir);
67+
File.WriteAllText(Path.Combine(generatedDir, "TestContext.g.cs"), "// generated file");
68+
File.WriteAllText(Path.Combine(generatedDir, "TestModel.g.cs"), "// generated model");
69+
70+
return new CleanTestContext(folder, appDir, efcptOutputDir);
71+
}
72+
73+
private static CleanResult ExecuteDotNetClean(CleanTestContext context)
74+
{
75+
var efcptDirExistedBefore = Directory.Exists(context.EfcptOutputDir);
76+
77+
var psi = new System.Diagnostics.ProcessStartInfo
78+
{
79+
FileName = TestPaths.DotNetExe,
80+
Arguments = "clean",
81+
WorkingDirectory = context.AppDir,
82+
RedirectStandardOutput = true,
83+
RedirectStandardError = true,
84+
UseShellExecute = false,
85+
CreateNoWindow = true
86+
};
87+
88+
using var process = System.Diagnostics.Process.Start(psi)!;
89+
var stdout = process.StandardOutput.ReadToEnd();
90+
var stderr = process.StandardError.ReadToEnd();
91+
process.WaitForExit(60000);
92+
93+
var output = stdout + stderr;
94+
var efcptDirExistsAfter = Directory.Exists(context.EfcptOutputDir);
95+
96+
return new CleanResult(context, process.ExitCode, output, efcptDirExistedBefore, efcptDirExistsAfter);
97+
}
98+
99+
[Scenario("dotnet clean removes efcpt output directory")]
100+
[Fact]
101+
public Task Dotnet_clean_removes_efcpt_output_directory()
102+
=> Given("project with efcpt output directory", SetupProjectWithEfcptOutput)
103+
.Then("efcpt directory exists before clean", ctx => Directory.Exists(ctx.EfcptOutputDir))
104+
.And("efcpt directory contains files", ctx =>
105+
Directory.GetFiles(ctx.EfcptOutputDir, "*", SearchOption.AllDirectories).Length > 0)
106+
.When("run dotnet clean", ExecuteDotNetClean)
107+
.Then("clean command succeeds", r =>
108+
{
109+
if (r.ExitCode != 0)
110+
throw new InvalidOperationException($"dotnet clean failed with exit code {r.ExitCode}. Output: {r.Output}");
111+
return true;
112+
})
113+
.And("efcpt directory existed before clean", r => r.EfcptDirExistedBefore)
114+
.And("efcpt directory is removed after clean", r => !r.EfcptDirExistsAfter)
115+
.Finally(r => r.Context.Dispose())
116+
.AssertPassed();
117+
118+
[Scenario("dotnet clean succeeds when efcpt directory does not exist")]
119+
[Fact]
120+
public Task Dotnet_clean_succeeds_when_efcpt_directory_does_not_exist()
121+
=> Given("project without efcpt output directory", () =>
122+
{
123+
var ctx = SetupProjectWithEfcptOutput();
124+
// Remove the efcpt directory to simulate a fresh state
125+
if (Directory.Exists(ctx.EfcptOutputDir))
126+
Directory.Delete(ctx.EfcptOutputDir, recursive: true);
127+
return ctx;
128+
})
129+
.Then("efcpt directory does not exist", ctx => !Directory.Exists(ctx.EfcptOutputDir))
130+
.When("run dotnet clean", ExecuteDotNetClean)
131+
.Then("clean command succeeds", r => r.ExitCode == 0)
132+
.And("efcpt directory still does not exist", r => !r.EfcptDirExistsAfter)
133+
.Finally(r => r.Context.Dispose())
134+
.AssertPassed();
135+
136+
[Scenario("dotnet clean outputs message about cleaning efcpt")]
137+
[Fact]
138+
public Task Dotnet_clean_outputs_message_about_cleaning_efcpt()
139+
=> Given("project with efcpt output directory", SetupProjectWithEfcptOutput)
140+
.When("run dotnet clean with normal verbosity", ctx =>
141+
{
142+
var efcptDirExistedBefore = Directory.Exists(ctx.EfcptOutputDir);
143+
144+
var psi = new System.Diagnostics.ProcessStartInfo
145+
{
146+
FileName = TestPaths.DotNetExe,
147+
Arguments = "clean -v normal",
148+
WorkingDirectory = ctx.AppDir,
149+
RedirectStandardOutput = true,
150+
RedirectStandardError = true,
151+
UseShellExecute = false,
152+
CreateNoWindow = true
153+
};
154+
155+
using var process = System.Diagnostics.Process.Start(psi)!;
156+
var stdout = process.StandardOutput.ReadToEnd();
157+
var stderr = process.StandardError.ReadToEnd();
158+
process.WaitForExit(60000);
159+
160+
var output = stdout + stderr;
161+
var efcptDirExistsAfter = Directory.Exists(ctx.EfcptOutputDir);
162+
163+
return new CleanResult(ctx, process.ExitCode, output, efcptDirExistedBefore, efcptDirExistsAfter);
164+
})
165+
.Then("clean command succeeds", r => r.ExitCode == 0)
166+
.And("output contains efcpt cleaning message", r =>
167+
r.Output.Contains("Cleaning efcpt output", StringComparison.OrdinalIgnoreCase) ||
168+
r.Output.Contains("efcpt", StringComparison.OrdinalIgnoreCase))
169+
.Finally(r => r.Context.Dispose())
170+
.AssertPassed();
171+
}

0 commit comments

Comments
 (0)