Merge pull request #3442 from lateralusX/jlorenss/win-atexit-commands
[mono.git] / mcs / nunit24 / ClientUtilities / util / VSProject.cs
1 // ****************************************************************\r
2 // Copyright 2002-2003, Charlie Poole\r
3 // This is free software licensed under the NUnit license. You may\r
4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4\r
5 // ****************************************************************\r
6 \r
7 using System;\r
8 using System.IO;\r
9 using System.Xml;\r
10 using System.Text.RegularExpressions;\r
11 \r
12 namespace NUnit.Util\r
13 {\r
14         /// <summary>\r
15         /// This class allows loading information about\r
16         /// configurations and assemblies in a Visual\r
17         /// Studio project file and inspecting them.\r
18         /// Only the most common project types are\r
19         /// supported and an exception is thrown if\r
20         /// an attempt is made to load an invalid\r
21         /// file or one of an unknown type.\r
22         /// </summary>\r
23         public class VSProject\r
24         {\r
25                 #region Static and Instance Variables\r
26 \r
27                 /// <summary>\r
28                 /// VS Project extentions\r
29                 /// </summary>\r
30                 private static readonly string[] validExtensions = { ".csproj", ".vbproj", ".vjsproj", ".vcproj" };\r
31                 \r
32                 /// <summary>\r
33                 /// VS Solution extension\r
34                 /// </summary>\r
35                 private static readonly string solutionExtension = ".sln";\r
36 \r
37                 /// <summary>\r
38                 /// Path to the file storing this project\r
39                 /// </summary>\r
40                 private string projectPath;\r
41 \r
42                 /// <summary>\r
43                 /// Collection of configs for the project\r
44                 /// </summary>\r
45                 private VSProjectConfigCollection configs;\r
46 \r
47                 #endregion\r
48 \r
49                 #region Constructor\r
50 \r
51                 public VSProject( string projectPath )\r
52                 {\r
53                         this.projectPath = Path.GetFullPath( projectPath );\r
54                         configs = new VSProjectConfigCollection();              \r
55 \r
56                         Load();\r
57                 }\r
58 \r
59                 #endregion\r
60 \r
61                 #region Properties\r
62 \r
63                 /// <summary>\r
64                 /// The name of the project.\r
65                 /// </summary>\r
66                 public string Name\r
67                 {\r
68                         get { return Path.GetFileNameWithoutExtension( projectPath ); }\r
69                 }\r
70 \r
71                 /// <summary>\r
72                 /// The path to the project\r
73                 /// </summary>\r
74                 public string ProjectPath\r
75                 {\r
76                         get { return projectPath; }\r
77                 }\r
78 \r
79                 /// <summary>\r
80                 /// Our collection of configurations\r
81                 /// </summary>\r
82                 public VSProjectConfigCollection Configs\r
83                 {\r
84                         get { return configs; }\r
85                 }\r
86 \r
87                 #endregion\r
88 \r
89                 #region Static Methods\r
90 \r
91                 public static bool IsProjectFile( string path )\r
92                 {\r
93                         if ( path.IndexOfAny( Path.InvalidPathChars ) >= 0 )\r
94                                 return false;\r
95 \r
96                         if ( path.ToLower().IndexOf( "http:" ) >= 0 )\r
97                                 return false;\r
98                 \r
99                         string extension = Path.GetExtension( path );\r
100 \r
101                         foreach( string validExtension in validExtensions )\r
102                                 if ( extension == validExtension )\r
103                                         return true;\r
104 \r
105                         return false;\r
106                 }\r
107 \r
108                 public static bool IsSolutionFile( string path )\r
109                 {\r
110                         return Path.GetExtension( path ) == solutionExtension;\r
111                 }\r
112 \r
113                 #endregion\r
114 \r
115                 #region Instance Methods\r
116 \r
117                 private void Load()\r
118                 {\r
119                         if ( !IsProjectFile( projectPath ) ) \r
120                                 ThrowInvalidFileType( projectPath );\r
121 \r
122                         string projectDirectory = Path.GetFullPath( Path.GetDirectoryName( projectPath ) );\r
123                         StreamReader rdr = new StreamReader( projectPath, System.Text.Encoding.UTF8 );\r
124                         string[] extensions = {"", ".exe", ".dll", ".lib", "" };\r
125                         \r
126                         try\r
127                         {\r
128                                 XmlDocument doc = new XmlDocument();\r
129                                 doc.Load( rdr );\r
130 \r
131                                 string extension = Path.GetExtension( projectPath );\r
132                                 string assemblyName = null;\r
133 \r
134                                 switch ( extension )\r
135                                 {\r
136                                         case ".vcproj":\r
137                                                 XmlNode topNode = doc.SelectSingleNode( "/VisualStudioProject" );\r
138 \r
139                                                 // TODO: This is all very hacked up... replace it.\r
140                                                 foreach ( XmlNode configNode in doc.SelectNodes( "/VisualStudioProject/Configurations/Configuration" ) )\r
141                                                 {\r
142                                                         string name = RequiredAttributeValue( configNode, "Name" );\r
143                                                         int config_type = System.Convert.ToInt32(RequiredAttributeValue(configNode, "ConfigurationType" ) );\r
144                                                         string dirName = name;\r
145                                                         int bar = dirName.IndexOf( '|' );\r
146                                                         if ( bar >= 0 )\r
147                                                                 dirName = dirName.Substring( 0, bar );\r
148                                                         string outputPath = RequiredAttributeValue( configNode, "OutputDirectory" );\r
149                                                         outputPath = outputPath.Replace( "$(SolutionDir)", Path.GetFullPath( Path.GetDirectoryName( projectPath ) ) + Path.DirectorySeparatorChar );\r
150                                                         outputPath = outputPath.Replace( "$(ConfigurationName)", dirName );\r
151 \r
152                                                         string outputDirectory = Path.Combine( projectDirectory, outputPath );\r
153                                                         XmlNode toolNode = configNode.SelectSingleNode( "Tool[@Name='VCLinkerTool']" );\r
154                                                         if ( toolNode != null )\r
155                                                         {\r
156                                                                 assemblyName = SafeAttributeValue( toolNode, "OutputFile" );\r
157                                                                 if ( assemblyName != null )\r
158                                                                         assemblyName = Path.GetFileName( assemblyName );\r
159                                                                 else\r
160                                                                         assemblyName = Path.GetFileNameWithoutExtension(projectPath) + extensions[config_type];\r
161                                                         }\r
162                                                         else\r
163                                                         {\r
164                                                                 toolNode = configNode.SelectSingleNode( "Tool[@Name='VCNMakeTool']" );\r
165                                                                 if ( toolNode != null )\r
166                                                                         assemblyName = Path.GetFileName( RequiredAttributeValue( toolNode, "Output" ) );\r
167                                                         }\r
168 \r
169                                                         assemblyName = assemblyName.Replace( "$(OutDir)", outputPath );\r
170                                                         assemblyName = assemblyName.Replace( "$(ProjectName)", this.Name );\r
171 \r
172                                                         VSProjectConfig config = new VSProjectConfig ( name );\r
173                                                         if ( assemblyName != null )\r
174                                                                 config.Assemblies.Add( Path.Combine( outputDirectory, assemblyName ) );\r
175                                                         \r
176                                                         this.configs.Add( config );\r
177                                                 }\r
178                                         \r
179                                                 break;\r
180 \r
181                                         case ".csproj":\r
182                                         case ".vbproj":\r
183                                         case ".vjsproj":\r
184                                                 LoadProject( projectDirectory, doc );\r
185                                                 break;\r
186 \r
187                                         default:\r
188                                                 break;\r
189                                 }\r
190                         }\r
191                         catch( FileNotFoundException )\r
192                         {\r
193                                 throw;\r
194                         }\r
195                         catch( Exception e )\r
196                         {\r
197                                 ThrowInvalidFormat( projectPath, e );\r
198                         }\r
199                         finally\r
200                         {\r
201                                 rdr.Close();\r
202                         }\r
203                 }\r
204 \r
205                 private bool LoadProject(string projectDirectory, XmlDocument doc)\r
206                 {\r
207                         bool loaded = LoadVS2003Project(projectDirectory, doc);\r
208                         if (loaded) return true;\r
209 \r
210                         loaded = LoadMSBuildProject(projectDirectory, doc);\r
211                         if (loaded) return true;\r
212 \r
213                         return false;\r
214                 }\r
215 \r
216                 private bool LoadVS2003Project(string projectDirectory, XmlDocument doc)\r
217                 {\r
218                         XmlNode settingsNode = doc.SelectSingleNode("/VisualStudioProject/*/Build/Settings");\r
219                         if (settingsNode == null)\r
220                                 return false;\r
221 \r
222                         string assemblyName = RequiredAttributeValue( settingsNode, "AssemblyName" );\r
223                         string outputType = RequiredAttributeValue( settingsNode, "OutputType" );\r
224 \r
225                         if (outputType == "Exe" || outputType == "WinExe")\r
226                                 assemblyName = assemblyName + ".exe";\r
227                         else\r
228                                 assemblyName = assemblyName + ".dll";\r
229 \r
230                         XmlNodeList nodes = settingsNode.SelectNodes("Config");\r
231                         if (nodes != null)\r
232                                 foreach (XmlNode configNode in nodes)\r
233                                 {\r
234                                         string name = RequiredAttributeValue( configNode, "Name" );\r
235                                         string outputPath = RequiredAttributeValue( configNode, "OutputPath" );\r
236                                         string outputDirectory = Path.Combine(projectDirectory, outputPath);\r
237                                         string assemblyPath = Path.Combine(outputDirectory, assemblyName);\r
238 \r
239                                         VSProjectConfig config = new VSProjectConfig(name);\r
240                                         config.Assemblies.Add(assemblyPath);\r
241 \r
242                                         configs.Add(config);\r
243                                 }\r
244 \r
245                         return true;\r
246                 }\r
247 \r
248                 private bool LoadMSBuildProject(string projectDirectory, XmlDocument doc)\r
249                 {\r
250                         XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);\r
251                         namespaceManager.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003");\r
252 \r
253                         XmlNodeList nodes = doc.SelectNodes("/msbuild:Project/msbuild:PropertyGroup", namespaceManager);\r
254                         if (nodes == null) return false;\r
255 \r
256                         XmlElement assemblyNameElement = (XmlElement)doc.SelectSingleNode("/msbuild:Project/msbuild:PropertyGroup/msbuild:AssemblyName", namespaceManager);\r
257                         string assemblyName = assemblyNameElement.InnerText;\r
258 \r
259                         XmlElement outputTypeElement = (XmlElement)doc.SelectSingleNode("/msbuild:Project/msbuild:PropertyGroup/msbuild:OutputType", namespaceManager);\r
260                         string outputType = outputTypeElement.InnerText;\r
261 \r
262                         if (outputType == "Exe" || outputType == "WinExe")\r
263                                 assemblyName = assemblyName + ".exe";\r
264                         else\r
265                                 assemblyName = assemblyName + ".dll";\r
266 \r
267                         foreach (XmlElement configNode in nodes)\r
268                         {\r
269                 if (configNode.Name != "PropertyGroup")\r
270                     continue;\r
271 \r
272                                 XmlAttribute conditionAttribute = configNode.Attributes["Condition"];\r
273                                 if (conditionAttribute == null) continue;\r
274 \r
275                                 string condition = conditionAttribute.Value;\r
276                                 int start = condition.IndexOf( "==" );\r
277                                 if ( start < 0 ) continue;\r
278 \r
279                                 string configurationName = condition.Substring( start + 2 ).Trim(new char[] {' ', '\'' } );\r
280                                 if ( configurationName.EndsWith( "|AnyCPU" ) )\r
281                                         configurationName = configurationName.Substring( 0, configurationName.Length - 7 );\r
282 \r
283                                 XmlElement outputPathElement = (XmlElement)configNode.SelectSingleNode("msbuild:OutputPath", namespaceManager);\r
284                                 string outputPath = outputPathElement.InnerText;\r
285 \r
286                                 string outputDirectory = Path.Combine(projectDirectory, outputPath);\r
287                                 string assemblyPath = Path.Combine(outputDirectory, assemblyName);\r
288 \r
289                                 VSProjectConfig config = new VSProjectConfig(configurationName);\r
290                                 config.Assemblies.Add(assemblyPath);\r
291 \r
292                                 configs.Add(config);\r
293                         }\r
294 \r
295                         return true;\r
296                 }\r
297 \r
298                 private void ThrowInvalidFileType(string projectPath)\r
299                 {\r
300                         throw new ArgumentException( \r
301                                 string.Format( "Invalid project file type: {0}", \r
302                                                                 Path.GetFileName( projectPath ) ) );\r
303                 }\r
304 \r
305                 private void ThrowInvalidFormat( string projectPath, Exception e )\r
306                 {\r
307                         throw new ArgumentException( \r
308                                 string.Format( "Invalid project file format: {0}", \r
309                                                                 Path.GetFileName( projectPath ) ), e );\r
310                 }\r
311 \r
312                 private string SafeAttributeValue( XmlNode node, string attrName )\r
313                 {\r
314                         XmlNode attrNode = node.Attributes[attrName];\r
315                         return attrNode == null ? null : attrNode.Value;\r
316                 }\r
317 \r
318                 private string RequiredAttributeValue( XmlNode node, string name )\r
319                 {\r
320                         string result = SafeAttributeValue( node, name );\r
321                         if ( result != null )\r
322                                 return result;\r
323 \r
324                         throw new ApplicationException( "Missing required attribute " + name );\r
325                 }\r
326                 #endregion\r
327         }\r
328 }\r