6
6
using System . Collections . Immutable ;
7
7
using System . Collections . ObjectModel ;
8
8
using System . Diagnostics ;
9
+ using System . Diagnostics . CodeAnalysis ;
9
10
using System . Security ;
10
11
using System . Text . Json ;
11
12
using System . Text . Json . Serialization ;
@@ -164,14 +165,26 @@ public VirtualProjectBuildingCommand(
164
165
/// </summary>
165
166
public bool NoWriteBuildMarkers { get ; init ; }
166
167
168
+ private SourceFile EntryPointSourceFile
169
+ {
170
+ get
171
+ {
172
+ if ( field == default )
173
+ {
174
+ field = SourceFile . Load ( EntryPointFileFullPath ) ;
175
+ }
176
+
177
+ return field ;
178
+ }
179
+ }
180
+
167
181
public ImmutableArray < CSharpDirective > Directives
168
182
{
169
183
get
170
184
{
171
185
if ( field . IsDefault )
172
186
{
173
- var sourceFile = SourceFile . Load ( EntryPointFileFullPath ) ;
174
- field = FindDirectives ( sourceFile , reportAllErrors : false , DiagnosticBag . ThrowOnFirst ( ) ) ;
187
+ field = FindDirectives ( EntryPointSourceFile , reportAllErrors : false , DiagnosticBag . ThrowOnFirst ( ) ) ;
175
188
Debug . Assert ( ! field . IsDefault ) ;
176
189
}
177
190
@@ -1047,6 +1060,23 @@ public ProjectInstance CreateProjectInstance(ProjectCollection projectCollection
1047
1060
private ProjectInstance CreateProjectInstance (
1048
1061
ProjectCollection projectCollection ,
1049
1062
Action < IDictionary < string , string > > ? addGlobalProperties )
1063
+ {
1064
+ var project = CreateProjectInstance ( projectCollection , Directives , addGlobalProperties ) ;
1065
+
1066
+ var directives = EvaluateDirectives ( project , Directives , EntryPointSourceFile , DiagnosticBag . ThrowOnFirst ( ) ) ;
1067
+ if ( directives != Directives )
1068
+ {
1069
+ Directives = directives ;
1070
+ project = CreateProjectInstance ( projectCollection , directives , addGlobalProperties ) ;
1071
+ }
1072
+
1073
+ return project ;
1074
+ }
1075
+
1076
+ private ProjectInstance CreateProjectInstance (
1077
+ ProjectCollection projectCollection ,
1078
+ ImmutableArray < CSharpDirective > directives ,
1079
+ Action < IDictionary < string , string > > ? addGlobalProperties )
1050
1080
{
1051
1081
var projectRoot = CreateProjectRootElement ( projectCollection ) ;
1052
1082
@@ -1069,7 +1099,7 @@ ProjectRootElement CreateProjectRootElement(ProjectCollection projectCollection)
1069
1099
var projectFileWriter = new StringWriter ( ) ;
1070
1100
WriteProjectFile (
1071
1101
projectFileWriter ,
1072
- Directives ,
1102
+ directives ,
1073
1103
isVirtualProject : true ,
1074
1104
targetFilePath : EntryPointFileFullPath ,
1075
1105
artifactsPath : ArtifactsPath ,
@@ -1604,6 +1634,28 @@ static bool Fill(ref WhiteSpaceInfo info, in SyntaxTriviaList triviaList, int in
1604
1634
}
1605
1635
}
1606
1636
1637
+ /// <summary>
1638
+ /// If there are any <c>#:project</c> <paramref name="directives"/>, expand <c>$()</c> in them and then resolve the project paths.
1639
+ /// </summary>
1640
+ public static ImmutableArray < CSharpDirective > EvaluateDirectives (
1641
+ ProjectInstance ? project ,
1642
+ ImmutableArray < CSharpDirective > directives ,
1643
+ SourceFile sourceFile ,
1644
+ DiagnosticBag diagnostics )
1645
+ {
1646
+ if ( directives . OfType < CSharpDirective . Project > ( ) . Any ( ) )
1647
+ {
1648
+ return directives
1649
+ . Select ( d => d is CSharpDirective . Project p
1650
+ ? ( project is null ? p : p . WithName ( project . ExpandString ( p . Name ) ) )
1651
+ . ResolveProjectPath ( sourceFile , diagnostics )
1652
+ : d )
1653
+ . ToImmutableArray ( ) ;
1654
+ }
1655
+
1656
+ return directives ;
1657
+ }
1658
+
1607
1659
public static SourceText ? RemoveDirectivesFromFile ( ImmutableArray < CSharpDirective > directives , SourceText text )
1608
1660
{
1609
1661
if ( directives . Length == 0 )
@@ -1882,8 +1934,26 @@ public sealed class Package(in ParseInfo info) : Named(info)
1882
1934
/// <summary>
1883
1935
/// <c>#:project</c> directive.
1884
1936
/// </summary>
1885
- public sealed class Project ( in ParseInfo info ) : Named ( info )
1937
+ public sealed class Project : Named
1886
1938
{
1939
+ [ SetsRequiredMembers ]
1940
+ public Project ( in ParseInfo info , string name ) : base ( info )
1941
+ {
1942
+ Name = name ;
1943
+ OriginalName = name ;
1944
+ UnresolvedName = name ;
1945
+ }
1946
+
1947
+ /// <summary>
1948
+ /// Preserved across <see cref="WithName"/> calls.
1949
+ /// </summary>
1950
+ public required string OriginalName { get ; init ; }
1951
+
1952
+ /// <summary>
1953
+ /// Preserved across <see cref="ResolveProjectPath"/> calls.
1954
+ /// </summary>
1955
+ public required string UnresolvedName { get ; init ; }
1956
+
1887
1957
public static new Project ? Parse ( in ParseContext context )
1888
1958
{
1889
1959
var directiveText = context . DirectiveText ;
@@ -1893,11 +1963,32 @@ public sealed class Project(in ParseInfo info) : Named(info)
1893
1963
return context . Diagnostics . AddError < Project ? > ( context . SourceFile , context . Info . Span , string . Format ( CliCommandStrings . MissingDirectiveName , directiveKind ) ) ;
1894
1964
}
1895
1965
1966
+ return new Project ( context . Info , directiveText ) ;
1967
+ }
1968
+
1969
+ public Project WithName ( string name , bool preserveUnresolvedName = false )
1970
+ {
1971
+ return name == Name
1972
+ ? this
1973
+ : new Project ( Info , name )
1974
+ {
1975
+ OriginalName = OriginalName ,
1976
+ UnresolvedName = preserveUnresolvedName ? UnresolvedName : name ,
1977
+ } ;
1978
+ }
1979
+
1980
+ /// <summary>
1981
+ /// If the directive points to a directory, returns a new directive pointing to the corresponding project file.
1982
+ /// </summary>
1983
+ public Project ResolveProjectPath ( SourceFile sourceFile , DiagnosticBag diagnostics )
1984
+ {
1985
+ var directiveText = Name ;
1986
+
1896
1987
try
1897
1988
{
1898
1989
// If the path is a directory like '../lib', transform it to a project file path like '../lib/lib.csproj'.
1899
1990
// Also normalize blackslashes to forward slashes to ensure the directive works on all platforms.
1900
- var sourceDirectory = Path . GetDirectoryName ( context . SourceFile . Path ) ?? "." ;
1991
+ var sourceDirectory = Path . GetDirectoryName ( sourceFile . Path ) ?? "." ;
1901
1992
var resolvedProjectPath = Path . Combine ( sourceDirectory , directiveText . Replace ( '\\ ' , '/' ) ) ;
1902
1993
if ( Directory . Exists ( resolvedProjectPath ) )
1903
1994
{
@@ -1915,18 +2006,10 @@ public sealed class Project(in ParseInfo info) : Named(info)
1915
2006
}
1916
2007
catch ( GracefulException e )
1917
2008
{
1918
- context . Diagnostics . AddError ( context . SourceFile , context . Info . Span , string . Format ( CliCommandStrings . InvalidProjectDirective , e . Message ) , e ) ;
2009
+ diagnostics . AddError ( sourceFile , Info . Span , string . Format ( CliCommandStrings . InvalidProjectDirective , e . Message ) , e ) ;
1919
2010
}
1920
2011
1921
- return new Project ( context . Info )
1922
- {
1923
- Name = directiveText ,
1924
- } ;
1925
- }
1926
-
1927
- public Project WithName ( string name )
1928
- {
1929
- return new Project ( Info ) { Name = name } ;
2012
+ return WithName ( directiveText , preserveUnresolvedName : true ) ;
1930
2013
}
1931
2014
1932
2015
public override string ToString ( ) => $ "#:project { Name } ";
0 commit comments