public static IEnumerable <string> EvaluateExpressionAsPaths(ExpressionNode expression, MSBuildRootDocument doc, int skipEndChars = 0)
        {
            if (expression == null)
            {
                yield return(Path.GetDirectoryName(doc.Filename));

                yield break;
            }

            if (expression is ExpressionText lit)
            {
                var path = TrimEndChars(lit.GetUnescapedValue());
                //FIXME handle encoding
                yield return(Projects.MSBuild.MSBuildProjectService.FromMSBuildPath(Path.GetDirectoryName(doc.Filename), path));

                yield break;
            }

            if (!(expression is Expression expr))
            {
                yield break;
            }

            //FIXME evaluate directly without the MSBuildEvaluationContext
            var sb = new StringBuilder();

            for (int i = 0; i < expr.Nodes.Count; i++)
            {
                var node = expr.Nodes [i];
                if (node is ExpressionText l)
                {
                    var val = l.GetUnescapedValue();
                    if (i == expr.Nodes.Count - 1)
                    {
                        val = TrimEndChars(val);
                    }
                    sb.Append(val);
                }
                else if (node is ExpressionProperty p)
                {
                    sb.Append($"$({p.Name})");
                }
                else
                {
                    yield break;
                }
            }

            var evalCtx = MSBuildEvaluationContext.Create(doc.RuntimeInformation, doc.Filename, doc.Filename);

            foreach (var variant in evalCtx.EvaluatePathWithPermutation(sb.ToString(), Path.GetDirectoryName(doc.Filename), null))
            {
                yield return(variant);
            }

            string TrimEndChars(string s) => s.Substring(0, Math.Min(s.Length, s.Length - skipEndChars));
        }
コード例 #2
0
        IEnumerable <Import> ResolveImport(HashSet <string> importedFiles, MSBuildRootDocument oldDoc, string projectPath, string thisFilePath, string importExpr, string sdk, PropertyValueCollector propVals, TaskMetadataBuilder taskBuilder, MSBuildSchemaProvider schemaProvider, CancellationToken token)
        {
            //FIXME: add support for MSBuildUserExtensionsPath, the context does not currently support it
            if (importExpr.IndexOf("$(MSBuildUserExtensionsPath)", StringComparison.OrdinalIgnoreCase) > -1)
            {
                yield break;
            }

            //TODO: re-use these contexts instead of recreating them
            var importEvalCtx = MSBuildEvaluationContext.Create(RuntimeInformation, projectPath, thisFilePath);

            bool foundAny   = false;
            bool isWildcard = false;

            //the ToList is necessary because nested parses can alter the list between this yielding values
            foreach (var filename in importEvalCtx.EvaluatePathWithPermutation(importExpr, Path.GetDirectoryName(thisFilePath), propVals).ToList())
            {
                if (string.IsNullOrEmpty(filename))
                {
                    continue;
                }

                //dedup
                if (!importedFiles.Add(filename))
                {
                    foundAny = true;
                    continue;
                }

                //wildcards
                var wildcardIdx = filename.IndexOf('*');

                //arbitrary limit to skip improbably short values from bad evaluation
                const int MIN_WILDCARD_STAR_IDX    = 15;
                const int MIN_WILDCARD_PATTERN_IDX = 10;
                if (wildcardIdx > MIN_WILDCARD_STAR_IDX)
                {
                    isWildcard |= true;
                    var lastSlash = filename.LastIndexOf(Path.DirectorySeparatorChar);
                    if (lastSlash < MIN_WILDCARD_PATTERN_IDX)
                    {
                        continue;
                    }
                    if (lastSlash > wildcardIdx)
                    {
                        continue;
                    }

                    string [] files;
                    try {
                        var dir = filename.Substring(0, lastSlash);
                        if (!Directory.Exists(dir))
                        {
                            continue;
                        }

                        //finding the folder's enough for this to "count" as resolved even if there aren't any files in it
                        foundAny = true;

                        var pattern = filename.Substring(lastSlash + 1);

                        files = Directory.GetFiles(dir, pattern);
                    } catch (Exception ex) {
                        LoggingService.LogError($"Error evaluating wildcard in import candidate '{filename}'", ex);
                        continue;
                    }

                    foreach (var f in files)
                    {
                        Import wildImport;
                        try {
                            wildImport = GetCachedOrParse(importedFiles, oldDoc, f, sdk, File.GetLastWriteTimeUtc(f), projectPath, propVals, taskBuilder, schemaProvider, token);
                        } catch (Exception ex) {
                            LoggingService.LogError($"Error reading wildcard import candidate '{files}'", ex);
                            continue;
                        }
                        yield return(wildImport);
                    }

                    continue;
                }

                Import import;
                try {
                    var fi = new FileInfo(filename);
                    if (!fi.Exists)
                    {
                        continue;
                    }
                    import = GetCachedOrParse(importedFiles, oldDoc, filename, sdk, fi.LastWriteTimeUtc, projectPath, propVals, taskBuilder, schemaProvider, token);
                } catch (Exception ex) {
                    LoggingService.LogError($"Error reading import candidate '{filename}'", ex);
                    continue;
                }

                foundAny = true;
                yield return(import);

                continue;
            }

            // we skip logging for wildcards as these are generally extensibility points that are often unused
            // this is here (rather than being folded into the next condition) for ease of breakpointing
            if (isWildcard)
            {
                foundAny = true;
            }

            if (!foundAny)
            {
                if (oldDoc == null && failedImports.Add(importExpr))
                {
                    LoggingService.LogDebug($"Could not resolve MSBuild import '{importExpr}'");
                }
                yield return(new Import(importExpr, sdk, DateTime.MinValue));
            }
        }