public void Build( XDocument doc, ITextSource textSource, MSBuildParserContext context) { var project = doc.Nodes.OfType <XElement> ().FirstOrDefault(x => x.Name == xnProject); if (project == null) { //TODO: error return; } var sdks = ResolveSdks(context, project).ToList(); var pel = MSBuildLanguageElement.Get("Project"); GetPropertiesToTrack(context.PropertyCollector, project); var importResolver = context.CreateImportResolver(Filename); AddSdkProps(sdks, context.PropertyCollector, importResolver); var resolver = new MSBuildSchemaBuilder(IsToplevel, context, importResolver); resolver.Run(doc, textSource, this); AddSdkTargets(sdks, context.PropertyCollector, importResolver); }
public MSBuildSchemaBuilder( bool isToplevel, MSBuildParserContext parseContext, MSBuildImportResolver resolveImport) { this.isToplevel = isToplevel; this.parseContext = parseContext; this.importResolver = resolveImport; }
void LoadTasks(MSBuildParserContext context, string label, string filename) { try { var import = context.GetCachedOrParse(label, filename, null, File.GetLastWriteTimeUtc(filename)); AddImport(import); } catch (Exception ex) { LoggingService.LogError($"Error loading tasks file {filename}", ex); } }
void ResolveImport(MSBuildImportElement element, MSBuildParserContext parseContext, MSBuildImportResolver importResolver) { var importAtt = element.ProjectAttribute; var sdkAtt = element.SdkAttribute; ExpressionNode import = null; string importTxt = null; if (importAtt?.Value != null) { import = importAtt.Value; importTxt = importAtt.XAttribute.Value; } if (sdkAtt?.Value is ExpressionText sdkTxt) { var loc = sdkAtt.XAttribute.ValueSpan; string sdkPath = parseContext.GetSdkPath(this, sdkTxt.Value, loc); import = import == null ? null : new ExpressionText(0, Path.Combine(sdkPath, importTxt), true); if (IsToplevel && sdkPath != null) { Annotations.Add(sdkAtt.XAttribute, new NavigationAnnotation(sdkPath, loc)); } } if (import != null) { bool wasResolved = false; var loc = importAtt.XAttribute.ValueSpan; foreach (var resolvedImport in importResolver.Resolve(import, importTxt, null)) { this.AddImport(resolvedImport); wasResolved |= resolvedImport.IsResolved; if (IsToplevel && wasResolved) { Annotations.Add(importAtt.XAttribute, new NavigationAnnotation(resolvedImport.Filename, loc)); } } if (!wasResolved && IsToplevel) { DiagnosticSeverity type = element.ConditionAttribute == null ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; Diagnostics.Add(CoreDiagnostics.UnresolvedImport, loc, importTxt); } } }
void ResolveImports(MSBuildProjectElement project, MSBuildParserContext context) { var sdks = ResolveSdks(project, context).ToList(); //tag the properties we need to track for the imports GetPropertiesToTrack(context.PropertyCollector, project); var importResolver = context.CreateImportResolver(Filename); AddSdkProps(sdks, context.PropertyCollector, importResolver); void ExtractProperties(MSBuildPropertyGroupElement pg) { foreach (var prop in pg.Elements) { context.PropertyCollector.Collect(prop.ElementName, prop.Value); } } foreach (var el in project.Elements) { switch (el) { case MSBuildPropertyGroupElement pg: ExtractProperties(pg); break; case MSBuildChooseElement choose: foreach (var c in choose.Elements) { foreach (var pg in c.GetElements <MSBuildPropertyGroupElement> ()) { ExtractProperties(pg); } } break; case MSBuildImportElement imp: ResolveImport(imp, context, importResolver); break; } } AddSdkTargets(sdks, context.PropertyCollector, importResolver); }
IEnumerable <(string id, string path, TextSpan span)> ResolveSdks(MSBuildParserContext context, XElement project) { var sdksAtt = project.Attributes.Get("Sdk", true); if (sdksAtt == null) { yield break; } string sdks = sdksAtt?.Value; if (string.IsNullOrEmpty(sdks)) { yield break; } int offset = IsToplevel ? sdksAtt.ValueOffset : sdksAtt.Span.Start; foreach (var sdk in SplitSdkValue(offset, sdksAtt.Value)) { if (sdk.id == null) { if (IsToplevel) { Diagnostics.Add(CoreDiagnostics.EmptySdkAttribute, sdk.span); } } else { var sdkPath = context.GetSdkPath(this, sdk.id, sdk.span); if (sdkPath != null) { yield return(sdk.id, sdkPath, sdk.span); } if (IsToplevel) { Annotations.Add(sdksAtt, new NavigationAnnotation(sdkPath, sdk.span) { IsSdk = true }); } } } }
IEnumerable <(string id, string path, TextSpan span)> ResolveSdks(MSBuildParserContext context, XElement project) { var sdksAtt = project.Attributes.Get(new XName("Sdk"), true); if (sdksAtt == null) { yield break; } string sdks = sdksAtt?.Value; if (string.IsNullOrEmpty(sdks)) { yield break; } int offset = IsToplevel ? sdksAtt.ValueOffset : sdksAtt.Span.Start; foreach (var sdk in SplitSdkValue(offset, sdksAtt.Value)) { if (sdk.id == null) { if (IsToplevel) { Errors.Add(new XmlDiagnosticInfo(DiagnosticSeverity.Warning, "Empty value", sdk.span)); } } else { var sdkPath = context.GetSdkPath(this, sdk.id, sdk.span); if (sdkPath != null) { yield return(sdk.id, sdkPath, sdk.span); } if (IsToplevel) { Annotations.Add(sdksAtt, new NavigationAnnotation(sdkPath, sdk.span)); } } } }
public void Build(XDocument doc, MSBuildParserContext context) { var project = doc.Nodes.OfType <XElement> ().FirstOrDefault(x => x.NameEquals("Project", true)); if (project == null) { //TODO: error return; } var projectElement = new MSBuildProjectElement(project); if (IsToplevel) { ProjectElement = projectElement; } ResolveImports(projectElement, context); InferredSchema = MSBuildInferredSchema.Build(projectElement, Filename, IsToplevel, context); }
public static MSBuildRootDocument Parse( ITextSource textSource, string filePath, MSBuildRootDocument previous, MSBuildSchemaProvider schemaProvider, IRuntimeInformation runtimeInfo, ITaskMetadataBuilder taskBuilder, CancellationToken token) { var xmlParser = new XmlTreeParser(new XmlRootState()); var(xdocument, _) = xmlParser.Parse(textSource.CreateReader()); var propVals = new PropertyValueCollector(true); var doc = new MSBuildRootDocument(filePath) { XDocument = xdocument, Text = textSource, RuntimeInformation = runtimeInfo }; var importedFiles = new HashSet <string> (StringComparer.OrdinalIgnoreCase); if (filePath != null) { try { doc.Schema = previous?.Schema ?? schemaProvider.GetSchema(filePath, null); } catch (Exception ex) { LoggingService.LogError("Error loading schema", ex); } importedFiles.Add(filePath); } var parseContext = new MSBuildParserContext( runtimeInfo, doc, previous, importedFiles, filePath, propVals, taskBuilder, schemaProvider, token); if (filePath != null) { doc.FileEvaluationContext = new MSBuildFileEvaluationContext(parseContext.RuntimeEvaluationContext, filePath, filePath); } else { doc.FileEvaluationContext = parseContext.RuntimeEvaluationContext; } string MakeRelativeMSBuildPathAbsolute(string path) { var dir = Path.GetDirectoryName(doc.Filename); path = path.Replace('\\', Path.DirectorySeparatorChar); return(Path.GetFullPath(Path.Combine(dir, path))); } Import TryImportFile(string label, string possibleFile) { try { var fi = new FileInfo(possibleFile); if (fi.Exists) { var imp = parseContext.GetCachedOrParse(label, possibleFile, null, fi.LastWriteTimeUtc); doc.AddImport(imp); return(imp); } } catch (Exception ex) when(parseContext.IsNotCancellation(ex)) { LoggingService.LogError($"Error importing '{possibleFile}'", ex); } return(null); } Import TryImportSibling(string ifHasThisExtension, string thenTryThisExtension) { if (filePath == null) { return(null); } var extension = Path.GetExtension(filePath); if (string.Equals(ifHasThisExtension, extension, StringComparison.OrdinalIgnoreCase)) { var siblingFilename = Path.ChangeExtension(filePath, thenTryThisExtension); return(TryImportFile("(implicit)", siblingFilename)); } return(null); } void TryImportIntellisenseImports(MSBuildSchema schema) { foreach (var intellisenseImport in schema.IntelliSenseImports) { TryImportFile("(from schema)", MakeRelativeMSBuildPathAbsolute(intellisenseImport)); } } try { //if this is a targets file, try to import the props _at the top_ var propsImport = TryImportSibling(".targets", ".props"); // this currently only happens in the root file // it's a quick hack to allow files to get some basic intellisense by // importing the files _that they themselves expect to be imported from_. // we also try to load them from the sibling props, as a paired targets/props // will likely share a schema file. var schema = doc.Schema ?? propsImport?.Document?.Schema; if (schema != null) { TryImportIntellisenseImports(doc.Schema); } doc.Build(xdocument, textSource, parseContext); //if this is a props file, try to import the targets _at the bottom_ var targetsImport = TryImportSibling(".props", ".targets"); //and if we didn't load intellisense import already, try to load them from the sibling targets if (schema == null && targetsImport?.Document?.Schema != null) { TryImportIntellisenseImports(targetsImport.Document.Schema); } } catch (Exception ex) when(parseContext.IsNotCancellation(ex)) { LoggingService.LogError($"Error building document '{filePath ?? "[unnamed]"}'", ex); } try { var binpath = parseContext.RuntimeInformation.BinPath; foreach (var t in Directory.GetFiles(binpath, "*.tasks")) { doc.LoadTasks(parseContext, "(core tasks)", t); } foreach (var t in Directory.GetFiles(binpath, "*.overridetasks")) { doc.LoadTasks(parseContext, "(core overridetasks)", t); } } catch (Exception ex) when(parseContext.IsNotCancellation(ex)) { LoggingService.LogError("Error resolving tasks", ex); } try { if (previous != null) { // try to recover some values that may have been collected from the imports, as they // will not have been re-evaluated var fx = previous.Frameworks.FirstOrDefault(); if (fx != null) { propVals.Collect("TargetFramework", fx.GetShortFolderName()); propVals.Collect("TargetFrameworkVersion", FrameworkInfoProvider.FormatDisplayVersion(fx.Version)); propVals.Collect("TargetFrameworkIdentifier", fx.Framework); } } doc.Frameworks = propVals.GetFrameworks(); } catch (Exception ex) { LoggingService.LogError("Error determining project framework", ex); doc.Frameworks = new List <NuGetFramework> (); } try { //this has to run in a second pass so that it runs after all the schemas are loaded var validator = new MSBuildDocumentValidator(); validator.Run(doc.XDocument, textSource, doc); } catch (Exception ex) when(parseContext.IsNotCancellation(ex)) { LoggingService.LogError("Error in validation", ex); } return(doc); }
public MSBuildImportResolver(MSBuildParserContext parseContext, string parentFilePath, IMSBuildEvaluationContext fileEvalContext) { this.parseContext = parseContext; this.parentFilePath = parentFilePath; this.fileEvalContext = fileEvalContext; }
public MSBuildImportResolver(MSBuildParserContext parseContext, string parentFilePath) : this(parseContext, parentFilePath, null) { }
IEnumerable <(string id, string path, TextSpan span)> ResolveSdks(MSBuildProjectElement project, MSBuildParserContext context) { var sdksAtt = project.SdkAttribute?.XAttribute; if (sdksAtt == null) { yield break; } string sdks = sdksAtt.Value; if (string.IsNullOrEmpty(sdks)) { yield break; } int offset = IsToplevel ? sdksAtt.ValueOffset : sdksAtt.Span.Start; foreach (var sdk in SplitSdkValue(offset, sdksAtt.Value)) { if (sdk.id == null) { if (IsToplevel) { Diagnostics.Add(CoreDiagnostics.EmptySdkAttribute, sdk.span); } } else { var sdkInfo = context.ResolveSdk(this, sdk.id, sdk.span); if (sdkInfo == null) { continue; } yield return(sdk.id, sdkInfo.Path, sdk.span); if (IsToplevel) { Annotations.Add(sdksAtt, new NavigationAnnotation(sdkInfo.Path, sdk.span) { IsSdk = true }); } if (sdkInfo.AdditionalPaths != null && sdkInfo.AdditionalPaths.Count > 0) { foreach (var p in sdkInfo.AdditionalPaths) { yield return(sdk.id, p, sdk.span); } if (IsToplevel) { foreach (var p in sdkInfo.AdditionalPaths) { Annotations.Add(sdksAtt, new NavigationAnnotation(p, sdk.span) { IsSdk = true }); } } } } } }
void ResolveImport(MSBuildImportElement element, MSBuildParserContext parseContext, MSBuildImportResolver importResolver) { var importAtt = element.ProjectAttribute; var sdkAtt = element.SdkAttribute; ExpressionNode[] import = null; string importTxt = null; if (importAtt?.Value != null) { import = new ExpressionNode[] { importAtt.Value }; importTxt = importAtt.XAttribute.Value; } if (sdkAtt?.Value is ExpressionText sdkTxt) { var loc = sdkAtt.XAttribute.ValueSpan; var sdkInfo = parseContext.ResolveSdk(this, sdkTxt.Value, loc); if (sdkInfo == null) { if (IsToplevel) { Diagnostics.Add(CoreDiagnostics.UnresolvedSdk, loc, sdkTxt.Value); } return; } if (import != null) { if (sdkInfo.AdditionalPaths != null && sdkInfo.AdditionalPaths.Count > 0) { import = new ExpressionNode[sdkInfo.AdditionalPaths.Count + 1]; for (int i = 0; i < sdkInfo.AdditionalPaths.Count; i++) { import[i + 1] = new ExpressionText(0, Path.Combine(sdkInfo.AdditionalPaths[i], importTxt), true); } } import[0] = new ExpressionText(0, Path.Combine(sdkInfo.Path, importTxt), true); } if (IsToplevel) { Annotations.Add(sdkAtt.XAttribute, new NavigationAnnotation(sdkInfo.Path, loc)); } } if (import != null) { bool wasResolved = false; var loc = importAtt.XAttribute.ValueSpan; foreach (var resolvedImport in import.SelectMany(imp => importResolver.Resolve(imp, importTxt, null))) { AddImport(resolvedImport); wasResolved |= resolvedImport.IsResolved; if (IsToplevel && wasResolved) { Annotations.Add(importAtt.XAttribute, new NavigationAnnotation(resolvedImport.Filename, loc)); } } if (!wasResolved && IsToplevel) { DiagnosticSeverity type = element.ConditionAttribute == null ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; Diagnostics.Add(CoreDiagnostics.UnresolvedImport, loc, importTxt); } } }