-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Allow SDKs to overwrite default file-based app properties #50885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9ebef1d
f0e01c3
fe0f22d
54963b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,7 +75,7 @@ internal sealed class VirtualProjectBuildingCommand : CommandBase | |
/// <remarks> | ||
/// Kept in sync with the default <c>dotnet new console</c> project file (enforced by <c>DotnetProjectAddTests.SameAsTemplate</c>). | ||
/// </remarks> | ||
private static readonly FrozenDictionary<string, string> s_defaultProperties = FrozenDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase, | ||
public static readonly FrozenDictionary<string, string> DefaultProperties = FrozenDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase, | ||
[ | ||
new("OutputType", "Exe"), | ||
new("TargetFramework", $"net{TargetFrameworkVersion}"), | ||
|
@@ -1140,8 +1140,12 @@ public static void WriteProjectFile( | |
string? targetFilePath = null, | ||
string? artifactsPath = null, | ||
bool includeRuntimeConfigInformation = true, | ||
string? userSecretsId = null) | ||
string? userSecretsId = null, | ||
IEnumerable<string>? excludeDefaultProperties = null) | ||
{ | ||
Debug.Assert(userSecretsId == null || !isVirtualProject); | ||
Debug.Assert(excludeDefaultProperties == null || !isVirtualProject); | ||
|
||
int processedDirectives = 0; | ||
|
||
var sdkDirectives = directives.OfType<CSharpDirective.Sdk>(); | ||
|
@@ -1180,6 +1184,20 @@ public static void WriteProjectFile( | |
<PublishDir>artifacts/$(MSBuildProjectName)</PublishDir> | ||
<PackageOutputPath>artifacts/$(MSBuildProjectName)</PackageOutputPath> | ||
<FileBasedProgram>true</FileBasedProgram> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
"""); | ||
|
||
// Write default properties before importing SDKs so they can be overridden by SDKs | ||
// (and implicit build files which are imported by the default .NET SDK). | ||
foreach (var (name, value) in DefaultProperties) | ||
{ | ||
writer.WriteLine($""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: IMO we shouldn't be writing raw XML project files like this. If the MSbuild in-memory object APIs don't make it convenient to create the structures we need I'd love to see issues raised calling out the pain points. Raw XML ties us to the format rather than the logical values we're trying to control. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was discussed previously: #47702 (comment) |
||
<{name}>{EscapeValue(value)}</{name}> | ||
"""); | ||
} | ||
|
||
writer.WriteLine($""" | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
|
@@ -1246,34 +1264,30 @@ public static void WriteProjectFile( | |
"""); | ||
|
||
// First write the default properties except those specified by the user. | ||
var customPropertyNames = propertyDirectives.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); | ||
foreach (var (name, value) in s_defaultProperties) | ||
if (!isVirtualProject) | ||
{ | ||
if (!customPropertyNames.Contains(name)) | ||
var customPropertyNames = propertyDirectives | ||
.Select(static d => d.Name) | ||
jjonescz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.Concat(excludeDefaultProperties ?? []) | ||
.ToHashSet(StringComparer.OrdinalIgnoreCase); | ||
foreach (var (name, value) in DefaultProperties) | ||
{ | ||
if (!customPropertyNames.Contains(name)) | ||
{ | ||
writer.WriteLine($""" | ||
<{name}>{EscapeValue(value)}</{name}> | ||
"""); | ||
} | ||
} | ||
|
||
if (userSecretsId != null && !customPropertyNames.Contains("UserSecretsId")) | ||
{ | ||
writer.WriteLine($""" | ||
<{name}>{EscapeValue(value)}</{name}> | ||
<UserSecretsId>{EscapeValue(userSecretsId)}</UserSecretsId> | ||
"""); | ||
} | ||
} | ||
|
||
if (userSecretsId != null && !customPropertyNames.Contains("UserSecretsId")) | ||
{ | ||
writer.WriteLine($""" | ||
<UserSecretsId>{EscapeValue(userSecretsId)}</UserSecretsId> | ||
"""); | ||
} | ||
|
||
// Write virtual-only properties. | ||
if (isVirtualProject) | ||
{ | ||
writer.WriteLine(""" | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
"""); | ||
} | ||
|
||
// Write custom properties. | ||
foreach (var property in propertyDirectives) | ||
{ | ||
|
@@ -1288,6 +1302,7 @@ public static void WriteProjectFile( | |
if (isVirtualProject) | ||
{ | ||
writer.WriteLine(""" | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
<Features>$(Features);FileBasedProgram</Features> | ||
"""); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -870,6 +870,128 @@ public void DirectoryBuildProps() | |
.And.HaveStdOut("Hello from TestName"); | ||
} | ||
|
||
/// <summary> | ||
/// Overriding default (implicit) properties of file-based apps via implicit build files. | ||
/// </summary> | ||
[Fact] | ||
public void DefaultProps_DirectoryBuildProps() | ||
{ | ||
var testInstance = _testAssetsManager.CreateTestDirectory(); | ||
File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ | ||
Console.WriteLine("Hi"); | ||
"""); | ||
File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ | ||
<Project> | ||
<PropertyGroup> | ||
<ImplicitUsings>disable</ImplicitUsings> | ||
</PropertyGroup> | ||
</Project> | ||
"""); | ||
|
||
new DotnetCommand(Log, "run", "Program.cs") | ||
.WithWorkingDirectory(testInstance.Path) | ||
.Execute() | ||
.Should().Fail() | ||
// error CS0103: The name 'Console' does not exist in the current context | ||
.And.HaveStdOutContaining("error CS0103"); | ||
|
||
// Converting to a project should not change the behavior. | ||
|
||
new DotnetCommand(Log, "project", "convert", "Program.cs") | ||
.WithWorkingDirectory(testInstance.Path) | ||
.Execute() | ||
.Should().Pass(); | ||
|
||
new DotnetCommand(Log, "run") | ||
.WithWorkingDirectory(Path.Join(testInstance.Path, "Program")) | ||
.Execute() | ||
.Should().Fail() | ||
// error CS0103: The name 'Console' does not exist in the current context | ||
.And.HaveStdOutContaining("error CS0103"); | ||
} | ||
|
||
/// <summary> | ||
/// Overriding default (implicit) properties of file-based apps from custom SDKs. | ||
/// </summary> | ||
[Fact] | ||
public void DefaultProps_CustomSdk() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DamianEdwards is the behavior captured by this test conceptually what you want to have working for aspire sdk? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so, yes. IIUC it's demonstrating a file-based app referencing a custom SDK, and that SDK disables implicit usings, which is something explicitly defaulted to on for file-based apps. We should then be able to achieve the same thing for AOT publishing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if your SDK wants to customize some properties in its Sdk.props like this, that should work. |
||
{ | ||
var testInstance = _testAssetsManager.CreateTestDirectory(); | ||
|
||
var sdkDir = Path.Join(testInstance.Path, "MySdk"); | ||
Directory.CreateDirectory(sdkDir); | ||
File.WriteAllText(Path.Join(sdkDir, "Sdk.props"), """ | ||
<Project> | ||
<PropertyGroup> | ||
<ImplicitUsings>disable</ImplicitUsings> | ||
</PropertyGroup> | ||
</Project> | ||
"""); | ||
File.WriteAllText(Path.Join(sdkDir, "Sdk.targets"), """ | ||
<Project /> | ||
"""); | ||
File.WriteAllText(Path.Join(sdkDir, "MySdk.csproj"), $""" | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<PackageType>MSBuildSdk</PackageType> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<None Include="Sdk.*" Pack="true" PackagePath="Sdk" /> | ||
</ItemGroup> | ||
</Project> | ||
"""); | ||
|
||
new DotnetCommand(Log, "pack") | ||
.WithWorkingDirectory(sdkDir) | ||
.Execute() | ||
.Should().Pass(); | ||
|
||
var appDir = Path.Join(testInstance.Path, "app"); | ||
Directory.CreateDirectory(appDir); | ||
File.WriteAllText(Path.Join(appDir, "NuGet.config"), $""" | ||
<configuration> | ||
<packageSources> | ||
<add key="local" value="{Path.Join(sdkDir, "bin", "Release")}" /> | ||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> | ||
</packageSources> | ||
</configuration> | ||
"""); | ||
File.WriteAllText(Path.Join(appDir, "Program.cs"), """ | ||
#:sdk Microsoft.NET.Sdk | ||
#:sdk [email protected] | ||
Console.WriteLine("Hi"); | ||
"""); | ||
|
||
// Use custom package cache to avoid reuse of the custom SDK packed by previous test runs. | ||
var packagesDir = Path.Join(testInstance.Path, ".packages"); | ||
|
||
new DotnetCommand(Log, "run", "Program.cs") | ||
.WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) | ||
.WithWorkingDirectory(appDir) | ||
.Execute() | ||
.Should().Fail() | ||
// error CS0103: The name 'Console' does not exist in the current context | ||
.And.HaveStdOutContaining("error CS0103"); | ||
|
||
// Converting to a project should not change the behavior. | ||
|
||
new DotnetCommand(Log, "project", "convert", "Program.cs") | ||
.WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) | ||
.WithWorkingDirectory(appDir) | ||
.Execute() | ||
.Should().Pass(); | ||
|
||
new DotnetCommand(Log, "run") | ||
.WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) | ||
.WithWorkingDirectory(Path.Join(appDir, "Program")) | ||
.Execute() | ||
.Should().Fail() | ||
// error CS0103: The name 'Console' does not exist in the current context | ||
.And.HaveStdOutContaining("error CS0103"); | ||
} | ||
|
||
[Fact] | ||
public void ComputeRunArguments_Success() | ||
{ | ||
|
@@ -3360,6 +3482,14 @@ public void Api() | |
<PublishDir>artifacts/$(MSBuildProjectName)</PublishDir> | ||
<PackageOutputPath>artifacts/$(MSBuildProjectName)</PackageOutputPath> | ||
<FileBasedProgram>true</FileBasedProgram> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
|
@@ -3370,16 +3500,9 @@ public void Api() | |
<Import Project="Sdk.props" Sdk="Aspire.Hosting.Sdk" Version="9.1.0" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
<TargetFramework>net11.0</TargetFramework> | ||
<LangVersion>preview</LangVersion> | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
<Features>$(Features);FileBasedProgram</Features> | ||
</PropertyGroup> | ||
|
||
|
@@ -3431,6 +3554,14 @@ public void Api_Diagnostic_01() | |
<PublishDir>artifacts/$(MSBuildProjectName)</PublishDir> | ||
<PackageOutputPath>artifacts/$(MSBuildProjectName)</PackageOutputPath> | ||
<FileBasedProgram>true</FileBasedProgram> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
|
@@ -3440,14 +3571,6 @@ public void Api_Diagnostic_01() | |
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
<Features>$(Features);FileBasedProgram</Features> | ||
</PropertyGroup> | ||
|
@@ -3499,6 +3622,14 @@ public void Api_Diagnostic_02() | |
<PublishDir>artifacts/$(MSBuildProjectName)</PublishDir> | ||
<PackageOutputPath>artifacts/$(MSBuildProjectName)</PackageOutputPath> | ||
<FileBasedProgram>true</FileBasedProgram> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
|
@@ -3508,14 +3639,6 @@ public void Api_Diagnostic_02() | |
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PublishAot>true</PublishAot> | ||
<PackAsTool>true</PackAsTool> | ||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<DisableDefaultItemsInProjectFolder>true</DisableDefaultItemsInProjectFolder> | ||
<RestoreUseStaticGraphEvaluation>false</RestoreUseStaticGraphEvaluation> | ||
<Features>$(Features);FileBasedProgram</Features> | ||
</PropertyGroup> | ||
|
Uh oh!
There was an error while loading. Please reload this page.