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 string[] GetFileList ( string directoryEscaped, string filespecEscaped, bool returnEscaped, bool forceEvaluateWildCards, IEnumerable <string> excludeSpecsEscaped, FileMatcher fileMatcher, object loggingMechanism = null, IElementLocation includeLocation = null, IElementLocation excludeLocation = null, IElementLocation importLocation = null, BuildEventContext buildEventContext = null, string buildEventFileInfoFullPath = null, bool disableExcludeDriveEnumerationWarning = false ) { ErrorUtilities.VerifyThrowInternalLength(filespecEscaped, nameof(filespecEscaped)); string[] fileList; // Used to properly detect and log drive enumerating wildcards when applicable. FileMatcher.SearchAction action = FileMatcher.SearchAction.None; string excludeFileSpec = string.Empty; if (!FilespecHasWildcards(filespecEscaped) || FilespecMatchesLazyWildcard(filespecEscaped, forceEvaluateWildCards)) { // Just return the original string. fileList = new string[] { returnEscaped?filespecEscaped : EscapingUtilities.UnescapeAll(filespecEscaped) }; } else { if (Traits.Instance.LogExpandedWildcards) { ErrorUtilities.DebugTraceMessage("Expanding wildcard for file spec {0}", filespecEscaped); } // Unescape before handing it to the filesystem. var directoryUnescaped = EscapingUtilities.UnescapeAll(directoryEscaped); var filespecUnescaped = EscapingUtilities.UnescapeAll(filespecEscaped); var excludeSpecsUnescaped = excludeSpecsEscaped?.Where(IsValidExclude).Select(i => EscapingUtilities.UnescapeAll(i)).ToList(); // 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. Also retrieves the search action // and relevant Exclude filespec for drive enumerating wildcard detection. (fileList, action, excludeFileSpec) = fileMatcher.GetFiles(directoryUnescaped, filespecUnescaped, excludeSpecsUnescaped); // Determines whether Exclude filespec or passed in file spec should be // used in drive enumeration warning or exception. bool excludeFileSpecIsEmpty = string.IsNullOrWhiteSpace(excludeFileSpec); string fileSpec = excludeFileSpecIsEmpty ? filespecUnescaped : excludeFileSpec; switch (action) { case (FileMatcher.SearchAction.LogDriveEnumeratingWildcard): switch (loggingMechanism) { // Logging mechanism received from ItemGroupIntrinsicTask. case TargetLoggingContext targetLoggingContext: LogDriveEnumerationWarningWithTargetLoggingContext( targetLoggingContext, includeLocation, excludeFileSpecIsEmpty, disableExcludeDriveEnumerationWarning, fileSpec); break; // Logging mechanism received from Evaluator. case ILoggingService loggingService: LogDriveEnumerationWarningWithLoggingService( loggingService, buildEventContext, buildEventFileInfoFullPath, filespecUnescaped); break; // Logging mechanism received from Evaluator and LazyItemEvaluator.IncludeOperation. case EvaluationLoggingContext evaluationLoggingContext: LogDriveEnumerationWarningWithEvaluationLoggingContext( evaluationLoggingContext, importLocation, excludeFileSpecIsEmpty, filespecUnescaped, fileSpec); break; default: throw new InternalErrorException($"Logging type {loggingMechanism.GetType()} is not understood by {nameof(GetFileList)}."); } break; case (FileMatcher.SearchAction.FailOnDriveEnumeratingWildcard): switch (loggingMechanism) { // Logging mechanism received from ItemGroupIntrinsicTask. case TargetLoggingContext targetLoggingContext: ThrowDriveEnumerationExceptionWithTargetLoggingContext( includeLocation, excludeLocation, excludeFileSpecIsEmpty, filespecUnescaped, fileSpec); break; // Logging mechanism received from Evaluator. case ILoggingService loggingService: ThrowDriveEnumerationExceptionWithLoggingService(includeLocation, filespecUnescaped); break; // Logging mechanism received from Evaluator and LazyItemEvaluator.IncludeOperation. case EvaluationLoggingContext evaluationLoggingContext: ThrowDriveEnumerationExceptionWithEvaluationLoggingContext( importLocation, includeLocation, excludeLocation, filespecUnescaped, fileSpec, excludeFileSpecIsEmpty); break; default: throw new InternalErrorException($"Logging type {loggingMechanism.GetType()} is not understood by {nameof(GetFileList)}."); } break; default: break; } 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, StringComparer.OrdinalIgnoreCase); if (returnEscaped) { // 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 interfere with 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]); } } } return(fileList); }