Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ static int ProcessBuildResponse(BuildResponse response, out bool fallbackToNorma
{
case CompletedBuildResponse completed:
Reporter.Verbose.WriteLine("Compiler server processed compilation.");

// Check if the compilation failed with CS0006 error (metadata file not found).
// This can happen when NuGet cache is cleared and referenced DLLs (e.g., analyzers or libraries) are missing.
if (completed.ReturnCode != 0 && completed.Output.Contains("error CS0006:", StringComparison.Ordinal))
{
Reporter.Verbose.WriteLine("CS0006 error detected in fast compilation path, falling back to full MSBuild.");
Reporter.Verbose.Write(completed.Output);
fallbackToNormalBuild = true;
return completed.ReturnCode;
}

Reporter.Output.Write(completed.Output);
fallbackToNormalBuild = false;
return completed.ReturnCode;
Expand Down
10 changes: 5 additions & 5 deletions src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,6 @@ public override int Execute()

if (buildLevel is BuildLevel.Csc)
{
if (binaryLogger is not null)
{
Reporter.Output.WriteLine(CliCommandStrings.NoBinaryLogBecauseRunningJustCsc.Yellow());
}

MarkBuildStart();

// Execute CSC.
Expand All @@ -269,6 +264,11 @@ public override int Execute()
MarkBuildSuccess(cache);
}

if (binaryLogger is not null)
{
Reporter.Output.WriteLine(CliCommandStrings.NoBinaryLogBecauseRunningJustCsc.Yellow());
}

return result;
}

Expand Down
116 changes: 112 additions & 4 deletions test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3128,7 +3128,13 @@ Release config
Build(testInstance, BuildLevel.Csc);
}

private void Build(TestDirectory testInstance, BuildLevel expectedLevel, ReadOnlySpan<string> args = default, string expectedOutput = "Hello from Program", string programFileName = "Program.cs")
private void Build(
TestDirectory testInstance,
BuildLevel expectedLevel,
ReadOnlySpan<string> args = default,
string expectedOutput = "Hello from Program",
string programFileName = "Program.cs",
Func<TestCommand, TestCommand>? customizeCommand = null)
{
string prefix = expectedLevel switch
{
Expand All @@ -3138,9 +3144,15 @@ private void Build(TestDirectory testInstance, BuildLevel expectedLevel, ReadOnl
_ => throw new ArgumentOutOfRangeException(paramName: nameof(expectedLevel)),
};

new DotnetCommand(Log, ["run", programFileName, "-bl", .. args])
.WithWorkingDirectory(testInstance.Path)
.Execute()
var command = new DotnetCommand(Log, ["run", programFileName, "-bl", .. args])
.WithWorkingDirectory(testInstance.Path);

if (customizeCommand != null)
{
command = customizeCommand(command);
}

command.Execute()
.Should().Pass()
.And.HaveStdOut(prefix + expectedOutput);

Expand Down Expand Up @@ -3787,6 +3799,102 @@ public void CscOnly_AfterMSBuild_AuxiliaryFilesNotReused()
Build(testInstance, BuildLevel.Csc, expectedOutput: "v3 ");
}

/// <summary>
/// Testing <see cref="CscOnly"/> optimization when the NuGet cache is cleared between builds.
/// See <see href="https://github.com/dotnet/sdk/issues/45169"/>.
/// </summary>
[Fact]
public void CscOnly_NuGetCacheCleared()
{
var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory);

var code = """
Console.Write("v1");
""";

var programPath = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programPath, code);

var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);

var packageDir = Path.Join(testInstance.Path, "packages");
TestCommand CustomizeCommand(TestCommand command) => command.WithEnvironmentVariable("NUGET_PACKAGES", packageDir);

Assert.False(Directory.Exists(packageDir));

// Ensure the packages exist first.
Build(testInstance, BuildLevel.All, expectedOutput: "v1", customizeCommand: CustomizeCommand);

Assert.True(Directory.Exists(packageDir));

// Now clear the build outputs (but not packages) to verify CSC is used even from "first run".
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);

code = code.Replace("v1", "v2");
File.WriteAllText(programPath, code);

Build(testInstance, BuildLevel.Csc, expectedOutput: "v2", customizeCommand: CustomizeCommand);

code = code.Replace("v2", "v3");
File.WriteAllText(programPath, code);

// Clear NuGet cache.
Directory.Delete(packageDir, recursive: true);
Assert.False(Directory.Exists(packageDir));

Build(testInstance, BuildLevel.All, expectedOutput: "v3", customizeCommand: CustomizeCommand);

Assert.True(Directory.Exists(packageDir));
}

/// <summary>
/// Combination of <see cref="CscOnly_NuGetCacheCleared"/> and <see cref="CscOnly_AfterMSBuild"/>.
/// </summary>
[Fact]
public void CscOnly_AfterMSBuild_NuGetCacheCleared()
{
var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory);

var code = """
#:property PublishAot=false
#:package [email protected]
new System.CommandLine.RootCommand("v1");
Console.WriteLine("v1");
""";

var programPath = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programPath, code);

var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);

var packageDir = Path.Join(testInstance.Path, "packages");
TestCommand CustomizeCommand(TestCommand command) => command.WithEnvironmentVariable("NUGET_PACKAGES", packageDir);

Assert.False(Directory.Exists(packageDir));

Build(testInstance, BuildLevel.All, expectedOutput: "v1", customizeCommand: CustomizeCommand);

Assert.True(Directory.Exists(packageDir));

code = code.Replace("v1", "v2");
File.WriteAllText(programPath, code);

Build(testInstance, BuildLevel.Csc, expectedOutput: "v2", customizeCommand: CustomizeCommand);

code = code.Replace("v2", "v3");
File.WriteAllText(programPath, code);

// Clear NuGet cache.
Directory.Delete(packageDir, recursive: true);
Assert.False(Directory.Exists(packageDir));

Build(testInstance, BuildLevel.All, expectedOutput: "v3", customizeCommand: CustomizeCommand);

Assert.True(Directory.Exists(packageDir));
}

private static string ToJson(string s) => JsonSerializer.Serialize(s);

/// <summary>
Expand Down
Loading