public void PropertyCollectorEvaluationContextTest() { var collector = new PropertyValueCollector(false); collector.Mark("Hello"); collector.Collect("Hello", new ExpressionText(0, "One", true)); collector.Collect("Hello", new ExpressionText(0, "Two", true)); var ctx = new MSBuildCollectedValuesEvaluationContext( new TestEvaluationContext(), collector ); var vals = ctx.EvaluateWithPermutation("$(Hello)").ToList(); Assert.AreEqual(2, vals.Count); Assert.AreEqual("One", vals[0]); Assert.AreEqual("Two", vals[1]); }
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}'"); } } }
void CollectTaskDefinition(XElement element) { string taskName = null, assemblyFile = null, assemblyName = null, taskFactory = null; foreach (var att in element.Attributes) { switch (att.Name.Name.ToLowerInvariant()) { case "assemblyfile": assemblyFile = att.Value; break; case "assemblyname": assemblyName = att.Value; break; case "taskfactory": taskFactory = att.Value; break; case "taskname": taskName = att.Value; break; } } if (taskName == null) { return; } int nameIdx = taskName.LastIndexOf('.'); string name = taskName.Substring(nameIdx + 1); if (string.IsNullOrEmpty(name)) { return; } if (taskFactory == null && (assemblyName != null || assemblyFile != null)) { //FIXME create this lazily and cache it var evalCtx = new MSBuildCollectedValuesEvaluationContext(new MSBuildFileEvaluationContext(parseContext.RuntimeEvaluationContext, parseContext.ProjectPath, Filename), parseContext.PropertyCollector); TaskInfo info = parseContext.TaskBuilder.CreateTaskInfo(taskName, assemblyName, assemblyFile, Filename, element.Span.Start, evalCtx); if (info != null) { Document.Tasks[info.Name] = info; return; } } //HACK: RoslynCodeTaskFactory determines the parameters automatically from the code, until we //can do this too we need to force inference bool forceInferAttributes = taskFactory != null && ( string.Equals(taskFactory, "RoslynCodeTaskFactory", StringComparison.OrdinalIgnoreCase) || ( string.Equals(taskFactory, "CodeTaskFactory", StringComparison.OrdinalIgnoreCase) && string.Equals(assemblyFile, "$(RoslynCodeTaskFactory)", StringComparison.OrdinalIgnoreCase )) && !element.Elements.Any(n => n.Name.Name == "ParameterGroup")); Document.Tasks[name] = new TaskInfo(name, null, null, null, null, Filename, element.Span.Start) { ForceInferAttributes = forceInferAttributes }; }