@@ -29,18 +29,19 @@ namespace ICSharpCode.Decompiler.Solution
2929 /// </summary>
3030 public static class SolutionCreator
3131 {
32- private static readonly XNamespace ProjectFileNamespace = XNamespace . Get ( "http://schemas.microsoft.com/developer/msbuild/2003" ) ;
32+ static readonly XNamespace NonSDKProjectFileNamespace = XNamespace . Get ( "http://schemas.microsoft.com/developer/msbuild/2003" ) ;
3333
3434 /// <summary>
3535 /// Writes a solution file to the specified <paramref name="targetFile"/>.
36+ /// Also fixes intra-solution project references in the project files.
3637 /// </summary>
3738 /// <param name="targetFile">The full path of the file to write.</param>
3839 /// <param name="projects">The projects contained in this solution.</param>
3940 ///
4041 /// <exception cref="ArgumentException">Thrown when <paramref name="targetFile"/> is null or empty.</exception>
4142 /// <exception cref="ArgumentNullException">Thrown when <paramref name="projects"/> is null.</exception>
4243 /// <exception cref="InvalidOperationException">Thrown when <paramref name="projects"/> contains no items.</exception>
43- public static void WriteSolutionFile ( string targetFile , IEnumerable < ProjectItem > projects )
44+ public static void WriteSolutionFile ( string targetFile , List < ProjectItem > projects )
4445 {
4546 if ( string . IsNullOrWhiteSpace ( targetFile ) )
4647 {
@@ -62,10 +63,10 @@ public static void WriteSolutionFile(string targetFile, IEnumerable<ProjectItem>
6263 WriteSolutionFile ( writer , projects , targetFile ) ;
6364 }
6465
65- FixProjectReferences ( projects ) ;
66+ FixAllProjectReferences ( projects ) ;
6667 }
6768
68- private static void WriteSolutionFile ( TextWriter writer , IEnumerable < ProjectItem > projects , string solutionFilePath )
69+ static void WriteSolutionFile ( TextWriter writer , List < ProjectItem > projects , string solutionFilePath )
6970 {
7071 WriteHeader ( writer ) ;
7172 WriteProjects ( writer , projects , solutionFilePath ) ;
@@ -90,7 +91,7 @@ private static void WriteHeader(TextWriter writer)
9091 writer . WriteLine ( "MinimumVisualStudioVersion = 10.0.40219.1" ) ;
9192 }
9293
93- private static void WriteProjects ( TextWriter writer , IEnumerable < ProjectItem > projects , string solutionFilePath )
94+ static void WriteProjects ( TextWriter writer , List < ProjectItem > projects , string solutionFilePath )
9495 {
9596 foreach ( var project in projects )
9697 {
@@ -103,7 +104,7 @@ private static void WriteProjects(TextWriter writer, IEnumerable<ProjectItem> pr
103104 }
104105 }
105106
106- private static IEnumerable < string > WriteSolutionConfigurations ( TextWriter writer , IEnumerable < ProjectItem > projects )
107+ static List < string > WriteSolutionConfigurations ( TextWriter writer , List < ProjectItem > projects )
107108 {
108109 var platforms = projects . GroupBy ( p => p . PlatformName ) . Select ( g => g . Key ) . ToList ( ) ;
109110
@@ -125,10 +126,10 @@ private static IEnumerable<string> WriteSolutionConfigurations(TextWriter writer
125126 return platforms ;
126127 }
127128
128- private static void WriteProjectConfigurations (
129+ static void WriteProjectConfigurations (
129130 TextWriter writer ,
130- IEnumerable < ProjectItem > projects ,
131- IEnumerable < string > solutionPlatforms )
131+ List < ProjectItem > projects ,
132+ List < string > solutionPlatforms )
132133 {
133134 writer . WriteLine ( "\t GlobalSection(ProjectConfigurationPlatforms) = postSolution" ) ;
134135
@@ -152,47 +153,80 @@ private static void WriteProjectConfigurations(
152153 writer . WriteLine ( "\t EndGlobalSection" ) ;
153154 }
154155
155- private static void FixProjectReferences ( IEnumerable < ProjectItem > projects )
156+ static void FixAllProjectReferences ( List < ProjectItem > projects )
156157 {
157- var projectsMap = projects . ToDictionary ( p => p . ProjectName , p => p ) ;
158+ var projectsMap = projects . ToDictionary (
159+ p => p . ProjectName ,
160+ p => p ) ;
158161
159162 foreach ( var project in projects )
160163 {
161164 XDocument projectDoc = XDocument . Load ( project . FilePath ) ;
162165
166+ if ( projectDoc . Root ? . Name . LocalName != "Project" )
167+ {
168+ throw new InvalidOperationException (
169+ $ "The file { project . FilePath } is not a valid project file, " +
170+ $ "no <Project> at the root; could not fix project references.") ;
171+ }
172+
173+ // sdk style projects don't use a namespace for the elements,
174+ // but we still need to use the namespace for non-sdk style projects.
175+ var sdkStyle = projectDoc . Root . Attribute ( "Sdk" ) != null ;
176+ var itemGroupTagName = sdkStyle ? "ItemGroup" : NonSDKProjectFileNamespace + "ItemGroup" ;
177+ var referenceTagName = sdkStyle ? "Reference" : NonSDKProjectFileNamespace + "Reference" ;
178+
163179 var referencesItemGroups = projectDoc . Root
164- . Elements ( ProjectFileNamespace + "ItemGroup" )
165- . Where ( e => e . Elements ( ProjectFileNamespace + "Reference" ) . Any ( ) ) ;
180+ . Elements ( itemGroupTagName )
181+ . Where ( e => e . Elements ( referenceTagName ) . Any ( ) )
182+ . ToList ( ) ;
166183
167184 foreach ( var itemGroup in referencesItemGroups )
168185 {
169- FixProjectReferences ( project . FilePath , itemGroup , projectsMap ) ;
186+ FixProjectReferences ( project . FilePath , itemGroup , projectsMap , sdkStyle ) ;
170187 }
171188
172189 projectDoc . Save ( project . FilePath ) ;
173190 }
174191 }
175192
176- private static void FixProjectReferences ( string projectFilePath , XElement itemGroup , IDictionary < string , ProjectItem > projects )
193+ static void FixProjectReferences ( string projectFilePath , XElement itemGroup ,
194+ Dictionary < string , ProjectItem > projects , bool sdkStyle )
177195 {
178- foreach ( var item in itemGroup . Elements ( ProjectFileNamespace + "Reference" ) . ToList ( ) )
196+
197+ XName GetElementName ( string name ) => sdkStyle ? name : NonSDKProjectFileNamespace + name ;
198+
199+ var referenceTagName = GetElementName ( "Reference" ) ;
200+ var projectReferenceTagName = GetElementName ( "ProjectReference" ) ;
201+
202+ foreach ( var item in itemGroup . Elements ( referenceTagName ) . ToList ( ) )
179203 {
180204 var assemblyName = item . Attribute ( "Include" ) ? . Value ;
181205 if ( assemblyName != null && projects . TryGetValue ( assemblyName , out var referencedProject ) )
182206 {
183207 item . Remove ( ) ;
184208
185- var projectReference = new XElement ( ProjectFileNamespace + "ProjectReference" ,
186- new XElement ( ProjectFileNamespace + "Project" , referencedProject . Guid . ToString ( "B" ) . ToUpperInvariant ( ) ) ,
187- new XElement ( ProjectFileNamespace + "Name" , referencedProject . ProjectName ) ) ;
188- projectReference . SetAttributeValue ( "Include" , GetRelativePath ( projectFilePath , referencedProject . FilePath ) ) ;
209+ var projectReference = new XElement (
210+ projectReferenceTagName ,
211+ new XAttribute ( "Include" , GetRelativePath ( projectFilePath , referencedProject . FilePath ) ) ) ;
212+
213+ // SDK-style projects do not use the <Project> and <Name> elements for project references.
214+ // (Instead, those get read from the .csproj file in "Include".)
215+ if ( ! sdkStyle )
216+ {
217+ projectReference . Add (
218+ // no ToUpper() for uuids, most Microsoft tools seem to emit them in lowercase
219+ // (no .ToLower() as .ToString("B") already outputs lowercase)
220+ new XElement ( NonSDKProjectFileNamespace + "Project" , referencedProject . Guid . ToString ( "B" ) ) ,
221+ new XElement ( NonSDKProjectFileNamespace + "Name" , referencedProject . ProjectName ) ) ;
222+ }
189223
190224 itemGroup . Add ( projectReference ) ;
191225 }
192226 }
193227 }
194228
195- private static string GetRelativePath ( string fromFilePath , string toFilePath )
229+ static string GetRelativePath ( string fromFilePath , string toFilePath )
196230 {
197231 Uri fromUri = new Uri ( fromFilePath ) ;
198232 Uri toUri = new Uri ( toFilePath ) ;
0 commit comments