OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections.Generic; | |
5 using System.Diagnostics; | |
6 using System.Diagnostics.CodeAnalysis; | |
7 using System.Globalization; | |
8 using System.IO; | |
9 using System.Runtime.InteropServices; | |
10 using Microsoft.VisualStudio; | |
11 using Microsoft.VisualStudio.OLE.Interop; | |
12 using Microsoft.VisualStudio.Shell; | |
13 //#define ConfigTrace | |
14 using Microsoft.VisualStudio.Shell.Interop; | |
15 using MSBuild = Microsoft.Build.BuildEngine; | |
16 | |
17 namespace Microsoft.VisualStudio.Project | |
18 { | |
19 [CLSCompliant(false), ComVisible(true)] | |
20 public class ProjectConfig : | |
21 IVsCfg, | |
22 IVsProjectCfg, | |
23 IVsProjectCfg2, | |
24 IVsProjectFlavorCfg, | |
25 IVsDebuggableProjectCfg, | |
26 ISpecifyPropertyPages, | |
27 IVsSpecifyProjectDesignerPages, | |
28 IVsCfgBrowseObject | |
29 { | |
30 #region constants | |
31 internal const string Debug = "Debug"; | |
32 internal const string Release = "Release"; | |
33 internal const string AnyCPU = "AnyCPU"; | |
34 #endregion | |
35 | |
36 #region fields | |
37 private ProjectNode project; | |
38 private string configName; | |
39 private MSBuild.BuildPropertyGroup currentConfig; | |
40 private List<OutputGroup> outputGroups; | |
41 private IProjectConfigProperties configurationProperties; | |
42 private IVsProjectFlavorCfg flavoredCfg; | |
43 private BuildableProjectConfig buildableCfg; | |
44 #endregion | |
45 | |
46 #region properties | |
47 public ProjectNode ProjectMgr | |
48 { | |
49 get | |
50 { | |
51 return this.project; | |
52 } | |
53 } | |
54 | |
55 public string ConfigName | |
56 { | |
57 get | |
58 { | |
59 return this.configName; | |
60 } | |
61 set | |
62 { | |
63 this.configName = value; | |
64 } | |
65 } | |
66 | |
67 public virtual object ConfigurationProperties | |
68 { | |
69 get | |
70 { | |
71 if (this.configurationProperties == null) | |
72 { | |
73 this.configurationProperties = new Proje
ctConfigProperties(this); | |
74 } | |
75 return this.configurationProperties; | |
76 } | |
77 } | |
78 | |
79 protected IList<OutputGroup> OutputGroups | |
80 { | |
81 get | |
82 { | |
83 if (null == this.outputGroups) | |
84 { | |
85 // Initialize output groups | |
86 this.outputGroups = new List<OutputGroup
>(); | |
87 | |
88 // Get the list of group names from the
project. | |
89 // The main reason we get it from the pr
oject is to make it easier for someone to modify | |
90 // it by simply overriding that method a
nd providing the correct MSBuild target(s). | |
91 IList<KeyValuePair<string, string>> grou
pNames = project.GetOutputGroupNames(); | |
92 | |
93 if (groupNames != null) | |
94 { | |
95 // Populate the output array | |
96 foreach (KeyValuePair<string, st
ring> group in groupNames) | |
97 { | |
98 OutputGroup outputGroup
= CreateOutputGroup(project, group); | |
99 this.outputGroups.Add(ou
tputGroup); | |
100 } | |
101 } | |
102 | |
103 } | |
104 return this.outputGroups; | |
105 } | |
106 } | |
107 #endregion | |
108 | |
109 #region ctors | |
110 public ProjectConfig(ProjectNode project, string configuration) | |
111 { | |
112 this.project = project; | |
113 this.configName = configuration; | |
114 | |
115 | |
116 // Because the project can be aggregated by a flavor, we
need to make sure | |
117 // we get the outer most implementation of that interfac
e (hence: project --> IUnknown --> Interface) | |
118 IntPtr projectUnknown = Marshal.GetIUnknownForObject(thi
s.ProjectMgr); | |
119 try | |
120 { | |
121 IVsProjectFlavorCfgProvider flavorCfgProvider =
(IVsProjectFlavorCfgProvider)Marshal.GetTypedObjectForIUnknown(projectUnknown, t
ypeof(IVsProjectFlavorCfgProvider)); | |
122 ErrorHandler.ThrowOnFailure(flavorCfgProvider.Cr
eateProjectFlavorCfg(this, out flavoredCfg)); | |
123 if (flavoredCfg == null) | |
124 throw new COMException(); | |
125 } | |
126 finally | |
127 { | |
128 if (projectUnknown != IntPtr.Zero) | |
129 Marshal.Release(projectUnknown); | |
130 } | |
131 // if the flavored object support XML fragment, initiali
ze it | |
132 IPersistXMLFragment persistXML = flavoredCfg as IPersist
XMLFragment; | |
133 if (null != persistXML) | |
134 { | |
135 this.project.LoadXmlFragment(persistXML, this.Di
splayName); | |
136 } | |
137 } | |
138 #endregion | |
139 | |
140 #region methods | |
141 protected virtual OutputGroup CreateOutputGroup(ProjectNode proj
ect, KeyValuePair<string, string> group) | |
142 { | |
143 OutputGroup outputGroup = new OutputGroup(group.Key, gro
up.Value, project, this); | |
144 return outputGroup; | |
145 } | |
146 | |
147 public void PrepareBuild(bool clean) | |
148 { | |
149 project.PrepareBuild(this.configName, clean); | |
150 } | |
151 | |
152 public virtual string GetConfigurationProperty(string propertyNa
me, bool resetCache) | |
153 { | |
154 MSBuild.BuildProperty property = GetMsBuildProperty(prop
ertyName, resetCache); | |
155 if (property == null) | |
156 return null; | |
157 | |
158 return property.FinalValue; | |
159 } | |
160 | |
161 public virtual void SetConfigurationProperty(string propertyName
, string propertyValue) | |
162 { | |
163 if (!this.project.QueryEditProjectFile(false)) | |
164 { | |
165 throw Marshal.GetExceptionForHR(VSConstants.OLE_
E_PROMPTSAVECANCELLED); | |
166 } | |
167 | |
168 string condition = String.Format(CultureInfo.InvariantCu
lture, ConfigProvider.configString, this.ConfigName); | |
169 this.project.BuildProject.SetProperty(propertyName, prop
ertyValue, condition); | |
170 | |
171 // property cache will need to be updated | |
172 this.currentConfig = null; | |
173 // Signal the output groups that something is changed | |
174 foreach (OutputGroup group in this.OutputGroups) | |
175 { | |
176 group.InvalidateGroup(); | |
177 } | |
178 this.project.SetProjectFileDirty(true); | |
179 | |
180 return; | |
181 } | |
182 | |
183 /// <summary> | |
184 /// If flavored, and if the flavor config can be dirty, ask it i
f it is dirty | |
185 /// </summary> | |
186 /// <param name="storageType">Project file or user file</param> | |
187 /// <returns>0 = not dirty</returns> | |
188 internal int IsFlavorDirty(_PersistStorageType storageType) | |
189 { | |
190 int isDirty = 0; | |
191 if (this.flavoredCfg != null && this.flavoredCfg is IPer
sistXMLFragment) | |
192 { | |
193 ErrorHandler.ThrowOnFailure(((IPersistXMLFragmen
t)this.flavoredCfg).IsFragmentDirty((uint)storageType, out isDirty)); | |
194 } | |
195 return isDirty; | |
196 } | |
197 | |
198 /// <summary> | |
199 /// If flavored, ask the flavor if it wants to provide an XML fr
agment | |
200 /// </summary> | |
201 /// <param name="flavor">Guid of the flavor</param> | |
202 /// <param name="storageType">Project file or user file</param> | |
203 /// <param name="fragment">Fragment that the flavor wants to sav
e</param> | |
204 /// <returns>HRESULT</returns> | |
205 internal int GetXmlFragment(Guid flavor, _PersistStorageType sto
rageType, out string fragment) | |
206 { | |
207 fragment = null; | |
208 int hr = VSConstants.S_OK; | |
209 if (this.flavoredCfg != null && this.flavoredCfg is IPer
sistXMLFragment) | |
210 { | |
211 Guid flavorGuid = flavor; | |
212 hr = ((IPersistXMLFragment)this.flavoredCfg).Sav
e(ref flavorGuid, (uint)storageType, out fragment, 1); | |
213 } | |
214 return hr; | |
215 } | |
216 #endregion | |
217 | |
218 #region IVsSpecifyPropertyPages | |
219 public void GetPages(CAUUID[] pages) | |
220 { | |
221 this.GetCfgPropertyPages(pages); | |
222 } | |
223 #endregion | |
224 | |
225 #region IVsSpecifyProjectDesignerPages | |
226 /// <summary> | |
227 /// Implementation of the IVsSpecifyProjectDesignerPages. It wil
l retun the pages that are configuration dependent. | |
228 /// </summary> | |
229 /// <param name="pages">The pages to return.</param> | |
230 /// <returns>VSConstants.S_OK</returns> | |
231 public virtual int GetProjectDesignerPages(CAUUID[] pages) | |
232 { | |
233 this.GetCfgPropertyPages(pages); | |
234 return VSConstants.S_OK; | |
235 } | |
236 #endregion | |
237 | |
238 #region IVsCfg methods | |
239 /// <summary> | |
240 /// The display name is a two part item | |
241 /// first part is the config name, 2nd part is the platform name | |
242 /// </summary> | |
243 public virtual int get_DisplayName(out string name) | |
244 { | |
245 name = DisplayName; | |
246 return VSConstants.S_OK; | |
247 } | |
248 | |
249 private string DisplayName | |
250 { | |
251 get | |
252 { | |
253 string name; | |
254 string[] platform = new string[1]; | |
255 uint[] actual = new uint[1]; | |
256 name = this.configName; | |
257 // currently, we only support one platform, so j
ust add it.. | |
258 IVsCfgProvider provider; | |
259 ErrorHandler.ThrowOnFailure(project.GetCfgProvid
er(out provider)); | |
260 ErrorHandler.ThrowOnFailure(((IVsCfgProvider2)pr
ovider).GetPlatformNames(1, platform, actual)); | |
261 if (!string.IsNullOrEmpty(platform[0])) | |
262 { | |
263 name += "|" + platform[0]; | |
264 } | |
265 return name; | |
266 } | |
267 } | |
268 public virtual int get_IsDebugOnly(out int fDebug) | |
269 { | |
270 fDebug = 0; | |
271 if (this.configName == "Debug") | |
272 { | |
273 fDebug = 1; | |
274 } | |
275 return VSConstants.S_OK; | |
276 } | |
277 public virtual int get_IsReleaseOnly(out int fRelease) | |
278 { | |
279 CCITracing.TraceCall(); | |
280 fRelease = 0; | |
281 if (this.configName == "Release") | |
282 { | |
283 fRelease = 1; | |
284 } | |
285 return VSConstants.S_OK; | |
286 } | |
287 #endregion | |
288 | |
289 #region IVsProjectCfg methods | |
290 public virtual int EnumOutputs(out IVsEnumOutputs eo) | |
291 { | |
292 CCITracing.TraceCall(); | |
293 eo = null; | |
294 return VSConstants.E_NOTIMPL; | |
295 } | |
296 | |
297 public virtual int get_BuildableProjectCfg(out IVsBuildableProje
ctCfg pb) | |
298 { | |
299 CCITracing.TraceCall(); | |
300 if (buildableCfg == null) | |
301 buildableCfg = new BuildableProjectConfig(this); | |
302 pb = buildableCfg; | |
303 return VSConstants.S_OK; | |
304 } | |
305 | |
306 public virtual int get_CanonicalName(out string name) | |
307 { | |
308 return ((IVsCfg)this).get_DisplayName(out name); | |
309 } | |
310 | |
311 public virtual int get_IsPackaged(out int pkgd) | |
312 { | |
313 CCITracing.TraceCall(); | |
314 pkgd = 0; | |
315 return VSConstants.S_OK; | |
316 } | |
317 | |
318 public virtual int get_IsSpecifyingOutputSupported(out int f) | |
319 { | |
320 CCITracing.TraceCall(); | |
321 f = 1; | |
322 return VSConstants.S_OK; | |
323 } | |
324 | |
325 public virtual int get_Platform(out Guid platform) | |
326 { | |
327 CCITracing.TraceCall(); | |
328 platform = Guid.Empty; | |
329 return VSConstants.E_NOTIMPL; | |
330 } | |
331 | |
332 public virtual int get_ProjectCfgProvider(out IVsProjectCfgProvi
der p) | |
333 { | |
334 CCITracing.TraceCall(); | |
335 p = null; | |
336 IVsCfgProvider cfgProvider = null; | |
337 this.project.GetCfgProvider(out cfgProvider); | |
338 if (cfgProvider != null) | |
339 { | |
340 p = cfgProvider as IVsProjectCfgProvider; | |
341 } | |
342 | |
343 return (null == p) ? VSConstants.E_NOTIMPL : VSConstants
.S_OK; | |
344 } | |
345 | |
346 public virtual int get_RootURL(out string root) | |
347 { | |
348 CCITracing.TraceCall(); | |
349 root = null; | |
350 return VSConstants.S_OK; | |
351 } | |
352 | |
353 public virtual int get_TargetCodePage(out uint target) | |
354 { | |
355 CCITracing.TraceCall(); | |
356 target = (uint)System.Text.Encoding.Default.CodePage; | |
357 return VSConstants.S_OK; | |
358 } | |
359 | |
360 public virtual int get_UpdateSequenceNumber(ULARGE_INTEGER[] li) | |
361 { | |
362 CCITracing.TraceCall(); | |
363 li[0] = new ULARGE_INTEGER(); | |
364 li[0].QuadPart = 0; | |
365 return VSConstants.S_OK; | |
366 } | |
367 | |
368 public virtual int OpenOutput(string name, out IVsOutput output) | |
369 { | |
370 CCITracing.TraceCall(); | |
371 output = null; | |
372 return VSConstants.E_NOTIMPL; | |
373 } | |
374 #endregion | |
375 | |
376 #region IVsDebuggableProjectCfg methods | |
377 /// <summary> | |
378 /// Called by the vs shell to start debugging (managed or unmana
ged). | |
379 /// Override this method to support other debug engines. | |
380 /// </summary> | |
381 /// <param name="grfLaunch">A flag that determines the condition
s under which to start the debugger. For valid grfLaunch values, see __VSDBGLAUN
CHFLAGS</param> | |
382 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code</returns> | |
383 public virtual int DebugLaunch(uint grfLaunch) | |
384 { | |
385 CCITracing.TraceCall(); | |
386 | |
387 try | |
388 { | |
389 VsDebugTargetInfo info = new VsDebugTargetInfo()
; | |
390 info.cbSize = (uint)Marshal.SizeOf(info); | |
391 info.dlo = Microsoft.VisualStudio.Shell.Interop.
DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; | |
392 | |
393 // On first call, reset the cache, following cal
ls will use the cached values | |
394 string property = GetConfigurationProperty("Star
tProgram", true); | |
395 if (string.IsNullOrEmpty(property)) | |
396 { | |
397 info.bstrExe = this.project.GetOutputAss
embly(this.ConfigName); | |
398 } | |
399 else | |
400 { | |
401 info.bstrExe = property; | |
402 } | |
403 | |
404 property = GetConfigurationProperty("WorkingDire
ctory", false); | |
405 if (string.IsNullOrEmpty(property)) | |
406 { | |
407 info.bstrCurDir = Path.GetDirectoryName(
info.bstrExe); | |
408 } | |
409 else | |
410 { | |
411 info.bstrCurDir = property; | |
412 } | |
413 | |
414 property = GetConfigurationProperty("CmdArgs", f
alse); | |
415 if (!string.IsNullOrEmpty(property)) | |
416 { | |
417 info.bstrArg = property; | |
418 } | |
419 | |
420 property = GetConfigurationProperty("RemoteDebug
Machine", false); | |
421 if (property != null && property.Length > 0) | |
422 { | |
423 info.bstrRemoteMachine = property; | |
424 } | |
425 | |
426 info.fSendStdoutToOutputWindow = 0; | |
427 | |
428 property = GetConfigurationProperty("EnableUnman
agedDebugging", false); | |
429 if (property != null && string.Compare(property,
"true", StringComparison.OrdinalIgnoreCase) == 0) | |
430 { | |
431 //Set the unmanged debugger | |
432 //TODO change to vsconstant when it is a
vailable in VsConstants (guidNativeOnlyEng was the old name, maybe it has got a
new name) | |
433 info.clsidCustom = new Guid("{3B476D35-A
401-11D2-AAD4-00C04F990171}"); | |
434 } | |
435 else | |
436 { | |
437 //Set the managed debugger | |
438 info.clsidCustom = VSConstants.CLSID_Com
PlusOnlyDebugEngine; | |
439 } | |
440 info.grfLaunch = grfLaunch; | |
441 VsShellUtilities.LaunchDebugger(this.project.Sit
e, info); | |
442 } | |
443 catch (Exception e) | |
444 { | |
445 Trace.WriteLine("Exception : " + e.Message); | |
446 | |
447 return Marshal.GetHRForException(e); | |
448 } | |
449 | |
450 return VSConstants.S_OK; | |
451 } | |
452 | |
453 /// <summary> | |
454 /// Determines whether the debugger can be launched, given the s
tate of the launch flags. | |
455 /// </summary> | |
456 /// <param name="flags">Flags that determine the conditions unde
r which to launch the debugger. | |
457 /// For valid grfLaunch values, see __VSDBGLAUNCHFLAGS or __VSDB
GLAUNCHFLAGS2.</param> | |
458 /// <param name="fCanLaunch">true if the debugger can be launche
d, otherwise false</param> | |
459 /// <returns>S_OK if the method succeeds, otherwise an error cod
e</returns> | |
460 public virtual int QueryDebugLaunch(uint flags, out int fCanLaun
ch) | |
461 { | |
462 CCITracing.TraceCall(); | |
463 string assembly = this.project.GetAssemblyName(this.Conf
igName); | |
464 fCanLaunch = (assembly != null && assembly.ToUpperInvari
ant().EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) ? 1 : 0; | |
465 if (fCanLaunch == 0) | |
466 { | |
467 string property = GetConfigurationProperty("Star
tProgram", true); | |
468 fCanLaunch = (property != null && property.Lengt
h > 0) ? 1 : 0; | |
469 } | |
470 return VSConstants.S_OK; | |
471 } | |
472 #endregion | |
473 | |
474 #region IVsProjectCfg2 Members | |
475 | |
476 public virtual int OpenOutputGroup(string szCanonicalName, out I
VsOutputGroup ppIVsOutputGroup) | |
477 { | |
478 ppIVsOutputGroup = null; | |
479 // Search through our list of groups to find the one the
y are looking forgroupName | |
480 foreach (OutputGroup group in OutputGroups) | |
481 { | |
482 string groupName; | |
483 group.get_CanonicalName(out groupName); | |
484 if (String.Compare(groupName, szCanonicalName, S
tringComparison.OrdinalIgnoreCase) == 0) | |
485 { | |
486 ppIVsOutputGroup = group; | |
487 break; | |
488 } | |
489 } | |
490 return (ppIVsOutputGroup != null) ? VSConstants.S_OK : V
SConstants.E_FAIL; | |
491 } | |
492 | |
493 public virtual int OutputsRequireAppRoot(out int pfRequiresAppRo
ot) | |
494 { | |
495 pfRequiresAppRoot = 0; | |
496 return VSConstants.E_NOTIMPL; | |
497 } | |
498 | |
499 public virtual int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg
) | |
500 { | |
501 // Delegate to the flavored configuration (to enable a f
lavor to take control) | |
502 // Since we can be asked for Configuration we don't supp
ort, avoid throwing and return the HRESULT directly | |
503 int hr = flavoredCfg.get_CfgType(ref iidCfg, out ppCfg); | |
504 | |
505 return hr; | |
506 } | |
507 | |
508 public virtual int get_IsPrivate(out int pfPrivate) | |
509 { | |
510 pfPrivate = 0; | |
511 return VSConstants.S_OK; | |
512 } | |
513 | |
514 public virtual int get_OutputGroups(uint celt, IVsOutputGroup[]
rgpcfg, uint[] pcActual) | |
515 { | |
516 // Are they only asking for the number of groups? | |
517 if (celt == 0) | |
518 { | |
519 if ((null == pcActual) || (0 == pcActual.Length)
) | |
520 { | |
521 throw new ArgumentNullException("pcActua
l"); | |
522 } | |
523 pcActual[0] = (uint)OutputGroups.Count; | |
524 return VSConstants.S_OK; | |
525 } | |
526 | |
527 // Check that the array of output groups is not null | |
528 if ((null == rgpcfg) || (rgpcfg.Length == 0)) | |
529 { | |
530 throw new ArgumentNullException("rgpcfg"); | |
531 } | |
532 | |
533 // Fill the array with our output groups | |
534 uint count = 0; | |
535 foreach (OutputGroup group in OutputGroups) | |
536 { | |
537 if (rgpcfg.Length > count && celt > count && gro
up != null) | |
538 { | |
539 rgpcfg[count] = group; | |
540 ++count; | |
541 } | |
542 } | |
543 | |
544 if (pcActual != null && pcActual.Length > 0) | |
545 pcActual[0] = count; | |
546 | |
547 // If the number asked for does not match the number ret
urned, return S_FALSE | |
548 return (count == celt) ? VSConstants.S_OK : VSConstants.
S_FALSE; | |
549 } | |
550 | |
551 public virtual int get_VirtualRoot(out string pbstrVRoot) | |
552 { | |
553 pbstrVRoot = null; | |
554 return VSConstants.E_NOTIMPL; | |
555 } | |
556 | |
557 #endregion | |
558 | |
559 #region IVsCfgBrowseObject | |
560 /// <summary> | |
561 /// Maps back to the configuration corresponding to the browse o
bject. | |
562 /// </summary> | |
563 /// <param name="cfg">The IVsCfg object represented by the brows
e object</param> | |
564 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
565 public virtual int GetCfg(out IVsCfg cfg) | |
566 { | |
567 cfg = this; | |
568 return VSConstants.S_OK; | |
569 } | |
570 | |
571 /// <summary> | |
572 /// Maps back to the hierarchy or project item object correspond
ing to the browse object. | |
573 /// </summary> | |
574 /// <param name="hier">Reference to the hierarchy object.</param
> | |
575 /// <param name="itemid">Reference to the project item.</param> | |
576 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
577 public virtual int GetProjectItem(out IVsHierarchy hier, out uin
t itemid) | |
578 { | |
579 if (this.project == null || this.project.NodeProperties
== null) | |
580 { | |
581 throw new InvalidOperationException(); | |
582 } | |
583 return this.project.NodeProperties.GetProjectItem(out hi
er, out itemid); | |
584 } | |
585 #endregion | |
586 | |
587 #region helper methods | |
588 /// <summary> | |
589 /// Splits the canonical configuration name into platform and co
nfiguration name. | |
590 /// </summary> | |
591 /// <param name="canonicalName">The canonicalName name.</param> | |
592 /// <param name="configName">The name of the configuration.</par
am> | |
593 /// <param name="platformName">The name of the platform.</param> | |
594 /// <returns>true if successfull.</returns> | |
595 internal static bool TrySplitConfigurationCanonicalName(string c
anonicalName, out string configName, out string platformName) | |
596 { | |
597 configName = String.Empty; | |
598 platformName = String.Empty; | |
599 | |
600 if (String.IsNullOrEmpty(canonicalName)) | |
601 { | |
602 return false; | |
603 } | |
604 | |
605 string[] splittedCanonicalName = canonicalName.Split(new
char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); | |
606 | |
607 if (splittedCanonicalName == null || (splittedCanonicalN
ame.Length != 1 && splittedCanonicalName.Length != 2)) | |
608 { | |
609 return false; | |
610 } | |
611 | |
612 configName = splittedCanonicalName[0]; | |
613 if (splittedCanonicalName.Length == 2) | |
614 { | |
615 platformName = splittedCanonicalName[1]; | |
616 } | |
617 | |
618 return true; | |
619 } | |
620 | |
621 private MSBuild.BuildProperty GetMsBuildProperty(string property
Name, bool resetCache) | |
622 { | |
623 if (resetCache || this.currentConfig == null) | |
624 { | |
625 // Get properties for current configuration from
project file and cache it | |
626 this.project.SetConfiguration(this.ConfigName); | |
627 this.currentConfig = this.project.BuildProject.E
valuatedProperties; | |
628 | |
629 project.SetCurrentConfiguration(); | |
630 } | |
631 | |
632 if (this.currentConfig == null) | |
633 throw new Exception("Failed to retrieve properti
es"); | |
634 | |
635 // return property asked for | |
636 return this.currentConfig[propertyName]; | |
637 } | |
638 | |
639 /// <summary> | |
640 /// Retrieves the configuration dependent property pages. | |
641 /// </summary> | |
642 /// <param name="pages">The pages to return.</param> | |
643 private void GetCfgPropertyPages(CAUUID[] pages) | |
644 { | |
645 // We do not check whether the supportsProjectDesigner i
s set to true on the ProjectNode. | |
646 // We rely that the caller knows what to call on us. | |
647 if (pages == null) | |
648 { | |
649 throw new ArgumentNullException("pages"); | |
650 } | |
651 | |
652 if (pages.Length == 0) | |
653 { | |
654 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "pages"); | |
655 } | |
656 | |
657 // Retrive the list of guids from hierarchy properties. | |
658 // Because a flavor could modify that list we must make
sure we are calling the outer most implementation of IVsHierarchy | |
659 string guidsList = String.Empty; | |
660 IVsHierarchy hierarchy = HierarchyNode.GetOuterHierarchy
(this.project); | |
661 object variant = null; | |
662 ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSCons
tants.VSITEMID_ROOT, (int)__VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList, out
variant), new int[] { VSConstants.DISP_E_MEMBERNOTFOUND, VSConstants.E_NOTIMPL }
); | |
663 guidsList = (string)variant; | |
664 | |
665 Guid[] guids = Utilities.GuidsArrayFromSemicolonDelimite
dStringOfGuids(guidsList); | |
666 if (guids == null || guids.Length == 0) | |
667 { | |
668 pages[0] = new CAUUID(); | |
669 pages[0].cElems = 0; | |
670 } | |
671 else | |
672 { | |
673 pages[0] = PackageUtilities.CreateCAUUIDFromGuid
Array(guids); | |
674 } | |
675 } | |
676 #endregion | |
677 | |
678 #region IVsProjectFlavorCfg Members | |
679 /// <summary> | |
680 /// This is called to let the flavored config let go | |
681 /// of any reference it may still be holding to the base config | |
682 /// </summary> | |
683 /// <returns></returns> | |
684 int IVsProjectFlavorCfg.Close() | |
685 { | |
686 // This is used to release the reference the flavored co
nfig is holding | |
687 // on the base config, but in our scenario these 2 are t
he same object | |
688 // so we have nothing to do here. | |
689 return VSConstants.S_OK; | |
690 } | |
691 | |
692 /// <summary> | |
693 /// Actual implementation of get_CfgType. | |
694 /// When not flavored or when the flavor delegate to use | |
695 /// we end up creating the requested config if we support it. | |
696 /// </summary> | |
697 /// <param name="iidCfg">IID representing the type of config obj
ect we should create</param> | |
698 /// <param name="ppCfg">Config object that the method created</p
aram> | |
699 /// <returns>HRESULT</returns> | |
700 int IVsProjectFlavorCfg.get_CfgType(ref Guid iidCfg, out IntPtr
ppCfg) | |
701 { | |
702 ppCfg = IntPtr.Zero; | |
703 | |
704 // See if this is an interface we support | |
705 if (iidCfg == typeof(IVsDebuggableProjectCfg).GUID) | |
706 ppCfg = Marshal.GetComInterfaceForObject(this, t
ypeof(IVsDebuggableProjectCfg)); | |
707 else if (iidCfg == typeof(IVsBuildableProjectCfg).GUID) | |
708 { | |
709 IVsBuildableProjectCfg buildableConfig; | |
710 this.get_BuildableProjectCfg(out buildableConfig
); | |
711 ppCfg = Marshal.GetComInterfaceForObject(buildab
leConfig, typeof(IVsBuildableProjectCfg)); | |
712 } | |
713 | |
714 // If not supported | |
715 if (ppCfg == IntPtr.Zero) | |
716 return VSConstants.E_NOINTERFACE; | |
717 | |
718 return VSConstants.S_OK; | |
719 } | |
720 | |
721 #endregion | |
722 } | |
723 | |
724 //======================================================================
======= | |
725 // NOTE: advises on out of proc build execution to maximize | |
726 // future cross-platform targeting capabilities of the VS tools. | |
727 | |
728 [CLSCompliant(false)] | |
729 [ComVisible(true)] | |
730 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledC
orrectly", MessageId = "Buildable")] | |
731 public class BuildableProjectConfig : IVsBuildableProjectCfg | |
732 { | |
733 #region fields | |
734 ProjectConfig config = null; | |
735 EventSinkCollection callbacks = new EventSinkCollection(); | |
736 #endregion | |
737 | |
738 #region ctors | |
739 public BuildableProjectConfig(ProjectConfig config) | |
740 { | |
741 this.config = config; | |
742 } | |
743 #endregion | |
744 | |
745 #region IVsBuildableProjectCfg methods | |
746 | |
747 public virtual int AdviseBuildStatusCallback(IVsBuildStatusCallb
ack callback, out uint cookie) | |
748 { | |
749 CCITracing.TraceCall(); | |
750 | |
751 cookie = callbacks.Add(callback); | |
752 return VSConstants.S_OK; | |
753 } | |
754 | |
755 public virtual int get_ProjectCfg(out IVsProjectCfg p) | |
756 { | |
757 CCITracing.TraceCall(); | |
758 | |
759 p = config; | |
760 return VSConstants.S_OK; | |
761 } | |
762 | |
763 public virtual int QueryStartBuild(uint options, int[] supported
, int[] ready) | |
764 { | |
765 CCITracing.TraceCall(); | |
766 if (supported != null && supported.Length > 0) | |
767 supported[0] = 1; | |
768 if (ready != null && ready.Length > 0) | |
769 ready[0] = (this.config.ProjectMgr.BuildInProgre
ss) ? 0 : 1; | |
770 return VSConstants.S_OK; | |
771 } | |
772 | |
773 public virtual int QueryStartClean(uint options, int[] supported
, int[] ready) | |
774 { | |
775 CCITracing.TraceCall(); | |
776 config.PrepareBuild(false); | |
777 if (supported != null && supported.Length > 0) | |
778 supported[0] = 1; | |
779 if (ready != null && ready.Length > 0) | |
780 ready[0] = (this.config.ProjectMgr.BuildInProgre
ss) ? 0 : 1; | |
781 return VSConstants.S_OK; | |
782 } | |
783 | |
784 public virtual int QueryStartUpToDateCheck(uint options, int[] s
upported, int[] ready) | |
785 { | |
786 CCITracing.TraceCall(); | |
787 config.PrepareBuild(false); | |
788 if (supported != null && supported.Length > 0) | |
789 supported[0] = 0; // TODO: | |
790 if (ready != null && ready.Length > 0) | |
791 ready[0] = (this.config.ProjectMgr.BuildInProgre
ss) ? 0 : 1; | |
792 return VSConstants.S_OK; | |
793 } | |
794 | |
795 public virtual int QueryStatus(out int done) | |
796 { | |
797 CCITracing.TraceCall(); | |
798 | |
799 done = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1; | |
800 return VSConstants.S_OK; | |
801 } | |
802 | |
803 public virtual int StartBuild(IVsOutputWindowPane pane, uint opt
ions) | |
804 { | |
805 CCITracing.TraceCall(); | |
806 config.PrepareBuild(false); | |
807 | |
808 // Current version of MSBuild wish to be called in an ST
A | |
809 uint flags = VSConstants.VS_BUILDABLEPROJECTCFGOPTS_REBU
ILD; | |
810 | |
811 // If we are not asked for a rebuild, then we build the
default target (by passing null) | |
812 this.Build(options, pane, ((options & flags) != 0) ? MsB
uildTarget.Rebuild : null); | |
813 | |
814 return VSConstants.S_OK; | |
815 } | |
816 | |
817 public virtual int StartClean(IVsOutputWindowPane pane, uint opt
ions) | |
818 { | |
819 CCITracing.TraceCall(); | |
820 config.PrepareBuild(true); | |
821 // Current version of MSBuild wish to be called in an ST
A | |
822 this.Build(options, pane, MsBuildTarget.Clean); | |
823 return VSConstants.S_OK; | |
824 } | |
825 | |
826 public virtual int StartUpToDateCheck(IVsOutputWindowPane pane,
uint options) | |
827 { | |
828 CCITracing.TraceCall(); | |
829 | |
830 return VSConstants.E_NOTIMPL; | |
831 } | |
832 | |
833 public virtual int Stop(int fsync) | |
834 { | |
835 CCITracing.TraceCall(); | |
836 | |
837 return VSConstants.S_OK; | |
838 } | |
839 | |
840 public virtual int UnadviseBuildStatusCallback(uint cookie) | |
841 { | |
842 CCITracing.TraceCall(); | |
843 | |
844 | |
845 callbacks.RemoveAt(cookie); | |
846 return VSConstants.S_OK; | |
847 } | |
848 | |
849 public virtual int Wait(uint ms, int fTickWhenMessageQNotEmpty) | |
850 { | |
851 CCITracing.TraceCall(); | |
852 | |
853 return VSConstants.E_NOTIMPL; | |
854 } | |
855 #endregion | |
856 | |
857 #region helpers | |
858 private void Build(uint options, IVsOutputWindowPane output, str
ing target) | |
859 { | |
860 if (!this.config.ProjectMgr.HasPassedSecurityChecks) | |
861 { | |
862 // From a security perspective, if there was som
ething truly malicious about the project, | |
863 // the user is about to toast himself by request
ing a build. Any further attempts at | |
864 // preventing damage by avoiding MSBuild targets
/tasks are futile. So, from this point | |
865 // forward, we might as well pretend that this p
roject has passed all security checks, | |
866 // and we're free to run any targets we like. | |
867 this.config.ProjectMgr.HasPassedSecurityChecks =
true; | |
868 } | |
869 | |
870 // 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. | |
871 bool shouldRepaintReferences = (target == null || target
== MsBuildTarget.Build || target == MsBuildTarget.Rebuild | |
872 || !this.config.ProjectMgr.HasPassedSecurityChec
ks); | |
873 | |
874 int shouldContinue = 1; | |
875 foreach (IVsBuildStatusCallback cb in callbacks) | |
876 { | |
877 try | |
878 { | |
879 ErrorHandler.ThrowOnFailure(cb.BuildBegi
n(ref shouldContinue)); | |
880 if (shouldContinue == 0) | |
881 return; | |
882 } | |
883 catch (Exception e) | |
884 { | |
885 // If those who ask for status have bugs
in their code it should not prevent the build/notification from happening | |
886 Debug.Fail(String.Format(CultureInfo.Cur
rentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.M
essage)); | |
887 } | |
888 } | |
889 | |
890 MSBuildResult result = MSBuildResult.Failed; | |
891 try | |
892 { | |
893 result = config.ProjectMgr.Build(options, this.c
onfig.ConfigName, output, target); | |
894 } | |
895 catch (Exception e) | |
896 { | |
897 Trace.WriteLine("Exception : " + e.Message); | |
898 ErrorHandler.ThrowOnFailure(output.OutputStringT
hreadSafe("Unhandled Exception:" + e.Message + "\n")); | |
899 throw; | |
900 } | |
901 finally | |
902 { | |
903 | |
904 int success = ((result == MSBuildResult.Successf
ul) ? 1 : 0); | |
905 | |
906 foreach (IVsBuildStatusCallback cb in callbacks) | |
907 { | |
908 try | |
909 { | |
910 ErrorHandler.ThrowOnFailure(cb.B
uildEnd(success)); | |
911 } | |
912 catch (Exception e) | |
913 { | |
914 // If those who ask for status h
ave bugs in their code it should not prevent the build/notification from happeni
ng | |
915 Debug.Fail(String.Format(Culture
Info.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICultu
re), e.Message)); | |
916 } | |
917 } | |
918 | |
919 ErrorHandler.ThrowOnFailure(output.FlushToTaskLi
st()); | |
920 | |
921 // Now repaint references if that is needed. | |
922 // We hardly rely here on the fact the ResolveAs
semblyReferences target has been run as part of the build. | |
923 // One scenario to think at is when an assembly
reference is renamed on disk thus becomming unresolvable, | |
924 // but msbuild can actually resolve it. | |
925 // Another one if the project was opened only fo
r browsing and now the user chooses to build or rebuild. | |
926 if (shouldRepaintReferences && (result == MSBuil
dResult.Successful)) | |
927 { | |
928 this.RefreshReferences(); | |
929 } | |
930 } | |
931 } | |
932 | |
933 /// <summary> | |
934 /// Refreshes references and redraws them correctly. | |
935 /// </summary> | |
936 private void RefreshReferences() | |
937 { | |
938 // Refresh the reference container node for assemblies t
hat could be resolved. | |
939 IReferenceContainer referenceContainer = this.config.Pro
jectMgr.GetReferenceContainer(); | |
940 if (referenceContainer != null) | |
941 { | |
942 foreach (ReferenceNode referenceNode in referenc
eContainer.EnumReferences()) | |
943 { | |
944 referenceNode.RefreshReference(); | |
945 } | |
946 } | |
947 } | |
948 #endregion | |
949 } | |
950 } | |
OLD | NEW |