Merge pull request #778 from cmorris98/master
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / DirectoryScanner.cs
1 //
2 // DirectoryScanner.cs: Class used by BuildItem.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31
32 using Microsoft.Build.Framework;
33 using Microsoft.Build.Utilities;
34
35 namespace Microsoft.Build.BuildEngine {
36         internal class DirectoryScanner {
37                 
38                 DirectoryInfo   baseDirectory;
39                 ITaskItem[]     includes, excludes;
40                 ITaskItem[]     matchedItems;
41
42                 static bool _runningOnWindows;
43                 
44                 static DirectoryScanner ()
45                 {
46                         PlatformID pid = Environment.OSVersion.Platform;
47                         _runningOnWindows =((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
48                 }
49
50                 public DirectoryScanner ()
51                 {
52                 }
53                 
54                 public void Scan ()
55                 {
56                         Dictionary <string, bool> excludedItems;
57                         List <ITaskItem> includedItems;
58                         
59                         if (includes == null)
60                                 throw new ArgumentNullException ("Includes");
61                         if (baseDirectory == null)
62                                 throw new ArgumentNullException ("BaseDirectory");
63                         
64                         excludedItems = new Dictionary <string, bool> ();
65                         includedItems = new List <ITaskItem> ();
66                         
67                         if (excludes != null)
68                                 foreach (ITaskItem excl in excludes)
69                                         ProcessExclude (excl.ItemSpec, excludedItems);
70
71                         foreach (ITaskItem include_item in includes)
72                                 ProcessInclude (include_item, excludedItems, includedItems);
73
74                         matchedItems = includedItems.ToArray ();
75                 }
76                 
77                 private void ProcessInclude (ITaskItem include_item, Dictionary <string, bool> excludedItems,
78                                 List <ITaskItem> includedItems)
79                 {
80                         string[] separatedPath;
81                         FileInfo[] fileInfo;
82
83                         string name = include_item.ItemSpec;
84                         if (!HasWildcard (name)) {
85                                 if (!excludedItems.ContainsKey (Path.GetFullPath(name)))
86                                         includedItems.Add (include_item);
87                         } else {
88                                 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
89                                         separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
90                                                         StringSplitOptions.RemoveEmptyEntries);
91                                 } else {
92                                         separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
93                                                         StringSplitOptions.RemoveEmptyEntries);
94                                 }
95                                 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
96                                         return;
97
98                                 int offset = 0;
99                                 string full_path;
100                                 if (Path.IsPathRooted (name)) {
101                                         full_path = name;
102                                         baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
103                                         if (IsRunningOnWindows)
104                                                 // skip the "drive:"
105                                                 offset = 1;
106                                 } else {
107                                         full_path = Path.GetFullPath (Path.Combine (Environment.CurrentDirectory, name));
108                                 }
109
110                                 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
111
112                                 int wildcard_offset = full_path.IndexOf ("**");
113                                 foreach (FileInfo fi in fileInfo) {
114                                         string itemName = fi.FullName;
115                                         if (!Path.IsPathRooted (name) && itemName.Length > baseDirectory.FullName.Length && itemName.StartsWith (baseDirectory.FullName))
116                                                 itemName = itemName.Substring (baseDirectory.FullName.Length + 1);
117
118                                         if (!excludedItems.ContainsKey (itemName) &&  !excludedItems.ContainsKey (Path.GetFullPath (itemName))) {
119                                                 TaskItem item = new TaskItem (include_item);
120                                                 item.ItemSpec = itemName;
121
122                                                 if (wildcard_offset >= 0) {
123                                                         string rec_dir = Path.GetDirectoryName (fi.FullName.Substring (wildcard_offset));
124                                                         if (rec_dir.Length > 0)
125                                                                 rec_dir += Path.DirectorySeparatorChar;
126                                                         item.SetMetadata ("RecursiveDir", rec_dir);
127                                                 }
128                                                 includedItems.Add (item);
129                                         }
130                                 }
131                         }
132                 }
133                 
134                 private void ProcessExclude (string name, Dictionary <string, bool> excludedItems)
135                 {
136                         string[] separatedPath;
137                         FileInfo[] fileInfo;
138                         
139                         if (name.IndexOf ('?') == -1 && name.IndexOf ('*') == -1) {
140                                 if (!excludedItems.ContainsKey (Path.GetFullPath (name)))
141                                         excludedItems.Add (Path.GetFullPath (name), true);
142                         } else {
143                                 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
144                                         separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
145                                                                         StringSplitOptions.RemoveEmptyEntries);
146                                 } else {
147                                         separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
148                                                                         StringSplitOptions.RemoveEmptyEntries);
149                                 }
150                                 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
151                                         return;
152
153                                 int offset = 0;
154                                 if (Path.IsPathRooted (name)) {
155                                         baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
156                                         if (IsRunningOnWindows)
157                                                 // skip the "drive:"
158                                                 offset = 1;
159                                 }
160
161                                 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
162                                 foreach (FileInfo fi in fileInfo)
163                                         if (!excludedItems.ContainsKey (fi.FullName))
164                                                 excludedItems.Add (fi.FullName, true);
165                         }
166                 }
167                 
168                 private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory)
169                 {
170                         return ParseIncludeExclude (input, ptr, directory, false);
171                 }
172
173                 private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory, bool recursive)
174                 {
175                         DirectoryInfo[] di;
176                         List <FileInfo> fileInfos = new List<FileInfo> ();
177
178                         if (input.Length > 1 && ptr == 0 && input [0] == String.Empty)
179                                 ptr++;
180
181                         string cur = input.Length > ptr ? input[ptr] : input[input.Length-1];
182                         bool dot = cur == ".";
183                         recursive = recursive || cur == "**";
184                         bool parent = cur == "..";
185
186                         if (input.Length <= ptr + 1) {
187                                 if (parent)
188                                         directory = directory.Parent;
189                                 if ((input.Length == ptr + 1 && !recursive) || input.Length <= ptr)
190                                         return directory.GetFiles (cur);
191                         }
192
193                         if (dot) {
194                                 di = new DirectoryInfo [1];
195                                 di [0] = directory;
196                         } else if (parent) {
197                                 di = new DirectoryInfo [1];
198                                 di [0] = directory.Parent;
199                         } else if (recursive)
200                         {
201                                 // Read this directory and all subdirectories recursive
202                                 Stack<DirectoryInfo> currentDirectories = new Stack<DirectoryInfo>();
203                                 currentDirectories.Push(directory);
204                                 List<DirectoryInfo> allDirectories = new List<DirectoryInfo>();
205
206                                 while (currentDirectories.Count > 0)
207                                 {
208                                         DirectoryInfo current = currentDirectories.Pop();
209                                         allDirectories.Insert (0, current);
210                                         foreach (DirectoryInfo dir in current.GetDirectories())
211                                         {
212                                                 currentDirectories.Push(dir);
213                                         }
214                                 }
215
216                                 // No further directories shall be read
217                                 di = allDirectories.ToArray();
218                         } else
219                                 di = directory.GetDirectories (cur);
220
221                         foreach (DirectoryInfo info in di)
222                                 fileInfos.AddRange (ParseIncludeExclude (input, ptr + 1, info, recursive));
223
224                         return fileInfos.ToArray ();
225                 }
226
227                 public static bool HasWildcard (string expression)
228                 {
229                         return expression.IndexOf ('?') >= 0 || expression.IndexOf ('*') >= 0;
230                 }
231                 
232                 public DirectoryInfo BaseDirectory {
233                         get { return baseDirectory; }
234                         set { baseDirectory = value; }
235                 }
236                 
237                 public ITaskItem[] Includes {
238                         get { return includes; }
239                         set { includes = value; }
240                 }
241                 
242                 public ITaskItem[] Excludes {
243                         get { return excludes; }
244                         set { excludes = value; }
245                 }
246                 
247                 public ITaskItem[] MatchedItems {
248                         get { return matchedItems; }
249                 }
250                 
251                 static bool IsRunningOnWindows {
252                         get { return _runningOnWindows; }
253                 }
254         }
255 }