Skip to content
Draft
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
3 changes: 3 additions & 0 deletions documentation/general/dotnet-run-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ Additionally, the implicit project file has the following customizations:
This avoids including files like `./**/*.resx` in simple file-based apps where users usually don't expect that.
See [multiple files](#multiple-files) for more details.

- `AssemblyName` is set to the entry-point file name without extension (e.g., `Program` for `Program.cs`)
to ensure the binary name is not affected by the virtual project file name (which includes the `.cs` extension).

## Grow up

When file-based programs reach an inflection point where build customizations in a project file are needed,
Expand Down
4 changes: 3 additions & 1 deletion src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public override RunApiOutput Execute()
builder.CreateProjectInstance(
new ProjectCollection(),
errorReporter,
out _,
out var project,
out var evaluatedDirectives,
validateAllDirectives: true);

Expand All @@ -91,6 +91,7 @@ public override RunApiOutput Execute()
return new RunApiOutput.Project
{
Content = csprojWriter.ToString(),
ProjectPath = project.FullPath,
Diagnostics = diagnostics.ToImmutableArray(),
};
}
Expand Down Expand Up @@ -163,6 +164,7 @@ public sealed class Error : RunApiOutput
public sealed class Project : RunApiOutput
{
public required string Content { get; init; }
public required string ProjectPath { get; init; }
public required ImmutableArray<SimpleDiagnostic> Diagnostics { get; init; }
}

Expand Down
6 changes: 4 additions & 2 deletions src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ internal static string GetArtifactsPath(string entryPointFileFullPath)
}

public static string GetVirtualProjectPath(string entryPointFilePath)
=> Path.ChangeExtension(entryPointFilePath, ".csproj");
=> entryPointFilePath + ".csproj";

/// <summary>
/// Obtains a temporary subdirectory for file-based app artifacts, e.g., <c>/tmp/dotnet/runfile/</c>.
Expand Down Expand Up @@ -375,7 +375,7 @@ ProjectRootElement CreateProjectRootElement(string projectFileText, ProjectColle
using var reader = new StringReader(projectFileText);
using var xmlReader = XmlReader.Create(reader);
var projectRoot = ProjectRootElement.Create(xmlReader, projectCollection);
projectRoot.FullPath = Path.ChangeExtension(EntryPointFileFullPath, ".csproj");
projectRoot.FullPath = GetVirtualProjectPath(EntryPointFileFullPath);
return projectRoot;
}
}
Expand Down Expand Up @@ -465,6 +465,7 @@ internal static void WriteProjectFile(
if (isVirtualProject)
{
Debug.Assert(!string.IsNullOrWhiteSpace(artifactsPath));
Debug.Assert(entryPointFilePath is not null);

// Note that ArtifactsPath needs to be specified before Sdk.props
// (usually it's recommended to specify it in Directory.Build.props
Expand All @@ -481,6 +482,7 @@ internal static void WriteProjectFile(
<FileBasedProgramsItemMapping>{CSharpDirective.IncludeOrExclude.DefaultMappingString}</FileBasedProgramsItemMapping>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder>
<AssemblyName>{EscapeValue(Path.GetFileNameWithoutExtension(entryPointFilePath))}</AssemblyName>
""");

// Only set these to false when using the default SDK with no additional SDKs
Expand Down
33 changes: 29 additions & 4 deletions test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,23 @@ public void EmbeddedResource_AlongsideProj([CombinatorialValues("sln", "slnx", "
.And.HaveStdOut(considered ? "Resource not found" : "[MyString, TestValue]");
}

[Fact]
public void Restore_NonExistentPackage()
{
var testInstance = _testAssetsManager.CreateTestDirectory();
var programFile = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programFile, """
#:package Microsoft.ThisPackageDoesNotExist@1.0.0
Console.WriteLine();
""");

new DotnetCommand(Log, "restore", "Program.cs")
.WithWorkingDirectory(testInstance.Path)
.Execute()
.Should().Fail()
.And.HaveStdOutContaining("Program.cs.csproj : error NU1101");
}

[Fact]
public void NoRestore_01()
{
Expand Down Expand Up @@ -5520,6 +5537,7 @@ public void Api()
Console.WriteLine();
""");

var projectPath = VirtualProjectBuilder.GetVirtualProjectPath(programPath);
new DotnetCommand(Log, "run-api")
.WithStandardInput($$"""
{"$type":"GetProject","EntryPointFileFullPath":{{ToJson(programPath)}},"ArtifactsPath":"/artifacts"}
Expand All @@ -5539,6 +5557,7 @@ public void Api()
<FileBasedProgramsItemMapping>.cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content</FileBasedProgramsItemMapping>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder>
<AssemblyName>Program</AssemblyName>
<OutputType>Exe</OutputType>
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down Expand Up @@ -5579,7 +5598,7 @@ public void Api()

</Project>

""")}},"Diagnostics":[]}
""")}},"ProjectPath":{{ToJson(projectPath)}},"Diagnostics":[]}
""");
}

Expand Down Expand Up @@ -5609,6 +5628,7 @@ public void Api_Evaluation()
var bPath = Path.Join(testInstance.Path, "B.cs");
File.WriteAllText(bPath, "");

var projectPath = VirtualProjectBuilder.GetVirtualProjectPath(programPath);
new DotnetCommand(Log, "run-api")
.WithStandardInput($$"""
{"$type":"GetProject","EntryPointFileFullPath":{{ToJson(programPath)}},"ArtifactsPath":"/artifacts"}
Expand All @@ -5628,6 +5648,7 @@ public void Api_Evaluation()
<FileBasedProgramsItemMapping>.cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content</FileBasedProgramsItemMapping>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder>
<AssemblyName>Program</AssemblyName>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
<OutputType>Exe</OutputType>
Expand Down Expand Up @@ -5667,7 +5688,7 @@ public void Api_Evaluation()

</Project>

""")}},"Diagnostics":[]}
""")}},"ProjectPath":{{ToJson(projectPath)}},"Diagnostics":[]}
""");
}

Expand All @@ -5681,6 +5702,7 @@ public void Api_Diagnostic_01()
#:property LangVersion=preview
""");

var projectPath = VirtualProjectBuilder.GetVirtualProjectPath(programPath);
new DotnetCommand(Log, "run-api")
.WithStandardInput($$"""
{"$type":"GetProject","EntryPointFileFullPath":{{ToJson(programPath)}},"ArtifactsPath":"/artifacts"}
Expand All @@ -5700,6 +5722,7 @@ public void Api_Diagnostic_01()
<FileBasedProgramsItemMapping>.cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content</FileBasedProgramsItemMapping>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder>
<AssemblyName>Program</AssemblyName>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
<OutputType>Exe</OutputType>
Expand Down Expand Up @@ -5734,7 +5757,7 @@ public void Api_Diagnostic_01()

</Project>

""")}},"Diagnostics":
""")}},"ProjectPath":{{ToJson(projectPath)}},"Diagnostics":
[{"Location":{
"Path":{{ToJson(programPath)}},
"Span":{"Start":{"Line":1,"Character":0},"End":{"Line":1,"Character":30}{{nop}}}{{nop}}},
Expand All @@ -5752,6 +5775,7 @@ public void Api_Diagnostic_02()
Console.WriteLine();
""");

var projectPath = VirtualProjectBuilder.GetVirtualProjectPath(programPath);
new DotnetCommand(Log, "run-api")
.WithStandardInput($$"""
{"$type":"GetProject","EntryPointFileFullPath":{{ToJson(programPath)}},"ArtifactsPath":"/artifacts"}
Expand All @@ -5771,6 +5795,7 @@ public void Api_Diagnostic_02()
<FileBasedProgramsItemMapping>.cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content</FileBasedProgramsItemMapping>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder>
<AssemblyName>Program</AssemblyName>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
<OutputType>Exe</OutputType>
Expand Down Expand Up @@ -5805,7 +5830,7 @@ public void Api_Diagnostic_02()

</Project>

""")}},"Diagnostics":
""")}},"ProjectPath":{{ToJson(projectPath)}},"Diagnostics":
[{"Location":{
"Path":{{ToJson(programPath)}},
"Span":{"Start":{"Line":0,"Character":0},"End":{"Line":1,"Character":0}{{nop}}}{{nop}}},
Expand Down
Loading