Skip to content

Commit 1443d16

Browse files
authored
[release/10.0.1xx-preview7] Override default properties with custom file-level directives (#49898)
2 parents fe3b54d + 60b9a1b commit 1443d16

File tree

3 files changed

+99
-92
lines changed

3 files changed

+99
-92
lines changed

src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ internal sealed class VirtualProjectBuildingCommand : CommandBase
6363
"MSBuild.rsp",
6464
];
6565

66+
/// <remarks>
67+
/// Kept in sync with the default <c>dotnet new console</c> project file (enforced by <c>DotnetProjectAddTests.SameAsTemplate</c>).
68+
/// </remarks>
69+
private static readonly FrozenDictionary<string, string> s_defaultProperties = FrozenDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase,
70+
[
71+
new("OutputType", "Exe"),
72+
new("TargetFramework", "net10.0"),
73+
new("ImplicitUsings", "enable"),
74+
new("Nullable", "enable"),
75+
new("PublishAot", "true"),
76+
]);
77+
6678
internal static readonly string TargetOverrides = """
6779
<!--
6880
Override targets which don't work with project files that are not present on disk.
@@ -719,34 +731,33 @@ public static void WriteProjectFile(
719731
writer.WriteLine();
720732
}
721733

722-
// Kept in sync with the default `dotnet new console` project file (enforced by `DotnetProjectAddTests.SameAsTemplate`).
723-
writer.WriteLine($"""
724-
<PropertyGroup>
725-
<OutputType>Exe</OutputType>
726-
<TargetFramework>net10.0</TargetFramework>
727-
<ImplicitUsings>enable</ImplicitUsings>
728-
<Nullable>enable</Nullable>
729-
<PublishAot>true</PublishAot>
730-
</PropertyGroup>
731-
""");
732-
733-
if (isVirtualProject)
734+
// Write default and custom properties.
734735
{
735736
writer.WriteLine("""
736-
737737
<PropertyGroup>
738-
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
739-
</PropertyGroup>
740738
""");
741-
}
742739

743-
if (propertyDirectives.Any())
744-
{
745-
writer.WriteLine("""
740+
// First write the default properties except those specified by the user.
741+
var customPropertyNames = propertyDirectives.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
742+
foreach (var (name, value) in s_defaultProperties)
743+
{
744+
if (!customPropertyNames.Contains(name))
745+
{
746+
writer.WriteLine($"""
747+
<{name}>{EscapeValue(value)}</{name}>
748+
""");
749+
}
750+
}
746751

747-
<PropertyGroup>
748-
""");
752+
// Write virtual-only properties.
753+
if (isVirtualProject)
754+
{
755+
writer.WriteLine("""
756+
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
757+
""");
758+
}
749759

760+
// Write custom properties.
750761
foreach (var property in propertyDirectives)
751762
{
752763
writer.WriteLine($"""
@@ -756,24 +767,23 @@ public static void WriteProjectFile(
756767
processedDirectives++;
757768
}
758769

759-
writer.WriteLine(" </PropertyGroup>");
760-
}
770+
// Write virtual-only properties which cannot be overridden.
771+
if (isVirtualProject)
772+
{
773+
writer.WriteLine("""
774+
<Features>$(Features);FileBasedProgram</Features>
775+
""");
776+
}
761777

762-
if (isVirtualProject)
763-
{
764-
// After `#:property` directives so they don't override this.
765778
writer.WriteLine("""
766-
767-
<PropertyGroup>
768-
<Features>$(Features);FileBasedProgram</Features>
769779
</PropertyGroup>
780+
770781
""");
771782
}
772783

773784
if (packageDirectives.Any())
774785
{
775786
writer.WriteLine("""
776-
777787
<ItemGroup>
778788
""");
779789

@@ -795,13 +805,15 @@ public static void WriteProjectFile(
795805
processedDirectives++;
796806
}
797807

798-
writer.WriteLine(" </ItemGroup>");
808+
writer.WriteLine("""
809+
</ItemGroup>
810+
811+
""");
799812
}
800813

801814
if (projectDirectives.Any())
802815
{
803816
writer.WriteLine("""
804-
805817
<ItemGroup>
806818
""");
807819

@@ -814,7 +826,10 @@ public static void WriteProjectFile(
814826
processedDirectives++;
815827
}
816828

817-
writer.WriteLine(" </ItemGroup>");
829+
writer.WriteLine("""
830+
</ItemGroup>
831+
832+
""");
818833
}
819834

820835
Debug.Assert(processedDirectives + directives.OfType<CSharpDirective.Shebang>().Count() == directives.Length);
@@ -824,7 +839,6 @@ public static void WriteProjectFile(
824839
Debug.Assert(targetFilePath is not null);
825840

826841
writer.WriteLine($"""
827-
828842
<ItemGroup>
829843
<Compile Include="{EscapeValue(targetFilePath)}" />
830844
</ItemGroup>
@@ -835,12 +849,12 @@ public static void WriteProjectFile(
835849
{
836850
var targetDirectory = Path.GetDirectoryName(targetFilePath) ?? "";
837851
writer.WriteLine($"""
838-
<ItemGroup>
839-
<RuntimeHostConfigurationOption Include="EntryPointFilePath" Value="{EscapeValue(targetFilePath)}" />
840-
<RuntimeHostConfigurationOption Include="EntryPointFileDirectoryPath" Value="{EscapeValue(targetDirectory)}" />
841-
</ItemGroup>
852+
<ItemGroup>
853+
<RuntimeHostConfigurationOption Include="EntryPointFilePath" Value="{EscapeValue(targetFilePath)}" />
854+
<RuntimeHostConfigurationOption Include="EntryPointFileDirectoryPath" Value="{EscapeValue(targetDirectory)}" />
855+
</ItemGroup>
842856
843-
""");
857+
""");
844858
}
845859

846860
foreach (var sdk in sdkDirectives)
@@ -856,12 +870,14 @@ public static void WriteProjectFile(
856870
""");
857871
}
858872

859-
writer.WriteLine();
860-
writer.WriteLine(TargetOverrides);
873+
writer.WriteLine($"""
874+
875+
{TargetOverrides}
876+
877+
""");
861878
}
862879

863880
writer.WriteLine("""
864-
865881
</Project>
866882
""");
867883

test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -642,26 +642,22 @@ public void Directives()
642642
#!/program
643643
#:sdk Microsoft.NET.Sdk
644644
#:sdk Aspire.Hosting.Sdk@9.1.0
645-
#:property TargetFramework=net11.0
645+
#:property TargetFramework=net472
646646
#:package System.CommandLine@2.0.0-beta4.22272.1
647647
#:property LangVersion=preview
648648
Console.WriteLine();
649649
""",
650-
expectedProject: $"""
650+
expectedProject: """
651651
<Project Sdk="Microsoft.NET.Sdk">
652652
653653
<Sdk Name="Aspire.Hosting.Sdk" Version="9.1.0" />
654654
655655
<PropertyGroup>
656656
<OutputType>Exe</OutputType>
657-
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework>
658657
<ImplicitUsings>enable</ImplicitUsings>
659658
<Nullable>enable</Nullable>
660659
<PublishAot>true</PublishAot>
661-
</PropertyGroup>
662-
663-
<PropertyGroup>
664-
<TargetFramework>net11.0</TargetFramework>
660+
<TargetFramework>net472</TargetFramework>
665661
<LangVersion>preview</LangVersion>
666662
</PropertyGroup>
667663
@@ -677,6 +673,44 @@ public void Directives()
677673
""");
678674
}
679675

676+
/// <summary>
677+
/// There should be only one <c>PropertyGroup</c> element when the default properties are overridden.
678+
/// </summary>
679+
[Fact]
680+
public void Directives_AllDefaultOverridden()
681+
{
682+
VerifyConversion(
683+
inputCSharp: """
684+
#!/program
685+
#:sdk Microsoft.NET.Web.Sdk
686+
#:property OutputType=Exe
687+
#:property TargetFramework=net472
688+
#:property Nullable=disable
689+
#:property PublishAot=false
690+
#:property Custom=1
691+
#:property ImplicitUsings=disable
692+
Console.WriteLine();
693+
""",
694+
expectedProject: """
695+
<Project Sdk="Microsoft.NET.Web.Sdk">
696+
697+
<PropertyGroup>
698+
<OutputType>Exe</OutputType>
699+
<TargetFramework>net472</TargetFramework>
700+
<Nullable>disable</Nullable>
701+
<PublishAot>false</PublishAot>
702+
<Custom>1</Custom>
703+
<ImplicitUsings>disable</ImplicitUsings>
704+
</PropertyGroup>
705+
706+
</Project>
707+
708+
""",
709+
expectedCSharp: """
710+
Console.WriteLine();
711+
""");
712+
}
713+
680714
[Fact]
681715
public void Directives_Variable()
682716
{
@@ -694,9 +728,6 @@ public void Directives_Variable()
694728
<ImplicitUsings>enable</ImplicitUsings>
695729
<Nullable>enable</Nullable>
696730
<PublishAot>true</PublishAot>
697-
</PropertyGroup>
698-
699-
<PropertyGroup>
700731
<MyProp>MyValue</MyProp>
701732
</PropertyGroup>
702733
@@ -772,9 +803,6 @@ public void Directives_Separators()
772803
<ImplicitUsings>enable</ImplicitUsings>
773804
<Nullable>enable</Nullable>
774805
<PublishAot>true</PublishAot>
775-
</PropertyGroup>
776-
777-
<PropertyGroup>
778806
<Prop1>One=a/b</Prop1>
779807
<Prop2>Two/a=b</Prop2>
780808
</PropertyGroup>
@@ -884,9 +912,6 @@ public void Directives_Escaping()
884912
<ImplicitUsings>enable</ImplicitUsings>
885913
<Nullable>enable</Nullable>
886914
<PublishAot>true</PublishAot>
887-
</PropertyGroup>
888-
889-
<PropertyGroup>
890915
<Prop>&lt;test&quot;&gt;</Prop>
891916
</PropertyGroup>
892917
@@ -921,9 +946,6 @@ public void Directives_Whitespace()
921946
<ImplicitUsings>enable</ImplicitUsings>
922947
<Nullable>enable</Nullable>
923948
<PublishAot>true</PublishAot>
924-
</PropertyGroup>
925-
926-
<PropertyGroup>
927949
<Name>Value</Name>
928950
<NugetPackageDescription>&quot;My package with spaces&quot;</NugetPackageDescription>
929951
</PropertyGroup>
@@ -968,9 +990,6 @@ public void Directives_AfterToken()
968990
<ImplicitUsings>enable</ImplicitUsings>
969991
<Nullable>enable</Nullable>
970992
<PublishAot>true</PublishAot>
971-
</PropertyGroup>
972-
973-
<PropertyGroup>
974993
<Prop1>1</Prop1>
975994
<Prop2>2</Prop2>
976995
</PropertyGroup>
@@ -1017,9 +1036,6 @@ public void Directives_AfterIf()
10171036
<ImplicitUsings>enable</ImplicitUsings>
10181037
<Nullable>enable</Nullable>
10191038
<PublishAot>true</PublishAot>
1020-
</PropertyGroup>
1021-
1022-
<PropertyGroup>
10231039
<Prop1>1</Prop1>
10241040
<Prop2>2</Prop2>
10251041
</PropertyGroup>
@@ -1063,9 +1079,6 @@ public void Directives_Comments()
10631079
<ImplicitUsings>enable</ImplicitUsings>
10641080
<Nullable>enable</Nullable>
10651081
<PublishAot>true</PublishAot>
1066-
</PropertyGroup>
1067-
1068-
<PropertyGroup>
10691082
<Prop1>1</Prop1>
10701083
<Prop2>2</Prop2>
10711084
</PropertyGroup>

test/dotnet.Tests/CommandTests/Run/RunFileTests.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,22 +1873,12 @@ public void Api()
18731873
18741874
<PropertyGroup>
18751875
<OutputType>Exe</OutputType>
1876-
<TargetFramework>{ToolsetInfo.CurrentTargetFramework}</TargetFramework>
18771876
<ImplicitUsings>enable</ImplicitUsings>
18781877
<Nullable>enable</Nullable>
18791878
<PublishAot>true</PublishAot>
1880-
</PropertyGroup>
1881-
1882-
<PropertyGroup>
18831879
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
1884-
</PropertyGroup>
1885-
1886-
<PropertyGroup>
18871880
<TargetFramework>net11.0</TargetFramework>
18881881
<LangVersion>preview</LangVersion>
1889-
</PropertyGroup>
1890-
1891-
<PropertyGroup>
18921882
<Features>$(Features);FileBasedProgram</Features>
18931883
</PropertyGroup>
18941884
@@ -1955,13 +1945,7 @@ public void Api_Diagnostic_01()
19551945
<ImplicitUsings>enable</ImplicitUsings>
19561946
<Nullable>enable</Nullable>
19571947
<PublishAot>true</PublishAot>
1958-
</PropertyGroup>
1959-
1960-
<PropertyGroup>
19611948
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
1962-
</PropertyGroup>
1963-
1964-
<PropertyGroup>
19651949
<Features>$(Features);FileBasedProgram</Features>
19661950
</PropertyGroup>
19671951
@@ -2027,13 +2011,7 @@ public void Api_Diagnostic_02()
20272011
<ImplicitUsings>enable</ImplicitUsings>
20282012
<Nullable>enable</Nullable>
20292013
<PublishAot>true</PublishAot>
2030-
</PropertyGroup>
2031-
2032-
<PropertyGroup>
20332014
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
2034-
</PropertyGroup>
2035-
2036-
<PropertyGroup>
20372015
<Features>$(Features);FileBasedProgram</Features>
20382016
</PropertyGroup>
20392017

0 commit comments

Comments
 (0)