Datei: NDOPackage/ProjectDescription.cs
Last Commit (33e9857)
| 1 | // |
| 2 | // Copyright ( c) 2002-2019 Mirko Matytschak |
| 3 | // (www.netdataobjects.de) |
| 4 | // |
| 5 | // Author: Mirko Matytschak |
| 6 | // |
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
| 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
| 9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
| 10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
| 11 | // conditions: |
| 12 | |
| 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
| 14 | // of the Software. |
| 15 | // |
| 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
| 17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| 19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| 20 | // DEALINGS IN THE SOFTWARE. |
| 21 | |
| 22 | |
| 23 | using System; |
| 24 | using SD = System.Diagnostics; |
| 25 | using System.Xml; |
| 26 | using System.Collections; |
| 27 | using System.Collections.Generic; |
| 28 | using System.IO; |
| 29 | using Microsoft.VisualStudio.Shell.Interop; |
| 30 | using Microsoft.VisualStudio.Shell; |
| 31 | using System.Linq; |
| 32 | using EnvDTE; |
| 33 | using Project = Community.VisualStudio.Toolkit.Project; |
| 34 | using Solution = Community.VisualStudio.Toolkit.Solution; |
| 35 | |
| 36 | namespace NDOVsPackage |
| 37 | { |
| 38 | |
| 39 | ····/// <summary> |
| 40 | ····/// ProjectDescription. |
| 41 | ····/// </summary> |
| 42 | ····[SD.CodeAnalysis.SuppressMessage( "Usage", "VSTHRD010:Invoke single-threaded types on Main thread", Justification = "This code always runs on the UI thread" )] |
| 43 | ····internal class ProjectDescription |
| 44 | ····{ |
| 45 | ········Solution solution = null; |
| 46 | ········Project project = null; |
| 47 | ········Dictionary<string, NDOReference> references = null; |
| 48 | ········bool debug; |
| 49 | ········string binFile; |
| 50 | ········string objPath; |
| 51 | ········string projPath; |
| 52 | ········string assemblyName; |
| 53 | ········ConfigurationOptions options; |
| 54 | ········bool isWebProject; |
| 55 | ········string keyFile = string.Empty; |
| 56 | ········string platformTarget; |
| 57 | ········string targetFramework; |
| 58 | |
| 59 | #if DEBUG |
| 60 | ········MessageAdapter messageAdapter; |
| 61 | ········public MessageAdapter MessageAdapter |
| 62 | ········{ |
| 63 | ············get { return messageAdapter; } |
| 64 | ············set { messageAdapter = value; } |
| 65 | ········} |
| 66 | #endif |
| 67 | |
| 68 | ········public string TargetFramework |
| 69 | ········{ |
| 70 | ············get { return this.targetFramework; } |
| 71 | ········} |
| 72 | |
| 73 | ········public string PlatformTarget |
| 74 | ········{ |
| 75 | ············get { return this.platformTarget; } |
| 76 | ········} |
| 77 | |
| 78 | ········public string KeyFile |
| 79 | ········{ |
| 80 | ············get { return keyFile; } |
| 81 | ············set { keyFile = value; } |
| 82 | ········} |
| 83 | |
| 84 | ········public bool IsWebProject |
| 85 | ········{ |
| 86 | ············get { return isWebProject; } |
| 87 | ········} |
| 88 | |
| 89 | ········public ConfigurationOptions ConfigurationOptions |
| 90 | ········{ |
| 91 | ············get { return options; } |
| 92 | ········} |
| 93 | |
| 94 | ········public string AssemblyName |
| 95 | ········{ |
| 96 | ············get { return assemblyName; } |
| 97 | ············set { assemblyName = value; } |
| 98 | ········} |
| 99 | |
| 100 | ········public ConfigurationOptions NewConfigurationOptions() |
| 101 | ········{ |
| 102 | ············return new ConfigurationOptions(project); |
| 103 | ········} |
| 104 | |
| 105 | ········public ProjectDescription() |
| 106 | ········{ |
| 107 | ········} |
| 108 | |
| 109 | ········public ProjectDescription(string fileName) |
| 110 | ········{ |
| 111 | ············this.projPath = Path.GetDirectoryName(fileName) + Path.DirectorySeparatorChar; |
| 112 | ············XmlDocument doc = new XmlDocument(); |
| 113 | ············try |
| 114 | ············{ |
| 115 | ················doc.Load(fileName); |
| 116 | ············} |
| 117 | ············catch(XmlException ex) |
| 118 | ············{ |
| 119 | ················throw new Exception("Parameter file '" + fileName + "' is not a valid Xml file. Line: " + ex.LineNumber.ToString() + " Position: " + ex.LinePosition.ToString()); |
| 120 | ············} |
| 121 | ············XmlHelper.AddNamespace(doc); |
| 122 | ············Load(doc); |
| 123 | ············options = new ConfigurationOptions(doc); |
| 124 | ········} |
| 125 | |
| 126 | ········string AbsolutePath(string path) |
| 127 | ········{ |
| 128 | ············if (Path.IsPathRooted(path)) |
| 129 | ················return path; |
| 130 | ············else |
| 131 | ················return Path.Combine(projPath, path); |
| 132 | ········} |
| 133 | |
| 134 | ········void Load(XmlDocument doc) |
| 135 | ········{ |
| 136 | ············string pns = XmlHelper.Pns(doc); |
| 137 | |
| 138 | ············XmlNode node = doc.SelectSingleNode("//" + pns + "Enhancer/" + pns + "ProjectDescription", XmlHelper.Nsmgr); |
| 139 | ············if (node == null) |
| 140 | ················throw new Exception("Parameters must have at least one //Enhancer/ProjectDescription entry."); |
| 141 | ············ |
| 142 | ············binFile = AbsolutePath((string) XmlHelper.GetNode(node, pns + "BinPath")); |
| 143 | ············objPath = AbsolutePath((string) XmlHelper.GetNode(node, pns + "ObjPath")); |
| 144 | ············keyFile = (string)XmlHelper.GetNode(node, pns + "KeyFile", string.Empty); |
| 145 | ············if (keyFile != string.Empty) |
| 146 | ················keyFile = AbsolutePath(keyFile); |
| 147 | ············assemblyName = (string)XmlHelper.GetNode(node, pns + "AssemblyName"); |
| 148 | ············debug = (bool) XmlHelper.GetNode(node, pns + "Debug", false); |
| 149 | ············isWebProject = (bool) XmlHelper.GetNode(node, pns + "IsWebProject", false); |
| 150 | ············XmlNodeList refList = doc.SelectNodes("//" + pns + "Enhancer/" + pns + "ProjectDescription/" + pns + "References/" + pns + "Reference", XmlHelper.Nsmgr); |
| 151 | ············references = new Dictionary<string, NDOReference>(); |
| 152 | ············foreach ( XmlNode rnode in refList ) |
| 153 | ············{ |
| 154 | ················string assName = (string) XmlHelper.GetAttribute(rnode, "AssemblyName"); |
| 155 | ················string assPath = AbsolutePath((string) XmlHelper.GetAttribute(rnode, "AssemblyPath")); |
| 156 | ················bool processDLL = true; |
| 157 | ················XmlAttribute attr = rnode.Attributes["CheckThisDLL"]; |
| 158 | ················if ( attr != null && string.Compare( attr.Value, "True", true ) != 0 ) |
| 159 | ····················processDLL = false; |
| 160 | |
| 161 | ················AddReference( assName, assPath, processDLL ); |
| 162 | ············} |
| 163 | ········} |
| 164 | |
| 165 | |
| 166 | ········private XmlNode MakeNode(string name, object value, XmlNode parentNode, XmlDocument doc) |
| 167 | ········{ |
| 168 | ············XmlElement el = doc.CreateElement(name); |
| 169 | ············parentNode.AppendChild(el); |
| 170 | ············if (value != null) |
| 171 | ················el.InnerText = value.ToString(); |
| 172 | ············return el; |
| 173 | ········} |
| 174 | |
| 175 | |
| 176 | ········public void ToXml(XmlNode parent) |
| 177 | ········{ |
| 178 | ············XmlDocument doc = (XmlDocument) parent.ParentNode; |
| 179 | ············XmlNode node = doc.CreateElement("ProjectDescription"); |
| 180 | ············parent.AppendChild(node); |
| 181 | ············string reference = this.projPath; |
| 182 | ············if (reference.EndsWith("\\")) |
| 183 | ················reference = reference.Substring(0, reference.Length - 1); |
| 184 | |
| 185 | ············MakeNode("BinPath", ExtendedPath.GetRelativePath(reference, binFile), node, doc); |
| 186 | ············MakeNode("ObjPath", ExtendedPath.GetRelativePath(reference, objPath), node, doc); |
| 187 | ············MakeNode("AssemblyName", assemblyName, node, doc); |
| 188 | ············MakeNode("Debug", debug, node, doc); |
| 189 | ············MakeNode("IsWebProject", isWebProject, node, doc); |
| 190 | ············MakeNode("KeyFile", keyFile, node, doc); |
| 191 | ············XmlNode refsNode = MakeNode("References", string.Empty, node, doc); |
| 192 | ············foreach ( string key in References.Keys ) |
| 193 | ············{ |
| 194 | ················NDOReference ndoreference = References[key]; |
| 195 | ················if ( ndoreference.Path == binFile ) |
| 196 | ····················continue; |
| 197 | ················XmlElement refNode = (XmlElement) MakeNode( "Reference", "", refsNode, doc ); |
| 198 | ················refNode.SetAttribute( "AssemblyName", ndoreference.Name ); |
| 199 | ················refNode.SetAttribute( "AssemblyPath", ExtendedPath.GetRelativePath( reference, ndoreference.Path ) ); |
| 200 | ················if ( !ndoreference.CheckThisDLL ) |
| 201 | ················{ |
| 202 | ····················refNode.SetAttribute( "CheckThisDLL", "False" ); |
| 203 | ················} |
| 204 | ············} |
| 205 | ········} |
| 206 | |
| 207 | ········string GetBuildProperty(IVsBuildPropertyStorage propertyStorage, string key, string configuration = "") |
| 208 | ········{ |
| 209 | ············string result = null; |
| 210 | ············if (propertyStorage != null) |
| 211 | ················propertyStorage.GetPropertyValue( key, configuration, (uint)_PersistStorageType.PST_PROJECT_FILE, out result ); |
| 212 | ············return result; |
| 213 | ········} |
| 214 | |
| 215 | ········public ProjectDescription( Project project ) |
| 216 | ········{ |
| 217 | ············this.solution = (Solution) project.Parent; |
| 218 | ············this.project = project; |
| 219 | |
| 220 | ············ThreadHelper.ThrowIfNotOnUIThread(); |
| 221 | ············var dteProj = project.DteProject(); |
| 222 | ············EnvDTE.Configuration conf = dteProj.ConfigurationManager.ActiveConfiguration; |
| 223 | ············//foreach (Property item in conf.Properties) |
| 224 | ············//{ |
| 225 | ············//····SD.Debug.WriteLine( $"{item.Name} = {item.Value}" ); |
| 226 | ············//} |
| 227 | |
| 228 | |
| 229 | ············// Get the MSBuild property storage |
| 230 | ············IVsBuildPropertyStorage propertyStorage = GetPropertyStorage( project ); |
| 231 | |
| 232 | ············try |
| 233 | ············{ |
| 234 | ················this.platformTarget = (string) conf.Properties.Item( "PlatformTarget" ).Value; |
| 235 | ············} |
| 236 | ············catch { } |
| 237 | ············try |
| 238 | ············{ |
| 239 | ················this.targetFramework = (string) dteProj.Properties.Item( "TargetFrameworkMoniker" ).Value; |
| 240 | ············} |
| 241 | ············catch { } |
| 242 | |
| 243 | |
| 244 | ············string outputPath = (string) conf.Properties.Item( "OutputPath" ).Value; |
| 245 | ············string fullPath = dteProj.Properties.Item( "FullPath" ).Value as string; |
| 246 | ············string outputFileName = GetBuildProperty( propertyStorage, "TargetFileName" ); |
| 247 | |
| 248 | ············if (String.IsNullOrEmpty( outputFileName )) |
| 249 | ············{ |
| 250 | ················int outputType = (int) dteProj.Properties.Item( "OutputType" ).Value; |
| 251 | ················// .NET Core Executables are dlls. |
| 252 | ················if (this.targetFramework.StartsWith( ".NETCoreApp" )) |
| 253 | ····················outputType = 2; |
| 254 | ················outputFileName = (string) dteProj.Properties.Item( "AssemblyName" ).Value + ( outputType == 2 ? ".dll" : ".exe" ); |
| 255 | ············} |
| 256 | |
| 257 | ············if (project.GetVsHierarchy().IsCapabilityMatch( "CPS" )) |
| 258 | ············{ |
| 259 | ················// new .csproj format |
| 260 | ················objPath = GetBuildProperty( propertyStorage, "IntermediateOutputPath" ); |
| 261 | ················string configuration = GetBuildProperty( propertyStorage, "Configuration" ); |
| 262 | ················debug = configuration == "Debug"; |
| 263 | ············} |
| 264 | ············else |
| 265 | ············{ |
| 266 | ················// old .csproj format |
| 267 | ················string debugInfo = (string) conf.Properties.Item( "DebugInfo" ).Value; |
| 268 | ················debug = debugInfo == "full"; |
| 269 | ················objPath = (string) conf.Properties.Item( "IntermediatePath" ).Value; |
| 270 | ············} |
| 271 | ············binFile = Path.Combine( fullPath, outputPath ); |
| 272 | ············binFile = Path.Combine( binFile, outputFileName ); |
| 273 | ············projPath = Path.GetDirectoryName( dteProj.FileName ) + "\\"; |
| 274 | ············string sign = GetBuildProperty( propertyStorage, "SignAssembly" ); |
| 275 | ············if (!String.IsNullOrEmpty( sign ) && String.Compare( sign, "true", true ) == 0) |
| 276 | ················keyFile = GetBuildProperty( propertyStorage, "AssemblyOriginatorKeyFile" ); |
| 277 | ············else |
| 278 | ················keyFile = null; |
| 279 | |
| 280 | ········} |
| 281 | |
| 282 | |
| 283 | ········private static IVsBuildPropertyStorage GetPropertyStorage( Project project ) |
| 284 | ········{ |
| 285 | ············ThreadHelper.ThrowIfNotOnUIThread(); |
| 286 | ············IVsHierarchy projectHierarchy = project.GetVsHierarchy(); |
| 287 | |
| 288 | ············IVsBuildPropertyStorage propertyStorage = null; |
| 289 | |
| 290 | ············if (projectHierarchy != null) |
| 291 | ············{ |
| 292 | ················propertyStorage = (IVsBuildPropertyStorage)projectHierarchy; |
| 293 | ············} |
| 294 | |
| 295 | ············return propertyStorage; |
| 296 | ········} |
| 297 | |
| 298 | ········public string ObjPath |
| 299 | ········{ |
| 300 | ············get { return objPath; } |
| 301 | ············set { objPath = value; } |
| 302 | ········} |
| 303 | |
| 304 | ········public string BinFile |
| 305 | ········{ |
| 306 | ············get { return binFile; } |
| 307 | ············set { binFile = value; } |
| 308 | ········} |
| 309 | |
| 310 | ········public bool Debug |
| 311 | ········{ |
| 312 | ············get { return debug; } |
| 313 | ············set { debug = value; } |
| 314 | ········} |
| 315 | |
| 316 | ········public string ProjPath |
| 317 | ········{ |
| 318 | ············get { return projPath; } |
| 319 | ············set { projPath = value; } |
| 320 | ········} |
| 321 | |
| 322 | ········private void AddReference( string name, string path, bool checkThisDLL ) |
| 323 | ········{ |
| 324 | ············if ( !references.ContainsKey( name ) ) |
| 325 | ················references.Add( name, new NDOReference( name, path, checkThisDLL ) ); |
| 326 | ········} |
| 327 | |
| 328 | |
| 329 | ········public void BuildReferences() |
| 330 | ········{ |
| 331 | ············if (this.references != null) |
| 332 | ················return; |
| 333 | |
| 334 | ············this.references = new Dictionary<string,NDOReference>(); |
| 335 | ············var allProjects = new Dictionary<string, string>(); |
| 336 | ············var solution = ApplicationObject.VisualStudioApplication.Solution; |
| 337 | ············ |
| 338 | ············foreach(var p in new ProjectIterator(solution).Projects) |
| 339 | ············{ |
| 340 | ················if (p.Name == project.Name) continue; |
| 341 | ················EnvDTE.ConfigurationManager cman = p.ConfigurationManager; |
| 342 | ················if (cman == null) |
| 343 | ····················continue; |
| 344 | ················var········ conf = cman.ActiveConfiguration; |
| 345 | ················if (conf.Properties == null) |
| 346 | ····················continue; |
| 347 | ················try // Skip the project, if a property is not present |
| 348 | ················{ |
| 349 | ····················string outputPath = (string)conf.Properties.Item("OutputPath").Value; |
| 350 | ····················string fullPath = (string)p.Properties.Item("FullPath").Value; |
| 351 | ····················string outputFileName = GetBuildProperty( GetPropertyStorage(this.project), "TargetFileName" ); |
| 352 | ····················//messages.Output(fullPath + outputPath + outputFileName); |
| 353 | ····················if (!allProjects.ContainsKey(p.Name)) |
| 354 | ························allProjects.Add(p.Name, fullPath + outputPath + outputFileName); |
| 355 | ················} |
| 356 | ················catch { } |
| 357 | ············} |
| 358 | ············ |
| 359 | |
| 360 | ············foreach ( var r in this.project.References ) |
| 361 | ············{ |
| 362 | ················string rname = ""; |
| 363 | ················//if (r.SourceProject != null) |
| 364 | ················//····rname = r.SourceProject.Name; |
| 365 | ················//else |
| 366 | ················rname = r.Name; |
| 367 | ················if (rname == project.Name) continue; |
| 368 | |
| 369 | ················if (allProjects.ContainsKey( rname )) |
| 370 | ················{ |
| 371 | ····················AddReference( r.Name, (string) allProjects[rname], false ); |
| 372 | ················} |
| 373 | ················else |
| 374 | ················{ |
| 375 | ····················var vsRef = r.VsReference; |
| 376 | ····················var path = vsRef.FullPath; |
| 377 | ····················// Referenzen, die auf keine gültige DLL verweisen... |
| 378 | ····················if (!String.IsNullOrEmpty( path ) && NDOAssemblyChecker.IsEnhanced( path )) |
| 379 | ························AddReference( rname, path, false ); |
| 380 | ················} |
| 381 | ············} |
| 382 | //············AddReference(project.Name, this.binFile); |
| 383 | |
| 384 | ········} |
| 385 | |
| 386 | ········ProjectItems GetItemCollection(string fileName) |
| 387 | ········{ |
| 388 | ············string relPath = ExtendedPath.GetRelativePath(this.projPath, fileName); |
| 389 | ············var dteProj = project.DteProject(); |
| 390 | ············ProjectItems result = dteProj.ProjectItems; |
| 391 | ············if (relPath.IndexOf(":\\") > -1) |
| 392 | ················return result; |
| 393 | ············string[] splittedName = relPath.Split(new char[] { '\\' }); |
| 394 | ············for (int i = 0; i < splittedName.Length - 1; i++) |
| 395 | ············{ |
| 396 | ················string name = splittedName[i]; |
| 397 | ················ProjectItem pi = result.Item(name); |
| 398 | ················if (pi != null) |
| 399 | ····················result = pi.ProjectItems; |
| 400 | ················else |
| 401 | ····················break; |
| 402 | ············} |
| 403 | ············return result; |
| 404 | ········} |
| 405 | |
| 406 | ········public void FixDllState() |
| 407 | ········{ |
| 408 | ············// The package works with a transient version of the ProjectDescription, which will be saved |
| 409 | ············// after a successful Build. But we need the CheckThisDLL state from the saved version for the UI. |
| 410 | |
| 411 | ············var fileName = ConfigurationOptions.GetNdoProjFileName( project.FullPath ); |
| 412 | |
| 413 | ············if (!String.IsNullOrEmpty( fileName ) && File.Exists( fileName )) |
| 414 | ············{ |
| 415 | ················ProjectDescription storedDescription = new ProjectDescription( fileName ); |
| 416 | ················var storedReferences = storedDescription.references.Values.ToArray(); |
| 417 | ················foreach (var reference in this.references.Values) |
| 418 | ················{ |
| 419 | ····················var storedReference = storedReferences.FirstOrDefault( r => r.Name == reference.Name ); |
| 420 | ····················if (storedReference != null) |
| 421 | ····················{ |
| 422 | ························reference.CheckThisDLL = storedReference.CheckThisDLL; |
| 423 | ····················} |
| 424 | ················} |
| 425 | ············} |
| 426 | ········} |
| 427 | |
| 428 | ········public void AddFileToProject(string fileName) |
| 429 | ········{ |
| 430 | ············//TODO: Make the search hierarchical |
| 431 | ············if (project == null) |
| 432 | ················return; |
| 433 | ············if (!File.Exists(fileName)) |
| 434 | ················return; |
| 435 | ············bool found = false; |
| 436 | |
| 437 | ············ProjectItems itemCollection = GetItemCollection(fileName); |
| 438 | |
| 439 | ············foreach (ProjectItem pi in itemCollection) |
| 440 | ············{ |
| 441 | ················if (string.Compare(Path.GetFileName(pi.Name), Path.GetFileName(fileName), true) == 0) |
| 442 | ················{ |
| 443 | ····················found = true; |
| 444 | ····················break; |
| 445 | ················} |
| 446 | ············} |
| 447 | ············if (!found) |
| 448 | ············{ |
| 449 | #if DEBUG |
| 450 | ················messageAdapter.WriteLine("··Adding file to project: " + fileName); |
| 451 | #endif |
| 452 | ················this.project.DteProject().ProjectItems.AddFromFile(fileName); |
| 453 | ············} |
| 454 | ········} |
| 455 | |
| 456 | |
| 457 | ········public Dictionary<string, NDOReference> References |
| 458 | ········{ |
| 459 | ············get |
| 460 | ············{ |
| 461 | ················BuildReferences(); |
| 462 | ················return references; |
| 463 | ············} |
| 464 | ········} |
| 465 | |
| 466 | ····} |
| 467 | } |
| 468 | escription( fileName ); |
| 469 | ················var storedReferences = storedDescription.references.Values.ToArray(); |
| 470 | ················foreach (var reference in this.references.Values) |
| 471 | ················{ |
| 472 | ····················var storedReference = storedReferences.FirstOrDefault( r => r.Name == reference.Name ); |
| 473 | ····················if (storedReference != null) |
| 474 | ····················{ |
| 475 | ························reference.CheckThisDLL = storedReference.CheckThisDLL; |
| 476 | ····················} |
| 477 | ················} |
| 478 | ············} |
| 479 | ········} |
| 480 | |
| 481 | ········public void AddFileToProject(string fileName) |
| 482 | ········{ |
| 483 | ············//TODO: Make the search hierarchical |
| 484 | ············if (project == null) |
| 485 | ················return; |
| 486 | ············if (!File.Exists(fileName)) |
| 487 | ················return; |
| 488 | ············bool found = false; |
| 489 | |
| 490 | ············ProjectItems itemCollection = GetItemCollection(fileName); |
| 491 | |
| 492 | ············foreach (ProjectItem pi in itemCollection) |
| 493 | ············{ |
| 494 | ················if (string.Compare(Path.GetFileName(pi.Name), Path.GetFileName(fileName), true) == 0) |
| 495 | ················{ |
| 496 | ····················found = true; |
| 497 | ····················break; |
| 498 | ················} |
| 499 | ············} |
| 500 | ············if (!found) |
| 501 | ············{ |
| 502 | #if DEBUG |
| 503 | ················messageAdapter.WriteLine("··Adding file to project: " + fileName); |
| 504 | #endif |
| 505 | ················this.project.DteProject().ProjectItems.AddFromFile(fileName); |
| 506 | ············} |
| 507 | ········} |
| 508 | |
| 509 | |
| 510 | ········public Dictionary<string, NDOReference> References |
| 511 | ········{ |
| 512 | ············get |
| 513 | ············{ |
| 514 | ················BuildReferences(); |
| 515 | ················return references; |
| 516 | ············} |
| 517 | ········} |
| 518 | |
| 519 | ····} |
| 520 | } |
| 521 |
New Commit (aa458ff)
| 1 | // |
| 2 | // Copyright ( c) 2002-2022 Mirko Matytschak |
| 3 | // (www.netdataobjects.de) |
| 4 | // |
| 5 | // Author: Mirko Matytschak |
| 6 | // |
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
| 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
| 9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
| 10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
| 11 | // conditions: |
| 12 | |
| 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
| 14 | // of the Software. |
| 15 | // |
| 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
| 17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| 19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| 20 | // DEALINGS IN THE SOFTWARE. |
| 21 | |
| 22 | |
| 23 | using System; |
| 24 | using SD = System.Diagnostics; |
| 25 | using System.Xml; |
| 26 | using System.Collections; |
| 27 | using System.Collections.Generic; |
| 28 | using System.IO; |
| 29 | using Microsoft.VisualStudio.Shell.Interop; |
| 30 | using Microsoft.VisualStudio.Shell; |
| 31 | using System.Linq; |
| 32 | using EnvDTE; |
| 33 | using Project = Community.VisualStudio.Toolkit.Project; |
| 34 | using Solution = Community.VisualStudio.Toolkit.Solution; |
| 35 | |
| 36 | namespace NDOVsPackage |
| 37 | { |
| 38 | |
| 39 | ····/// <summary> |
| 40 | ····/// ProjectDescription. |
| 41 | ····/// </summary> |
| 42 | ····[SD.CodeAnalysis.SuppressMessage( "Usage", "VSTHRD010:Invoke single-threaded types on Main thread", Justification = "This code always runs on the UI thread" )] |
| 43 | ····internal class ProjectDescription |
| 44 | ····{ |
| 45 | ········Solution solution = null; |
| 46 | ········Project project = null; |
| 47 | ········Dictionary<string, NDOReference> references = null; |
| 48 | ········bool debug; |
| 49 | ········string binFile; |
| 50 | ········string objPath; |
| 51 | ········string projPath; |
| 52 | ········string assemblyName; |
| 53 | ········ConfigurationOptions options; |
| 54 | ········bool isWebProject; |
| 55 | ········string keyFile = string.Empty; |
| 56 | ········string platformTarget; |
| 57 | ········string targetFramework; |
| 58 | |
| 59 | #if DEBUG |
| 60 | ········MessageAdapter messageAdapter; |
| 61 | ········public MessageAdapter MessageAdapter |
| 62 | ········{ |
| 63 | ············get { return messageAdapter; } |
| 64 | ············set { messageAdapter = value; } |
| 65 | ········} |
| 66 | #endif |
| 67 | |
| 68 | ········public string TargetFramework |
| 69 | ········{ |
| 70 | ············get { return this.targetFramework; } |
| 71 | ········} |
| 72 | |
| 73 | ········public string PlatformTarget |
| 74 | ········{ |
| 75 | ············get { return this.platformTarget; } |
| 76 | ········} |
| 77 | |
| 78 | ········public string KeyFile |
| 79 | ········{ |
| 80 | ············get { return keyFile; } |
| 81 | ············set { keyFile = value; } |
| 82 | ········} |
| 83 | |
| 84 | ········public bool IsWebProject |
| 85 | ········{ |
| 86 | ············get { return isWebProject; } |
| 87 | ········} |
| 88 | |
| 89 | ········public ConfigurationOptions ConfigurationOptions |
| 90 | ········{ |
| 91 | ············get { return options; } |
| 92 | ········} |
| 93 | |
| 94 | ········public string AssemblyName |
| 95 | ········{ |
| 96 | ············get { return assemblyName; } |
| 97 | ············set { assemblyName = value; } |
| 98 | ········} |
| 99 | |
| 100 | ········public ConfigurationOptions NewConfigurationOptions() |
| 101 | ········{ |
| 102 | ············return new ConfigurationOptions(project); |
| 103 | ········} |
| 104 | |
| 105 | ········public ProjectDescription() |
| 106 | ········{ |
| 107 | ········} |
| 108 | |
| 109 | ········public ProjectDescription(string fileName) |
| 110 | ········{ |
| 111 | ············this.projPath = Path.GetDirectoryName(fileName) + Path.DirectorySeparatorChar; |
| 112 | ············XmlDocument doc = new XmlDocument(); |
| 113 | ············try |
| 114 | ············{ |
| 115 | ················doc.Load(fileName); |
| 116 | ············} |
| 117 | ············catch(XmlException ex) |
| 118 | ············{ |
| 119 | ················throw new Exception("Parameter file '" + fileName + "' is not a valid Xml file. Line: " + ex.LineNumber.ToString() + " Position: " + ex.LinePosition.ToString()); |
| 120 | ············} |
| 121 | ············XmlHelper.AddNamespace(doc); |
| 122 | ············Load(doc); |
| 123 | ············options = new ConfigurationOptions(doc); |
| 124 | ········} |
| 125 | |
| 126 | ········string AbsolutePath(string path) |
| 127 | ········{ |
| 128 | ············if (Path.IsPathRooted(path)) |
| 129 | ················return path; |
| 130 | ············else |
| 131 | ················return Path.Combine(projPath, path); |
| 132 | ········} |
| 133 | |
| 134 | ········void Load(XmlDocument doc) |
| 135 | ········{ |
| 136 | ············string pns = XmlHelper.Pns(doc); |
| 137 | |
| 138 | ············XmlNode node = doc.SelectSingleNode("//" + pns + "Enhancer/" + pns + "ProjectDescription", XmlHelper.Nsmgr); |
| 139 | ············if (node == null) |
| 140 | ················throw new Exception("Parameters must have at least one //Enhancer/ProjectDescription entry."); |
| 141 | ············ |
| 142 | ············binFile = AbsolutePath((string) XmlHelper.GetNode(node, pns + "BinPath")); |
| 143 | ············objPath = AbsolutePath((string) XmlHelper.GetNode(node, pns + "ObjPath")); |
| 144 | ············keyFile = (string)XmlHelper.GetNode(node, pns + "KeyFile", string.Empty); |
| 145 | ············if (keyFile != string.Empty) |
| 146 | ················keyFile = AbsolutePath(keyFile); |
| 147 | ············assemblyName = (string)XmlHelper.GetNode(node, pns + "AssemblyName"); |
| 148 | ············debug = (bool) XmlHelper.GetNode(node, pns + "Debug", false); |
| 149 | ············isWebProject = (bool) XmlHelper.GetNode(node, pns + "IsWebProject", false); |
| 150 | ············XmlNodeList refList = doc.SelectNodes("//" + pns + "Enhancer/" + pns + "ProjectDescription/" + pns + "References/" + pns + "Reference", XmlHelper.Nsmgr); |
| 151 | ············references = new Dictionary<string, NDOReference>(); |
| 152 | ············foreach ( XmlNode rnode in refList ) |
| 153 | ············{ |
| 154 | ················string assName = (string) XmlHelper.GetAttribute(rnode, "AssemblyName"); |
| 155 | ················string assPath = AbsolutePath((string) XmlHelper.GetAttribute(rnode, "AssemblyPath")); |
| 156 | ················bool processDLL = true; |
| 157 | ················XmlAttribute attr = rnode.Attributes["CheckThisDLL"]; |
| 158 | ················if ( attr != null && string.Compare( attr.Value, "True", true ) != 0 ) |
| 159 | ····················processDLL = false; |
| 160 | |
| 161 | ················AddReference( assName, assPath, processDLL ); |
| 162 | ············} |
| 163 | ········} |
| 164 | |
| 165 | |
| 166 | ········private XmlNode MakeNode(string name, object value, XmlNode parentNode, XmlDocument doc) |
| 167 | ········{ |
| 168 | ············XmlElement el = doc.CreateElement(name); |
| 169 | ············parentNode.AppendChild(el); |
| 170 | ············if (value != null) |
| 171 | ················el.InnerText = value.ToString(); |
| 172 | ············return el; |
| 173 | ········} |
| 174 | |
| 175 | |
| 176 | ········public void ToXml(XmlNode parent) |
| 177 | ········{ |
| 178 | ············XmlDocument doc = (XmlDocument) parent.ParentNode; |
| 179 | ············XmlNode node = doc.CreateElement("ProjectDescription"); |
| 180 | ············parent.AppendChild(node); |
| 181 | ············string reference = this.projPath; |
| 182 | ············if (reference.EndsWith("\\")) |
| 183 | ················reference = reference.Substring(0, reference.Length - 1); |
| 184 | |
| 185 | ············MakeNode("BinPath", ExtendedPath.GetRelativePath(reference, binFile), node, doc); |
| 186 | ············MakeNode("ObjPath", ExtendedPath.GetRelativePath(reference, objPath), node, doc); |
| 187 | ············MakeNode("AssemblyName", assemblyName, node, doc); |
| 188 | ············MakeNode("Debug", debug, node, doc); |
| 189 | ············MakeNode("IsWebProject", isWebProject, node, doc); |
| 190 | ············MakeNode("KeyFile", keyFile, node, doc); |
| 191 | ············XmlNode refsNode = MakeNode("References", string.Empty, node, doc); |
| 192 | ············foreach ( string key in References.Keys ) |
| 193 | ············{ |
| 194 | ················NDOReference ndoreference = References[key]; |
| 195 | ················if ( ndoreference.Path == binFile ) |
| 196 | ····················continue; |
| 197 | ················XmlElement refNode = (XmlElement) MakeNode( "Reference", "", refsNode, doc ); |
| 198 | ················refNode.SetAttribute( "AssemblyName", ndoreference.Name ); |
| 199 | ················refNode.SetAttribute( "AssemblyPath", ExtendedPath.GetRelativePath( reference, ndoreference.Path ) ); |
| 200 | ················if ( !ndoreference.CheckThisDLL ) |
| 201 | ················{ |
| 202 | ····················refNode.SetAttribute( "CheckThisDLL", "False" ); |
| 203 | ················} |
| 204 | ············} |
| 205 | ········} |
| 206 | |
| 207 | ········string GetBuildProperty(IVsBuildPropertyStorage propertyStorage, string key, string configuration = "") |
| 208 | ········{ |
| 209 | ············string result = null; |
| 210 | ············if (propertyStorage != null) |
| 211 | ················propertyStorage.GetPropertyValue( key, configuration, (uint)_PersistStorageType.PST_PROJECT_FILE, out result ); |
| 212 | ············return result; |
| 213 | ········} |
| 214 | |
| 215 | ········public ProjectDescription( Project project ) |
| 216 | ········{ |
| 217 | ············this.solution = (Solution) project.Parent; |
| 218 | ············this.project = project; |
| 219 | |
| 220 | ············ThreadHelper.ThrowIfNotOnUIThread(); |
| 221 | ············var dteProj = project.DteProject(); |
| 222 | ············EnvDTE.Configuration conf = dteProj.ConfigurationManager.ActiveConfiguration; |
| 223 | ············//foreach (Property item in conf.Properties) |
| 224 | ············//{ |
| 225 | ············//····SD.Debug.WriteLine( $"{item.Name} = {item.Value}" ); |
| 226 | ············//} |
| 227 | |
| 228 | |
| 229 | ············// Get the MSBuild property storage |
| 230 | ············IVsBuildPropertyStorage propertyStorage = GetPropertyStorage( project ); |
| 231 | |
| 232 | ············try |
| 233 | ············{ |
| 234 | ················this.platformTarget = (string) conf.Properties.Item( "PlatformTarget" ).Value; |
| 235 | ············} |
| 236 | ············catch { } |
| 237 | ············try |
| 238 | ············{ |
| 239 | ················this.targetFramework = (string) dteProj.Properties.Item( "TargetFrameworkMoniker" ).Value; |
| 240 | ············} |
| 241 | ············catch { } |
| 242 | |
| 243 | |
| 244 | ············string outputPath = (string) conf.Properties.Item( "OutputPath" ).Value; |
| 245 | ············string fullPath = dteProj.Properties.Item( "FullPath" ).Value as string; |
| 246 | ············string outputFileName = GetBuildProperty( propertyStorage, "TargetFileName" ); |
| 247 | |
| 248 | ············if (String.IsNullOrEmpty( outputFileName )) |
| 249 | ············{ |
| 250 | ················int outputType = (int) dteProj.Properties.Item( "OutputType" ).Value; |
| 251 | ················// .NET Core Executables are dlls. |
| 252 | ················if (this.targetFramework.StartsWith( ".NETCoreApp" )) |
| 253 | ····················outputType = 2; |
| 254 | ················outputFileName = (string) dteProj.Properties.Item( "AssemblyName" ).Value + ( outputType == 2 ? ".dll" : ".exe" ); |
| 255 | ············} |
| 256 | |
| 257 | ············if (project.GetVsHierarchy().IsCapabilityMatch( "CPS" )) |
| 258 | ············{ |
| 259 | ················// new .csproj format |
| 260 | ················objPath = GetBuildProperty( propertyStorage, "IntermediateOutputPath" ); |
| 261 | ················string configuration = GetBuildProperty( propertyStorage, "Configuration" ); |
| 262 | ················debug = configuration == "Debug"; |
| 263 | ············} |
| 264 | ············else |
| 265 | ············{ |
| 266 | ················// old .csproj format |
| 267 | ················string debugInfo = (string) conf.Properties.Item( "DebugInfo" ).Value; |
| 268 | ················debug = debugInfo == "full"; |
| 269 | ················objPath = (string) conf.Properties.Item( "IntermediatePath" ).Value; |
| 270 | ············} |
| 271 | ············binFile = Path.Combine( fullPath, outputPath ); |
| 272 | ············binFile = Path.Combine( binFile, outputFileName ); |
| 273 | ············projPath = Path.GetDirectoryName( dteProj.FileName ) + "\\"; |
| 274 | ············string sign = GetBuildProperty( propertyStorage, "SignAssembly" ); |
| 275 | ············if (!String.IsNullOrEmpty( sign ) && String.Compare( sign, "true", true ) == 0) |
| 276 | ················keyFile = GetBuildProperty( propertyStorage, "AssemblyOriginatorKeyFile" ); |
| 277 | ············else |
| 278 | ················keyFile = null; |
| 279 | |
| 280 | ········} |
| 281 | |
| 282 | |
| 283 | ········private static IVsBuildPropertyStorage GetPropertyStorage( Project project ) |
| 284 | ········{ |
| 285 | ············ThreadHelper.ThrowIfNotOnUIThread(); |
| 286 | ············IVsHierarchy projectHierarchy = project.GetVsHierarchy(); |
| 287 | |
| 288 | ············IVsBuildPropertyStorage propertyStorage = null; |
| 289 | |
| 290 | ············if (projectHierarchy != null) |
| 291 | ············{ |
| 292 | ················propertyStorage = (IVsBuildPropertyStorage)projectHierarchy; |
| 293 | ············} |
| 294 | |
| 295 | ············return propertyStorage; |
| 296 | ········} |
| 297 | |
| 298 | ········public string ObjPath |
| 299 | ········{ |
| 300 | ············get { return objPath; } |
| 301 | ············set { objPath = value; } |
| 302 | ········} |
| 303 | |
| 304 | ········public string BinFile |
| 305 | ········{ |
| 306 | ············get { return binFile; } |
| 307 | ············set { binFile = value; } |
| 308 | ········} |
| 309 | |
| 310 | ········public bool Debug |
| 311 | ········{ |
| 312 | ············get { return debug; } |
| 313 | ············set { debug = value; } |
| 314 | ········} |
| 315 | |
| 316 | ········public string ProjPath |
| 317 | ········{ |
| 318 | ············get { return projPath; } |
| 319 | ············set { projPath = value; } |
| 320 | ········} |
| 321 | |
| 322 | ········private void AddReference( string name, string path, bool checkThisDLL ) |
| 323 | ········{ |
| 324 | ············if ( !references.ContainsKey( name ) ) |
| 325 | ················references.Add( name, new NDOReference( name, path, checkThisDLL ) ); |
| 326 | ········} |
| 327 | |
| 328 | |
| 329 | ········public void BuildReferences() |
| 330 | ········{ |
| 331 | ············if (this.references != null) |
| 332 | ················return; |
| 333 | |
| 334 | ············this.references = new Dictionary<string,NDOReference>(); |
| 335 | ············var allProjects = new Dictionary<string, string>(); |
| 336 | ············var solution = ApplicationObject.VisualStudioApplication.Solution; |
| 337 | ············ |
| 338 | ············foreach(var p in new ProjectIterator(solution).Projects) |
| 339 | ············{ |
| 340 | ················if (p.Name == project.Name) continue; |
| 341 | ················EnvDTE.ConfigurationManager cman = p.ConfigurationManager; |
| 342 | ················if (cman == null) |
| 343 | ····················continue; |
| 344 | ················var········ conf = cman.ActiveConfiguration; |
| 345 | ················if (conf.Properties == null) |
| 346 | ····················continue; |
| 347 | ················try // Skip the project, if a property is not present |
| 348 | ················{ |
| 349 | ····················string outputPath = (string)conf.Properties.Item("OutputPath").Value; |
| 350 | ····················string fullPath = (string)p.Properties.Item("FullPath").Value; |
| 351 | ····················string outputFileName = GetBuildProperty( GetPropertyStorage(this.project), "TargetFileName" ); |
| 352 | ····················//messages.Output(fullPath + outputPath + outputFileName); |
| 353 | ····················if (!allProjects.ContainsKey(p.Name)) |
| 354 | ························allProjects.Add(p.Name, fullPath + outputPath + outputFileName); |
| 355 | ················} |
| 356 | ················catch { } |
| 357 | ············} |
| 358 | ············ |
| 359 | |
| 360 | ············foreach ( var r in this.project.References ) |
| 361 | ············{ |
| 362 | ················string rname = ""; |
| 363 | ················//if (r.SourceProject != null) |
| 364 | ················//····rname = r.SourceProject.Name; |
| 365 | ················//else |
| 366 | ················rname = r.Name; |
| 367 | ················if (rname == project.Name) continue; |
| 368 | |
| 369 | ················if (allProjects.ContainsKey( rname )) |
| 370 | ················{ |
| 371 | ····················AddReference( r.Name, (string) allProjects[rname], false ); |
| 372 | ················} |
| 373 | ················else |
| 374 | ················{ |
| 375 | ····················var vsRef = r.VsReference; |
| 376 | ····················var path = vsRef.FullPath; |
| 377 | ····················// Referenzen, die auf keine gültige DLL verweisen... |
| 378 | ····················if (!String.IsNullOrEmpty( path ) && NDOAssemblyChecker.IsEnhanced( path )) |
| 379 | ························AddReference( rname, path, false ); |
| 380 | ················} |
| 381 | ············} |
| 382 | //············AddReference(project.Name, this.binFile); |
| 383 | |
| 384 | ········} |
| 385 | |
| 386 | ········ProjectItems GetItemCollection(string fileName) |
| 387 | ········{ |
| 388 | ············string relPath = ExtendedPath.GetRelativePath(this.projPath, fileName); |
| 389 | ············var dteProj = project.DteProject(); |
| 390 | ············ProjectItems result = dteProj.ProjectItems; |
| 391 | ············if (relPath.IndexOf(":\\") > -1) |
| 392 | ················return result; |
| 393 | ············string[] splittedName = relPath.Split(new char[] { '\\' }); |
| 394 | ············for (int i = 0; i < splittedName.Length - 1; i++) |
| 395 | ············{ |
| 396 | ················string name = splittedName[i]; |
| 397 | ················ProjectItem pi = result.Item(name); |
| 398 | ················if (pi != null) |
| 399 | ····················result = pi.ProjectItems; |
| 400 | ················else |
| 401 | ····················break; |
| 402 | ············} |
| 403 | ············return result; |
| 404 | ········} |
| 405 | |
| 406 | ········public void FixDllState() |
| 407 | ········{ |
| 408 | ············// The package works with a transient version of the ProjectDescription, which will be saved |
| 409 | ············// after a successful Build. But we need the CheckThisDLL state from the saved version for the UI. |
| 410 | |
| 411 | ············var fileName = ConfigurationOptions.GetNdoProjFileName( project.FullPath ); |
| 412 | |
| 413 | ············if (!String.IsNullOrEmpty( fileName ) && File.Exists( fileName )) |
| 414 | ············{ |
| 415 | ················ProjectDescription storedDescription = new ProjectDescription( fileName ); |
| 416 | ················var storedReferences = storedDescription.references.Values.ToArray(); |
| 417 | ················foreach (var reference in this.references.Values) |
| 418 | ················{ |
| 419 | ····················var storedReference = storedReferences.FirstOrDefault( r => r.Name == reference.Name ); |
| 420 | ····················if (storedReference != null) |
| 421 | ····················{ |
| 422 | ························reference.CheckThisDLL = storedReference.CheckThisDLL; |
| 423 | ····················} |
| 424 | ················} |
| 425 | ············} |
| 426 | ········} |
| 427 | |
| 428 | ········public void AddFileToProject(string fileName) |
| 429 | ········{ |
| 430 | ············//TODO: Make the search hierarchical |
| 431 | ············if (project == null) |
| 432 | ················return; |
| 433 | ············if (!File.Exists(fileName)) |
| 434 | ················return; |
| 435 | ············bool found = false; |
| 436 | |
| 437 | ············ProjectItems itemCollection = GetItemCollection(fileName); |
| 438 | |
| 439 | ············foreach (ProjectItem pi in itemCollection) |
| 440 | ············{ |
| 441 | ················if (string.Compare(Path.GetFileName(pi.Name), Path.GetFileName(fileName), true) == 0) |
| 442 | ················{ |
| 443 | ····················found = true; |
| 444 | ····················break; |
| 445 | ················} |
| 446 | ············} |
| 447 | ············if (!found) |
| 448 | ············{ |
| 449 | #if DEBUG |
| 450 | ················messageAdapter.WriteLine("··Adding file to project: " + fileName); |
| 451 | #endif |
| 452 | ················this.project.DteProject().ProjectItems.AddFromFile(fileName); |
| 453 | ············} |
| 454 | ········} |
| 455 | |
| 456 | |
| 457 | ········public Dictionary<string, NDOReference> References |
| 458 | ········{ |
| 459 | ············get |
| 460 | ············{ |
| 461 | ················BuildReferences(); |
| 462 | ················return references; |
| 463 | ············} |
| 464 | ········} |
| 465 | |
| 466 | ····} |
| 467 | } |
| 468 |