using System.Collections.Generic;
using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
namespace Microsoft.Build.BuildEngine {
internal class DirectoryScanner {
DirectoryInfo baseDirectory;
- string includes;
- string excludes;
- string[] matchedFilenames;
+ ITaskItem[] includes, excludes;
+ ITaskItem[] matchedItems;
+
+ static bool _runningOnWindows;
+ static DirectoryScanner ()
+ {
+ PlatformID pid = Environment.OSVersion.Platform;
+ _runningOnWindows =((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
+ }
+
public DirectoryScanner ()
{
}
public void Scan ()
{
Dictionary <string, bool> excludedItems;
- List <string> includedItems;
- string[] splitInclude, splitExclude;
+ List <ITaskItem> includedItems;
if (includes == null)
throw new ArgumentNullException ("Includes");
- if (excludes == null)
- throw new ArgumentNullException ("Excludes");
if (baseDirectory == null)
throw new ArgumentNullException ("BaseDirectory");
excludedItems = new Dictionary <string, bool> ();
- includedItems = new List <string> ();
-
- splitInclude = includes.Split (';');
- splitExclude = excludes.Split (';');
+ includedItems = new List <ITaskItem> ();
- if (excludes != String.Empty) {
- foreach (string si in splitExclude) {
- ProcessExclude (si, excludedItems);
- }
- }
- if (includes != String.Empty) {
- foreach (string si in splitInclude) {
- ProcessInclude (si, excludedItems, includedItems);
- }
- }
+ if (excludes != null)
+ foreach (ITaskItem excl in excludes)
+ ProcessExclude (excl.ItemSpec, excludedItems);
+
+ foreach (ITaskItem include_item in includes)
+ ProcessInclude (include_item, excludedItems, includedItems);
- matchedFilenames = includedItems.ToArray ();
+ matchedItems = includedItems.ToArray ();
}
- private void ProcessInclude (string name, Dictionary <string, bool> excludedItems, List <string> includedItems)
+ private void ProcessInclude (ITaskItem include_item, Dictionary <string, bool> excludedItems,
+ List <ITaskItem> includedItems)
{
string[] separatedPath;
FileInfo[] fileInfo;
- if (name.IndexOf ('?') == -1 && name.IndexOf ('*') == -1) {
+ string name = include_item.ItemSpec;
+ if (!HasWildcard (name)) {
if (!excludedItems.ContainsKey (Path.GetFullPath(name)))
- includedItems.Add (name);
+ includedItems.Add (include_item);
} else {
if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
- separatedPath = name.Split (Path.DirectorySeparatorChar);
+ separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
+ StringSplitOptions.RemoveEmptyEntries);
} else {
- separatedPath = name.Split (Path.AltDirectorySeparatorChar);
+ separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
+ StringSplitOptions.RemoveEmptyEntries);
}
if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
return;
- fileInfo = ParseIncludeExclude (separatedPath, 0, baseDirectory);
- foreach (FileInfo fi in fileInfo)
- if (!excludedItems.ContainsKey (fi.FullName))
- includedItems.Add (fi.FullName);
+
+ int offset = 0;
+ if (Path.IsPathRooted (name)) {
+ baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
+ if (IsRunningOnWindows)
+ // skip the "drive:"
+ offset = 1;
+ }
+
+ string full_path = Path.GetFullPath (Path.Combine (Environment.CurrentDirectory, include_item.ItemSpec));
+ fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
+
+ int wildcard_offset = full_path.IndexOf ("**");
+ foreach (FileInfo fi in fileInfo) {
+ string itemName = fi.FullName;
+ if (!Path.IsPathRooted (name) && itemName.Length > baseDirectory.FullName.Length && itemName.StartsWith (baseDirectory.FullName))
+ itemName = itemName.Substring (baseDirectory.FullName.Length + 1);
+
+ if (!excludedItems.ContainsKey (itemName) && !excludedItems.ContainsKey (Path.GetFullPath (itemName))) {
+ TaskItem item = new TaskItem (include_item);
+ item.ItemSpec = itemName;
+
+ if (wildcard_offset >= 0) {
+ string rec_dir = Path.GetDirectoryName (fi.FullName.Substring (wildcard_offset));
+ if (rec_dir.Length > 0)
+ rec_dir += Path.DirectorySeparatorChar;
+ item.SetMetadata ("RecursiveDir", rec_dir);
+ }
+ includedItems.Add (item);
+ }
+ }
}
}
excludedItems.Add (Path.GetFullPath (name), true);
} else {
if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
- separatedPath = name.Split (Path.DirectorySeparatorChar);
+ separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
+ StringSplitOptions.RemoveEmptyEntries);
} else {
- separatedPath = name.Split (Path.AltDirectorySeparatorChar);
+ separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
+ StringSplitOptions.RemoveEmptyEntries);
}
if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
return;
- fileInfo = ParseIncludeExclude (separatedPath, 0, baseDirectory);
+
+ int offset = 0;
+ if (Path.IsPathRooted (name)) {
+ baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
+ if (IsRunningOnWindows)
+ // skip the "drive:"
+ offset = 1;
+ }
+
+ fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
foreach (FileInfo fi in fileInfo)
if (!excludedItems.ContainsKey (fi.FullName))
excludedItems.Add (fi.FullName, true);
private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory)
{
+ return ParseIncludeExclude (input, ptr, directory, false);
+ }
+
+ private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory, bool recursive)
+ {
+ DirectoryInfo[] di;
+ List <FileInfo> fileInfos = new List<FileInfo> ();
+
if (input.Length > 1 && ptr == 0 && input [0] == String.Empty)
ptr++;
- if (input.Length == ptr + 1) {
- FileInfo[] fi;
- fi = directory.GetFiles (input [ptr]);
- return fi;
- } else {
- DirectoryInfo[] di;
- FileInfo[] fi;
- List <FileInfo> fileInfos = new List <FileInfo> ();
- if (input [ptr] == ".") {
- di = new DirectoryInfo [1];
- di [0] = directory;
- } else if (input [ptr] == "..") {
- di = new DirectoryInfo [1];
- di [0] = directory.Parent;
- } else
- di = directory.GetDirectories (input [ptr]);
- foreach (DirectoryInfo info in di) {
- fi = ParseIncludeExclude (input, ptr + 1, info);
- foreach (FileInfo file in fi)
- fileInfos.Add (file);
- }
- fi = new FileInfo [fileInfos.Count];
- int i = 0;
- foreach (FileInfo file in fileInfos)
- fi [i++] = file;
- return fi;
+
+ string cur = input.Length > ptr ? input[ptr] : input[input.Length-1];
+ bool dot = cur == ".";
+ recursive = recursive || cur == "**";
+ bool parent = cur == "..";
+
+ if (input.Length <= ptr + 1) {
+ if (parent)
+ directory = directory.Parent;
+ if ((input.Length == ptr + 1 && !recursive) || input.Length <= ptr)
+ return directory.GetFiles (cur);
}
+
+ if (dot) {
+ di = new DirectoryInfo [1];
+ di [0] = directory;
+ } else if (parent) {
+ di = new DirectoryInfo [1];
+ di [0] = directory.Parent;
+ } else if (recursive)
+ {
+ // Read this directory and all subdirectories recursive
+ Stack<DirectoryInfo> currentDirectories = new Stack<DirectoryInfo>();
+ currentDirectories.Push(directory);
+ List<DirectoryInfo> allDirectories = new List<DirectoryInfo>();
+
+ while (currentDirectories.Count > 0)
+ {
+ DirectoryInfo current = currentDirectories.Pop();
+ allDirectories.Insert (0, current);
+ foreach (DirectoryInfo dir in current.GetDirectories())
+ {
+ currentDirectories.Push(dir);
+ }
+ }
+
+ // No further directories shall be read
+ di = allDirectories.ToArray();
+ } else
+ di = directory.GetDirectories (cur);
+
+ foreach (DirectoryInfo info in di)
+ fileInfos.AddRange (ParseIncludeExclude (input, ptr + 1, info, recursive));
+
+ return fileInfos.ToArray ();
+ }
+
+ public static bool HasWildcard (string expression)
+ {
+ return expression.IndexOf ('?') >= 0 || expression.IndexOf ('*') >= 0;
}
public DirectoryInfo BaseDirectory {
set { baseDirectory = value; }
}
- public string Includes {
+ public ITaskItem[] Includes {
get { return includes; }
set { includes = value; }
}
- public string Excludes {
+ public ITaskItem[] Excludes {
get { return excludes; }
set { excludes = value; }
}
- public string[] MatchedFilenames {
- get { return matchedFilenames; }
+ public ITaskItem[] MatchedItems {
+ get { return matchedItems; }
}
+ static bool IsRunningOnWindows {
+ get { return _runningOnWindows; }
+ }
}
}