OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Diagnostics; | |
5 using System.IO; | |
6 using Microsoft.VisualStudio; | |
7 using Microsoft.VisualStudio.Shell.Interop; | |
8 using Microsoft.Win32; | |
9 using IServiceProvider = System.IServiceProvider; | |
10 using MSBuild = Microsoft.Build.BuildEngine; | |
11 using VSRegistry = Microsoft.VisualStudio.Shell.VSRegistry; | |
12 | |
13 namespace Microsoft.VisualStudio.Project | |
14 { | |
15 /// <summary> | |
16 /// This class defines and sets the so called global properties that are
needed to be provided | |
17 /// before a project builds. | |
18 /// </summary> | |
19 internal class GlobalPropertyHandler : IDisposable | |
20 { | |
21 #region constants | |
22 /// <summary> | |
23 /// The registry relative path entry for finding the fxcop insta
lldir | |
24 /// </summary> | |
25 private const string FxCopRegistryRelativePathEntry = "Setup\\ED
ev"; | |
26 | |
27 /// <summary> | |
28 /// The registry installation Directory key name. | |
29 /// </summary> | |
30 private const string FxCopRegistryInstallDirKeyName = "FxCopDir"
; | |
31 | |
32 /// <summary> | |
33 /// This is the constant that will be set as the value of the VS
IDEResolvedNonMSBuildProjectOutputs global property. | |
34 /// </summary> | |
35 private const string VSIDEResolvedNonMSBuildProjectOutputsValue
= "<VSIDEResolvedNonMSBuildProjectOutputs></VSIDEResolvedNonMSBuildProjectOutput
s>"; | |
36 | |
37 | |
38 #endregion | |
39 | |
40 #region fields | |
41 /// <summary> | |
42 /// Raised when the active project configuration for a project i
n the solution has changed. | |
43 /// </summary> | |
44 internal event EventHandler<ActiveConfigurationChangedEventArgs>
ActiveConfigurationChanged; | |
45 | |
46 /// <summary> | |
47 /// Defines the global properties of the associated build projec
t. | |
48 /// </summary> | |
49 private MSBuild.BuildPropertyGroup globalProjectProperties; | |
50 | |
51 /// <summary> | |
52 /// Defines the global properties of the associated build engine
. | |
53 /// </summary> | |
54 private MSBuild.BuildPropertyGroup globalEngineProperties; | |
55 | |
56 /// <summary> | |
57 /// Flag determining if the object has been disposed. | |
58 /// </summary> | |
59 private bool isDisposed; | |
60 | |
61 /// <summary> | |
62 /// Defines an object that will be a mutex for this object for s
ynchronizing thread calls. | |
63 /// </summary> | |
64 private static volatile object Mutex = new object(); | |
65 | |
66 /// <summary> | |
67 /// Defines the configuration change listener. | |
68 /// </summary> | |
69 private UpdateConfigPropertiesListener configurationChangeListen
er; | |
70 #endregion | |
71 | |
72 #region constructors | |
73 /// <summary> | |
74 /// Overloaded constructor. | |
75 /// </summary> | |
76 /// <param name="project">An instance of a build project</param> | |
77 /// <exception cref="ArgumentNullException">Is thrown if the pas
sed Project is null.</exception> | |
78 internal GlobalPropertyHandler(MSBuild.Project project) | |
79 { | |
80 Debug.Assert(project != null, "The project parameter pas
sed cannot be null"); | |
81 | |
82 this.globalProjectProperties = project.GlobalProperties; | |
83 | |
84 Debug.Assert(project.ParentEngine != null, "The parent e
ngine has not been initialized"); | |
85 | |
86 this.globalEngineProperties = project.ParentEngine.Globa
lProperties; | |
87 } | |
88 #endregion | |
89 | |
90 #region IDisposable Members | |
91 | |
92 /// <summary> | |
93 /// The IDispose interface Dispose method for disposing the obje
ct determinastically. | |
94 /// </summary> | |
95 public void Dispose() | |
96 { | |
97 this.Dispose(true); | |
98 GC.SuppressFinalize(this); | |
99 } | |
100 | |
101 #endregion | |
102 | |
103 #region methods | |
104 /// <summary> | |
105 /// Initializes MSBuild project properties. This method is calle
d before the first project re-evaluation happens in order to set the global prop
erties. | |
106 /// </summary> | |
107 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Glob
alization", "CA1308:NormalizeStringsToUppercase")] | |
108 internal virtual void InitializeGlobalProperties() | |
109 { | |
110 | |
111 // Set the BuildingInsideVisualStudio property to true. | |
112 this.SetGlobalProperty(GlobalProperty.BuildingInsideVisu
alStudio.ToString(), "true"); | |
113 | |
114 // Set the ResolvedNonMSBuildProjectOutputs property to
empty. This is so that it has some deterministic value, even | |
115 // if it's empty. This is important because of the way
global properties are merged together when one | |
116 // project is calling the <MSBuild> task on another proj
ect. | |
117 this.SetGlobalProperty(GlobalProperty.VSIDEResolvedNonMS
BuildProjectOutputs.ToString(), VSIDEResolvedNonMSBuildProjectOutputsValue); | |
118 | |
119 // Set the RunCodeAnalysisOverride property to false. T
his is so that it has some deterministic value. | |
120 // This is important because of the way global propertie
s are merged together when one | |
121 // project is calling the <MSBuild> task on another proj
ect. | |
122 this.SetGlobalProperty(GlobalProperty.RunCodeAnalysisOnc
e.ToString(), "false"); | |
123 | |
124 // Set Configuration=Debug. This is a perf optimization
, not strictly required for correct functionality. | |
125 // Since most people keep most of their projects with Ac
tive Configuration = "Debug" during development, | |
126 // setting this up front makes it faster to load the pro
ject. This way, we don't have to change the | |
127 // value of Configuration down the road, forcing MSBuild
to have to re-evaluate the project. | |
128 this.SetGlobalProperty(GlobalProperty.Configuration.ToSt
ring(), ProjectConfig.Debug); | |
129 | |
130 // Set Platform=AnyCPU. This is a perf optimization, no
t strictly required for correct functionality. | |
131 // Since most people keep most of their projects with Ac
tive Platform = "AnyCPU" during development, | |
132 // setting this up front makes it faster to load the pro
ject. This way, we don't have to change the | |
133 // value of Platform down the road, forcing MSBuild to h
ave to re-evaluate the project. | |
134 this.SetGlobalProperty(GlobalProperty.Platform.ToString(
), ProjectConfig.AnyCPU); | |
135 | |
136 // Set the solution related msbuild global properties. | |
137 this.SetSolutionProperties(); | |
138 | |
139 // Set the VS location global property. | |
140 this.SetGlobalProperty(GlobalProperty.DevEnvDir.ToString
(), GetEnvironmentDirectoryLocation()); | |
141 | |
142 // Set the fxcop location global property. | |
143 this.SetGlobalProperty(GlobalProperty.FxCopDir.ToString(
), GetFxCopDirectoryLocation()); | |
144 } | |
145 | |
146 /// <summary> | |
147 /// Initializes the internal configuration change listener. | |
148 /// </summary> | |
149 /// <param name="hierarchy">The associated service hierarchy.</p
aram> | |
150 /// <param name="serviceProvider">The associated service provide
r.</param> | |
151 internal void RegisterConfigurationChangeListener(IVsHierarchy h
ierarchy, IServiceProvider serviceProvider) | |
152 { | |
153 Debug.Assert(hierarchy != null, "The passed hierarchy ca
nnot be null"); | |
154 Debug.Assert(serviceProvider != null, "The passed servic
e provider cannot be null"); | |
155 Debug.Assert(this.configurationChangeListener == null, "
The configuration change listener has already been initialized"); | |
156 this.configurationChangeListener = new UpdateConfigPrope
rtiesListener(this, serviceProvider); | |
157 } | |
158 | |
159 /// <summary> | |
160 /// The method that does the cleanup. | |
161 /// </summary> | |
162 /// <param name="disposing">true if called from IDispose.Dispose
; false if called from Finalizer.</param> | |
163 protected virtual void Dispose(bool disposing) | |
164 { | |
165 // Everybody can go here. | |
166 if(!this.isDisposed) | |
167 { | |
168 // Synchronize calls to the Dispose simultanious
ly. | |
169 lock(Mutex) | |
170 { | |
171 if(disposing) | |
172 { | |
173 this.configurationChangeListener
.Dispose(); | |
174 } | |
175 | |
176 this.isDisposed = true; | |
177 } | |
178 } | |
179 } | |
180 | |
181 /// <summary> | |
182 /// Called when the active project configuration for a project i
n the solution has changed. | |
183 /// </summary> | |
184 /// <param name="hierarchy">The project whose configuration has
changed.</param> | |
185 private void RaiseActiveConfigurationChanged(IVsHierarchy hierar
chy) | |
186 { | |
187 // Save event in temporary variable to avoid race condit
ion. | |
188 EventHandler<ActiveConfigurationChangedEventArgs> tempEv
ent = this.ActiveConfigurationChanged; | |
189 if(tempEvent != null) | |
190 { | |
191 tempEvent(this, new ActiveConfigurationChangedEv
entArgs(hierarchy)); | |
192 } | |
193 } | |
194 | |
195 /// <summary> | |
196 /// Sets the solution related global properties (SolutionName, S
olutionFileName, SolutionPath, SolutionDir, SolutionExt). | |
197 /// </summary> | |
198 private void SetSolutionProperties() | |
199 { | |
200 IVsSolution solution = Microsoft.VisualStudio.Shell.Pack
age.GetGlobalService(typeof(IVsSolution)) as IVsSolution; | |
201 Debug.Assert(solution != null, "Could not retrieve the s
olution service from the global service provider"); | |
202 | |
203 string solutionDirectory, solutionFile, userOptionsFile; | |
204 | |
205 // We do not want to throw. If we cannot set the solutio
n related constants we set them to empty string. | |
206 ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out
solutionDirectory, out solutionFile, out userOptionsFile)); | |
207 | |
208 if(solutionDirectory == null) | |
209 { | |
210 solutionDirectory = String.Empty; | |
211 } | |
212 | |
213 this.SetGlobalProperty(GlobalProperty.SolutionDir.ToStri
ng(), solutionDirectory); | |
214 | |
215 if(solutionFile == null) | |
216 { | |
217 solutionFile = String.Empty; | |
218 } | |
219 | |
220 this.SetGlobalProperty(GlobalProperty.SolutionPath.ToStr
ing(), solutionFile); | |
221 | |
222 string solutionFileName = (solutionFile.Length == 0) ? S
tring.Empty : Path.GetFileName(solutionFile); | |
223 this.SetGlobalProperty(GlobalProperty.SolutionFileName.T
oString(), solutionFileName); | |
224 | |
225 string solutionName = (solutionFile.Length == 0) ? Strin
g.Empty : Path.GetFileNameWithoutExtension(solutionFile); | |
226 this.SetGlobalProperty(GlobalProperty.SolutionName.ToStr
ing(), solutionName); | |
227 | |
228 string solutionExtension = String.Empty; | |
229 if(solutionFile.Length > 0 && Path.HasExtension(solution
File)) | |
230 { | |
231 solutionExtension = Path.GetExtension(solutionFi
le); | |
232 } | |
233 | |
234 this.SetGlobalProperty(GlobalProperty.SolutionExt.ToStri
ng(), solutionExtension); | |
235 } | |
236 | |
237 /// <summary> | |
238 /// Retrieves the Devenv installation directory. | |
239 /// </summary> | |
240 private static string GetEnvironmentDirectoryLocation() | |
241 { | |
242 IVsShell shell = Microsoft.VisualStudio.Shell.Package.Ge
tGlobalService(typeof(IVsShell)) as IVsShell; | |
243 Debug.Assert(shell != null, "Could not retrieve the IVsS
hell service from the global service provider"); | |
244 | |
245 object installDirAsObject; | |
246 | |
247 // We do not want to throw. If we cannot set the solutio
n related constants we set them to empty string. | |
248 ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSS
PROPID.VSSPROPID_InstallDirectory, out installDirAsObject)); | |
249 | |
250 string installDir = ((string)installDirAsObject); | |
251 | |
252 if(String.IsNullOrEmpty(installDir)) | |
253 { | |
254 return String.Empty; | |
255 } | |
256 | |
257 // Ensure that we have traimnling backslash as this is d
one for the langproj macros too. | |
258 if(installDir[installDir.Length - 1] != Path.DirectorySe
paratorChar) | |
259 { | |
260 installDir += Path.DirectorySeparatorChar; | |
261 } | |
262 | |
263 return installDir; | |
264 } | |
265 | |
266 | |
267 /// <summary> | |
268 /// Retrieves the fxcop dierctory location | |
269 /// </summary> | |
270 private static string GetFxCopDirectoryLocation() | |
271 { | |
272 using(RegistryKey root = VSRegistry.RegistryRoot(__VsLoc
alRegistryType.RegType_Configuration)) | |
273 { | |
274 if(null == root) | |
275 { | |
276 return String.Empty; | |
277 } | |
278 | |
279 using(RegistryKey key = root.OpenSubKey(FxCopReg
istryRelativePathEntry)) | |
280 { | |
281 if(key != null) | |
282 { | |
283 string fxcopInstallDir = key.Get
Value(FxCopRegistryInstallDirKeyName) as string; | |
284 | |
285 return (fxcopInstallDir == null)
? String.Empty : fxcopInstallDir; | |
286 } | |
287 } | |
288 } | |
289 | |
290 return String.Empty; | |
291 } | |
292 | |
293 /// <summary> | |
294 /// Sets a global property on the associated build project and b
uild engine. | |
295 /// </summary> | |
296 /// <param name="propertyName">The name of teh property to set.<
/param> | |
297 /// <param name="propertyValue">Teh value of teh property.</para
m> | |
298 private void SetGlobalProperty(string propertyName, string prope
rtyValue) | |
299 { | |
300 this.globalProjectProperties.SetProperty(propertyName, p
ropertyValue, true); | |
301 | |
302 // Set the same global property on the parent Engine obj
ect. The Project | |
303 // object, when it was created, got a clone of the globa
l properties from | |
304 // the engine. So changing it in the Project doesn't im
pact the Engine. | |
305 // However, we do need the Engine to have this new globa
l property setting | |
306 // as well, because with project-to-project references,
any child projects | |
307 // are going to get their initial global properties from
the Engine when | |
308 // they are created. | |
309 this.globalEngineProperties.SetProperty(propertyName, pr
opertyValue, true); | |
310 } | |
311 #endregion | |
312 | |
313 #region nested types | |
314 /// <summary> | |
315 /// Defines a class that will listen to configuration changes an
d will update platform and configuration name changes accordingly. | |
316 /// </summary> | |
317 private class UpdateConfigPropertiesListener : UpdateSolutionEve
ntsListener | |
318 { | |
319 #region fields | |
320 | |
321 /// <summary> | |
322 /// Defines the containing object. | |
323 /// </summary> | |
324 private GlobalPropertyHandler globalPropertyHandler; | |
325 #endregion | |
326 | |
327 #region constructors | |
328 /// <summary> | |
329 /// Overloaded constructor. | |
330 /// </summary> | |
331 /// <param name="globalProperties"></param> | |
332 /// <param name="associatedHierachy">The associated hier
rachy.</param> | |
333 /// <param name="serviceProvider">The associated service
provider</param> | |
334 internal UpdateConfigPropertiesListener(GlobalPropertyHa
ndler globalPropertyHandler, IServiceProvider serviceProvider) | |
335 : base(serviceProvider) | |
336 { | |
337 this.globalPropertyHandler = globalPropertyHandl
er; | |
338 } | |
339 #endregion | |
340 | |
341 #region methods | |
342 /// <summary> | |
343 /// Called when the active project configuration for a p
roject in the solution has changed. | |
344 /// </summary> | |
345 /// <param name="hierarchy">The project whose configurat
ion has changed.</param> | |
346 /// <returns>If the method succeeds, it returns S_OK. If
it fails, it returns an error code.</returns> | |
347 public override int OnActiveProjectCfgChange(IVsHierarch
y hierarchy) | |
348 { | |
349 this.globalPropertyHandler.RaiseActiveConfigurat
ionChanged(hierarchy); | |
350 return VSConstants.S_OK; | |
351 } | |
352 #endregion | |
353 } | |
354 #endregion | |
355 } | |
356 } | |
OLD | NEW |