[MSBuild] Fix wildcard parsing such as Include="dir\**"
authorAndreia Gaita <shana@spoiledcat.net>
Fri, 20 Jul 2012 15:09:01 +0000 (17:09 +0200)
committerAndreia Gaita <shana@spoiledcat.net>
Fri, 20 Jul 2012 15:11:16 +0000 (17:11 +0200)
The directory scanner was failing if the wildcard was the last item
on the path. Fix it, and also fix the excluded items detection to
make sure it checks the full path as well (paths may or may not be
rooted). Add a test for this.

mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/DirectoryScanner.cs
mcs/class/Microsoft.Build.Tasks/Test/Microsoft.Build.Tasks/CreateItemTest.cs

index d9c7345c4657dd93beae390b1d5b165402ccbe62..69c721c9df9cca3c0c778187da4850ec089c68d4 100644 (file)
@@ -110,9 +110,14 @@ namespace Microsoft.Build.BuildEngine {
 
                                int wildcard_offset = full_path.IndexOf ("**");
                                foreach (FileInfo fi in fileInfo) {
-                                       if (!excludedItems.ContainsKey (fi.FullName)) {
+                                       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 = fi.FullName;
+                                               item.ItemSpec = itemName;
+
                                                if (wildcard_offset >= 0) {
                                                        string rec_dir = Path.GetDirectoryName (fi.FullName.Substring (wildcard_offset));
                                                        if (rec_dir.Length > 0)
@@ -151,6 +156,7 @@ namespace Microsoft.Build.BuildEngine {
                                                // skip the "drive:"
                                                offset = 1;
                                }
+
                                fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
                                foreach (FileInfo fi in fileInfo)
                                        if (!excludedItems.ContainsKey (fi.FullName))
@@ -160,55 +166,61 @@ namespace Microsoft.Build.BuildEngine {
                
                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 if (input[ptr] == "**")
+
+                       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)
                                {
-                                       // 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())
                                        {
-                                               DirectoryInfo current = currentDirectories.Pop();
-                                               allDirectories.Insert (0, current);
-                                               foreach (DirectoryInfo dir in current.GetDirectories())
-                                               {
-                                                       currentDirectories.Push(dir);
-                                               }                                               
+                                               currentDirectories.Push(dir);
                                        }
-                                       
-                                       // No further directories shall be read
-                                       di = allDirectories.ToArray();                                  
-                               } 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;
-                       }
+
+                               // 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)
index cfe90118c3871f96118b8ed58999c812adc2d090..aa8c2575840057c885ef517a4d5e6d0ead60b17a 100755 (executable)
@@ -262,16 +262,66 @@ namespace MonoTests.Microsoft.Build.Tasks {
 
                        // Setup
 
+                       string projectdir = Path.Combine ("Test", "resources");
+                       string basedir = "dir";
+                       string aaa = PathCombine (basedir, "a", "aa", "aaa");
+                       string bb = Path.Combine (basedir, "b", "bb");
+                       string c = Path.Combine (basedir, "c");
+
+                       string[] dirs = { aaa, bb, c };
+                       string[] files = {
+                                                               PathCombine (aaa, "foo.dll"),
+                                                               PathCombine (bb, "bar.dll"),
+                                                               PathCombine (bb, "sample.txt"),
+                                                               Path.Combine (basedir, "xyz.dll")
+                                                         };
+
+                       string documentString = @"
+                               <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" " + Consts.ToolsVersionString + @">
+                                       <Target Name='Main'>
+                                               <CreateItem Include='dir\**'>
+                                                       <Output TaskParameter='Include' ItemName='CI1' />
+                                               </CreateItem>
+                                               <Message Text=""CI1: @(CI1)""/>
+                                       </Target>
+                               </Project>";
+
+                       try {
+                               CreateDirectoriesAndFiles (projectdir, dirs, files);
+                               File.WriteAllText (Path.Combine (projectdir, "wild1.proj"), documentString);
+                               proj.Load (Path.Combine (projectdir, "wild1.proj"));
+                               if (!proj.Build ("Main"))
+                                       Assert.Fail ("Build failed");
+
+                               string full_base_dir = Path.GetFullPath (basedir);
+                               logger.CheckLoggedAny ("CI1: " + String.Join (";", files), MessageImportance.Normal, "A1");
+                       } catch (AssertionException) {
+                               logger.DumpMessages ();
+                               throw;
+                       } finally {
+                               Directory.Delete (Path.Combine (projectdir, basedir), true);
+                       }
+               }
+
+               [Test]
+               public void TestItemsWithWildcards2 () {
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       TestMessageLogger logger = new TestMessageLogger ();
+                       engine.RegisterLogger (logger);
+
+                       // Setup
+
                        string basedir = PathCombine ("Test", "resources", "dir");
                        string aaa = PathCombine ("a", "aa", "aaa");
                        string bb = Path.Combine ("b", "bb");
 
                        string[] dirs = { aaa, bb, "c" };
                        string[] files = {
-                                                               PathCombine (basedir, aaa, "foo.dll"),
-                                                               PathCombine (basedir, bb, "bar.dll"),
-                                                               PathCombine (basedir, bb, "sample.txt"),
-                                                               Path.Combine (basedir, "xyz.dll")
+                                                               PathCombine (aaa, "foo.dll"),
+                                                               PathCombine (bb, "bar.dll"),
+                                                               PathCombine (bb, "sample.txt"),
+                                                               Path.Combine ("xyz.dll")
                                                          };
 
                        string documentString = @"
@@ -326,7 +376,7 @@ namespace MonoTests.Microsoft.Build.Tasks {
                                Directory.CreateDirectory (Path.Combine (basedir, dir));
 
                        foreach (string file in files)
-                               File.WriteAllText (file, String.Empty);
+                               File.WriteAllText (Path.Combine (basedir, file), String.Empty);
                }
 
                string PathCombine (string path1, params string[] parts) {