2 // DirectoryScanner.cs: Class used by BuildItem.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections.Generic;
32 using Microsoft.Build.Framework;
33 using Microsoft.Build.Utilities;
35 namespace Microsoft.Build.BuildEngine {
36 internal class DirectoryScanner {
38 DirectoryInfo baseDirectory;
39 ITaskItem[] includes, excludes;
40 ITaskItem[] matchedItems;
43 static bool _runningOnWindows;
45 static DirectoryScanner ()
47 PlatformID pid = Environment.OSVersion.Platform;
48 _runningOnWindows =((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
51 public DirectoryScanner ()
57 Dictionary <string, bool> excludedItems;
58 List <ITaskItem> includedItems;
61 throw new ArgumentNullException ("Includes");
62 if (baseDirectory == null)
63 throw new ArgumentNullException ("BaseDirectory");
65 excludedItems = new Dictionary <string, bool> ();
66 includedItems = new List <ITaskItem> ();
69 foreach (ITaskItem excl in excludes)
70 ProcessExclude (excl.ItemSpec, excludedItems);
72 foreach (ITaskItem include_item in includes)
73 ProcessInclude (include_item, excludedItems, includedItems);
75 matchedItems = includedItems.ToArray ();
78 private void ProcessInclude (ITaskItem include_item, Dictionary <string, bool> excludedItems,
79 List <ITaskItem> includedItems)
81 string[] separatedPath;
84 string name = include_item.ItemSpec;
85 if (!HasWildcard (name)) {
86 if (!excludedItems.ContainsKey (Path.GetFullPath (name))) {
87 includedItems.Add (include_item);
88 if (projectFile != null)
89 include_item.SetMetadata ("DefiningProjectFullPath", projectFile);
92 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
93 separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
94 StringSplitOptions.RemoveEmptyEntries);
96 separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
97 StringSplitOptions.RemoveEmptyEntries);
99 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
104 if (Path.IsPathRooted (name)) {
105 // The path may start with a root indicator, but at the same time can
106 // contain relative paths inbetween
107 full_path = Path.GetFullPath (name);
108 baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
109 if (IsRunningOnWindows)
113 full_path = Path.GetFullPath (Path.Combine (Environment.CurrentDirectory, name));
116 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
118 int wildcard_offset = full_path.IndexOf ("**");
119 foreach (FileInfo fi in fileInfo) {
120 string itemName = fi.FullName;
121 if (!Path.IsPathRooted (name) && itemName.Length > baseDirectory.FullName.Length && itemName.StartsWith (baseDirectory.FullName))
122 itemName = itemName.Substring (baseDirectory.FullName.Length + 1);
124 if (!excludedItems.ContainsKey (itemName) && !excludedItems.ContainsKey (Path.GetFullPath (itemName))) {
125 TaskItem item = new TaskItem (include_item);
126 item.ItemSpec = itemName;
128 if (wildcard_offset >= 0) {
129 string rec_dir = Path.GetDirectoryName (fi.FullName.Substring (wildcard_offset));
130 if (rec_dir.Length > 0)
131 rec_dir += Path.DirectorySeparatorChar;
132 item.SetMetadata ("RecursiveDir", rec_dir);
134 if (projectFile != null)
135 item.SetMetadata ("DefiningProjectFullPath", projectFile);
136 includedItems.Add (item);
142 private void ProcessExclude (string name, Dictionary <string, bool> excludedItems)
144 string[] separatedPath;
147 if (name.IndexOf ('?') == -1 && name.IndexOf ('*') == -1) {
148 if (!excludedItems.ContainsKey (Path.GetFullPath (name)))
149 excludedItems.Add (Path.GetFullPath (name), true);
151 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
152 separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
153 StringSplitOptions.RemoveEmptyEntries);
155 separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
156 StringSplitOptions.RemoveEmptyEntries);
158 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
162 if (Path.IsPathRooted (name)) {
163 baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
164 if (IsRunningOnWindows)
169 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
170 foreach (FileInfo fi in fileInfo)
171 if (!excludedItems.ContainsKey (fi.FullName))
172 excludedItems.Add (fi.FullName, true);
176 private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory)
178 return ParseIncludeExclude (input, ptr, directory, false);
181 private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory, bool recursive)
184 List <FileInfo> fileInfos = new List<FileInfo> ();
186 if (input.Length > 1 && ptr == 0 && input [0] == String.Empty)
189 string cur = input.Length > ptr ? input[ptr] : input[input.Length-1];
190 bool dot = cur == ".";
191 recursive = recursive || cur == "**";
192 bool parent = cur == "..";
194 if (input.Length <= ptr + 1) {
196 directory = directory.Parent;
197 if ((input.Length == ptr + 1 && !recursive) || input.Length <= ptr)
198 return directory.GetFiles (cur);
202 di = new DirectoryInfo [1];
205 di = new DirectoryInfo [1];
206 di [0] = directory.Parent;
207 } else if (recursive)
209 // Read this directory and all subdirectories recursive
210 Stack<DirectoryInfo> currentDirectories = new Stack<DirectoryInfo>();
211 currentDirectories.Push(directory);
212 List<DirectoryInfo> allDirectories = new List<DirectoryInfo>();
214 while (currentDirectories.Count > 0)
216 DirectoryInfo current = currentDirectories.Pop();
217 allDirectories.Insert (0, current);
218 foreach (DirectoryInfo dir in current.GetDirectories())
220 currentDirectories.Push(dir);
224 // No further directories shall be read
225 di = allDirectories.ToArray();
227 di = directory.GetDirectories (cur);
229 foreach (DirectoryInfo info in di)
230 fileInfos.AddRange (ParseIncludeExclude (input, ptr + 1, info, recursive));
232 return fileInfos.ToArray ();
235 public static bool HasWildcard (string expression)
237 return expression.IndexOf ('?') >= 0 || expression.IndexOf ('*') >= 0;
240 public DirectoryInfo BaseDirectory {
241 get { return baseDirectory; }
242 set { baseDirectory = value; }
245 public string ProjectFile {
246 get { return projectFile; }
247 set { projectFile = value; }
250 public ITaskItem[] Includes {
251 get { return includes; }
252 set { includes = value; }
255 public ITaskItem[] Excludes {
256 get { return excludes; }
257 set { excludes = value; }
260 public ITaskItem[] MatchedItems {
261 get { return matchedItems; }
264 static bool IsRunningOnWindows {
265 get { return _runningOnWindows; }