Exemplo n.º 1
0
        static string Evaluate(this IMSBuildEvaluationContext context, ExpressionNode expression, int depth)
        {
            if (depth == maxEvaluationDepth)
            {
                throw new Exception("Property evaluation exceeded maximum depth");
            }

            switch (expression)
            {
            case ExpressionText text:
                return(text.Value);

            case ExpressionProperty prop: {
                if (!prop.IsSimpleProperty)
                {
                    LoggingService.LogWarning("Only simple properties are supported in imports");
                    return(null);
                }
                if (context.TryGetProperty(prop.Name, out var value))
                {
                    return(Evaluate(context, value.Value, depth + 1));
                }
                return(null);
            }

            case ConcatExpression expr: {
                var sb = new StringBuilder();
                foreach (var n in expr.Nodes)
                {
                    switch (n)
                    {
                    case ExpressionText t:
                        sb.Append(t.Value);
                        continue;

                    case ExpressionProperty p:
                        if (!p.IsSimpleProperty)
                        {
                            LoggingService.LogWarning("Only simple properties are supported in imports");
                            return(null);
                        }
                        if (context.TryGetProperty(p.Name, out var value))
                        {
                            sb.Append(Evaluate(context, value.Value, depth + 1));
                        }
                        continue;

                    default:
                        LoggingService.LogWarning("Only simple properties are supported in imports");
                        return(null);
                    }
                }
                return(sb.ToString());
            }

            default:
                LoggingService.LogWarning("Only simple properties and expressions are supported in imports");
                return(null);
            }
        }
Exemplo n.º 2
0
        public IEnumerable <Import> Resolve(string importExpr, string sdk)
        {
            fileEvalContext = fileEvalContext
                              ?? new MSBuildFileEvaluationContext(
                parseContext.RuntimeEvaluationContext,
                parseContext.ProjectPath, parentFilePath);

            return(parseContext.ResolveImport(
                       fileEvalContext,
                       parentFilePath,
                       importExpr, sdk));
        }
Exemplo n.º 3
0
 // FIXME: need to make this more efficient.
 // can we ignore results where a property was simply not found?
 // can we tokenize it and check each level of the path exists before drilling down?
 // can we cache the filesystem lookups?
 public static IEnumerable <string> EvaluatePathWithPermutation(
     this IMSBuildEvaluationContext context,
     ExpressionNode pathExpression,
     string baseDirectory)
 {
     foreach (var p in EvaluateWithPermutation(context, null, pathExpression, 0))
     {
         if (p == null)
         {
             continue;
         }
         yield return(MSBuildEscaping.FromMSBuildPath(p, baseDirectory));
     }
 }
Exemplo n.º 4
0
 internal MSBuildPropertyValue Collapse(IMSBuildEvaluationContext context)
 {
     if (multiple != null)
     {
         var oldMultiple = multiple;
         multiple = new ExpressionNode[oldMultiple.Count];
         for (int i = 0; i < multiple.Count; i++)
         {
             multiple[i] = new ExpressionText(0, context.Evaluate(oldMultiple[i]), true);
         }
         Value = multiple[0];
     }
     else
     {
         Value = new ExpressionText(0, context.Evaluate(Value), true);
     }
     isCollapsed = true;
     return(this);
 }
 internal MSBuildPropertyValue Collapse(IMSBuildEvaluationContext context)
 {
     if (multiple != null)
     {
         var oldMultiple = multiple;
         multiple = new string[oldMultiple.Count];
         for (int i = 0; i < multiple.Count; i++)
         {
             multiple[i] = Collapse(context, oldMultiple[i]);
         }
         Value = multiple[0];
     }
     else
     {
         Value = Collapse(context, Value);
     }
     isCollapsed = true;
     return(this);
 }
        public MSBuildFileEvaluationContext(
            IMSBuildEvaluationContext runtimeContext,
            string projectPath,
            string thisFilePath)
        {
            this.runtimeContext = runtimeContext ?? throw new ArgumentNullException(nameof(runtimeContext));

            // this file path properties
            if (thisFilePath != null)
            {
                values["MSBuildThisFile"]          = MSBuildEscaping.EscapeString(Path.GetFileName(thisFilePath));
                values["MSBuildThisFileDirectory"] = MSBuildEscaping.ToMSBuildPath(Path.GetDirectoryName(thisFilePath)) + "\\";
                //"MSBuildThisFileDirectoryNoRoot" is this actually used for anything?
                values["MSBuildThisFileExtension"] = MSBuildEscaping.EscapeString(Path.GetExtension(thisFilePath));
                values["MSBuildThisFileFullPath"]  = MSBuildEscaping.ToMSBuildPath(Path.GetFullPath(thisFilePath));
                values["MSBuildThisFileName"]      = MSBuildEscaping.EscapeString(Path.GetFileNameWithoutExtension(thisFilePath));
            }

            if (projectPath == null)
            {
                return;
            }

            // project path properties
            string escapedProjectDir = MSBuildEscaping.ToMSBuildPath(Path.GetDirectoryName(projectPath));

            values["MSBuildProjectDirectory"] = escapedProjectDir;
            // "MSBuildProjectDirectoryNoRoot" is this actually used for anything?
            values["MSBuildProjectExtension"] = MSBuildEscaping.EscapeString(Path.GetExtension(projectPath));
            values["MSBuildProjectFile"]      = MSBuildEscaping.EscapeString(Path.GetFileName(projectPath));
            values["MSBuildProjectFullPath"]  = MSBuildEscaping.ToMSBuildPath(Path.GetFullPath(projectPath));
            values["MSBuildProjectName"]      = MSBuildEscaping.EscapeString(Path.GetFileNameWithoutExtension(projectPath));

            //don't have a better value, this is as good as anything
            values["MSBuildStartupDirectory"] = escapedProjectDir;

            //HACK: we don't get a usable value for this without real evaluation so hardcode 'obj'
            var projectExtensionsPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(projectPath), "obj"));

            values["MSBuildProjectExtensionsPath"] = MSBuildEscaping.ToMSBuildPath(projectExtensionsPath) + "\\";
        }
        internal IEnumerable <Import> ResolveImport(
            IMSBuildEvaluationContext fileContext,
            string thisFilePath,
            ExpressionNode importExpr,
            string importExprString,
            string sdk)
        {
            //FIXME: add support for MSBuildUserExtensionsPath, the context does not currently support it
            if (importExprString.IndexOf("$(MSBuildUserExtensionsPath)", StringComparison.OrdinalIgnoreCase) > -1)
            {
                yield break;
            }

            //TODO: can we we re-use this context? the propvals may change between evaluations
            var context = new MSBuildCollectedValuesEvaluationContext(fileContext, PropertyCollector);

            bool foundAny   = false;
            bool isWildcard = false;

            foreach (var filename in context.EvaluatePathWithPermutation(importExpr, Path.GetDirectoryName(thisFilePath)))
            {
                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) when(IsNotCancellation(ex))
                    {
                        LoggingService.LogError($"Error evaluating wildcard in import candidate '{filename}'", ex);
                        continue;
                    }

                    foreach (var f in files)
                    {
                        Import wildImport;
                        try {
                            wildImport = GetCachedOrParse(importExprString, f, sdk, File.GetLastWriteTimeUtc(f));
                        } catch (Exception ex) when(IsNotCancellation(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(importExprString, filename, sdk, fi.LastWriteTimeUtc);
                } catch (Exception ex) when(IsNotCancellation(ex))
                {
                    LoggingService.LogError($"Error reading import candidate '{filename}'", ex);
                    continue;
                }

                foundAny = true;
                yield return(import);

                continue;
            }

            //yield a placeholder for tooltips, imports pad etc to query
            if (!foundAny)
            {
                yield return(new Import(importExprString, sdk, null, DateTime.MinValue));
            }

            // 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 (!foundAny && !isWildcard)
            {
                if (PreviousRootDocument == null && failedImports.Add(importExprString))
                {
                    LoggingService.LogDebug($"Could not resolve MSBuild import '{importExprString}'");
                }
            }
        }
 public TaskInfo CreateTaskInfo(
     string typeName, string assemblyName, ExpressionNode assemblyFile, string assemblyFileStr,
     string declaredInFile, int declaredAtOffset, IMSBuildEvaluationContext evaluationContext)
 {
     return(null);
 }
        public TaskInfo CreateTaskInfo(
            string typeName, string assemblyName, string assemblyFile,
            string declaredInFile, int declaredAtOffset,
            IMSBuildEvaluationContext evaluationContext)
        {
            //ignore this, it's redundant
            if (assemblyName != null && assemblyName.StartsWith("Microsoft.Build.Tasks.v", StringComparison.Ordinal))
            {
                return(null);
            }

            var             tasks    = GetTaskAssembly(assemblyName, assemblyFile, declaredInFile, evaluationContext);
            IAssemblySymbol assembly = tasks?.assembly;

            if (assembly == null)
            {
                //TODO log this?
                return(null);
            }

            string asmShortName;

            if (string.IsNullOrEmpty(assemblyName))
            {
                asmShortName = Path.GetFileNameWithoutExtension(tasks.Value.path);
            }
            else
            {
                asmShortName = new AssemblyName(assemblyName).Name;
            }

            INamedTypeSymbol FindType(INamespaceSymbol ns, string name)
            {
                foreach (var m in ns.GetMembers())
                {
                    switch (m)
                    {
                    case INamedTypeSymbol ts:
                        if (ts.Name == name)
                        {
                            return(ts);
                        }
                        continue;

                    case INamespaceSymbol childNs:
                        var found = FindType(childNs, name);
                        if (found != null)
                        {
                            return(found);
                        }
                        continue;
                    }
                }
                return(null);
            }

            var type = assembly.GetTypeByMetadataName(typeName) ?? FindType(assembly.GlobalNamespace, typeName);

            if (type == null)
            {
                switch (typeName)
                {
                case "Microsoft.Build.Tasks.RequiresFramework35SP1Assembly":
                case "Microsoft.Build.Tasks.ResolveNativeReference":
                    //we don't care about these, they're not present on Mac and they're just noise
                    return(null);
                }
                LoggingService.LogWarning($"Did not resolve {typeName}");
                return(null);
            }

            var ti = new TaskInfo(type.Name, RoslynHelpers.GetDescription(type), type.GetFullName(), assemblyName, assemblyFile, declaredInFile, declaredAtOffset);

            PopulateTaskInfoFromType(ti, type);
            return(ti);
        }
Exemplo n.º 10
0
 public static IEnumerable <string> EvaluateWithPermutation(this IMSBuildEvaluationContext context, string expression)
 => EvaluateWithPermutation(context, null, ExpressionParser.Parse(expression), 0);
Exemplo n.º 11
0
 public MSBuildImportResolver(MSBuildParserContext parseContext, string parentFilePath, IMSBuildEvaluationContext fileEvalContext)
 {
     this.parseContext    = parseContext;
     this.parentFilePath  = parentFilePath;
     this.fileEvalContext = fileEvalContext;
 }
 public MSBuildCollectedValuesEvaluationContext(IMSBuildEvaluationContext fileContext, PropertyValueCollector collector)
 {
     this.collector   = collector;
     this.fileContext = fileContext;
 }
Exemplo n.º 13
0
 public static string EvaluatePath(
     this IMSBuildEvaluationContext context,
     ExpressionNode expression,
     string baseDirectory)
 => MSBuildEscaping.FromMSBuildPath(context.Evaluate(expression), baseDirectory);
Exemplo n.º 14
0
 public static string EvaluatePath(
     this IMSBuildEvaluationContext context,
     string expression,
     string baseDirectory)
 => context.EvaluatePath(ExpressionParser.Parse(expression), baseDirectory);
        //FIXME: recursive
        string Collapse(IMSBuildEvaluationContext context, string expression)
        {
            var expr = ExpressionParser.Parse(expression);

            return(context.Evaluate(expr));
        }
Exemplo n.º 16
0
 public static string Evaluate(this IMSBuildEvaluationContext context, ExpressionNode expression) => Evaluate(context, expression, 0);
Exemplo n.º 17
0
 public static string Evaluate(this IMSBuildEvaluationContext context, string expression)
 => Evaluate(context, ExpressionParser.Parse(expression));
Exemplo n.º 18
0
        static IEnumerable <string> EvaluateWithPermutation(this IMSBuildEvaluationContext context, string prefix, ExpressionNode expression, int depth)
        {
            switch (expression)
            {
            // yield plain text
            case ExpressionText text:
                yield return(prefix + text.Value);

                yield break;

            // recursively yield evaluated property
            case ExpressionProperty prop: {
                if (!prop.IsSimpleProperty)
                {
                    LoggingService.LogWarning("Only simple properties are supported in imports");
                    break;
                }
                if (context.TryGetProperty(prop.Name, out var value))
                {
                    if (value.HasMultipleValues)
                    {
                        if (value.IsCollapsed)
                        {
                            foreach (var v in value.GetValues())
                            {
                                yield return(prefix + ((ExpressionText)v).Value);
                            }
                        }
                        else
                        {
                            foreach (var v in value.GetValues())
                            {
                                foreach (var evaluated in EvaluateWithPermutation(context, prefix, v, depth + 1))
                                {
                                    yield return(evaluated);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (value.IsCollapsed)
                        {
                            yield return(prefix + ((ExpressionText)value.Value).Value);
                        }
                        else
                        {
                            foreach (var evaluated in EvaluateWithPermutation(context, prefix, value.Value, depth + 1))
                            {
                                yield return(evaluated);
                            }
                        }
                    }
                    break;
                }
                else
                {
                    yield return(prefix);
                }
                yield break;
            }

            case ConcatExpression expr: {
                var nodes = expr.Nodes;

                if (nodes.Count == 0)
                {
                    yield break;
                }

                if (nodes.Count == 1)
                {
                    foreach (var evaluated in EvaluateWithPermutation(context, prefix, nodes[0], depth + 1))
                    {
                        yield return(evaluated);
                    }
                    yield break;
                }

                var zero = nodes[0];
                var skip = new ExpressionNode[nodes.Count - 1];
                for (int i = 1; i < nodes.Count; i++)
                {
                    skip[i - 1] = nodes[i];
                }

                foreach (var zeroVal in EvaluateWithPermutation(context, prefix, zero, depth + 1))
                {
                    ExpressionNode inner = skip.Length == 1 ? skip[0] : new ConcatExpression(0, 0, skip);
                    foreach (var v in EvaluateWithPermutation(context, zeroVal, inner, depth + 1))
                    {
                        yield return(v);
                    }
                }
                yield break;
            }

            default:
                LoggingService.LogWarning("Only simple properties and expressions are supported in imports");
                yield break;
            }
        }
Exemplo n.º 19
0
 public static IEnumerable <string> EvaluateWithPermutation(this IMSBuildEvaluationContext context, ExpressionNode expression)
 => EvaluateWithPermutation(context, null, expression, 0);