w32file-unix.c: In function 'mono_w32file_get_file_size':
[mono-project.git] / mcs / class / System.Web / System.Web.Compilation / BuildManagerDirectoryBuilder.cs
blobc0168aaea4e1bfdc82ba4115822b9967659184ff
1 //
2 // System.Web.Compilation.BuildManagerDirectoryBuilder
3 //
4 // Authors:
5 // Marek Habersack (mhabersack@novell.com)
6 //
7 // (C) 2008-2009 Novell, Inc (http://www.novell.com)
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.Collections.Generic;
33 using System.Reflection;
34 using System.Web;
35 using System.Web.Configuration;
36 using System.Web.Hosting;
37 using System.Web.Util;
39 namespace System.Web.Compilation
41 sealed class BuildManagerDirectoryBuilder
43 sealed class BuildProviderItem
45 public BuildProvider Provider;
46 public int ListIndex;
47 public int ParentIndex;
49 public BuildProviderItem (BuildProvider bp, int listIndex, int parentIndex)
51 this.Provider = bp;
52 this.ListIndex = listIndex;
53 this.ParentIndex = parentIndex;
57 readonly VirtualPath virtualPath;
58 readonly string virtualPathDirectory;
59 CompilationSection compilationSection;
60 Dictionary <string, BuildProvider> buildProviders;
61 VirtualPathProvider vpp;
63 CompilationSection CompilationSection {
64 get {
65 if (compilationSection == null)
66 compilationSection = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
67 return compilationSection;
71 public BuildManagerDirectoryBuilder (VirtualPath virtualPath)
73 if (virtualPath == null)
74 throw new ArgumentNullException ("virtualPath");
76 this.vpp = HostingEnvironment.VirtualPathProvider;
77 this.virtualPath = virtualPath;
78 this.virtualPathDirectory = VirtualPathUtility.GetDirectory (virtualPath.Absolute);
81 public List <BuildProviderGroup> Build (bool single)
83 if (StrUtils.StartsWith (virtualPath.AppRelative, "~/App_Themes/")) {
84 var themebp = new ThemeDirectoryBuildProvider ();
85 themebp.SetVirtualPath (virtualPath);
87 return GetSingleBuildProviderGroup (themebp);
90 CompilationSection section = CompilationSection;
91 BuildProviderCollection bpcoll = section != null ? section.BuildProviders : null;
93 if (bpcoll == null || bpcoll.Count == 0)
94 return null;
96 if (virtualPath.IsFake) {
97 BuildProvider bp = GetBuildProvider (virtualPath, bpcoll);
99 if (bp == null)
100 return null;
102 return GetSingleBuildProviderGroup (bp);
105 if (single) {
106 AddVirtualFile (GetVirtualFile (virtualPath.Absolute), bpcoll);
107 } else {
108 var cache = new Dictionary <string, bool> (RuntimeHelpers.StringEqualityComparer);
109 AddVirtualDir (GetVirtualDirectory (virtualPath.Absolute), bpcoll, cache);
110 cache = null;
111 if (buildProviders == null || buildProviders.Count == 0)
112 AddVirtualFile (GetVirtualFile (virtualPath.Absolute), bpcoll);
115 if (buildProviders == null || buildProviders.Count == 0)
116 return null;
118 var buildProviderGroups = new List <BuildProviderGroup> ();
119 foreach (BuildProvider bp in buildProviders.Values)
120 AssignToGroup (bp, buildProviderGroups);
122 if (buildProviderGroups == null || buildProviderGroups.Count == 0) {
123 buildProviderGroups = null;
124 return null;
127 // We need to reverse the order, so that the build happens from the least
128 // dependant assemblies to the most dependant ones, more or less.
129 buildProviderGroups.Reverse ();
131 return buildProviderGroups;
134 bool AddBuildProvider (BuildProvider buildProvider)
136 if (buildProviders == null)
137 buildProviders = new Dictionary <string, BuildProvider> (RuntimeHelpers.StringEqualityComparer);
139 string bpPath = buildProvider.VirtualPath;
140 if (buildProviders.ContainsKey (bpPath))
141 return false;
143 buildProviders.Add (bpPath, buildProvider);
144 return true;
147 void AddVirtualDir (VirtualDirectory vdir, BuildProviderCollection bpcoll, Dictionary <string, bool> cache)
149 if (vdir == null)
150 return;
152 BuildProvider bp;
153 IDictionary <string, bool> deps;
154 var dirs = new List <string> ();
155 string fileVirtualPath;
157 foreach (VirtualFile file in vdir.Files) {
158 fileVirtualPath = file.VirtualPath;
159 if (BuildManager.IgnoreVirtualPath (fileVirtualPath))
160 continue;
162 bp = GetBuildProvider (fileVirtualPath, bpcoll);
163 if (bp == null)
164 continue;
165 if (!AddBuildProvider (bp))
166 continue;
168 deps = bp.ExtractDependencies ();
169 if (deps == null)
170 continue;
172 string depDir, s;
173 dirs.Clear ();
174 foreach (var dep in deps) {
175 s = dep.Key;
176 depDir = VirtualPathUtility.GetDirectory (s); // dependencies are assumed to contain absolute paths
177 if (cache.ContainsKey (depDir))
178 continue;
179 cache.Add (depDir, true);
180 AddVirtualDir (GetVirtualDirectory (s), bpcoll, cache);
185 void AddVirtualFile (VirtualFile file, BuildProviderCollection bpcoll)
187 if (file == null || BuildManager.IgnoreVirtualPath (file.VirtualPath))
188 return;
190 BuildProvider bp = GetBuildProvider (file.VirtualPath, bpcoll);
191 if (bp == null)
192 return;
193 AddBuildProvider (bp);
196 List <BuildProviderGroup> GetSingleBuildProviderGroup (BuildProvider bp)
198 var ret = new List <BuildProviderGroup> ();
199 var group = new BuildProviderGroup ();
200 group.AddProvider (bp);
201 ret.Add (group);
203 return ret;
206 VirtualDirectory GetVirtualDirectory (string virtualPath)
208 if (!vpp.DirectoryExists (VirtualPathUtility.GetDirectory (virtualPath)))
209 return null;
211 return vpp.GetDirectory (virtualPath);
214 VirtualFile GetVirtualFile (string virtualPath)
216 if (!vpp.FileExists (virtualPath))
217 return null;
219 return vpp.GetFile (virtualPath);
222 Type GetBuildProviderCodeDomType (BuildProvider bp)
224 CompilerType ct = bp.CodeCompilerType;
225 if (ct == null) {
226 string language = bp.LanguageName;
228 if (String.IsNullOrEmpty (language))
229 language = CompilationSection.DefaultLanguage;
231 ct = BuildManager.GetDefaultCompilerTypeForLanguage (language, CompilationSection, false);
234 Type ret = ct != null ? ct.CodeDomProviderType : null;
235 if (ret == null)
236 throw new HttpException ("Unable to determine code compilation language provider for virtual path '" + bp.VirtualPath + "'.");
238 return ret;
241 void AssignToGroup (BuildProvider buildProvider, List <BuildProviderGroup> groups)
243 if (IsDependencyCycle (buildProvider))
244 throw new HttpException ("Dependency cycles are not suppported: " + buildProvider.VirtualPath);
246 BuildProviderGroup myGroup = null;
247 string bpVirtualPath = buildProvider.VirtualPath;
248 string bpPath = VirtualPathUtility.GetDirectory (bpVirtualPath);
249 bool canAdd;
251 if (BuildManager.HasCachedItemNoLock (buildProvider.VirtualPath))
252 return;
254 StringComparison stringComparison = RuntimeHelpers.StringComparison;
255 if (buildProvider is ApplicationFileBuildProvider || buildProvider is ThemeDirectoryBuildProvider) {
256 // global.asax and theme directory go into their own assemblies
257 myGroup = new BuildProviderGroup ();
258 myGroup.Standalone = true;
259 InsertGroup (myGroup, groups);
260 } else {
261 Type bpCodeDomType = GetBuildProviderCodeDomType (buildProvider);
262 foreach (BuildProviderGroup group in groups) {
263 if (group.Standalone)
264 continue;
266 if (group.Count == 0) {
267 myGroup = group;
268 break;
271 canAdd = true;
272 foreach (BuildProvider bp in group) {
273 if (IsDependency (buildProvider, bp)) {
274 canAdd = false;
275 break;
278 // There should be one assembly per virtual dir
279 if (String.Compare (bpPath, VirtualPathUtility.GetDirectory (bp.VirtualPath), stringComparison) != 0) {
280 canAdd = false;
281 break;
284 // Different languages go to different assemblies
285 if (bpCodeDomType != null) {
286 Type type = GetBuildProviderCodeDomType (bp);
287 if (type != null) {
288 if (type != bpCodeDomType) {
289 canAdd = false;
290 break;
296 if (!canAdd)
297 continue;
299 myGroup = group;
300 break;
303 if (myGroup == null) {
304 myGroup = new BuildProviderGroup ();
305 InsertGroup (myGroup, groups);
309 myGroup.AddProvider (buildProvider);
310 if (String.Compare (bpPath, virtualPathDirectory, stringComparison) == 0)
311 myGroup.Master = true;
314 void InsertGroup (BuildProviderGroup group, List <BuildProviderGroup> groups)
316 if (group.Application) {
317 groups.Insert (groups.Count - 1, group);
318 return;
321 int index;
322 if (group.Standalone)
323 index = groups.FindLastIndex (SkipApplicationGroup);
324 else
325 index = groups.FindLastIndex (SkipStandaloneGroups);
327 if (index == -1)
328 groups.Add (group);
329 else
330 groups.Insert (index == 0 ? 0 : index - 1, group);
333 static bool SkipStandaloneGroups (BuildProviderGroup group)
335 if (group == null)
336 return false;
338 return group.Standalone;
341 static bool SkipApplicationGroup (BuildProviderGroup group)
343 if (group == null)
344 return false;
346 return group.Application;
349 bool IsDependency (BuildProvider bp1, BuildProvider bp2)
351 IDictionary <string, bool> deps = bp1.ExtractDependencies ();
352 if (deps == null)
353 return false;
355 if (deps.ContainsKey (bp2.VirtualPath))
356 return true;
358 BuildProvider bp;
359 // It won't loop forever as by the time this method is called, we are sure there are no cycles
360 foreach (var dep in deps) {
361 if (!buildProviders.TryGetValue (dep.Key, out bp))
362 continue;
364 if (IsDependency (bp, bp2))
365 return true;
368 return false;
371 bool IsDependencyCycle (BuildProvider buildProvider)
373 var cache = new Dictionary <BuildProvider, bool> ();
374 cache.Add (buildProvider, true);
375 return IsDependencyCycle (cache, buildProvider.ExtractDependencies ());
378 bool IsDependencyCycle (Dictionary <BuildProvider, bool> cache, IDictionary <string, bool> deps)
380 if (deps == null)
381 return false;
383 BuildProvider bp;
384 foreach (var d in deps) {
385 if (!buildProviders.TryGetValue (d.Key, out bp))
386 continue;
387 if (cache.ContainsKey (bp))
388 return true;
389 cache.Add (bp, true);
390 if (IsDependencyCycle (cache, bp.ExtractDependencies ()))
391 return true;
392 cache.Remove (bp);
395 return false;
398 public static BuildProvider GetBuildProvider (string virtualPath, BuildProviderCollection coll)
400 return GetBuildProvider (new VirtualPath (virtualPath), coll);
403 public static BuildProvider GetBuildProvider (VirtualPath virtualPath, BuildProviderCollection coll)
405 if (virtualPath == null || String.IsNullOrEmpty (virtualPath.Original) || coll == null)
406 return null;
408 string extension = virtualPath.Extension;
409 BuildProvider bp = coll.GetProviderInstanceForExtension (extension);
410 if (bp == null) {
411 if (String.Compare (extension, ".asax", StringComparison.OrdinalIgnoreCase) == 0)
412 bp = new ApplicationFileBuildProvider ();
413 else if (StrUtils.StartsWith (virtualPath.AppRelative, "~/App_Themes/"))
414 bp = new ThemeDirectoryBuildProvider ();
416 if (bp != null)
417 bp.SetVirtualPath (virtualPath);
419 return bp;
422 object[] attrs = bp.GetType ().GetCustomAttributes (typeof (BuildProviderAppliesToAttribute), true);
423 if (attrs != null && attrs.Length != 0) {
424 BuildProviderAppliesTo appliesTo = ((BuildProviderAppliesToAttribute)attrs [0]).AppliesTo;
425 if ((appliesTo & BuildProviderAppliesTo.Web) == 0)
426 return null;
429 bp.SetVirtualPath (virtualPath);
430 return bp;