PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectConfig.cs

http://github.com/IronLanguages/main
C# | 1023 lines | 751 code | 130 blank | 142 comment | 143 complexity | cb6e39942390a78a413a5cb6baee5024 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. * ***************************************************************************/
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Diagnostics;
  17. using System.Diagnostics.CodeAnalysis;
  18. using System.Globalization;
  19. using System.IO;
  20. using System.Runtime.InteropServices;
  21. using Microsoft.VisualStudio;
  22. using Microsoft.VisualStudio.OLE.Interop;
  23. using Microsoft.VisualStudio.Shell;
  24. //#define ConfigTrace
  25. using Microsoft.VisualStudio.Shell.Interop;
  26. using MSBuild = Microsoft.Build.Evaluation;
  27. using MSBuildExecution = Microsoft.Build.Execution;
  28. using MSBuildConstruction = Microsoft.Build.Construction;
  29. namespace Microsoft.VisualStudio.Project
  30. {
  31. [CLSCompliant(false), ComVisible(true)]
  32. public class ProjectConfig :
  33. IVsCfg,
  34. IVsProjectCfg,
  35. IVsProjectCfg2,
  36. IVsProjectFlavorCfg,
  37. IVsDebuggableProjectCfg,
  38. ISpecifyPropertyPages,
  39. IVsSpecifyProjectDesignerPages,
  40. IVsCfgBrowseObject
  41. {
  42. #region constants
  43. internal const string Debug = "Debug";
  44. internal const string Release = "Release";
  45. internal const string AnyCPU = "AnyCPU";
  46. #endregion
  47. #region fields
  48. private ProjectNode project;
  49. private string configName;
  50. private MSBuildExecution.ProjectInstance currentConfig;
  51. private List<OutputGroup> outputGroups;
  52. private IProjectConfigProperties configurationProperties;
  53. private IVsProjectFlavorCfg flavoredCfg;
  54. private BuildableProjectConfig buildableCfg;
  55. #endregion
  56. #region properties
  57. public ProjectNode ProjectMgr
  58. {
  59. get
  60. {
  61. return this.project;
  62. }
  63. }
  64. public string ConfigName
  65. {
  66. get
  67. {
  68. return this.configName;
  69. }
  70. set
  71. {
  72. this.configName = value;
  73. }
  74. }
  75. public virtual object ConfigurationProperties
  76. {
  77. get
  78. {
  79. if(this.configurationProperties == null)
  80. {
  81. this.configurationProperties = new ProjectConfigProperties(this);
  82. }
  83. return this.configurationProperties;
  84. }
  85. }
  86. protected IList<OutputGroup> OutputGroups
  87. {
  88. get
  89. {
  90. if(null == this.outputGroups)
  91. {
  92. // Initialize output groups
  93. this.outputGroups = new List<OutputGroup>();
  94. // Get the list of group names from the project.
  95. // The main reason we get it from the project is to make it easier for someone to modify
  96. // it by simply overriding that method and providing the correct MSBuild target(s).
  97. IList<KeyValuePair<string, string>> groupNames = project.GetOutputGroupNames();
  98. if(groupNames != null)
  99. {
  100. // Populate the output array
  101. foreach(KeyValuePair<string, string> group in groupNames)
  102. {
  103. OutputGroup outputGroup = CreateOutputGroup(project, group);
  104. this.outputGroups.Add(outputGroup);
  105. }
  106. }
  107. }
  108. return this.outputGroups;
  109. }
  110. }
  111. #endregion
  112. #region ctors
  113. public ProjectConfig(ProjectNode project, string configuration)
  114. {
  115. this.project = project;
  116. this.configName = configuration;
  117. // Because the project can be aggregated by a flavor, we need to make sure
  118. // we get the outer most implementation of that interface (hence: project --> IUnknown --> Interface)
  119. IntPtr projectUnknown = Marshal.GetIUnknownForObject(this.ProjectMgr);
  120. try
  121. {
  122. IVsProjectFlavorCfgProvider flavorCfgProvider = (IVsProjectFlavorCfgProvider)Marshal.GetTypedObjectForIUnknown(projectUnknown, typeof(IVsProjectFlavorCfgProvider));
  123. ErrorHandler.ThrowOnFailure(flavorCfgProvider.CreateProjectFlavorCfg(this, out flavoredCfg));
  124. if(flavoredCfg == null)
  125. throw new COMException();
  126. }
  127. finally
  128. {
  129. if(projectUnknown != IntPtr.Zero)
  130. Marshal.Release(projectUnknown);
  131. }
  132. // if the flavored object support XML fragment, initialize it
  133. IPersistXMLFragment persistXML = flavoredCfg as IPersistXMLFragment;
  134. if(null != persistXML)
  135. {
  136. this.project.LoadXmlFragment(persistXML, this.DisplayName);
  137. }
  138. }
  139. #endregion
  140. #region methods
  141. protected virtual OutputGroup CreateOutputGroup(ProjectNode project, KeyValuePair<string, string> group)
  142. {
  143. OutputGroup outputGroup = new OutputGroup(group.Key, group.Value, project, this);
  144. return outputGroup;
  145. }
  146. public void PrepareBuild(bool clean)
  147. {
  148. project.PrepareBuild(this.configName, clean);
  149. }
  150. public virtual string GetConfigurationProperty(string propertyName, bool resetCache)
  151. {
  152. MSBuildExecution.ProjectPropertyInstance property = GetMsBuildProperty(propertyName, resetCache);
  153. if (property == null)
  154. return null;
  155. return property.EvaluatedValue;
  156. }
  157. public virtual void SetConfigurationProperty(string propertyName, string propertyValue)
  158. {
  159. if(!this.project.QueryEditProjectFile(false))
  160. {
  161. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  162. }
  163. string condition = String.Format(CultureInfo.InvariantCulture, ConfigProvider.configString, this.ConfigName);
  164. SetPropertyUnderCondition(propertyName, propertyValue, condition);
  165. // property cache will need to be updated
  166. this.currentConfig = null;
  167. // Signal the output groups that something is changed
  168. foreach(OutputGroup group in this.OutputGroups)
  169. {
  170. group.InvalidateGroup();
  171. }
  172. this.project.SetProjectFileDirty(true);
  173. return;
  174. }
  175. /// <summary>
  176. /// Emulates the behavior of SetProperty(name, value, condition) on the old MSBuild object model.
  177. /// This finds a property group with the specified condition (or creates one if necessary) then sets the property in there.
  178. /// </summary>
  179. private void SetPropertyUnderCondition(string propertyName, string propertyValue, string condition)
  180. {
  181. string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim();
  182. if (conditionTrimmed.Length == 0)
  183. {
  184. this.project.BuildProject.SetProperty(propertyName, propertyValue);
  185. return;
  186. }
  187. // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition.
  188. // So do it ourselves.
  189. MSBuildConstruction.ProjectPropertyGroupElement newGroup = null;
  190. foreach (MSBuildConstruction.ProjectPropertyGroupElement group in this.project.BuildProject.Xml.PropertyGroups)
  191. {
  192. if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase))
  193. {
  194. newGroup = group;
  195. break;
  196. }
  197. }
  198. if (newGroup == null)
  199. {
  200. newGroup = this.project.BuildProject.Xml.AddPropertyGroup(); // Adds after last existing PG, else at start of project
  201. newGroup.Condition = condition;
  202. }
  203. foreach (MSBuildConstruction.ProjectPropertyElement property in newGroup.PropertiesReversed) // If there's dupes, pick the last one so we win
  204. {
  205. if (String.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) && property.Condition.Length == 0)
  206. {
  207. property.Value = propertyValue;
  208. return;
  209. }
  210. }
  211. newGroup.AddProperty(propertyName, propertyValue);
  212. }
  213. /// <summary>
  214. /// If flavored, and if the flavor config can be dirty, ask it if it is dirty
  215. /// </summary>
  216. /// <param name="storageType">Project file or user file</param>
  217. /// <returns>0 = not dirty</returns>
  218. internal int IsFlavorDirty(_PersistStorageType storageType)
  219. {
  220. int isDirty = 0;
  221. if(this.flavoredCfg != null && this.flavoredCfg is IPersistXMLFragment)
  222. {
  223. ErrorHandler.ThrowOnFailure(((IPersistXMLFragment)this.flavoredCfg).IsFragmentDirty((uint)storageType, out isDirty));
  224. }
  225. return isDirty;
  226. }
  227. /// <summary>
  228. /// If flavored, ask the flavor if it wants to provide an XML fragment
  229. /// </summary>
  230. /// <param name="flavor">Guid of the flavor</param>
  231. /// <param name="storageType">Project file or user file</param>
  232. /// <param name="fragment">Fragment that the flavor wants to save</param>
  233. /// <returns>HRESULT</returns>
  234. internal int GetXmlFragment(Guid flavor, _PersistStorageType storageType, out string fragment)
  235. {
  236. fragment = null;
  237. int hr = VSConstants.S_OK;
  238. if(this.flavoredCfg != null && this.flavoredCfg is IPersistXMLFragment)
  239. {
  240. Guid flavorGuid = flavor;
  241. hr = ((IPersistXMLFragment)this.flavoredCfg).Save(ref flavorGuid, (uint)storageType, out fragment, 1);
  242. }
  243. return hr;
  244. }
  245. #endregion
  246. #region IVsSpecifyPropertyPages
  247. public void GetPages(CAUUID[] pages)
  248. {
  249. this.GetCfgPropertyPages(pages);
  250. }
  251. #endregion
  252. #region IVsSpecifyProjectDesignerPages
  253. /// <summary>
  254. /// Implementation of the IVsSpecifyProjectDesignerPages. It will retun the pages that are configuration dependent.
  255. /// </summary>
  256. /// <param name="pages">The pages to return.</param>
  257. /// <returns>VSConstants.S_OK</returns>
  258. public virtual int GetProjectDesignerPages(CAUUID[] pages)
  259. {
  260. this.GetCfgPropertyPages(pages);
  261. return VSConstants.S_OK;
  262. }
  263. #endregion
  264. #region IVsCfg methods
  265. /// <summary>
  266. /// The display name is a two part item
  267. /// first part is the config name, 2nd part is the platform name
  268. /// </summary>
  269. public virtual int get_DisplayName(out string name)
  270. {
  271. name = DisplayName;
  272. return VSConstants.S_OK;
  273. }
  274. private string DisplayName
  275. {
  276. get
  277. {
  278. string name;
  279. string[] platform = new string[1];
  280. uint[] actual = new uint[1];
  281. name = this.configName;
  282. // currently, we only support one platform, so just add it..
  283. IVsCfgProvider provider;
  284. ErrorHandler.ThrowOnFailure(project.GetCfgProvider(out provider));
  285. ErrorHandler.ThrowOnFailure(((IVsCfgProvider2)provider).GetPlatformNames(1, platform, actual));
  286. if(!string.IsNullOrEmpty(platform[0]))
  287. {
  288. name += "|" + platform[0];
  289. }
  290. return name;
  291. }
  292. }
  293. public virtual int get_IsDebugOnly(out int fDebug)
  294. {
  295. fDebug = 0;
  296. if(this.configName == "Debug")
  297. {
  298. fDebug = 1;
  299. }
  300. return VSConstants.S_OK;
  301. }
  302. public virtual int get_IsReleaseOnly(out int fRelease)
  303. {
  304. CCITracing.TraceCall();
  305. fRelease = 0;
  306. if(this.configName == "Release")
  307. {
  308. fRelease = 1;
  309. }
  310. return VSConstants.S_OK;
  311. }
  312. #endregion
  313. #region IVsProjectCfg methods
  314. public virtual int EnumOutputs(out IVsEnumOutputs eo)
  315. {
  316. CCITracing.TraceCall();
  317. eo = null;
  318. return VSConstants.E_NOTIMPL;
  319. }
  320. public virtual int get_BuildableProjectCfg(out IVsBuildableProjectCfg pb)
  321. {
  322. CCITracing.TraceCall();
  323. if(buildableCfg == null)
  324. buildableCfg = new BuildableProjectConfig(this);
  325. pb = buildableCfg;
  326. return VSConstants.S_OK;
  327. }
  328. public virtual int get_CanonicalName(out string name)
  329. {
  330. return ((IVsCfg)this).get_DisplayName(out name);
  331. }
  332. public virtual int get_IsPackaged(out int pkgd)
  333. {
  334. CCITracing.TraceCall();
  335. pkgd = 0;
  336. return VSConstants.S_OK;
  337. }
  338. public virtual int get_IsSpecifyingOutputSupported(out int f)
  339. {
  340. CCITracing.TraceCall();
  341. f = 1;
  342. return VSConstants.S_OK;
  343. }
  344. public virtual int get_Platform(out Guid platform)
  345. {
  346. CCITracing.TraceCall();
  347. platform = Guid.Empty;
  348. return VSConstants.E_NOTIMPL;
  349. }
  350. public virtual int get_ProjectCfgProvider(out IVsProjectCfgProvider p)
  351. {
  352. CCITracing.TraceCall();
  353. p = null;
  354. IVsCfgProvider cfgProvider = null;
  355. this.project.GetCfgProvider(out cfgProvider);
  356. if(cfgProvider != null)
  357. {
  358. p = cfgProvider as IVsProjectCfgProvider;
  359. }
  360. return (null == p) ? VSConstants.E_NOTIMPL : VSConstants.S_OK;
  361. }
  362. public virtual int get_RootURL(out string root)
  363. {
  364. CCITracing.TraceCall();
  365. root = null;
  366. return VSConstants.S_OK;
  367. }
  368. public virtual int get_TargetCodePage(out uint target)
  369. {
  370. CCITracing.TraceCall();
  371. target = (uint)System.Text.Encoding.Default.CodePage;
  372. return VSConstants.S_OK;
  373. }
  374. public virtual int get_UpdateSequenceNumber(ULARGE_INTEGER[] li)
  375. {
  376. if (li == null)
  377. {
  378. throw new ArgumentNullException("li");
  379. }
  380. CCITracing.TraceCall();
  381. li[0] = new ULARGE_INTEGER();
  382. li[0].QuadPart = 0;
  383. return VSConstants.S_OK;
  384. }
  385. public virtual int OpenOutput(string name, out IVsOutput output)
  386. {
  387. CCITracing.TraceCall();
  388. output = null;
  389. return VSConstants.E_NOTIMPL;
  390. }
  391. #endregion
  392. #region IVsDebuggableProjectCfg methods
  393. /// <summary>
  394. /// Called by the vs shell to start debugging (managed or unmanaged).
  395. /// Override this method to support other debug engines.
  396. /// </summary>
  397. /// <param name="grfLaunch">A flag that determines the conditions under which to start the debugger. For valid grfLaunch values, see __VSDBGLAUNCHFLAGS</param>
  398. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code</returns>
  399. public virtual int DebugLaunch(uint grfLaunch)
  400. {
  401. CCITracing.TraceCall();
  402. try
  403. {
  404. VsDebugTargetInfo info = new VsDebugTargetInfo();
  405. info.cbSize = (uint)Marshal.SizeOf(info);
  406. info.dlo = Microsoft.VisualStudio.Shell.Interop.DEBUG_LAUNCH_OPERATION.DLO_CreateProcess;
  407. // On first call, reset the cache, following calls will use the cached values
  408. string property = GetConfigurationProperty("StartProgram", true);
  409. if(string.IsNullOrEmpty(property))
  410. {
  411. info.bstrExe = this.project.GetOutputAssembly(this.ConfigName);
  412. }
  413. else
  414. {
  415. info.bstrExe = property;
  416. }
  417. property = GetConfigurationProperty("WorkingDirectory", false);
  418. if(string.IsNullOrEmpty(property))
  419. {
  420. info.bstrCurDir = Path.GetDirectoryName(info.bstrExe);
  421. }
  422. else
  423. {
  424. info.bstrCurDir = property;
  425. }
  426. property = GetConfigurationProperty("CmdArgs", false);
  427. if(!string.IsNullOrEmpty(property))
  428. {
  429. info.bstrArg = property;
  430. }
  431. property = GetConfigurationProperty("RemoteDebugMachine", false);
  432. if(property != null && property.Length > 0)
  433. {
  434. info.bstrRemoteMachine = property;
  435. }
  436. info.fSendStdoutToOutputWindow = 0;
  437. property = GetConfigurationProperty("EnableUnmanagedDebugging", false);
  438. if(property != null && string.Compare(property, "true", StringComparison.OrdinalIgnoreCase) == 0)
  439. {
  440. //Set the unmanged debugger
  441. //TODO change to vsconstant when it is available in VsConstants (guidNativeOnlyEng was the old name, maybe it has got a new name)
  442. info.clsidCustom = new Guid("{3B476D35-A401-11D2-AAD4-00C04F990171}");
  443. }
  444. else
  445. {
  446. //Set the managed debugger
  447. info.clsidCustom = VSConstants.CLSID_ComPlusOnlyDebugEngine;
  448. }
  449. info.grfLaunch = grfLaunch;
  450. VsShellUtilities.LaunchDebugger(this.project.Site, info);
  451. }
  452. catch(Exception e)
  453. {
  454. Trace.WriteLine("Exception : " + e.Message);
  455. return Marshal.GetHRForException(e);
  456. }
  457. return VSConstants.S_OK;
  458. }
  459. /// <summary>
  460. /// Determines whether the debugger can be launched, given the state of the launch flags.
  461. /// </summary>
  462. /// <param name="flags">Flags that determine the conditions under which to launch the debugger.
  463. /// For valid grfLaunch values, see __VSDBGLAUNCHFLAGS or __VSDBGLAUNCHFLAGS2.</param>
  464. /// <param name="fCanLaunch">true if the debugger can be launched, otherwise false</param>
  465. /// <returns>S_OK if the method succeeds, otherwise an error code</returns>
  466. public virtual int QueryDebugLaunch(uint flags, out int fCanLaunch)
  467. {
  468. CCITracing.TraceCall();
  469. string assembly = this.project.GetAssemblyName(this.ConfigName);
  470. fCanLaunch = (assembly != null && assembly.ToUpperInvariant().EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) ? 1 : 0;
  471. if(fCanLaunch == 0)
  472. {
  473. string property = GetConfigurationProperty("StartProgram", true);
  474. fCanLaunch = (property != null && property.Length > 0) ? 1 : 0;
  475. }
  476. return VSConstants.S_OK;
  477. }
  478. #endregion
  479. #region IVsProjectCfg2 Members
  480. public virtual int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup)
  481. {
  482. ppIVsOutputGroup = null;
  483. // Search through our list of groups to find the one they are looking forgroupName
  484. foreach(OutputGroup group in OutputGroups)
  485. {
  486. string groupName;
  487. group.get_CanonicalName(out groupName);
  488. if(String.Compare(groupName, szCanonicalName, StringComparison.OrdinalIgnoreCase) == 0)
  489. {
  490. ppIVsOutputGroup = group;
  491. break;
  492. }
  493. }
  494. return (ppIVsOutputGroup != null) ? VSConstants.S_OK : VSConstants.E_FAIL;
  495. }
  496. public virtual int OutputsRequireAppRoot(out int pfRequiresAppRoot)
  497. {
  498. pfRequiresAppRoot = 0;
  499. return VSConstants.E_NOTIMPL;
  500. }
  501. public virtual int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg)
  502. {
  503. // Delegate to the flavored configuration (to enable a flavor to take control)
  504. // Since we can be asked for Configuration we don't support, avoid throwing and return the HRESULT directly
  505. int hr = flavoredCfg.get_CfgType(ref iidCfg, out ppCfg);
  506. return hr;
  507. }
  508. public virtual int get_IsPrivate(out int pfPrivate)
  509. {
  510. pfPrivate = 0;
  511. return VSConstants.S_OK;
  512. }
  513. public virtual int get_OutputGroups(uint celt, IVsOutputGroup[] rgpcfg, uint[] pcActual)
  514. {
  515. // Are they only asking for the number of groups?
  516. if(celt == 0)
  517. {
  518. if((null == pcActual) || (0 == pcActual.Length))
  519. {
  520. throw new ArgumentNullException("pcActual");
  521. }
  522. pcActual[0] = (uint)OutputGroups.Count;
  523. return VSConstants.S_OK;
  524. }
  525. // Check that the array of output groups is not null
  526. if((null == rgpcfg) || (rgpcfg.Length == 0))
  527. {
  528. throw new ArgumentNullException("rgpcfg");
  529. }
  530. // Fill the array with our output groups
  531. uint count = 0;
  532. foreach(OutputGroup group in OutputGroups)
  533. {
  534. if(rgpcfg.Length > count && celt > count && group != null)
  535. {
  536. rgpcfg[count] = group;
  537. ++count;
  538. }
  539. }
  540. if(pcActual != null && pcActual.Length > 0)
  541. pcActual[0] = count;
  542. // If the number asked for does not match the number returned, return S_FALSE
  543. return (count == celt) ? VSConstants.S_OK : VSConstants.S_FALSE;
  544. }
  545. public virtual int get_VirtualRoot(out string pbstrVRoot)
  546. {
  547. pbstrVRoot = null;
  548. return VSConstants.E_NOTIMPL;
  549. }
  550. #endregion
  551. #region IVsCfgBrowseObject
  552. /// <summary>
  553. /// Maps back to the configuration corresponding to the browse object.
  554. /// </summary>
  555. /// <param name="cfg">The IVsCfg object represented by the browse object</param>
  556. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
  557. public virtual int GetCfg(out IVsCfg cfg)
  558. {
  559. cfg = this;
  560. return VSConstants.S_OK;
  561. }
  562. /// <summary>
  563. /// Maps back to the hierarchy or project item object corresponding to the browse object.
  564. /// </summary>
  565. /// <param name="hier">Reference to the hierarchy object.</param>
  566. /// <param name="itemid">Reference to the project item.</param>
  567. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
  568. public virtual int GetProjectItem(out IVsHierarchy hier, out uint itemid)
  569. {
  570. if(this.project == null || this.project.NodeProperties == null)
  571. {
  572. throw new InvalidOperationException();
  573. }
  574. return this.project.NodeProperties.GetProjectItem(out hier, out itemid);
  575. }
  576. #endregion
  577. #region helper methods
  578. /// <summary>
  579. /// Splits the canonical configuration name into platform and configuration name.
  580. /// </summary>
  581. /// <param name="canonicalName">The canonicalName name.</param>
  582. /// <param name="configName">The name of the configuration.</param>
  583. /// <param name="platformName">The name of the platform.</param>
  584. /// <returns>true if successfull.</returns>
  585. internal static bool TrySplitConfigurationCanonicalName(string canonicalName, out string configName, out string platformName)
  586. {
  587. configName = String.Empty;
  588. platformName = String.Empty;
  589. if(String.IsNullOrEmpty(canonicalName))
  590. {
  591. return false;
  592. }
  593. string[] splittedCanonicalName = canonicalName.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
  594. if(splittedCanonicalName == null || (splittedCanonicalName.Length != 1 && splittedCanonicalName.Length != 2))
  595. {
  596. return false;
  597. }
  598. configName = splittedCanonicalName[0];
  599. if(splittedCanonicalName.Length == 2)
  600. {
  601. platformName = splittedCanonicalName[1];
  602. }
  603. return true;
  604. }
  605. private MSBuildExecution.ProjectPropertyInstance GetMsBuildProperty(string propertyName, bool resetCache)
  606. {
  607. if (resetCache || this.currentConfig == null)
  608. {
  609. // Get properties for current configuration from project file and cache it
  610. this.project.SetConfiguration(this.ConfigName);
  611. this.project.BuildProject.ReevaluateIfNecessary();
  612. // Create a snapshot of the evaluated project in its current state
  613. this.currentConfig = this.project.BuildProject.CreateProjectInstance();
  614. // Restore configuration
  615. project.SetCurrentConfiguration();
  616. }
  617. if (this.currentConfig == null)
  618. throw new Exception("Failed to retrieve properties");
  619. // return property asked for
  620. return this.currentConfig.GetProperty(propertyName);
  621. }
  622. /// <summary>
  623. /// Retrieves the configuration dependent property pages.
  624. /// </summary>
  625. /// <param name="pages">The pages to return.</param>
  626. private void GetCfgPropertyPages(CAUUID[] pages)
  627. {
  628. // We do not check whether the supportsProjectDesigner is set to true on the ProjectNode.
  629. // We rely that the caller knows what to call on us.
  630. if(pages == null)
  631. {
  632. throw new ArgumentNullException("pages");
  633. }
  634. if(pages.Length == 0)
  635. {
  636. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "pages");
  637. }
  638. // Retrive the list of guids from hierarchy properties.
  639. // Because a flavor could modify that list we must make sure we are calling the outer most implementation of IVsHierarchy
  640. string guidsList = String.Empty;
  641. IVsHierarchy hierarchy = HierarchyNode.GetOuterHierarchy(this.project);
  642. object variant = null;
  643. ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList, out variant), new int[] { VSConstants.DISP_E_MEMBERNOTFOUND, VSConstants.E_NOTIMPL });
  644. guidsList = (string)variant;
  645. Guid[] guids = Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(guidsList);
  646. if(guids == null || guids.Length == 0)
  647. {
  648. pages[0] = new CAUUID();
  649. pages[0].cElems = 0;
  650. }
  651. else
  652. {
  653. pages[0] = PackageUtilities.CreateCAUUIDFromGuidArray(guids);
  654. }
  655. }
  656. #endregion
  657. #region IVsProjectFlavorCfg Members
  658. /// <summary>
  659. /// This is called to let the flavored config let go
  660. /// of any reference it may still be holding to the base config
  661. /// </summary>
  662. /// <returns></returns>
  663. int IVsProjectFlavorCfg.Close()
  664. {
  665. // This is used to release the reference the flavored config is holding
  666. // on the base config, but in our scenario these 2 are the same object
  667. // so we have nothing to do here.
  668. return VSConstants.S_OK;
  669. }
  670. /// <summary>
  671. /// Actual implementation of get_CfgType.
  672. /// When not flavored or when the flavor delegate to use
  673. /// we end up creating the requested config if we support it.
  674. /// </summary>
  675. /// <param name="iidCfg">IID representing the type of config object we should create</param>
  676. /// <param name="ppCfg">Config object that the method created</param>
  677. /// <returns>HRESULT</returns>
  678. int IVsProjectFlavorCfg.get_CfgType(ref Guid iidCfg, out IntPtr ppCfg)
  679. {
  680. ppCfg = IntPtr.Zero;
  681. // See if this is an interface we support
  682. if(iidCfg == typeof(IVsDebuggableProjectCfg).GUID)
  683. ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg));
  684. else if(iidCfg == typeof(IVsBuildableProjectCfg).GUID)
  685. {
  686. IVsBuildableProjectCfg buildableConfig;
  687. this.get_BuildableProjectCfg(out buildableConfig);
  688. ppCfg = Marshal.GetComInterfaceForObject(buildableConfig, typeof(IVsBuildableProjectCfg));
  689. }
  690. // If not supported
  691. if(ppCfg == IntPtr.Zero)
  692. return VSConstants.E_NOINTERFACE;
  693. return VSConstants.S_OK;
  694. }
  695. #endregion
  696. }
  697. //=============================================================================
  698. // NOTE: advises on out of proc build execution to maximize
  699. // future cross-platform targeting capabilities of the VS tools.
  700. [CLSCompliant(false)]
  701. [ComVisible(true)]
  702. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Buildable")]
  703. public class BuildableProjectConfig : IVsBuildableProjectCfg
  704. {
  705. #region fields
  706. ProjectConfig config = null;
  707. EventSinkCollection callbacks = new EventSinkCollection();
  708. #endregion
  709. #region ctors
  710. public BuildableProjectConfig(ProjectConfig config)
  711. {
  712. this.config = config;
  713. }
  714. #endregion
  715. #region IVsBuildableProjectCfg methods
  716. public virtual int AdviseBuildStatusCallback(IVsBuildStatusCallback callback, out uint cookie)
  717. {
  718. CCITracing.TraceCall();
  719. cookie = callbacks.Add(callback);
  720. return VSConstants.S_OK;
  721. }
  722. public virtual int get_ProjectCfg(out IVsProjectCfg p)
  723. {
  724. CCITracing.TraceCall();
  725. p = config;
  726. return VSConstants.S_OK;
  727. }
  728. public virtual int QueryStartBuild(uint options, int[] supported, int[] ready)
  729. {
  730. CCITracing.TraceCall();
  731. if(supported != null && supported.Length > 0)
  732. supported[0] = 1;
  733. if(ready != null && ready.Length > 0)
  734. ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1;
  735. return VSConstants.S_OK;
  736. }
  737. public virtual int QueryStartClean(uint options, int[] supported, int[] ready)
  738. {
  739. CCITracing.TraceCall();
  740. config.PrepareBuild(false);
  741. if(supported != null && supported.Length > 0)
  742. supported[0] = 1;
  743. if(ready != null && ready.Length > 0)
  744. ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1;
  745. return VSConstants.S_OK;
  746. }
  747. public virtual int QueryStartUpToDateCheck(uint options, int[] supported, int[] ready)
  748. {
  749. CCITracing.TraceCall();
  750. config.PrepareBuild(false);
  751. if(supported != null && supported.Length > 0)
  752. supported[0] = 0; // TODO:
  753. if(ready != null && ready.Length > 0)
  754. ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1;
  755. return VSConstants.S_OK;
  756. }
  757. public virtual int QueryStatus(out int done)
  758. {
  759. CCITracing.TraceCall();
  760. done = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1;
  761. return VSConstants.S_OK;
  762. }
  763. public virtual int StartBuild(IVsOutputWindowPane pane, uint options)
  764. {
  765. CCITracing.TraceCall();
  766. config.PrepareBuild(false);
  767. // Current version of MSBuild wish to be called in an STA
  768. uint flags = VSConstants.VS_BUILDABLEPROJECTCFGOPTS_REBUILD;
  769. // If we are not asked for a rebuild, then we build the default target (by passing null)
  770. this.Build(options, pane, ((options & flags) != 0) ? MsBuildTarget.Rebuild : null);
  771. return VSConstants.S_OK;
  772. }
  773. public virtual int StartClean(IVsOutputWindowPane pane, uint options)
  774. {
  775. CCITracing.TraceCall();
  776. config.PrepareBuild(true);
  777. // Current version of MSBuild wish to be called in an STA
  778. this.Build(options, pane, MsBuildTarget.Clean);
  779. return VSConstants.S_OK;
  780. }
  781. public virtual int StartUpToDateCheck(IVsOutputWindowPane pane, uint options)
  782. {
  783. CCITracing.TraceCall();
  784. return VSConstants.E_NOTIMPL;
  785. }
  786. public virtual int Stop(int fsync)
  787. {
  788. CCITracing.TraceCall();
  789. return VSConstants.S_OK;
  790. }
  791. public virtual int UnadviseBuildStatusCallback(uint cookie)
  792. {
  793. CCITracing.TraceCall();
  794. callbacks.RemoveAt(cookie);
  795. return VSConstants.S_OK;
  796. }
  797. public virtual int Wait(uint ms, int fTickWhenMessageQNotEmpty)
  798. {
  799. CCITracing.TraceCall();
  800. return VSConstants.E_NOTIMPL;
  801. }
  802. #endregion
  803. #region helpers
  804. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  805. private bool NotifyBuildBegin()
  806. {
  807. int shouldContinue = 1;
  808. foreach (IVsBuildStatusCallback cb in callbacks)
  809. {
  810. try
  811. {
  812. ErrorHandler.ThrowOnFailure(cb.BuildBegin(ref shouldContinue));
  813. if (shouldContinue == 0)
  814. {
  815. return false;
  816. }
  817. }
  818. catch (Exception e)
  819. {
  820. // If those who ask for status have bugs in their code it should not prevent the build/notification from happening
  821. Debug.Fail(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.Message));
  822. }
  823. }
  824. return true;
  825. }
  826. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  827. private void NotifyBuildEnd(MSBuildResult result, string buildTarget)
  828. {
  829. int success = ((result == MSBuildResult.Successful) ? 1 : 0);
  830. foreach (IVsBuildStatusCallback cb in callbacks)
  831. {
  832. try
  833. {
  834. ErrorHandler.ThrowOnFailure(cb.BuildEnd(success));
  835. }
  836. catch (Exception e)
  837. {
  838. // If those who ask for status have bugs in their code it should not prevent the build/notification from happening
  839. Debug.Fail(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.Message));
  840. }
  841. finally
  842. {
  843. // We want to refresh the references if we are building with the Build or Rebuild target or if the project was opened for browsing only.
  844. bool shouldRepaintReferences = (buildTarget == null || buildTarget == MsBuildTarget.Build || buildTarget == MsBuildTarget.Rebuild);
  845. // Now repaint references if that is needed.
  846. // We hardly rely here on the fact the ResolveAssemblyReferences target has been run as part of the build.
  847. // One scenario to think at is when an assembly reference is renamed on disk thus becomming unresolvable,
  848. // but msbuild can actually resolve it.
  849. // Another one if the project was opened only for browsing and now the user chooses to build or rebuild.
  850. if (shouldRepaintReferences && (result == MSBuildResult.Successful))
  851. {
  852. this.RefreshReferences();
  853. }
  854. }
  855. }
  856. }
  857. private void Build(uint options, IVsOutputWindowPane output, string target)
  858. {
  859. if (!this.NotifyBuildBegin())
  860. {
  861. return;
  862. }
  863. try
  864. {
  865. config.ProjectMgr.BuildAsync(options, this.config.ConfigName, output, target, (result, buildTarget) => this.NotifyBuildEnd(result, buildTarget));
  866. }
  867. catch(Exception e)
  868. {
  869. Trace.WriteLine("Exception : " + e.Message);
  870. ErrorHandler.ThrowOnFailure(output.OutputStringThreadSafe("Unhandled Exception:" + e.Message + "\n"));
  871. this.NotifyBuildEnd(MSBuildResult.Failed, target);
  872. throw;
  873. }
  874. finally
  875. {
  876. ErrorHandler.ThrowOnFailure(output.FlushToTaskList());
  877. }
  878. }
  879. /// <summary>
  880. /// Refreshes references and redraws them correctly.
  881. /// </summary>
  882. private void RefreshReferences()
  883. {
  884. // Refresh the reference container node for assemblies that could be resolved.
  885. IReferenceContainer referenceContainer = this.config.ProjectMgr.GetReferenceContainer();
  886. foreach(ReferenceNode referenceNode in referenceContainer.EnumReferences())
  887. {
  888. referenceNode.RefreshReference();
  889. }
  890. }
  891. #endregion
  892. }
  893. }