private static (ITaskItem[] Element, FileMatcher.SearchAction Action, string FileSpec) ExpandWildcards(ITaskItem[] expand) { // Used to detect and log drive enumerating wildcard patterns. string[] files; FileMatcher.SearchAction action = FileMatcher.SearchAction.None; string itemSpec = string.Empty; if (expand == null) { return(null, action, itemSpec); } else { var expanded = new List <ITaskItem>(); foreach (ITaskItem i in expand) { if (FileMatcher.HasWildcards(i.ItemSpec)) { (files, action, _) = FileMatcher.Default.GetFiles(null /* use current directory */, i.ItemSpec); itemSpec = i.ItemSpec; if (action == FileMatcher.SearchAction.FailOnDriveEnumeratingWildcard) { return(expanded.ToArray(), action, itemSpec); } foreach (string file in files) { TaskItem newItem = new TaskItem(i) { ItemSpec = file }; // Compute the RecursiveDir portion. FileMatcher.Result match = FileMatcher.Default.FileMatch(i.ItemSpec, file); if (match.isLegalFileSpec && match.isMatch) { if (!string.IsNullOrEmpty(match.wildcardDirectoryPart)) { newItem.SetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir, match.wildcardDirectoryPart); } } expanded.Add(newItem); } } else { expanded.Add(i); } } return(expanded.ToArray(), action, itemSpec); } }
private static bool IsValidExclude(string exclude) { // TODO: assumption on legal path characters: https://github.com/dotnet/msbuild/issues/781 // Excludes that have both wildcards and non escaped wildcards will never be matched on Windows, because // wildcard characters are invalid in Windows paths. // Filtering these excludes early keeps the glob expander simpler. Otherwise unescaping logic would reach all the way down to // filespec parsing (parse escaped string (to correctly ignore escaped wildcards) and then // unescape the path fragments to unfold potentially escaped wildcard chars) var hasBothWildcardsAndEscapedWildcards = FileMatcher.HasWildcards(exclude) && EscapingUtilities.ContainsEscapedWildcards(exclude); return(!hasBothWildcardsAndEscapedWildcards); }
void ProcessItemSpec(OperationBuilder operationBuilder, string itemSpec, ElementLocation itemSpecLocation) { // Code corresponds to Evaluator.CreateItemsFromInclude // STEP 1: Expand properties in Include string evaluatedIncludeEscaped = _outerExpander.ExpandIntoStringLeaveEscaped(itemSpec, ExpanderOptions.ExpandProperties, itemSpecLocation); // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedIncludeEscaped.Length > 0) { IList <string> includeSplitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedIncludeEscaped); foreach (string includeSplitEscaped in includeSplitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string bool isItemListExpression; ProcessSingleItemVectorExpressionForInclude(includeSplitEscaped, operationBuilder, itemSpecLocation, out isItemListExpression); if (!isItemListExpression) { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(includeSplitEscaped); bool containsRealWildcards = FileMatcher.HasWildcards(includeSplitEscaped); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. // Just return the original string. operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Value, (object)includeSplitEscaped)); } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. string filespecUnescaped = EscapingUtilities.UnescapeAll(includeSplitEscaped); operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Glob, (object)filespecUnescaped)); } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Value, (object)includeSplitEscaped)); } } } } }
internal static bool FilespecHasWildcards(string filespecEscaped) { if (!FileMatcher.HasWildcards(filespecEscaped)) { return(false); } // If the item's Include has both escaped wildcards and real wildcards, then it's // not clear what they are asking us to do. Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. return(!EscapingUtilities.ContainsEscapedWildcards(filespecEscaped)); }
/// <summary> /// Expand wildcards in the item list. /// </summary> /// <param name="expand"></param> /// <returns>Array of items expanded</returns> public static ITaskItem[] ExpandWildcards(ITaskItem[] expand) { if (expand == null) { return(null); } var expanded = new List <ITaskItem>(expand.Length); foreach (ITaskItem item in expand) { if (FileMatcher.HasWildcards(item.ItemSpec)) { string[] files; string directoryName = Path.GetDirectoryName(item.ItemSpec); string searchPattern = Path.GetFileName(item.ItemSpec); // Very often with TLog files we're talking about // a directory and a simply wildcarded filename // Optimize for that case here. if (!FileMatcher.HasWildcards(directoryName) && Directory.Exists(directoryName)) { files = Directory.GetFiles(directoryName, searchPattern); } else { files = FileMatcher.Default.GetFiles(null, item.ItemSpec); } foreach (string file in files) { expanded.Add(new TaskItem(item) { ItemSpec = file }); } } else { expanded.Add(item); } } return(expanded.ToArray()); }
/// <summary> /// Expand wildcards in the item list. /// </summary> private static ITaskItem[] ExpandWildcards(ITaskItem[] expand) { if (expand == null) { return(null); } else { var expanded = new List <ITaskItem>(); foreach (ITaskItem i in expand) { if (FileMatcher.HasWildcards(i.ItemSpec)) { string[] files = FileMatcher.Default.GetFiles(null /* use current directory */, i.ItemSpec); foreach (string file in files) { TaskItem newItem = new TaskItem(i) { ItemSpec = file }; // Compute the RecursiveDir portion. FileMatcher.Result match = FileMatcher.Default.FileMatch(i.ItemSpec, file); if (match.isLegalFileSpec && match.isMatch) { if (!string.IsNullOrEmpty(match.wildcardDirectoryPart)) { newItem.SetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir, match.wildcardDirectoryPart); } } expanded.Add(newItem); } } else { expanded.Add(i); } } return(expanded.ToArray()); } }
/// <summary> /// Expand wildcards in the item list. /// </summary> /// <param name="expand"></param> /// <returns></returns> private static ITaskItem[] ExpandWildcards(ITaskItem[] expand) { if (expand == null) { return(null); } else { ArrayList expanded = new ArrayList(); foreach (ITaskItem i in expand) { if (FileMatcher.HasWildcards(i.ItemSpec)) { string[] files = FileMatcher.GetFiles(null /* use current directory */, i.ItemSpec); foreach (string file in files) { TaskItem newItem = new TaskItem((ITaskItem)i); newItem.ItemSpec = file; // Compute the RecursiveDir portion. FileMatcher.Result match = FileMatcher.FileMatch(i.ItemSpec, file); if (match.isLegalFileSpec && match.isMatch) { if (match.wildcardDirectoryPart != null && match.wildcardDirectoryPart.Length > 0) { newItem.SetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir, match.wildcardDirectoryPart); } } expanded.Add(newItem); } } else { expanded.Add(i); } } return((ITaskItem[])expanded.ToArray(typeof(ITaskItem))); } }
public static ITaskItem[] ExpandWildcards(ITaskItem[] expand) { if (expand == null) { return(null); } List <ITaskItem> list = new List <ITaskItem>(expand.Length); foreach (ITaskItem item in expand) { if (FileMatcher.HasWildcards(item.ItemSpec)) { string[] files; string directoryName = Path.GetDirectoryName(item.ItemSpec); string fileName = Path.GetFileName(item.ItemSpec); if (!FileMatcher.HasWildcards(directoryName) && Directory.Exists(directoryName)) { files = Directory.GetFiles(directoryName, fileName); } else { files = FileMatcher.GetFiles(null, item.ItemSpec); } foreach (string str3 in files) { TaskItem item2 = new TaskItem(item) { ItemSpec = str3 }; list.Add(item2); } } else { list.Add(item); } } return(list.ToArray()); }
private static bool FilespecHasWildcards(string filespecEscaped) { bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(filespecEscaped); bool containsRealWildcards = FileMatcher.HasWildcards(filespecEscaped); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. return(false); } else if (!containsEscapedWildcards && containsRealWildcards) { return(true); } else { return(false); } }
private IEnumerable <ItemFragment> BuildItemFragments(IElementLocation itemSpecLocation, bool expandProperties) { var builder = ImmutableList.CreateBuilder <ItemFragment>(); // Code corresponds to Evaluator.CreateItemsFromInclude var evaluatedItemspecEscaped = ItemSpecString; if (string.IsNullOrEmpty(evaluatedItemspecEscaped)) { return(builder.ToImmutable()); } // STEP 1: Expand properties in Include if (expandProperties) { evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped(ItemSpecString, ExpanderOptions.ExpandProperties, itemSpecLocation); } // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedItemspecEscaped.Length > 0) { var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped); foreach (var splitEscaped in splitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string bool isItemListExpression; var itemReferenceFragment = ProcessItemExpression(splitEscaped, itemSpecLocation, out isItemListExpression); if (isItemListExpression) { builder.Add(itemReferenceFragment); } else { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList var containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(splitEscaped); var containsRealWildcards = FileMatcher.HasWildcards(splitEscaped); // '*' is an illegal character to have in a filename. // todo file-system assumption on legal path characters: https://github.com/Microsoft/msbuild/issues/781 if (containsEscapedWildcards && containsRealWildcards) { // Just return the original string. builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File)); } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped); builder.Add(new GlobFragment(filespecUnescaped, itemSpecLocation.File)); } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File)); } } } } return(builder.ToImmutable()); }
/// <summary> /// Used for the purposes of evaluating an item specification. Given a filespec that may include wildcard characters * and /// ?, we translate it into an actual list of files. If the input filespec doesn't contain any wildcard characters, and it /// doesn't appear to point to an actual file on disk, then we just give back the input string as an array of length one, /// assuming that it wasn't really intended to be a filename (as items are not required to necessarily represent files). /// </summary> /// <owner>RGoel</owner> /// <param name="filespec">The filespec to evaluate.</param> /// <returns>Array of file paths.</returns> internal static string[] GetFileListEscaped ( string directory, string filespec ) { ErrorUtilities.VerifyThrow(filespec.Length > 0, "Need a valid file-spec."); string[] fileList; bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(filespec); bool containsRealWildcards = FileMatcher.HasWildcards(filespec); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. // Just return the original string. fileList = new string[] { EscapingUtilities.Escape(filespec) }; } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. string filespecUnescaped = EscapingUtilities.UnescapeAll(filespec); // Get the list of actual files which match the filespec. Put // the list into a string array. If the filespec started out // as a relative path, we will get back a bunch of relative paths. // If the filespec started out as an absolute path, we will get // back a bunch of absolute paths. fileList = FileMatcher.GetFiles(directory, filespecUnescaped); ErrorUtilities.VerifyThrow(fileList != null, "We must have a list of files here, even if it's empty."); // Before actually returning the file list, we sort them alphabetically. This // provides a certain amount of extra determinism and reproducability. That is, // we're sure that the build will behave in exactly the same way every time, // and on every machine. Array.Sort(fileList); // We must now go back and make sure all special characters are escaped because we always // store data in the engine in escaped form so it doesn't screw up our parsing. // Note that this means that characters that were not escaped in the original filespec // may now be escaped, but that's not easy to avoid. for (int i = 0; i < fileList.Length; i++) { fileList[i] = EscapingUtilities.Escape(fileList[i]); } } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file or the OM host. fileList = new string[] { filespec }; } return(fileList); }
private List <ItemSpecFragment> BuildItemFragments(IElementLocation itemSpecLocation, string projectDirectory, bool expandProperties) { // Code corresponds to Evaluator.CreateItemsFromInclude var evaluatedItemspecEscaped = ItemSpecString; if (string.IsNullOrEmpty(evaluatedItemspecEscaped)) { return(new List <ItemSpecFragment>()); } // STEP 1: Expand properties in Include if (expandProperties) { evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped( ItemSpecString, ExpanderOptions.ExpandProperties, itemSpecLocation); } var semicolonCount = 0; foreach (var c in evaluatedItemspecEscaped) { if (c == ';') { semicolonCount++; } } // estimate the number of fragments with the number of semicolons. This is will overestimate in case of transforms with semicolons, but won't underestimate. var fragments = new List <ItemSpecFragment>(semicolonCount + 1); // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedItemspecEscaped.Length > 0) { var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped); foreach (var splitEscaped in splitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string var itemReferenceFragment = ProcessItemExpression( splitEscaped, itemSpecLocation, projectDirectory, out var isItemListExpression); if (isItemListExpression) { fragments.Add(itemReferenceFragment); } else { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList if (!FileMatcher.HasWildcards(splitEscaped)) { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file fragments.Add(new ValueFragment(splitEscaped, projectDirectory)); } else if (EscapingUtilities.ContainsEscapedWildcards(splitEscaped)) { // '*' is an illegal character to have in a filename. // todo: file-system assumption on legal path characters: https://github.com/dotnet/msbuild/issues/781 // Just return the original string. fragments.Add(new ValueFragment(splitEscaped, projectDirectory)); } else { // Unescape before handing it to the filesystem. var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped); fragments.Add(new GlobFragment(filespecUnescaped, projectDirectory)); } } } } return(fragments); }