/// <summary>
        ///     Parse the given <paramref name="fileSpec" /> into a <see cref="MSBuildGlob" /> using a given
        ///     <paramref name="globRoot" />.
        /// </summary>
        /// <param name="globRoot">
        ///     The root of the glob.
        ///     The fixed directory part of the glob and the match arguments (<see cref="IsMatch" /> and <see cref="MatchInfo" />)
        ///     will get normalized against this root.
        ///     If empty, the current working directory is used.
        ///     Cannot be null, and cannot contain invalid path arguments.
        /// </param>
        /// <param name="fileSpec">The string to parse</param>
        /// <returns></returns>
        public static MSBuildGlob Parse(string globRoot, string fileSpec)
        {
            ErrorUtilities.VerifyThrowArgumentNull(globRoot, nameof(globRoot));
            ErrorUtilities.VerifyThrowArgumentNull(fileSpec, nameof(fileSpec));
            ErrorUtilities.VerifyThrowArgumentInvalidPath(globRoot, nameof(globRoot));

            if (globRoot == string.Empty)
            {
                globRoot = Directory.GetCurrentDirectory();
            }

            globRoot = FileUtilities.NormalizePath(globRoot).WithTrailingSlash();

            var lazyState = new Lazy <GlobState>(() =>
            {
                string fixedDirectoryPart    = null;
                string wildcardDirectoryPart = null;
                string filenamePart          = null;

                string matchFileExpression;
                bool needsRecursion;
                bool isLegalFileSpec;

                FileMatcher.GetFileSpecInfo(
                    fileSpec,
                    out fixedDirectoryPart,
                    out wildcardDirectoryPart,
                    out filenamePart,
                    out matchFileExpression,
                    out needsRecursion,
                    out isLegalFileSpec,
                    FileMatcher.s_defaultGetFileSystemEntries,
                    (fixedDirPart, wildcardDirPart, filePart) =>
                {
                    var normalizedFixedPart = NormalizeTheFixedDirectoryPartAgainstTheGlobRoot(fixedDirPart, globRoot);

                    return(Tuple.Create(normalizedFixedPart, wildcardDirPart, filePart));
                });

                // compile the regex since it's expected to be used multiple times
                var regex = isLegalFileSpec
                    ? new Regex(matchFileExpression, FileMatcher.DefaultRegexOptions | RegexOptions.Compiled)
                    : null;

                return(new GlobState(globRoot, fileSpec, isLegalFileSpec, fixedDirectoryPart, wildcardDirectoryPart, filenamePart, matchFileExpression, needsRecursion, regex));
            },
                                                 true);

            return(new MSBuildGlob(lazyState));
        }
Exemplo n.º 2
0
        /// <summary>
        ///     Parse the given <paramref name="fileSpec" /> into a <see cref="MSBuildGlob" /> using a given
        ///     <paramref name="globRoot" />.
        /// </summary>
        /// <param name="globRoot">
        ///     The root of the glob.
        ///     The fixed directory part of the glob and the match arguments (<see cref="IsMatch" /> and <see cref="MatchInfo" />)
        ///     will get normalized against this root.
        ///     If empty, the current working directory is used.
        ///     Cannot be null, and cannot contain invalid path arguments.
        /// </param>
        /// <param name="fileSpec">The string to parse</param>
        /// <returns></returns>
        public static MSBuildGlob Parse(string globRoot, string fileSpec)
        {
            ErrorUtilities.VerifyThrowArgumentNull(globRoot, nameof(globRoot));
            ErrorUtilities.VerifyThrowArgumentNull(fileSpec, nameof(fileSpec));
            ErrorUtilities.VerifyThrowArgumentInvalidPath(globRoot, nameof(globRoot));

            if (globRoot == string.Empty)
            {
                globRoot = Directory.GetCurrentDirectory();
            }

            globRoot = FileUtilities.NormalizePath(globRoot).WithTrailingSlash();

            string fixedDirectoryPart    = null;
            string wildcardDirectoryPart = null;
            string filenamePart          = null;

            string matchFileExpression;
            bool   needsRecursion;
            bool   isLegalFileSpec;

            FileMatcher.GetFileSpecInfo(
                fileSpec,
                out fixedDirectoryPart,
                out wildcardDirectoryPart,
                out filenamePart,
                out matchFileExpression,
                out needsRecursion,
                out isLegalFileSpec,
                FileMatcher.s_defaultGetFileSystemEntries,
                (fixedDirPart, wildcardDirPart, filePart) =>
            {
                var normalizedFixedPart = NormalizeTheFixedDirectoryPartAgainstTheGlobRoot(fixedDirPart, globRoot);

                return(Tuple.Create(normalizedFixedPart, wildcardDirPart, filePart));
            });

            return(new MSBuildGlob(
                       globRoot,
                       fileSpec,
                       fixedDirectoryPart,
                       wildcardDirectoryPart,
                       filenamePart,
                       matchFileExpression,
                       needsRecursion,
                       isLegalFileSpec));
        }
Exemplo n.º 3
0
        // this method parses the glob and extracts the fixed directory part in order to normalize it and make it absolute
        // without this normalization step, strings pointing outside the globbing cone would still match when they shouldn't
        // for example, we dont want "**/*.cs" to match "../Shared/Foo.cs"
        private static Regex CreateRegex(string unescapedFileSpec, string currentDirectory)
        {
            Regex  regex                 = null;
            string fixedDirPart          = null;
            string wildcardDirectoryPart = null;
            string filenamePart          = null;

            FileMatcher.SplitFileSpec(
                unescapedFileSpec,
                out fixedDirPart,
                out wildcardDirectoryPart,
                out filenamePart,
                FileMatcher.s_defaultGetFileSystemEntries);

            if (FileUtilities.PathIsInvalid(fixedDirPart))
            {
                return(null);
            }

            var absoluteFixedDirPart   = Path.Combine(currentDirectory, fixedDirPart);
            var normalizedFixedDirPart = string.IsNullOrEmpty(absoluteFixedDirPart)
                                         // currentDirectory is empty for some in-memory projects
                ? Directory.GetCurrentDirectory()
                : FileUtilities.GetFullPathNoThrow(absoluteFixedDirPart);

            normalizedFixedDirPart = FileUtilities.EnsureTrailingSlash(normalizedFixedDirPart);

            var recombinedFileSpec = string.Join("", normalizedFixedDirPart, wildcardDirectoryPart, filenamePart);

            bool isRecursive;
            bool isLegal;

            FileMatcher.GetFileSpecInfo(
                recombinedFileSpec,
                out regex,
                out isRecursive,
                out isLegal,
                FileMatcher.s_defaultGetFileSystemEntries);

            return(isLegal ? regex : null);
        }
Exemplo n.º 4
0
        internal static Func <string, bool> GetMatchTester(string filespec, string currentDirectory)
        {
            var   unescapedSpec = EscapingUtilities.UnescapeAll(filespec);
            Regex regex         = null;

            // TODO: assumption on file system case sensitivity: https://github.com/Microsoft/msbuild/issues/781

            if (FilespecHasWildcards(filespec))
            {
                Regex regexFileMatch;
                bool  isRecursive;
                bool  isLegal;

                //  TODO: If creating Regex's here ends up being expensive perf-wise, consider how to avoid it in common cases
                FileMatcher.GetFileSpecInfo(
                    unescapedSpec,
                    out regexFileMatch,
                    out isRecursive,
                    out isLegal,
                    FileMatcher.s_defaultGetFileSystemEntries);

                // If the spec is not legal, it doesn't match anything
                regex = isLegal ? regexFileMatch : null;
            }

            return(file =>
            {
                var unescapedFile = EscapingUtilities.UnescapeAll(file);

                // check if there is a regex matching the file
                if (regex != null)
                {
                    return regex.IsMatch(unescapedFile);
                }

                return FileUtilities.ComparePathsNoThrow(unescapedSpec, unescapedFile, currentDirectory);
            });
        }
Exemplo n.º 5
0
        //  Returns a Func that will return true IFF its argument matches any of the specified filespecs
        internal static Func <string, bool> GetMatchTester(IList <string> filespecs)
        {
            List <Regex>     regexes      = null;
            HashSet <string> exactmatches = null;

            foreach (var spec in filespecs)
            {
                if (FilespecHasWildcards(spec))
                {
                    Regex regexFileMatch;
                    bool  isRecursive;
                    bool  isLegal;
                    //  TODO: If creating Regex's here ends up being expensive perf-wise, consider how to avoid it in common cases
                    FileMatcher.GetFileSpecInfo
                    (
                        spec,
                        out regexFileMatch,
                        out isRecursive,
                        out isLegal,
                        FileMatcher.s_defaultGetFileSystemEntries
                    );

                    if (isLegal)
                    {
                        if (regexes == null)
                        {
                            regexes = new List <Regex>();
                        }
                        regexes.Add(regexFileMatch);
                    }
                    else
                    {
                        //  If the spec is not legal, it doesn't match anything
                    }
                }
                else
                {
                    if (exactmatches == null)
                    {
                        //  TODO: How to handle case sensitivity here?  Existing behavior is to be case-insensitive,
                        //  which works for Windows but probably isn't the right thing on other OS's
                        exactmatches = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                    }
                    exactmatches.Add(spec);
                }
            }

            return(file =>
            {
                if (exactmatches != null)
                {
                    if (exactmatches.Contains(file))
                    {
                        return true;
                    }
                }

                if (regexes != null)
                {
                    foreach (Regex regex in regexes)
                    {
                        if (regex.IsMatch(file))
                        {
                            return true;
                        }
                    }
                }

                return false;
            });
        }