├── .editorconfig
├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── BinaryCompatReport.txt
├── Directory.Build.props
├── Directory.Build.rsp
├── Directory.Build.targets
├── Directory.Packages.props
├── LICENSE
├── MSBuildStructuredLog.Avalonia.sln
├── MSBuildStructuredLog.sln
├── PRIVACY.md
├── PublishNativeAOT.cmd
├── PublishNativeAOT.sh
├── README.md
├── ThirdPartyNotices
├── appveyor.yml
├── build-macos.cake
├── chocopush.cmd
├── docs
├── BinlogOrGtfo.png
├── BuildTargets.png
├── CompileTargets.png
├── ILoveMSBuild.pdn
├── MSBuildAssemblies.png
├── MSBuildBatSignal.png
├── MSBuildIssues.png
├── Screenshot1.png
├── Screenshot2.png
├── ScreenshotRoslyn.png
├── StructuredLogSchema.txt
└── StructuredLogger.png
├── global.json
├── key.snk
├── nuget.config
├── run.sh
├── src
├── BinlogTool
│ ├── BinlogTool.csproj
│ ├── BinlogToolCommandBase.cs
│ ├── CompilerInvocations.cs
│ ├── DoubleWrites.cs
│ ├── DumpRecords.cs
│ ├── ListNuGet.cs
│ ├── ListProperties.cs
│ ├── ListTools.cs
│ ├── Log.cs
│ ├── Program.cs
│ ├── Redact.cs
│ ├── SaveFiles.cs
│ ├── SaveStrings.cs
│ ├── Search.cs
│ ├── binlogtoolexe.nuspec
│ └── publish.cmd
├── ResourcesGenerator
│ ├── Program.cs
│ ├── ResourceCreator.cs
│ └── ResourcesGenerator.csproj
├── Samples
│ └── TimesAndDurations
│ │ ├── Program.cs
│ │ └── TimesAndDurations.csproj
├── StructuredLogViewer.Avalonia
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── AvaloniaExtensions.cs
│ ├── BuildParametersScreen.cs
│ ├── Controls
│ │ ├── BuildControl.xaml
│ │ ├── BuildControl.xaml.cs
│ │ ├── DocumentWell.xaml
│ │ ├── DocumentWell.xaml.cs
│ │ ├── ImportLinkHighlighter.cs
│ │ ├── NavigationHelper.cs
│ │ ├── NodeIsSelectedToColorConverter.cs
│ │ ├── NodeLowRelevanceToOpactityConverter.cs
│ │ ├── ProjectIconConverter.cs
│ │ ├── ProxyNodeIconConverter.cs
│ │ ├── SearchAndResultsControl.xaml
│ │ ├── SearchAndResultsControl.xaml.cs
│ │ ├── SourceFileTab.cs
│ │ ├── SplitterPanel.cs
│ │ ├── StringEmptinessToVisibilityConverter.cs
│ │ ├── TextViewerControl.xaml
│ │ └── TextViewerControl.xaml.cs
│ ├── Entitlements.plist
│ ├── FileTypes.cs
│ ├── Info.plist
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── Program.cs
│ ├── Resources.xaml
│ ├── StructuredLogViewer.Avalonia.csproj
│ ├── StructuredLogViewer.icns
│ ├── StructuredLogger.ico
│ └── Styles.xaml
├── StructuredLogViewer.Core
│ ├── BuildProgress.cs
│ ├── ButtonNode.cs
│ ├── ClipboardService.cs
│ ├── Command.cs
│ ├── CustomContentNode.cs
│ ├── DialogService.cs
│ ├── DotnetUtilities.cs
│ ├── ExceptionHandler.cs
│ ├── HostedBuild.cs
│ ├── PreprocessedFileManager.cs
│ ├── ProjectGraph
│ │ ├── MSAGLProjectGraphConstructor.cs
│ │ └── RuntimeGraph.cs
│ ├── ProjectImport.cs
│ ├── SettingsService.cs
│ ├── SingleGlobalInstance.cs
│ ├── SourceFiles
│ │ ├── ArchiveFileResolver.cs
│ │ ├── ISourceFileResolver.cs
│ │ ├── LocalSourceFileResolver.cs
│ │ ├── SourceFileResolver.cs
│ │ └── SourceText.cs
│ ├── StructuredLogViewer.Core.csproj
│ ├── Timeline
│ │ ├── Block.cs
│ │ ├── Lane.cs
│ │ └── Timeline.cs
│ ├── TypingConcurrentOperation.cs
│ ├── Utilities.cs
│ └── WelcomeScreen.cs
├── StructuredLogViewer
│ ├── App.config
│ ├── AvaloniaExtensions.cs
│ ├── BuildParametersScreen.cs
│ ├── Controls
│ │ ├── BuildControl.xaml
│ │ ├── BuildControl.xaml.cs
│ │ ├── Classifier.cs
│ │ ├── ContextMenuUtilities.cs
│ │ ├── DocumentOutline.cs
│ │ ├── DocumentWell.xaml
│ │ ├── DocumentWell.xaml.cs
│ │ ├── ImportLinkHighlighter.cs
│ │ ├── NavigationHelper.cs
│ │ ├── NodeHyperlinkControl.cs
│ │ ├── ProjectGraphControl.xaml
│ │ ├── ProjectGraphControl.xaml.cs
│ │ ├── RedactInputControl.xaml
│ │ ├── RedactInputControl.xaml.cs
│ │ ├── SearchAndResultsControl.xaml
│ │ ├── SearchAndResultsControl.xaml.cs
│ │ ├── SourceFileTab.cs
│ │ ├── SourceFileTabHeader.cs
│ │ ├── SplitterPanel.cs
│ │ ├── StringEmptinessToVisibilityConverter.cs
│ │ ├── TextViewerControl.xaml
│ │ ├── TextViewerControl.xaml.cs
│ │ ├── TimelineControl.xaml
│ │ ├── TimelineControl.xaml.cs
│ │ ├── TracingControl.xaml
│ │ ├── TracingControl.xaml.cs
│ │ ├── TreeViewExtensions.cs
│ │ ├── TreeViewItemExtensions.cs
│ │ └── VisualTreeUtilities.cs
│ ├── Entrypoint.cs
│ ├── FileAssociations.cs
│ ├── MSBuildLocator.cs
│ ├── MSBuildStructuredLogViewer.nuspec
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── Resources
│ │ └── SolutionFile.xshd
│ ├── ScrollViewerHelper.cs
│ ├── StructuredLogViewer.csproj
│ ├── StructuredLogger.ico
│ ├── TaskExtensions.cs
│ ├── WindowPosition.cs
│ ├── app.manifest
│ ├── icons
│ │ └── msbuild-structured-log-viewer.png
│ ├── msbuild-structured-log-viewer.nuspec
│ ├── themes
│ │ ├── Button.xaml
│ │ ├── CheckBox.xaml
│ │ ├── ComboBox.xaml
│ │ ├── ComboBoxItem.xaml
│ │ ├── ContextMenu.xaml
│ │ ├── Generic.xaml
│ │ ├── GroupBox.xaml
│ │ ├── MenuItem.xaml
│ │ ├── ScrollBar.xaml
│ │ ├── TabItem.xaml
│ │ ├── ThemeManager.cs
│ │ └── TreeViewItem.xaml
│ └── tools
│ │ ├── .skipAutoUninstall
│ │ ├── LICENSE.txt
│ │ ├── VERIFICATION.txt
│ │ ├── chocolateyinstall.ps1
│ │ └── chocolateyuninstall.ps1
├── StructuredLogger.Tests
│ ├── App.config
│ ├── AssertEx.cs
│ ├── BinaryLoggerTests.cs
│ ├── BinarySerializationTests.cs
│ ├── CompilerInvocationTests.cs
│ ├── ConditionParserTests.cs
│ ├── Differ.cs
│ ├── ItemGroupParserTests.cs
│ ├── MSBuild.cs
│ ├── ParseIntoWordsTests.cs
│ ├── ResourceStringTests.cs
│ ├── Roundtrip.cs
│ ├── StringsTests.cs
│ ├── StructuredLogger.Tests.csproj
│ ├── TestUtilities.cs
│ └── TextUtilitiesTests.cs
├── StructuredLogger.Utils
│ ├── BinlogRedactor.cs
│ ├── NuGetSearch.cs
│ ├── SecretsSearch.cs
│ └── StructuredLogger.Utils.csproj
├── StructuredLogger
│ ├── Analyzers
│ │ ├── BuildAnalyzer.cs
│ │ ├── CompilerInvocations.cs
│ │ ├── CppAnalyzer.cs
│ │ ├── CscTaskAnalyzer.cs
│ │ ├── DoubleWritesAnalyzer.cs
│ │ ├── FileCopyMap.cs
│ │ ├── ImportTreeAnalyzer.cs
│ │ ├── MessageTaskAnalyzer.cs
│ │ ├── ProjectReferenceGraph.cs
│ │ ├── ResolveAssemblyReferenceAnalyzer.cs
│ │ ├── Stats.cs
│ │ └── TargetGraph.cs
│ ├── AssemblyLoadBuildEventArgs2.cs
│ ├── BinaryLog.cs
│ ├── BinaryLogger
│ │ ├── ArrayDictionary.cs
│ │ ├── AsyncBufferedReadStream.cs
│ │ ├── BinLogReader.cs
│ │ ├── BinaryLogRecord.cs
│ │ ├── BinaryLogRecordKind.cs
│ │ ├── BinaryLogReplayEventSource.cs
│ │ ├── BinaryLogger.cs
│ │ ├── BuildCanceledEventArgs.cs
│ │ ├── BuildCheckEventArgs.cs
│ │ ├── BuildEventArgsDispatcher.cs
│ │ ├── BuildEventArgsFieldFlags.cs
│ │ ├── BuildEventArgsFields.cs
│ │ ├── BuildEventArgsReader.Viewer.cs
│ │ ├── BuildEventArgsReader.cs
│ │ ├── BuildEventArgsWriter.cs
│ │ ├── BuildSubmissionStartedEventArgs.cs
│ │ ├── EnvironmentVariableReadEventArgs2.cs
│ │ ├── EvaluationIdProvider.cs
│ │ ├── ExtendedBuildErrorEventArgs.cs
│ │ ├── ExtendedBuildMessageEventArgs.cs
│ │ ├── ExtendedBuildWarningEventArgs.cs
│ │ ├── ExtendedCriticalBuildMessageEventArgs.cs
│ │ ├── ExtendedCustomBuildEventArgs.cs
│ │ ├── ExtendedDataFields.cs
│ │ ├── ExtendedPropertyInitialValueSetEventArgs.cs
│ │ ├── ExtendedPropertyReassignmentEventArgs.cs
│ │ ├── FileUsedEventArgs.cs
│ │ ├── IExtendedBuildEventArgs.cs
│ │ ├── IsExternalInit.cs
│ │ ├── Postprocessing
│ │ │ ├── ArchiveFileEventArgs.cs
│ │ │ ├── ArchiveFileEventArgsExtensions.cs
│ │ │ ├── BinaryLogReaderErrorEventArgs.cs
│ │ │ ├── EmbeddedContentEventArgs.cs
│ │ │ ├── IBuildEventArgsReaderNotifications.cs
│ │ │ ├── ReaderErrorType.cs
│ │ │ ├── StreamChunkOverreadException.cs
│ │ │ └── StringReadEventArgs.cs
│ │ ├── ProjectImportsCollector.cs
│ │ ├── ProjectImportsCollectorExtensions.cs
│ │ ├── ResourceUtilities.cs
│ │ ├── TaskItemData.cs
│ │ ├── TaskParameterEventArgs2.cs
│ │ ├── TaskStartedEventArgs2.cs
│ │ └── Utilities.cs
│ ├── ConditionNode.cs
│ ├── Construction
│ │ ├── BuildStatistics.cs
│ │ ├── Construction.cs
│ │ ├── ItemGroupParser.cs
│ │ └── MessageProcessor.cs
│ ├── CustomAppDomainManager.cs
│ ├── ErrorReporting.cs
│ ├── ObjectModel
│ │ ├── AbstractDiagnostic.cs
│ │ ├── AddItem.cs
│ │ ├── ArchiveFile.cs
│ │ ├── BaseNode.cs
│ │ ├── Build.cs
│ │ ├── ChildrenList.cs
│ │ ├── CriticalBuildMessage.cs
│ │ ├── EntryTarget.cs
│ │ ├── Error.cs
│ │ ├── EvaluationProfileEntry.cs
│ │ ├── Folder.cs
│ │ ├── IHasExtendedData.cs
│ │ ├── IHasLineNumber.cs
│ │ ├── IHasRelevance.cs
│ │ ├── IHasSourceFile.cs
│ │ ├── IPreprocessable.cs
│ │ ├── IProjectOrEvaluation.cs
│ │ ├── Import.cs
│ │ ├── Item.cs
│ │ ├── Message.cs
│ │ ├── Metadata.cs
│ │ ├── NameValueNode.cs
│ │ ├── NamedNode.cs
│ │ ├── NoImport.cs
│ │ ├── NodeFlags.cs
│ │ ├── Note.cs
│ │ ├── ObservableObject.cs
│ │ ├── Package.cs
│ │ ├── Parameter.cs
│ │ ├── Project.cs
│ │ ├── ProjectEvaluation.cs
│ │ ├── Property.cs
│ │ ├── RemoveItem.cs
│ │ ├── SearchableItem.cs
│ │ ├── SourceFile.cs
│ │ ├── SourceFileLine.cs
│ │ ├── Target.cs
│ │ ├── Task.cs
│ │ ├── TaskItem.cs
│ │ ├── Tasks
│ │ │ ├── CompilationWrites.cs
│ │ │ ├── CopyTask.cs
│ │ │ ├── CscTask.cs
│ │ │ ├── FileCopyOperation.cs
│ │ │ ├── FscTask.cs
│ │ │ ├── ManagedCompilerTask.cs
│ │ │ ├── ResolveAssemblyReferenceTask.cs
│ │ │ ├── RobocopyTask.cs
│ │ │ └── VbcTask.cs
│ │ ├── TextNode.cs
│ │ ├── TimedNode.cs
│ │ ├── TreeNode.cs
│ │ ├── Warning.cs
│ │ └── readme.txt
│ ├── PathUtils.cs
│ ├── PlatformUtilities.cs
│ ├── Progress.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── ReaderSettings.cs
│ ├── Reflector.cs
│ ├── Search
│ │ ├── HighlightedText.cs
│ │ ├── ISearchExtension.cs
│ │ ├── NodeQueryMatcher.cs
│ │ ├── PropertiesAndItemsSearch.cs
│ │ ├── ProxyNode.cs
│ │ ├── ResultTree.cs
│ │ ├── Search.cs
│ │ ├── SearchIndex.cs
│ │ └── SearchResult.cs
│ ├── Serialization
│ │ ├── AttributeNames.cs
│ │ ├── Binary
│ │ │ ├── BetterBinaryReader.cs
│ │ │ ├── BetterBinaryWriter.cs
│ │ │ ├── BuildLogReader.cs
│ │ │ ├── BuildLogWriter.cs
│ │ │ ├── TreeBinaryReader.cs
│ │ │ └── TreeBinaryWriter.cs
│ │ ├── Serialization.cs
│ │ ├── StringCache.cs
│ │ ├── StringWriter.cs
│ │ ├── XlinqLogReader.cs
│ │ ├── XmlLogReader.cs
│ │ └── XmlLogWriter.cs
│ ├── Span.cs
│ ├── Strings
│ │ ├── JsonParser.cs
│ │ ├── ResourcesCollection.cs
│ │ ├── Strings.cs
│ │ ├── Strings.json
│ │ └── readme.txt
│ ├── StructuredLogger.cs
│ ├── StructuredLogger.csproj
│ ├── TextUtilities.cs
│ ├── UnknownDataBehavior.cs
│ ├── Utilities.cs
│ └── key.snk
├── TaskRunner
│ ├── AppDomainInitializer.cs
│ ├── BuildEngine.cs
│ ├── Executor.cs
│ ├── MissingPropertyException.cs
│ ├── Program.cs
│ ├── TaskRunner.csproj
│ ├── app.config
│ └── app.manifest
└── Tests
│ ├── 1.proj
│ └── 2.proj
└── version.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Don't mess with my line endings.
3 | ###############################################################################
4 | * -text
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [KirillOsenkov]
4 |
--------------------------------------------------------------------------------
/BinaryCompatReport.txt:
--------------------------------------------------------------------------------
1 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Data.OData, Version=5.6.1.0, PublicKeyToken=31bf3856ad364e35'
2 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Data.Services.Client, Version=5.6.1.0, PublicKeyToken=31bf3856ad364e35'
3 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Web.XmlTransform, Version=2.1.0.0, PublicKeyToken=b03f5f7f11d50a3a'
4 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | $(MSBuildProjectName)
6 | $([System.Text.RegularExpressions.Regex]::Replace($(MSBuildProjectName), '_[^_]+_wpftmp', '', System.Text.RegularExpressions.RegexOptions.IgnoreCase))
7 | $(MSBuildThisFileDirectory)obj\$(BasePathProjectName)\
8 | $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\
9 | true
10 | <_FindDependencies>false
11 |
12 | false
13 | false
14 |
15 |
16 |
17 | false
18 | $(DefineConstants);SIGN;
19 | false
20 | $(MSBuildThisFileDirectory)\key.snk
21 | embedded
22 | latest
23 | $(NoWarn);NU1701;CS8632
24 | False
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Directory.Build.rsp:
--------------------------------------------------------------------------------
1 | /nologo
2 | /r
3 | /m
4 | /bl
5 | /verbosity:minimal
6 | /clp:Summary;ForceNoAlign
7 |
--------------------------------------------------------------------------------
/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Kirill Osenkov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PRIVACY.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy
2 |
3 | ## MSBuild binary logs
4 |
5 | Collecting MSBuild binary logs from MSBuild as described on https://msbuildlog.com may collect your source code, secrets from your source code and environment and store them in the .binlog file, exclusively for the purpose of making .binlog files useful for build analysis. Treat .binlog files like you treat your source code and environment variables. Do not share .binlog files publicly unless you are confident they contain no secrets or private information. See details at https://msbuildlog.com/#security
6 |
7 | ## MSBuild Structured Log Viewer
8 |
9 | The MSBuild Structured Log Viewer app opens .binlog files but it makes no web or cloud requests and all viewing and processing is done on the local machine. It will not send any information anywhere.
10 |
11 | The only network call the binlog viewer makes is to self-update via https://github.com/KirillOsenkov/MSBuildStructuredLog/releases.
12 |
--------------------------------------------------------------------------------
/PublishNativeAOT.cmd:
--------------------------------------------------------------------------------
1 | dotnet publish -c Release -r win-x64 -p:PublishNativeAot=True src/StructuredLogViewer.Avalonia/StructuredLogViewer.Avalonia.csproj
--------------------------------------------------------------------------------
/PublishNativeAOT.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | unameOut="$(uname -s)"
3 | case "${unameOut}" in
4 | Darwin*) rid=osx-arm64;;
5 | Linux*) rid=linux-x64;;
6 | *)
7 | echo "Unknown OS"
8 | exit 1
9 | ;;
10 | esac
11 | dotnet publish -c Release -r ${rid} -p:PublishNativeAot=True src/StructuredLogViewer.Avalonia/StructuredLogViewer.Avalonia.csproj
12 |
--------------------------------------------------------------------------------
/ThirdPartyNotices:
--------------------------------------------------------------------------------
1 | Microsoft Automatic Graph Layout,MSAGL
2 |
3 | Copyright (c) Microsoft Corporation
4 |
5 | All rights reserved.
6 |
7 | MIT License
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining
10 | a copy of this software and associated documentation files (the
11 | ""Software""), to deal in the Software without restriction, including
12 | without limitation the rights to use, copy, modify, merge, publish,
13 | distribute, sublicense, and/or sell copies of the Software, and to
14 | permit persons to whom the Software is furnished to do so, subject to
15 | the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be
18 | included in all copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND,
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/chocopush.cmd:
--------------------------------------------------------------------------------
1 | choco push --source https://push.chocolatey.org bin\StructuredLogViewer\Release\net472\msbuild-structured-log-viewer.*.nupkg
--------------------------------------------------------------------------------
/docs/BinlogOrGtfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/BinlogOrGtfo.png
--------------------------------------------------------------------------------
/docs/BuildTargets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/BuildTargets.png
--------------------------------------------------------------------------------
/docs/CompileTargets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/CompileTargets.png
--------------------------------------------------------------------------------
/docs/ILoveMSBuild.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/ILoveMSBuild.pdn
--------------------------------------------------------------------------------
/docs/MSBuildAssemblies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildAssemblies.png
--------------------------------------------------------------------------------
/docs/MSBuildBatSignal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildBatSignal.png
--------------------------------------------------------------------------------
/docs/MSBuildIssues.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildIssues.png
--------------------------------------------------------------------------------
/docs/Screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/Screenshot1.png
--------------------------------------------------------------------------------
/docs/Screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/Screenshot2.png
--------------------------------------------------------------------------------
/docs/ScreenshotRoslyn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/ScreenshotRoslyn.png
--------------------------------------------------------------------------------
/docs/StructuredLogSchema.txt:
--------------------------------------------------------------------------------
1 | Build
2 | Project
3 | Project
4 | Target
5 | Target
6 | Task
7 | Folder(Parameters)
8 | Property
9 | InputParameter
10 | Folder(Output items)
11 | Property
12 | InputParameter
13 | Folder(Output properties)
14 | Property
15 | Folder(Messages)
16 | Message
17 | Folder(Messages)
18 | Property
19 | InputParameter
20 | Item
21 | Property
22 | Message
23 | Property
24 | Folder(Properties)
25 | Folder(Global)
26 | Property
27 | Property
28 | Folder(Messages)
29 | Folder(Properties)
30 | Property
31 | Folder(Warnings)
32 | Warning
33 | Folder(Errors)
34 | Error
35 |
--------------------------------------------------------------------------------
/docs/StructuredLogger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/StructuredLogger.png
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "allowPrerelease": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/key.snk
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
3 | cd "${DIR}/src/StructuredLogViewer.Avalonia" || exit
4 | dotnet run StructuredLogViewer.Avalonia.csproj
5 |
--------------------------------------------------------------------------------
/src/BinlogTool/BinlogToolCommandBase.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace BinlogTool
5 | {
6 | public abstract class BinlogToolCommandBase
7 | {
8 | protected Build ReadBuild(string binLogFilePath, bool throwOnPathNotFound = true)
9 | {
10 | if (string.IsNullOrEmpty(binLogFilePath) || !File.Exists(binLogFilePath))
11 | {
12 | if(throwOnPathNotFound)
13 | {
14 | throw new FileNotFoundException("Specified binlog was not found.", binLogFilePath);
15 | }
16 |
17 | return null;
18 | }
19 |
20 | return BinaryLog.ReadBuild(binLogFilePath);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/BinlogTool/CompilerInvocations.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace BinlogTool
5 | {
6 | public class CompilerInvocations : BinlogToolCommandBase
7 | {
8 | public void Run(string binLogFilePath, string outputFilePath)
9 | {
10 | var invocations = CompilerInvocationsReader.ReadInvocations(binLogFilePath);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/BinlogTool/DoubleWrites.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.Build.Logging.StructuredLogger;
6 |
7 | namespace BinlogTool
8 | {
9 | public class DoubleWrites : BinlogToolCommandBase
10 | {
11 | public void Run(string binLogFilePath, string outputFilePath)
12 | {
13 | var build = this.ReadBuild(binLogFilePath);
14 | BuildAnalyzer.AnalyzeBuild(build);
15 | var doubleWrites = DoubleWritesAnalyzer.GetDoubleWrites(build);
16 | var sb = new StringBuilder();
17 | foreach (var doubleWrite in doubleWrites.OrderBy(s => s.Key))
18 | {
19 | sb.AppendLine(doubleWrite.Key);
20 | foreach (var source in doubleWrite.Value.OrderBy(s => s))
21 | {
22 | sb.AppendLine($" {source}");
23 | }
24 | }
25 |
26 | if (!string.IsNullOrEmpty(outputFilePath))
27 | {
28 | File.WriteAllText(outputFilePath, sb.ToString());
29 | }
30 | else
31 | {
32 | Console.WriteLine(sb.ToString());
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/BinlogTool/ListProperties.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Microsoft.Build.Framework;
6 | using Microsoft.Build.Logging;
7 | using Microsoft.Build.Logging.StructuredLogger;
8 |
9 | namespace BinlogTool
10 | {
11 | public class ListProperties : BinlogToolCommandBase
12 | {
13 | private Dictionary> propertyValues = new(StringComparer.OrdinalIgnoreCase);
14 |
15 | public void Run(string binLogFilePath)
16 | {
17 | var records = BinaryLog.ReadRecords(binLogFilePath);
18 | foreach (var record in records)
19 | {
20 | var args = record.Args;
21 | if (args is ProjectEvaluationFinishedEventArgs evaluationFinished)
22 | {
23 | VisitProperties(record, evaluationFinished.Properties);
24 | }
25 | else if (args is ProjectStartedEventArgs projectStarted)
26 | {
27 | VisitProperties(record, projectStarted.Properties);
28 | }
29 | }
30 |
31 | Print();
32 | }
33 |
34 | private void Print()
35 | {
36 | foreach (var property in propertyValues.OrderBy(kvp => kvp.Key))
37 | {
38 | var values = property.Value;
39 | foreach (var value in values.OrderBy(s => s))
40 | {
41 | Console.WriteLine($"{property.Key}={value}");
42 | }
43 | }
44 | }
45 |
46 | private void VisitProperties(Record record, IEnumerable properties)
47 | {
48 | if (properties == null)
49 | {
50 | return;
51 | }
52 |
53 | foreach (var kvp in properties)
54 | {
55 | if (kvp is KeyValuePair keyValuePair)
56 | {
57 | VisitProperty(record, keyValuePair.Key, keyValuePair.Value);
58 | }
59 | else if (kvp is DictionaryEntry entry)
60 | {
61 | VisitProperty(record, Convert.ToString(entry.Key), Convert.ToString(entry.Value));
62 | }
63 | }
64 | }
65 |
66 | private void VisitProperty(Record record, string key, string value)
67 | {
68 | if (!propertyValues.TryGetValue(key, out var bucket))
69 | {
70 | bucket = [];
71 | propertyValues[key] = bucket;
72 | }
73 |
74 | bucket.Add(value);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/BinlogTool/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinlogTool
4 | {
5 | public class Log
6 | {
7 | private static readonly object consoleLock = new object();
8 |
9 | public static bool Quiet { get; internal set; }
10 |
11 | public static void Write(string message, ConsoleColor color = ConsoleColor.Gray)
12 | {
13 | if (Quiet)
14 | {
15 | return;
16 | }
17 |
18 | lock (consoleLock)
19 | {
20 | var oldColor = Console.ForegroundColor;
21 | Console.ForegroundColor = color;
22 | Console.Write(message);
23 | if (color != oldColor)
24 | {
25 | Console.ForegroundColor = oldColor;
26 | }
27 | }
28 | }
29 |
30 | public static void WriteLine(string message = "", ConsoleColor color = ConsoleColor.Gray)
31 | {
32 | if (Quiet)
33 | {
34 | return;
35 | }
36 |
37 | lock (consoleLock)
38 | {
39 | var oldColor = Console.ForegroundColor;
40 | Console.ForegroundColor = color;
41 | Console.WriteLine(message);
42 | if (color != oldColor)
43 | {
44 | Console.ForegroundColor = oldColor;
45 | }
46 | }
47 | }
48 |
49 | public static void WriteError(string message)
50 | {
51 | lock (consoleLock)
52 | {
53 | var oldColor = Console.ForegroundColor;
54 | Console.ForegroundColor = ConsoleColor.Red;
55 | Console.Error.WriteLine(message);
56 | if (oldColor != ConsoleColor.Red)
57 | {
58 | Console.ForegroundColor = oldColor;
59 | }
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/BinlogTool/Redact.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using StructuredLogger.Utils;
6 |
7 | namespace BinlogTool
8 | {
9 | internal static class Redact
10 | {
11 | public static void Run(List inputs, List tokens, bool inPlace, bool recurse)
12 | {
13 | if (!inputs.Any())
14 | {
15 | // Default - current directory
16 | inputs.Add(string.Empty);
17 | }
18 |
19 | var inputBinlogs = inputs.SelectMany(input => Searcher.FindBinlogs(input, recurse)).ToList();
20 |
21 | if (!inputBinlogs.Any())
22 | {
23 | Log.WriteError("No binlogs found.");
24 | return;
25 | }
26 |
27 | if (inputBinlogs.Count > 1)
28 | {
29 | Log.WriteLine(
30 | $"Found {inputBinlogs.Count} binlog files. Will redact secrets in all. (found files: {(string.Join(',', inputBinlogs))})");
31 | }
32 |
33 | BinlogRedactorOptions options = new BinlogRedactorOptions(string.Empty)
34 | {
35 | TokensToRedact = tokens.ToArray(),
36 | IdentifyReplacemenets = true,
37 | AutodetectCommonPatterns = true,
38 | AutodetectUsername = true,
39 | ProcessEmbeddedFiles = true,
40 | };
41 |
42 | var overallStopwatch = Stopwatch.StartNew();
43 |
44 | foreach (var inputBinlog in inputBinlogs)
45 | {
46 | options.InputPath = inputBinlog;
47 | options.OutputFileName = GetOutputFileName(inputBinlog);
48 |
49 | Log.WriteLine($"Redacting binlog {inputBinlog} to {options.OutputFileName} ({GetFileSizeInKB(inputBinlog)} KB)");
50 |
51 | var stopwatch = Stopwatch.StartNew();
52 |
53 | BinlogRedactor.RedactSecrets(options, progress: null);
54 |
55 | stopwatch.Stop();
56 | Log.WriteLine($"Redacting done. Duration: {stopwatch.Elapsed}");
57 | }
58 |
59 | overallStopwatch.Stop();
60 | if(inputBinlogs.Count > 1)
61 | {
62 | Log.WriteLine($"Redacting all binlogs done. Duration: {overallStopwatch.Elapsed}");
63 | }
64 |
65 | string GetOutputFileName(string inputFileName)
66 | {
67 | if (inPlace)
68 | {
69 | return inputFileName;
70 | }
71 |
72 | return Path.ChangeExtension(inputFileName, ".redacted.binlog");
73 | }
74 |
75 | long GetFileSizeInKB(string path)
76 | => new FileInfo(path).Length / 1024;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/BinlogTool/SaveStrings.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace BinlogTool
5 | {
6 | public class SaveStrings : BinlogToolCommandBase
7 | {
8 | public void Run(string binLogFilePath, string outputFilePath)
9 | {
10 | var build = this.ReadBuild(binLogFilePath);
11 | var strings = build.StringTable.Instances.OrderBy(s => s).ToArray();
12 |
13 | Serialization.WriteStringsToFile(outputFilePath, strings);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/BinlogTool/binlogtoolexe.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | binlogtoolexe
5 | 1.0.27
6 | Kirill Osenkov
7 | MIT
8 | https://licenses.nuget.org/MIT
9 | msbuild-structured-log-viewer.png
10 | readme.md
11 | https://github.com/KirillOsenkov/MSBuildStructuredLog
12 | A tool to read, process and analyze MSBuild .binlog files. Read more at https://msbuildlog.com.
13 | MSBuild Log Logger Structure Structured Binlog BinlogTool
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/BinlogTool/publish.cmd:
--------------------------------------------------------------------------------
1 | rem dotnet publish -r win-x64 --self-contained=false /p:Configuration=Release /p:PublishSingleFile=true /bl:publish.binlog
2 | msbuild /p:Configuration=Release /t:PublishBinlogToolExe /p:RuntimeIdentifier=win-x64
--------------------------------------------------------------------------------
/src/ResourcesGenerator/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace ResourcesGenerator
6 | {
7 | class Program
8 | {
9 | ///
10 | /// To regenerate Strings.json, add the strings you want to consume from MSBuild to
11 | /// and run this program
12 | ///
13 | static void Main()
14 | {
15 | var options = new Microsoft.Build.Locator.VisualStudioInstanceQueryOptions()
16 | {
17 | DiscoveryTypes = Microsoft.Build.Locator.DiscoveryType.VisualStudioSetup | Microsoft.Build.Locator.DiscoveryType.DotNetSdk
18 | };
19 | var instances = Microsoft.Build.Locator.MSBuildLocator.QueryVisualStudioInstances(options)
20 | .OrderByDescending(i => i.Version).ToArray();
21 | var instance =
22 | instances.FirstOrDefault(i => !i.MSBuildPath.Contains("Preview")) ??
23 | instances.FirstOrDefault();
24 |
25 | var candidates = new[]
26 | {
27 | @"C:\msbuild\artifacts\bin\bootstrap\net472\MSBuild\Current\Bin\amd64",
28 | instance?.MSBuildPath,
29 | Path.Combine(
30 | Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
31 | "Microsoft Visual Studio", "2022", "Enterprise", "MSBuild", "Current", "Bin"),
32 | };
33 |
34 | var msbuildPath = candidates.FirstOrDefault(c => c != null && Directory.Exists(c));
35 |
36 | if (Directory.Exists(msbuildPath))
37 | {
38 | ResourceCreator.CreateResourceFile(msbuildPath);
39 | }
40 | else
41 | {
42 | Console.Error.WriteLine("Couldn't find MSBuild at " + msbuildPath);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ResourcesGenerator/ResourcesGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net472
6 | x86
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Samples/TimesAndDurations/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using Microsoft.Build.Logging.StructuredLogger;
5 |
6 | namespace TimesAndDurations
7 | {
8 | class Program
9 | {
10 | static void Main(string[] args)
11 | {
12 | if (args.Length != 1)
13 | {
14 | return;
15 | }
16 |
17 | var logFilePath = args[0];
18 | if (!File.Exists(logFilePath))
19 | {
20 | return;
21 | }
22 |
23 | var build = Serialization.Read(logFilePath);
24 |
25 | var targets = build
26 | .FindChildrenRecursive(t => t.StartTime > DateTime.MinValue && t.EndTime < DateTime.MaxValue)
27 | .OrderByDescending(t => t.Duration)
28 | .ToArray();
29 |
30 | foreach (var target in targets.Take(10))
31 | {
32 | Console.WriteLine($"Target {target.Name}: {TextUtilities.Display(target.StartTime, displayDate: false)} {TextUtilities.Display(target.EndTime, displayDate: false)} {target.DurationText}");
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Samples/TimesAndDurations/TimesAndDurations.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace StructuredLogViewer.Avalonia
6 | {
7 | public class App : Application
8 | {
9 | public override void Initialize()
10 | {
11 | AvaloniaXamlLoader.Load(this);
12 | }
13 |
14 | public override void OnFrameworkInitializationCompleted()
15 | {
16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
17 | desktop.MainWindow = new MainWindow();
18 |
19 | base.OnFrameworkInitializationCompleted();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/AvaloniaExtensions.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using System.Collections;
3 |
4 | namespace StructuredLogViewer.Avalonia
5 | {
6 | public static class AvaloniaExtensions
7 | {
8 | public static void AddItem(this ItemsControl itemsControl, object o)
9 | {
10 | (itemsControl.Items as IList)?.Add(o);
11 | }
12 |
13 | public static void RemoveItem(this ItemsControl itemsControl, object o)
14 | {
15 | (itemsControl.Items as IList)?.Remove(o);
16 | }
17 |
18 | public static void ClearItems(this ItemsControl itemsControl)
19 | {
20 | (itemsControl.Items as IList)?.Clear();
21 | }
22 |
23 | public static void RegisterControl(this Control parent, out TControl control, string name)
24 | where TControl : Control
25 | {
26 | control = parent.FindControl(name);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/DocumentWell.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/NavigationHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace StructuredLogViewer.Avalonia.Controls
5 | {
6 | public class NavigationHelper
7 | {
8 | public Build Build { get; }
9 | public SourceFileResolver SourceFileResolver { get; }
10 |
11 | public event Action OpenFileRequested;
12 |
13 | public NavigationHelper(Build build, SourceFileResolver sourceFileResolver)
14 | {
15 | Build = build;
16 | SourceFileResolver = sourceFileResolver;
17 | }
18 |
19 | public void OpenFile(string filePath)
20 | {
21 | OpenFileRequested?.Invoke(filePath);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/NodeIsSelectedToColorConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Avalonia.Data.Converters;
4 | using Avalonia.Media;
5 |
6 | namespace StructuredLogViewer.Avalonia.Controls
7 | {
8 | public class NodeIsSelectedToColorConverter : IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value is bool isSelected)
13 | return isSelected ? Brushes.Black : parameter;
14 |
15 | return Brushes.Black;
16 | }
17 |
18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
19 | => throw new NotSupportedException();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/NodeLowRelevanceToOpactityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Avalonia.Data.Converters;
4 |
5 | namespace StructuredLogViewer.Avalonia.Controls
6 | {
7 | public class NodeLowRelevanceToOpacityConverter : IValueConverter
8 | {
9 | private static readonly object boxedLowRelevance = 0.25;
10 | private static readonly object boxedHighRelevance = 1.0;
11 |
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | if (value is bool isLowRelevance)
15 | return isLowRelevance ? boxedLowRelevance : boxedHighRelevance;
16 |
17 | return boxedHighRelevance;
18 | }
19 |
20 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
21 | => throw new NotSupportedException();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/ProjectIconConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using Avalonia;
5 | using Avalonia.Data.Converters;
6 | using Avalonia.Media;
7 |
8 | namespace StructuredLogViewer.Avalonia.Controls
9 | {
10 | public class ProjectIconConverter : IValueConverter
11 | {
12 | private readonly Dictionary icons = new Dictionary();
13 |
14 | public DrawingGroup ProjectExtensionToIcon(string projectExtension)
15 | {
16 | switch (projectExtension)
17 | {
18 | case ".sln":
19 | return GetIcon("SlnIcon");
20 |
21 | case ".csproj":
22 | return GetIcon("CSProjIcon");
23 |
24 | case ".vbproj":
25 | return GetIcon("VBProjIcon");
26 |
27 | case ".fsproj":
28 | return GetIcon("FSProjIcon");
29 |
30 | default:
31 | return GetIcon("GenericProjectIcon");
32 | }
33 | }
34 |
35 | private DrawingGroup GetIcon(string resourceName)
36 | {
37 | if (!icons.TryGetValue(resourceName, out var icon))
38 | {
39 | if (!Application.Current.Resources.TryGetResource(resourceName, null, out var resource))
40 | resource = null;
41 |
42 | icon = resource as DrawingGroup;
43 | icons[resourceName] = icon;
44 | }
45 |
46 | return icon;
47 | }
48 |
49 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
50 | => ProjectExtensionToIcon(value as string);
51 |
52 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
53 | => throw new NotSupportedException();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/SearchAndResultsControl.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
10 |
17 |
24 |
25 |
26 |
27 |
30 |
33 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/SourceFileTab.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Microsoft.Build.Logging.StructuredLogger;
4 |
5 | namespace StructuredLogViewer.Avalonia.Controls
6 | {
7 | public class SourceFileTab
8 | {
9 | public string FileName => Path.GetFileName(FilePath);
10 | public string FilePath { get; set; }
11 |
12 | public TextViewerControl Content { get; set; }
13 |
14 | public Command Close { get; }
15 | public event Action CloseRequested;
16 |
17 | public SourceFileTab()
18 | {
19 | Close = new Command(() => CloseRequested?.Invoke(this));
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/StringEmptinessToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Avalonia.Data.Converters;
4 |
5 | namespace StructuredLogViewer.Avalonia.Controls
6 | {
7 | public class StringEmptinessToVisibilityConverter : IValueConverter
8 | {
9 | public static StringEmptinessToVisibilityConverter Instance { get; } = new StringEmptinessToVisibilityConverter();
10 |
11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
12 | {
13 | var text = value as string;
14 | bool result = string.IsNullOrEmpty(text);
15 | if (parameter is string s && s == "Invert")
16 | {
17 | result = !result;
18 | }
19 |
20 | return result;
21 | }
22 |
23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
24 | {
25 | throw new NotSupportedException();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Controls/TextViewerControl.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
17 |
25 |
33 |
41 |
46 |
51 |
52 |
53 |
59 |
60 |
61 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Entitlements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.automation.apple-events
10 |
11 | com.apple.security.network.client
12 |
13 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/FileTypes.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Platform.Storage;
2 |
3 | namespace StructuredLogViewer.Avalonia;
4 |
5 | public class FileTypes
6 | {
7 | public static string BinlogDefaultExtension = ".binlog";
8 |
9 | public static FilePickerFileType Binlog { get; } =
10 | new("Binary Log")
11 | {
12 | Patterns = new[] { "*.binlog", "*.buildlog" },
13 | MimeTypes = new[] { "application/binlog", "application/buildlog" },
14 | AppleUniformTypeIdentifiers = new []{ "public.data" }
15 | };
16 |
17 | public static FilePickerFileType Xml { get; } =
18 | new("XML")
19 | {
20 | Patterns = new[] { "*.xml" },
21 | MimeTypes = new[] { "application/xml", "text/xml" },
22 | AppleUniformTypeIdentifiers = new[] { "public.xml" }
23 | };
24 |
25 | public static FilePickerFileType MsBuildProj { get; } =
26 | new("MsBuild project file")
27 | {
28 | Patterns = new[] { "*.csproj", "*.fsproj", "*.vbproj", "*.shproj", "*.wapproj", "*.vcxproj", "*.vcproj", "*.msbuildproj" },
29 | MimeTypes = new[] { "application/xml", "text/xml" },
30 | AppleUniformTypeIdentifiers = new[] { "public.xml" }
31 | };
32 |
33 | public static FilePickerFileType Sln { get; } =
34 | new("Solution File")
35 | {
36 | Patterns = new[] { "*.sln", "*.slnf" },
37 | MimeTypes = new[] { "text/plain" },
38 | AppleUniformTypeIdentifiers = new[] { "public.text" }
39 | };
40 |
41 | public static FilePickerFileType Exe { get; } =
42 | new("Executable")
43 | {
44 | Patterns = new[] { "*.exe", "*.dll" },
45 | MimeTypes = new[] { "application/octet-stream" },
46 | AppleUniformTypeIdentifiers = new []{ "public.data", "public.executable", "public.windows-executable" }
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CFBundleIconFile
7 | StructuredLogViewer.icns
8 | CFBundleName
9 | StructuredLogViewer
10 | CFBundleExecutable
11 | StructuredLogViewer.Avalonia
12 | CFBundleDisplayName
13 | MSBuild Structured Log Viewer
14 | CFBundleIdentifier
15 | com.msbuildlog.viewer
16 |
17 | CFBundleVersion
18 | 0.0.1
19 | CFBundleShortVersionString
20 | 0.0.1
21 |
22 | NSPrincipalClass
23 | NSApplication
24 | CFBundlePackageType
25 | APPL
26 | NSHighResolutionCapable
27 | True
28 | CFBundleDevelopmentRegion
29 | en
30 | CFBundleInfoDictionaryVersion
31 | 6.0
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
55 |
56 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/Program.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 |
3 | namespace StructuredLogViewer.Avalonia
4 | {
5 | class Program
6 | {
7 | public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure().UsePlatformDetect();
8 |
9 | public static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/StructuredLogViewer.Avalonia.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net8.0
6 | StructuredLogger.ico
7 | true
8 |
9 |
10 |
11 | true
12 | false
13 | Size
14 |
15 |
16 |
17 |
18 | %(Filename)
19 |
20 |
21 | Designer
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/StructuredLogViewer.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer.Avalonia/StructuredLogViewer.icns
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Avalonia/StructuredLogger.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer.Avalonia/StructuredLogger.ico
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/BuildProgress.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class BuildProgress : ObservableObject
4 | {
5 | public BuildProgress()
6 | {
7 | }
8 |
9 | public Progress Progress { get; } = new Progress();
10 |
11 | private string progressText;
12 | public string ProgressText
13 | {
14 | get => progressText;
15 | set => SetField(ref progressText, value);
16 | }
17 |
18 | private bool isIndeterminate;
19 | public bool IsIndeterminate
20 | {
21 | get => isIndeterminate;
22 | set => SetField(ref isIndeterminate, value);
23 | }
24 |
25 | private double value;
26 | public double Value
27 | {
28 | get => value;
29 | set => SetField(ref this.value, value);
30 | }
31 |
32 | private string bufferText;
33 | public string BufferText
34 | {
35 | get => bufferText;
36 | set => SetField(ref bufferText, value);
37 | }
38 |
39 | private string msbuildCommandLine;
40 | public string MSBuildCommandLine
41 | {
42 | get => msbuildCommandLine;
43 | set
44 | {
45 | if (SetField(ref msbuildCommandLine, value))
46 | {
47 | RaisePropertyChanged(nameof(ShowCommandLine));
48 | }
49 | }
50 | }
51 |
52 | public bool ShowCommandLine => !string.IsNullOrEmpty(MSBuildCommandLine);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/ButtonNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class ButtonNode : TextNode
7 | {
8 | public ICommand Command => new Command(OnClick);
9 |
10 | public Action OnClick { get; set; }
11 |
12 | private bool isEnabled = true;
13 | public bool IsEnabled
14 | {
15 | get => isEnabled;
16 | set => SetField(ref isEnabled, value);
17 | }
18 |
19 | protected override bool IsSelectable => false;
20 |
21 | public override string TypeName => nameof(ButtonNode);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/ClipboardService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace StructuredLogViewer
4 | {
5 | public static class ClipboardService
6 | {
7 | public static void SetText(string text)
8 | {
9 | Set?.Invoke(text);
10 | }
11 |
12 | public static event Action Set;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/Command.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class Command : ICommand
7 | {
8 | private readonly Action execute;
9 | private readonly Func canExecute;
10 |
11 | public Command(Action execute)
12 | : this(execute, () => true)
13 | {
14 | }
15 |
16 | public Command(Action execute, Func canExecute)
17 | {
18 | this.execute = execute;
19 | this.canExecute = canExecute;
20 | }
21 |
22 | public event EventHandler CanExecuteChanged { add { } remove { } }
23 | public bool CanExecute(object parameter) => canExecute();
24 | public void Execute(object parameter) => execute();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/CustomContentNode.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class CustomContentNode : TextNode
4 | {
5 | protected override bool IsSelectable => false;
6 |
7 | public override string TypeName => nameof(CustomContentNode);
8 |
9 | private object content;
10 | public object Content
11 | {
12 | get => content;
13 | set => SetField(ref content, value);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/DialogService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace StructuredLogViewer
4 | {
5 | public static class DialogService
6 | {
7 | public static void ShowMessageBox(string message)
8 | {
9 | ShowMessageBoxEvent?.Invoke(message);
10 | }
11 |
12 | public static event Action ShowMessageBoxEvent;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/ExceptionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using Microsoft.Build.Logging.StructuredLogger;
4 |
5 | namespace StructuredLogViewer
6 | {
7 | public class ExceptionHandler
8 | {
9 | public static void Initialize()
10 | {
11 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
12 | }
13 |
14 | private static void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
15 | {
16 | ErrorReporting.ReportException(e.Exception);
17 | }
18 |
19 | private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
20 | {
21 | ErrorReporting.ReportException(e.ExceptionObject as Exception);
22 | }
23 |
24 | public static Exception Unwrap(Exception ex)
25 | {
26 | if (ex is ReflectionTypeLoadException reflectionTypeLoadException)
27 | {
28 | if (reflectionTypeLoadException.LoaderExceptions != null && reflectionTypeLoadException.LoaderExceptions.Length > 0)
29 | {
30 | return Unwrap(reflectionTypeLoadException.LoaderExceptions[0]);
31 | }
32 | }
33 |
34 | if (ex is TargetInvocationException tie)
35 | {
36 | return Unwrap(tie.InnerException);
37 | }
38 |
39 | if (ex is AggregateException ae)
40 | {
41 | return Unwrap(ae.Flatten().InnerExceptions[0]);
42 | }
43 |
44 | if (ex?.InnerException != null)
45 | {
46 | return Unwrap(ex.InnerException);
47 | }
48 |
49 | return ex;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/ProjectImport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace StructuredLogViewer
5 | {
6 | public struct ProjectImport : IEquatable
7 | {
8 | public ProjectImport(string importedProject, int line, int column, Import import)
9 | {
10 | ProjectPath = importedProject;
11 | Line = line;
12 | Column = column;
13 | Import = import;
14 | }
15 |
16 | public string ProjectPath { get; set; }
17 |
18 | ///
19 | /// 0-based
20 | ///
21 | public int Line { get; set; }
22 | public int Column { get; set; }
23 |
24 | public Import Import { get; set; }
25 |
26 | public static bool operator ==(ProjectImport left, ProjectImport right) => left.Equals(right);
27 | public static bool operator !=(ProjectImport left, ProjectImport right) => !(left == right);
28 |
29 | public bool Equals(ProjectImport other)
30 | {
31 | return ProjectPath == other.ProjectPath
32 | && Line == other.Line
33 | && Column == other.Column;
34 | }
35 |
36 | public override bool Equals(object obj)
37 | {
38 | if (obj is ProjectImport other)
39 | {
40 | return Equals(other);
41 | }
42 |
43 | return false;
44 | }
45 |
46 | public override int GetHashCode()
47 | {
48 | return (ProjectPath, Line, Column).GetHashCode();
49 | }
50 |
51 | public override string ToString()
52 | {
53 | return $"{ProjectPath} ({Line},{Column})";
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/SingleGlobalInstance.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading;
5 |
6 | namespace StructuredLogViewer.Core
7 | {
8 | public class SingleGlobalInstance : IDisposable
9 | {
10 | public bool HasHandle = false;
11 | private Mutex mutex;
12 |
13 | public static SingleGlobalInstance Acquire(string mutexName, int millisecondsTimeout = -1)
14 | {
15 | return new SingleGlobalInstance(mutexName, millisecondsTimeout);
16 | }
17 |
18 | private SingleGlobalInstance(string mutexName, int millisecondsTimeout = -1)
19 | {
20 | try
21 | {
22 | mutex = new Mutex(false, mutexName);
23 | HasHandle = mutex.WaitOne(millisecondsTimeout);
24 | }
25 | catch (AbandonedMutexException)
26 | {
27 | HasHandle = true;
28 | }
29 | }
30 |
31 | public void Dispose()
32 | {
33 | if (mutex != null)
34 | {
35 | if (HasHandle)
36 | {
37 | mutex.ReleaseMutex();
38 | }
39 |
40 | mutex.Dispose();
41 | mutex = null;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/SourceFiles/ArchiveFileResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Build.Logging.StructuredLogger;
4 |
5 | namespace StructuredLogViewer
6 | {
7 | public class ArchiveFileResolver : ISourceFileResolver
8 | {
9 | private readonly Dictionary fileContents = new Dictionary(StringComparer.OrdinalIgnoreCase);
10 |
11 | public Dictionary Files => fileContents;
12 |
13 | public ArchiveFileResolver(IEnumerable files)
14 | {
15 | foreach (var file in files)
16 | {
17 | AddFile(file.FullPath, file.Text);
18 | }
19 | }
20 |
21 | public IEnumerable FindFileNames(string substring)
22 | {
23 | foreach (var file in Files)
24 | {
25 | if (file.Key.IndexOf(substring, StringComparison.OrdinalIgnoreCase) != -1)
26 | {
27 | yield return file.Key;
28 | }
29 | }
30 | }
31 |
32 | public SourceText GetSourceFileText(string filePath)
33 | {
34 | filePath = ArchiveFile.CalculateArchivePath(filePath);
35 | fileContents.TryGetValue(filePath, out SourceText result);
36 | return result;
37 | }
38 |
39 | private void AddFile(string fullName, string text)
40 | {
41 | fileContents[fullName] = new SourceText(text);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/SourceFiles/ISourceFileResolver.cs:
--------------------------------------------------------------------------------
1 | namespace StructuredLogViewer
2 | {
3 | public interface ISourceFileResolver
4 | {
5 | SourceText GetSourceFileText(string filePath);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/SourceFiles/LocalSourceFileResolver.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace StructuredLogViewer
4 | {
5 | public class LocalSourceFileResolver : ISourceFileResolver
6 | {
7 | public SourceText GetSourceFileText(string filePath)
8 | {
9 | try
10 | {
11 | if (File.Exists(filePath))
12 | {
13 | return new SourceText(File.ReadAllText(filePath));
14 | }
15 |
16 | return null;
17 | }
18 | catch
19 | {
20 | return null;
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/SourceFiles/SourceFileResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using Microsoft.Build.Logging.StructuredLogger;
5 |
6 | namespace StructuredLogViewer
7 | {
8 | public class SourceFileResolver : ISourceFileResolver
9 | {
10 | private readonly List resolvers = new List
11 | {
12 | new LocalSourceFileResolver()
13 | };
14 |
15 | private const string buildsourceszip = ".buildsources.zip";
16 |
17 | private readonly Dictionary fileContentsCache = new Dictionary(StringComparer.OrdinalIgnoreCase);
18 |
19 | public ArchiveFileResolver ArchiveFile { get; private set; }
20 |
21 | public SourceFileResolver(IEnumerable files)
22 | {
23 | ArchiveFile = new ArchiveFileResolver(files);
24 | resolvers.Insert(0, ArchiveFile);
25 | }
26 |
27 | public SourceFileResolver(string logFilePath)
28 | {
29 | if (!string.IsNullOrEmpty(logFilePath))
30 | {
31 | var buildSources = Path.ChangeExtension(logFilePath, buildsourceszip);
32 | if (File.Exists(buildSources))
33 | {
34 | var files = Build.ReadSourceFiles(buildSources);
35 | ArchiveFile = new ArchiveFileResolver(files);
36 | resolvers.Insert(0, ArchiveFile);
37 | }
38 | }
39 | }
40 |
41 | public bool HasFile(string filePath)
42 | {
43 | return GetSourceFileText(filePath) != null;
44 | }
45 |
46 | public SourceText GetSourceFileText(string filePath)
47 | {
48 | if (filePath == null)
49 | {
50 | return null;
51 | }
52 |
53 | lock (fileContentsCache)
54 | {
55 | if (fileContentsCache.TryGetValue(filePath, out var result))
56 | {
57 | return result;
58 | }
59 |
60 | foreach (var resolver in resolvers)
61 | {
62 | var candidate = resolver.GetSourceFileText(filePath);
63 | if (candidate != null)
64 | {
65 | fileContentsCache[filePath] = candidate;
66 | return candidate;
67 | }
68 | }
69 |
70 | fileContentsCache[filePath] = null;
71 | return null;
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/StructuredLogViewer.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer.Core/Timeline/Lane.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 |
5 | namespace StructuredLogViewer
6 | {
7 | public class Lane
8 | {
9 | public ConcurrentBag Blocks { get; set; } = new();
10 |
11 | public void Add(Block block)
12 | {
13 | if (block.StartTime == default(DateTime) || block.EndTime == default(DateTime))
14 | {
15 | return;
16 | }
17 |
18 | if (block.EndTime <= block.StartTime)
19 | {
20 | return;
21 | }
22 |
23 | Blocks.Add(block);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/AvaloniaExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace StructuredLogViewer;
4 |
5 | internal static class AvaloniaExtensions
6 | {
7 | public static void AddItem(this ItemsControl itemsControl, object o)
8 | {
9 | itemsControl.Items.Add(o);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/BuildParametersScreen.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.Windows;
4 | using System.Windows.Data;
5 | using System.Windows.Input;
6 | using StructuredLogViewer;
7 |
8 | namespace Microsoft.Build.Logging.StructuredLogger
9 | {
10 | public class BuildParametersScreen : ObservableObject
11 | {
12 | public event Action BuildRequested;
13 | public event Action CancelRequested;
14 |
15 | public BuildParametersScreen()
16 | {
17 | UpdateMSBuildLocations();
18 | }
19 |
20 | public void SaveSelectedMSBuild()
21 | {
22 | SettingsService.AddRecentMSBuildLocation(MSBuildLocation);
23 | }
24 |
25 | public void UpdateMSBuildLocations()
26 | {
27 | MSBuildLocations.Clear();
28 | foreach (var msbuild in SettingsService.GetRecentMSBuildLocations(MSBuildLocator.GetMSBuildLocations()))
29 | {
30 | MSBuildLocations.Add(msbuild);
31 | }
32 |
33 | if (MSBuildLocations.Count > 0)
34 | {
35 | CollectionViewSource.GetDefaultView(MSBuildLocations).MoveCurrentToFirst();
36 | }
37 | }
38 |
39 | public ObservableCollection MSBuildLocations { get; } = new ObservableCollection();
40 |
41 | public string MSBuildLocation => CollectionViewSource.GetDefaultView(MSBuildLocations).CurrentItem as string;
42 |
43 | private string prefixArguments;
44 | public string PrefixArguments
45 | {
46 | get => prefixArguments;
47 | set => SetField(ref prefixArguments, value);
48 | }
49 |
50 | public string MSBuildArguments { get; set; }
51 | public string PostfixArguments { get; set; }
52 |
53 | private ICommand buildCommand;
54 | public ICommand BuildCommand => buildCommand ?? (buildCommand = new Command(Build));
55 | private void Build() => BuildRequested?.Invoke();
56 |
57 | private ICommand cancelCommand;
58 | public ICommand CancelCommand => cancelCommand ?? (cancelCommand = new Command(Cancel));
59 | private void Cancel() => CancelRequested?.Invoke();
60 |
61 | private ICommand copyCommand;
62 | public ICommand CopyCommand => copyCommand ?? (copyCommand = new Command(Copy));
63 | private void Copy()
64 | {
65 | string commandLine = $@"{MSBuildLocation.QuoteIfNeeded()} {PrefixArguments} {MSBuildArguments} {PostfixArguments}";
66 | Clipboard.SetText(commandLine);
67 | }
68 |
69 | private ICommand browseForMSBuildCommand;
70 | public ICommand BrowseForMSBuildCommand => browseForMSBuildCommand ?? (browseForMSBuildCommand = new Command(BrowseForMSBuild));
71 | private void BrowseForMSBuild()
72 | {
73 | MSBuildLocator.BrowseForMSBuildExe();
74 | UpdateMSBuildLocations();
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/Classifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Windows.Controls;
4 | using System.Windows.Documents;
5 | using System.Windows.Media;
6 | using Microsoft.Language.Xml;
7 |
8 | namespace StructuredLogViewer
9 | {
10 | public class Classifier
11 | {
12 | private static readonly SolidColorBrush[] brushes = new[]
13 | {
14 | null, // None
15 | Brushes.Red, // XmlAttributeName
16 | null, // XmlAttributeQuotes
17 | Brushes.Blue, // XmlAttributeValue
18 | Brushes.Gray, // XmlCDataSection
19 | Brushes.Green, // XmlComment
20 | Brushes.Blue, // XmlDelimiter
21 | Brushes.Red, // XmlEntityReference
22 | Brushes.Firebrick, // XmlName new SolidColorBrush(Color.FromRgb(163, 21, 21))
23 | Brushes.Gray, // XmlProcessingInstruction
24 | null, // XmlText
25 | };
26 |
27 | public void Classify(RichTextBox richTextBox, string text)
28 | {
29 | var document = richTextBox.Document;
30 | Paragraph paragraph = new Paragraph();
31 | document.Blocks.Add(paragraph);
32 |
33 | if (!Utilities.LooksLikeXml(text))
34 | {
35 | paragraph.Inlines.Add(text);
36 | return;
37 | }
38 |
39 | var contentStart = document.ContentStart;
40 |
41 | var sw = Stopwatch.StartNew();
42 | var root = Parser.ParseText(text);
43 | var elapsed = sw.Elapsed;
44 | sw = Stopwatch.StartNew();
45 | ClassifierVisitor.Visit(root, 0, text.Length, (start, length, node, classification) =>
46 | {
47 | var brush = brushes[(int)classification];
48 |
49 | var inline = new Run(text.Substring(start, length));
50 | if (brush != null)
51 | {
52 | inline.Foreground = brush;
53 | }
54 |
55 | paragraph.Inlines.Add(inline);
56 | });
57 | elapsed = sw.Elapsed;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/ContextMenuUtilities.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace StructuredLogViewer.Controls
4 | {
5 | public class ContextMenuUtilities : UIElement
6 | {
7 | public static readonly DependencyProperty IsContextMenuOpenProperty = DependencyProperty.RegisterAttached(
8 | "IsContextMenuOpen",
9 | typeof(bool),
10 | typeof(UIElement),
11 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
12 |
13 | public static bool GetIsContextMenuOpen(UIElement element)
14 | => (bool)element.GetValue(IsContextMenuOpenProperty);
15 |
16 | public static void SetIsContextMenuOpen(UIElement element, bool value)
17 | => element.SetValue(IsContextMenuOpenProperty, value);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/DocumentOutline.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 | using StructuredLogViewer.Controls;
4 |
5 | namespace StructuredLogViewer
6 | {
7 | public class EditorExtension
8 | {
9 | public PreprocessedFileManager.PreprocessContext PreprocessContext { get; set; }
10 |
11 | public event Action ImportSelected;
12 |
13 | public void Install(TextViewerControl textViewerControl)
14 | {
15 | textViewerControl.EditorExtension = this;
16 | var textEditor = textViewerControl.TextEditor;
17 | var textArea = textEditor.TextArea;
18 | var caret = textArea.Caret;
19 | caret.PositionChanged += (s, e) =>
20 | {
21 | var caretOffset = textEditor.CaretOffset;
22 | var projectImport = PreprocessContext.GetImportFromPosition(caretOffset);
23 | ImportSelected?.Invoke(projectImport);
24 | };
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/DocumentWell.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/NavigationHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace StructuredLogViewer.Controls
5 | {
6 | public class NavigationHelper
7 | {
8 | public Build Build { get; }
9 | public SourceFileResolver SourceFileResolver { get; }
10 |
11 | public event Action OpenFileRequested;
12 |
13 | public NavigationHelper(Build build, SourceFileResolver sourceFileResolver)
14 | {
15 | Build = build;
16 | SourceFileResolver = sourceFileResolver;
17 | }
18 |
19 | public void OpenFile(string filePath)
20 | {
21 | OpenFileRequested?.Invoke(filePath);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/ProjectGraphControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/ProjectGraphControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using System.Windows.Input;
3 | using Microsoft.Msagl.Drawing;
4 | using Microsoft.Msagl.WpfGraphControl;
5 |
6 | namespace StructuredLogViewer.Controls
7 | {
8 | public partial class ProjectGraphControl : UserControl
9 | {
10 | public ProjectGraphControl()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | public void Dispose()
16 | {
17 | Panel.Children.Clear();
18 | BuildControl = null;
19 | }
20 |
21 | public BuildControl BuildControl { get; set; }
22 |
23 | public void SetGraph(Graph graph)
24 | {
25 | graph.IsVisible = true;
26 |
27 | GraphViewer graphViewer = new GraphViewer();
28 |
29 | graphViewer.BindToPanel(Panel);
30 |
31 | graphViewer.Graph = graph;
32 | }
33 |
34 | private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
35 | {
36 | if (sender is TextBlock textBlock && textBlock.Tag is Block block)
37 | {
38 | BuildControl.SelectItem(block.Node);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/SourceFileTab.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace StructuredLogViewer.Controls
4 | {
5 | public class SourceFileTab
6 | {
7 | public string FilePath { get; set; }
8 | public string Text { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/SourceFileTabHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Microsoft.Build.Logging.StructuredLogger;
4 |
5 | namespace StructuredLogViewer.Controls
6 | {
7 | public class SourceFileTabHeader
8 | {
9 | private readonly SourceFileTab tab;
10 |
11 | public SourceFileTabHeader(SourceFileTab sourceFileTab)
12 | {
13 | this.tab = sourceFileTab;
14 | Close = new Command(InvokeClose);
15 | Header = Path.GetFileName(tab.FilePath);
16 | }
17 |
18 | public string Header { get; private set; }
19 |
20 | public Command Close { get; }
21 | public event Action CloseRequested;
22 | public void InvokeClose() => CloseRequested?.Invoke(tab);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/StringEmptinessToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 |
6 | namespace StructuredLogViewer
7 | {
8 | public class StringEmptinessToVisibilityConverter : IValueConverter
9 | {
10 | public static readonly StringEmptinessToVisibilityConverter Instance = new StringEmptinessToVisibilityConverter();
11 |
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | var text = value as string;
15 | bool result = string.IsNullOrEmpty(text);
16 | if (parameter is string s && s == "Invert")
17 | {
18 | result = !result;
19 | }
20 |
21 | return result ? Visibility.Collapsed : Visibility.Visible;
22 | }
23 |
24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
25 | {
26 | throw new NotImplementedException();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Controls/TimelineControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
13 |
14 |
15 |
16 |
21 |
25 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Entrypoint.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Windows;
4 | using System.Windows.Threading;
5 | using Microsoft.Build.Logging.StructuredLogger;
6 |
7 | namespace StructuredLogViewer
8 | {
9 | public class Entrypoint
10 | {
11 | [STAThread]
12 | public static void Main(string[] args)
13 | {
14 | ExceptionHandler.Initialize();
15 | DialogService.ShowMessageBoxEvent += message => MessageBox.Show(message);
16 | ClipboardService.Set += Clipboard.SetText;
17 |
18 | AppDomain.MonitoringIsEnabled = true;
19 |
20 | var app = new Application();
21 | app.DispatcherUnhandledException += OnDispatcherUnhandledException;
22 | var window = new MainWindow();
23 | app.Run(window);
24 |
25 | // wait for potential background operations to finish before shutting down
26 | window.InProgressTask.Wait();
27 | }
28 |
29 | private static void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
30 | {
31 | ErrorReporting.ReportException(e.Exception);
32 | DialogService.ShowMessageBox(
33 | "Unexpected exception. Sorry about that.\r\nPlease Ctrl+C to copy this text and file an issue at https://github.com/KirillOsenkov/MSBuildStructuredLog/issues/new\r\n\r\n" + e.Exception.ToString());
34 | e.Handled = true;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/Resources/SolutionFile.xshd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | VisualStudioVersion
9 | MinimumVisualStudioVersion
10 | Project
11 | EndProject
12 | ProjectSection
13 | EndProjectSection
14 | Global
15 | EndGlobal
16 | GlobalSection
17 | EndGlobalSection
18 | HideSolutionNode
19 | SolutionGuid
20 |
21 |
22 |
23 |
24 | \{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}
25 |
26 |
27 |
28 |
29 | [=()"]+
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/StructuredLogger.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/StructuredLogger.ico
--------------------------------------------------------------------------------
/src/StructuredLogViewer/TaskExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace StructuredLogViewer
4 | {
5 | internal static class TaskExtensions
6 | {
7 | ///
8 | /// Consumes a task and doesn't do anything with it. Useful for fire-and-forget calls to async methods within sync methods.
9 | ///
10 | /// The task whose result is to be ignored.
11 | public static void Ignore(this Task task)
12 | { /* this is it */ }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | amd64 arm64
15 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png
--------------------------------------------------------------------------------
/src/StructuredLogViewer/msbuild-structured-log-viewer.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | msbuild-structured-log-viewer
6 | 1.0.0
7 | https://github.com/KirillOsenkov/MSBuildStructuredLog
8 | flcdrg, KirillOsenkov
9 | MSBuild Structured Log Viewer
10 | Kirill Osenkov
11 | https://msbuildlog.com/
12 | http://cdn.rawgit.com/KirillOsenkov/MSBuildStructuredLog/main/src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png
13 | 2016 Kirill Osenkov
14 | https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/main/LICENSE
15 | false
16 | https://github.com/KirillOsenkov/MSBuildStructuredLog
17 | https://github.com/KirillOsenkov/MSBuildStructuredLog/wiki
18 | https://github.com/KirillOsenkov/MSBuildStructuredLog/issues
19 | msbuild
20 | A rich interactive log viewer app for MSBuild logs
21 | MSBuild 15.3 introduces a new command-line switch: `/bl` to record all build events to a structured/binary log file:
22 |
23 | Double-click the `.binlog` file to open it in MSBuild Structured Log Viewer:
24 |
25 | 
26 |
27 | Set the default back to the log tab.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/StructuredLogViewer/tools/.skipAutoUninstall:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/tools/.skipAutoUninstall
--------------------------------------------------------------------------------
/src/StructuredLogViewer/tools/LICENSE.txt:
--------------------------------------------------------------------------------
1 | From: https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/main/LICENSE
2 |
3 | LICENSE
4 |
5 | The MIT License (MIT)
6 |
7 | Copyright (c) 2016 Kirill Osenkov
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in all
17 | copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
--------------------------------------------------------------------------------
/src/StructuredLogViewer/tools/VERIFICATION.txt:
--------------------------------------------------------------------------------
1 | VERIFICATION
2 | Verification is intended to assist the Chocolatey moderators and community
3 | in verifying that this package's contents are trustworthy.
4 |
5 | Original downloads are located at https://github.com/KirillOsenkov/MSBuildStructuredLog/releases
6 |
7 | 1. Download the .exe from the same release version that corresponds to this package version.
8 | 2. Calculate the checksum for the downloaded file and compare that to the one listed for this package on Chocolatey.org
--------------------------------------------------------------------------------
/src/StructuredLogViewer/tools/chocolateyinstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'; # stop on all errors
2 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
3 | $fileLocation = Join-Path $toolsDir 'MSBuildStructuredLogSetup.exe'
4 |
5 | $packageArgs = @{
6 | packageName = $env:ChocolateyPackageName
7 | unzipLocation = $toolsDir
8 | fileType = 'EXE'
9 | file = $fileLocation
10 |
11 | softwareName = 'MSBuild Structured Log Viewer'
12 |
13 | validExitCodes= @(0)
14 | silentArgs = '-s' # Squirrel
15 | }
16 |
17 | Install-ChocolateyInstallPackage @packageArgs
--------------------------------------------------------------------------------
/src/StructuredLogViewer/tools/chocolateyuninstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'; # stop on all errors
2 |
3 | $packageArgs = @{
4 | packageName = $env:ChocolateyPackageName
5 | softwareName = 'MSBuild Structured Log Viewer' #part or all of the Display Name as you see it in Programs and Features. It should be enough to be unique
6 | fileType = 'EXE' #only one of these: MSI or EXE (ignore MSU for now)
7 | silentArgs = '--uninstall -s' # Squirrel
8 | validExitCodes= @(0) #please insert other valid exit codes here
9 | }
10 |
11 | $uninstall = Get-UninstallRegistryKey -softwareName $packageArgs.softwareName
12 |
13 | $packageArgs.file = ($uninstall.InstallLocation + "\update.exe")
14 |
15 | Uninstall-ChocolateyPackage @packageArgs
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/AssertEx.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using DiffPlex.DiffBuilder;
3 | using DiffPlex.DiffBuilder.Model;
4 | using Xunit;
5 |
6 | namespace StructuredLogger.Tests
7 | {
8 | internal static class AssertEx
9 | {
10 | ///
11 | /// Asserts that two strings are equal, and prints a diff between the two if they are not.
12 | ///
13 | /// The expected string. This is presented as the "baseline/before" side in the diff.
14 | /// The actual string. This is presented as the changed or "after" side in the diff.
15 | /// The message to precede the diff, if the values are not equal.
16 | public static void EqualOrDiff(string expected, string actual, string message = null)
17 | {
18 | if (expected == actual)
19 | {
20 | return;
21 | }
22 |
23 | var diffBuilder = new InlineDiffBuilder(new DiffPlex.Differ());
24 | var diff = diffBuilder.BuildDiffModel(expected, actual, ignoreWhitespace: false);
25 | var messageBuilder = new StringBuilder();
26 | messageBuilder.AppendLine(
27 | string.IsNullOrEmpty(message)
28 | ? "Actual and expected values differ. Expected shown in baseline of diff:"
29 | : message);
30 |
31 | foreach (var line in diff.Lines)
32 | {
33 | switch (line.Type)
34 | {
35 | case ChangeType.Inserted:
36 | messageBuilder.Append("+");
37 | break;
38 | case ChangeType.Deleted:
39 | messageBuilder.Append("-");
40 | break;
41 | default:
42 | messageBuilder.Append(" ");
43 | break;
44 | }
45 |
46 | messageBuilder.AppendLine(line.Text);
47 | }
48 |
49 | Assert.Fail(messageBuilder.ToString());
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/CompilerInvocationTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 | using Xunit;
4 |
5 | namespace StructuredLogger.Tests
6 | {
7 | public class CompilerInvocationTests
8 | {
9 | [Theory]
10 | [InlineData(
11 | @"C:\Program Files\dotnet\dotnet.exe exec ""C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\Roslyn\bincore\csc.dll"" /noconfig", "/noconfig")]
12 | [InlineData(@"foo\csc.exe a.cs /out:a.dll", "a.cs /out:a.dll")]
13 | public void Parse1(string arg, string expected)
14 | {
15 | var result = CompilerInvocationsReader.TrimCompilerExeFromCommandLine(arg, CompilerInvocation.CSharp);
16 | Assert.Equal(expected, result);
17 | }
18 |
19 | //[Fact]
20 | internal void ReadRecordsTest()
21 | {
22 | var binlog = @"C:\temp\msbuild.binlog";
23 | var records = BinaryLog.ReadRecords(binlog);
24 | string lastTask = null;
25 |
26 | foreach (var record in records)
27 | {
28 | if (record.Args is TaskStartedEventArgs taskStarted)
29 | {
30 | lastTask = taskStarted.TaskName;
31 | }
32 | else if (record.Args is TaskParameterEventArgs taskParameters)
33 | {
34 | if (lastTask == "Csc" && taskParameters.ItemType == "Compile")
35 | {
36 | foreach (ITaskItem item in taskParameters.Items)
37 | {
38 | var csFilePath = item.ItemSpec;
39 | }
40 | }
41 | }
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/Differ.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 |
4 | namespace StructuredLogger.Tests
5 | {
6 | public class Differ
7 | {
8 | public static bool AreDifferent(string file1, string file2)
9 | {
10 | var source = File.ReadAllText(file1);
11 | var destination = File.ReadAllText(file2);
12 | if (source != destination)
13 | {
14 | // Process.Start("devenv", $"/diff \"{Path.GetTestFile(file1)}\" \"{Path.GetTestFile(file2)}\"");
15 | return true;
16 | }
17 | else
18 | {
19 | return false;
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/ParseIntoWordsTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text;
3 | using Xunit;
4 |
5 | namespace StructuredLogger.Tests
6 | {
7 | public class ParseIntoWordsTests
8 | {
9 | [Fact]
10 | public void TestParseIntoWords()
11 | {
12 | T("a b", "a", "b");
13 | T("a \"b c\"", "a", "b c");
14 | T("a \"b\" c\"", "a", "b", "c\"");
15 | T("a (b c)", "a", "(b c)");
16 | T("a (b\" c)", "a", "(b\" c)");
17 | T("a (b\"f\" c)", "a", "(b\"f\" c)");
18 | T("a \"b(\"", "a", "b(");
19 | T("a \"b)\"", "a", "b)");
20 | T("a \"(b\"", "a", "(b");
21 | T("a \")b\"", "a", ")b");
22 | T("a \")b(\"", "a", ")b(");
23 | T("a \"(b)\"", "a", "(b)");
24 | }
25 |
26 | private static void T(string query, params string[] expectedParts)
27 | {
28 | var actualParts = ParseIntoWords(query);
29 | Assert.Equal(expectedParts, actualParts);
30 | }
31 |
32 | private static List ParseIntoWords(string query)
33 | {
34 | var result = new List();
35 |
36 | StringBuilder currentWord = new StringBuilder();
37 | bool isInParentheses = false;
38 | bool isInQuotes = false;
39 | for (int i = 0; i < query.Length; i++)
40 | {
41 | char c = query[i];
42 | switch (c)
43 | {
44 | case ' ' when !isInParentheses && !isInQuotes:
45 | result.Add(TrimQuotes(currentWord.ToString()));
46 | currentWord.Clear();
47 | break;
48 | case '(' when !isInParentheses && !isInQuotes:
49 | isInParentheses = true;
50 | currentWord.Append(c);
51 | break;
52 | case ')' when isInParentheses && !isInQuotes:
53 | isInParentheses = false;
54 | currentWord.Append(c);
55 | break;
56 | case '"' when !isInParentheses:
57 | isInQuotes = !isInQuotes;
58 | currentWord.Append(c);
59 | break;
60 | default:
61 | currentWord.Append(c);
62 | break;
63 | }
64 | }
65 |
66 | result.Add(TrimQuotes(currentWord.ToString()));
67 |
68 | return result;
69 | }
70 |
71 | private static string TrimQuotes(string word)
72 | {
73 | if (word.Length > 2 && word[0] == '"' && word[word.Length - 1] == '"')
74 | {
75 | word = word.Substring(1, word.Length - 2);
76 | }
77 |
78 | return word;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/ResourceStringTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Logging.StructuredLogger;
2 | using Xunit;
3 |
4 | namespace StructuredLogger.Tests
5 | {
6 | public class ResourceStringTests
7 | {
8 | [Fact]
9 | public void TestInitialize()
10 | {
11 | lock (typeof(Strings))
12 | {
13 | var resources = StringsSet.ResourcesCollection;
14 | var cultures = resources.Keys;
15 |
16 | foreach (var culture in cultures)
17 | {
18 | Strings.Initialize(culture);
19 | Assert.Equal(culture, Strings.ResourceSet.Culture);
20 | Assert.NotNull(Strings.OutputItemsMessagePrefix);
21 | }
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/StructuredLogger.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | StructuredLogger.Tests
4 | net472
5 | true
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | True
37 | ..\StructuredLogger\key.snk
38 | False
39 |
40 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Tests/TestUtilities.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Reflection;
4 |
5 | namespace StructuredLogger.Tests
6 | {
7 | public class TestUtilities
8 | {
9 | public static string GetTestFile(string fileName)
10 | {
11 | return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fileName);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/StructuredLogger.Utils/StructuredLogger.Utils.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net8.0
4 | true
5 | true
6 | true
7 |
8 |
9 |
10 |
11 |
12 | $(DefineConstants);NETCORE
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | True
24 | ..\StructuredLogger\key.snk
25 | False
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Analyzers/MessageTaskAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class MessageTaskAnalyzer
6 | {
7 | public static void Analyze(Task task)
8 | {
9 | var message = task.Children.OfType().FirstOrDefault();
10 | if (message?.ShortenedText != null)
11 | {
12 | task.Name = "Message: " + message.ShortenedText;
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using Microsoft.Build.Framework;
6 | using Microsoft.Build.Shared;
7 |
8 | // TODO: remove whole file once AssemblyLoadBuildEventArgs are imported via Microsoft.Build.Framework package
9 |
10 | namespace Microsoft.Build.Framework
11 | {
12 | internal enum AssemblyLoadingContext
13 | {
14 | TaskRun,
15 | Evaluation,
16 | SdkResolution,
17 | LoggerInitialization,
18 | }
19 |
20 | internal sealed class AssemblyLoadBuildEventArgs : BuildMessageEventArgs
21 | {
22 | private const string DefaultAppDomainDescriptor = "[Default]";
23 |
24 | public AssemblyLoadBuildEventArgs()
25 | { }
26 |
27 | public AssemblyLoadBuildEventArgs(
28 | AssemblyLoadingContext loadingContext,
29 | string? loadingInitiator,
30 | string? assemblyName,
31 | string assemblyPath,
32 | Guid mvid,
33 | string? customAppDomainDescriptor,
34 | MessageImportance importance = MessageImportance.Low)
35 | : base(null, null, null, importance, DateTime.UtcNow, assemblyName, assemblyPath, mvid)
36 | {
37 | LoadingContext = loadingContext;
38 | LoadingInitiator = loadingInitiator;
39 | AssemblyName = assemblyName;
40 | AssemblyPath = assemblyPath;
41 | MVID = mvid;
42 | AppDomainDescriptor = customAppDomainDescriptor;
43 | }
44 |
45 | public AssemblyLoadingContext LoadingContext { get; private set; }
46 | public string? LoadingInitiator { get; private set; }
47 | public string? AssemblyName { get; private set; }
48 | public string? AssemblyPath { get; private set; }
49 | public Guid MVID { get; private set; }
50 | // Null string indicates that load occurred on Default AppDomain (for both Core and Framework).
51 | public string? AppDomainDescriptor { get; private set; }
52 |
53 | public override string Message
54 | {
55 | get
56 | {
57 | if (RawMessage == null)
58 | {
59 | string? loadingInitiator = LoadingInitiator == null ? null : $" ({LoadingInitiator})";
60 | RawMessage = string.Format("Assembly loaded during {0}{1}: {2} (location: {3}, MVID: {4}, AppDomain: {5})", LoadingContext.ToString(), loadingInitiator, AssemblyName, AssemblyPath, MVID.ToString(), AppDomainDescriptor ?? DefaultAppDomainDescriptor);
61 | }
62 |
63 | return RawMessage;
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BinaryLogRecord.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 | using Microsoft.Build.Logging.StructuredLogger;
3 |
4 | namespace Microsoft.Build.Logging
5 | {
6 | public class Record
7 | {
8 | public BinaryLogRecordKind Kind;
9 | public byte[] Bytes;
10 | public BuildEventArgs Args;
11 | public long Start;
12 | public long Length;
13 | }
14 |
15 | public readonly record struct RecordInfo(BinaryLogRecordKind Kind, long Start, long Length);
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public enum BinaryLogRecordKind
4 | {
5 | EndOfFile = 0,
6 | BuildStarted,
7 | BuildFinished,
8 | ProjectStarted,
9 | ProjectFinished,
10 | TargetStarted,
11 | TargetFinished,
12 | TaskStarted,
13 | TaskFinished,
14 | Error,
15 | Warning,
16 | Message,
17 | TaskCommandLine,
18 | CriticalBuildMessage,
19 | ProjectEvaluationStarted,
20 | ProjectEvaluationFinished,
21 | ProjectImported,
22 | ProjectImportArchive,
23 | TargetSkipped,
24 | PropertyReassignment,
25 | UninitializedPropertyRead,
26 | EnvironmentVariableRead,
27 | PropertyInitialValueSet,
28 | NameValueList,
29 | String,
30 | TaskParameter,
31 | FileUsed,
32 | AssemblyLoad,
33 | BuildCheckMessage,
34 | BuildCheckWarning,
35 | BuildCheckError,
36 | BuildCheckTracing,
37 | BuildCheckAcquisition,
38 | BuildSubmissionStarted,
39 | BuildCanceled,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BuildCanceledEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.IO;
6 |
7 | namespace StructuredLogger.BinaryLogger
8 | {
9 | ///
10 | /// This class represents the event arguments for build canceled events.
11 | ///
12 | internal sealed class BuildCanceledEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs
13 | {
14 |
15 | ///
16 | /// Constructor which allows the timestamp to be set.
17 | ///
18 | /// text message
19 | /// Timestamp when the event was created
20 | public BuildCanceledEventArgs(
21 | string message,
22 | DateTime eventTimestamp)
23 | : this(message, eventTimestamp, null)
24 | {
25 | }
26 |
27 | ///
28 | /// Constructor which allows the timestamp to be set.
29 | ///
30 | /// text message
31 | /// Timestamp when the event was created
32 | /// message arguments
33 | public BuildCanceledEventArgs(
34 | string message,
35 | DateTime eventTimestamp,
36 | params object[]? messageArgs)
37 | : base(message, null, "MSBuild", eventTimestamp, messageArgs)
38 | {
39 | if (string.IsNullOrWhiteSpace(message))
40 | {
41 | throw new ArgumentException("Message cannot be null or consist only white-space characters.");
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BuildCheckEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Build.Framework;
4 |
5 | namespace StructuredLogger.BinaryLogger
6 | {
7 | ///
8 | /// Base class for all build check event args.
9 | /// Not intended to be extended by external code.
10 | ///
11 | internal abstract class BuildCheckEventArgs : BuildEventArgs
12 | { }
13 |
14 | ///
15 | /// Transport mean for the BuildCheck tracing data from additional nodes.
16 | ///
17 | ///
18 | internal sealed class BuildCheckTracingEventArgs(Dictionary tracingData) : BuildCheckEventArgs
19 | {
20 | public Dictionary TracingData { get; private set; } = tracingData;
21 | }
22 |
23 | internal sealed class BuildCheckAcquisitionEventArgs(string acquisitionPath, string projectPath) : BuildCheckEventArgs
24 | {
25 | public string AcquisitionPath { get; private set; } = acquisitionPath;
26 |
27 | public string ProjectPath { get; private set; } = projectPath;
28 | }
29 |
30 | internal sealed class BuildCheckResultMessage : BuildMessageEventArgs
31 | {
32 | public BuildCheckResultMessage(string message) => RawMessage = message;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BuildEventArgsFieldFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Logging
4 | {
5 | ///
6 | /// A bitmask to specify which fields on a BuildEventArgs object are present; used in serialization
7 | ///
8 | [Flags]
9 | internal enum BuildEventArgsFieldFlags
10 | {
11 | None = 0,
12 | BuildEventContext = 1 << 0,
13 | HelpKeyword = 1 << 1,
14 | Message = 1 << 2,
15 | SenderName = 1 << 3,
16 | ThreadId = 1 << 4,
17 | Timestamp = 1 << 5,
18 | Subcategory = 1 << 6,
19 | Code = 1 << 7,
20 | File = 1 << 8,
21 | ProjectFile = 1 << 9,
22 | LineNumber = 1 << 10,
23 | ColumnNumber = 1 << 11,
24 | EndLineNumber = 1 << 12,
25 | EndColumnNumber = 1 << 13,
26 | Arguments = 1 << 14,
27 | Importance = 1 << 15,
28 | Extended = 1 << 16,
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/BuildEventArgsFields.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Build.Framework;
3 |
4 | namespace Microsoft.Build.Logging
5 | {
6 | ///
7 | /// Represents a collective set of common properties on BuildEventArgs. Used for deserialization.
8 | ///
9 | internal class BuildEventArgsFields
10 | {
11 | public BuildEventArgsFieldFlags Flags { get; set; }
12 |
13 | public string Message { get; set; }
14 | public object[] Arguments { get; set; }
15 | public BuildEventContext BuildEventContext { get; set; }
16 | public int ThreadId { get; set; }
17 | public string HelpKeyword { get; set; }
18 | public string SenderName { get; set; }
19 | public DateTime Timestamp { get; set; }
20 | public MessageImportance Importance { get; set; } = MessageImportance.Low;
21 |
22 | public string Subcategory { get; set; }
23 | public string Code { get; set; }
24 | public string File { get; set; }
25 | public string ProjectFile { get; set; }
26 | public int LineNumber { get; set; }
27 | public int ColumnNumber { get; set; }
28 | public int EndLineNumber { get; set; }
29 | public int EndColumnNumber { get; set; }
30 | public ExtendedDataFields Extended { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/EnvironmentVariableReadEventArgs2.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 |
3 | namespace StructuredLogger.BinaryLogger
4 | {
5 | public class EnvironmentVariableReadEventArgs2 : EnvironmentVariableReadEventArgs
6 | {
7 | ///
8 | /// Initializes an instance of the EnvironmentVariableReadEventArgs class.
9 | ///
10 | /// The name of the environment variable that was read.
11 | /// The value of the environment variable that was read.
12 | /// file associated with the event
13 | /// line number (0 if not applicable)
14 | /// column number (0 if not applicable)
15 | public EnvironmentVariableReadEventArgs2(
16 | string environmentVarName,
17 | string environmentVarValue,
18 | string file,
19 | int line,
20 | int column)
21 | : base(environmentVarName, environmentVarValue)
22 | {
23 | LineNumber = line;
24 | ColumnNumber = column;
25 | File = file;
26 | }
27 |
28 | // the properties in the base class have an internal setter
29 | public new int LineNumber { get; set; }
30 | public new int ColumnNumber { get; set; }
31 | public new string File { get; set; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/EvaluationIdProvider.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 | //-----------------------------------------------------------------------
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Threading;
10 |
11 | namespace Microsoft.Build.Logging.StructuredLogger
12 | {
13 | ///
14 | /// Assigns unique evaluation ids. Thread safe.
15 | ///
16 | internal static class EvaluationIdProvider
17 | {
18 | private static long _sAssignedId = -1;
19 | private static readonly long ProcessId = Process.GetCurrentProcess().Id;
20 |
21 | ///
22 | /// Returns a unique evaluation id
23 | ///
24 | ///
25 | /// The id is guaranteed to be unique across all running processes.
26 | /// Additionally, it is monotonically increasing for callers on the same process id
27 | ///
28 | public static long GetNextId()
29 | {
30 | checked
31 | {
32 | var nextId = Interlocked.Increment(ref _sAssignedId);
33 | // Returns a unique number based on nextId (a unique number for this process) and the current process Id
34 | // Uses the Cantor pairing function (https://en.wikipedia.org/wiki/Pairing_function) to guarantee uniqueness
35 | return (((nextId + ProcessId) * (nextId + ProcessId + 1)) / 2) + ProcessId;
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/ExtendedDataFields.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Microsoft.Build.Logging;
4 |
5 | internal class ExtendedDataFields
6 | {
7 | public string ExtendedType { get; set; }
8 | public IDictionary ExtendedMetadata { get; set; }
9 | public string ExtendedData { get; set; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/ExtendedPropertyInitialValueSetEventArgs.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 |
3 | namespace StructuredLogger.BinaryLogger
4 | {
5 | internal class ExtendedPropertyInitialValueSetEventArgs : BuildMessageEventArgs
6 | {
7 | ///
8 | /// Creates an instance of the class.
9 | ///
10 | /// The name of the property.
11 | /// The value of the property.
12 | /// The source of the property.
13 | /// The file associated with the event.
14 | /// The line number (0 if not applicable).
15 | /// The column number (0 if not applicable).
16 | /// The message of the property.
17 | /// The help keyword.
18 | /// The sender name of the event.
19 | /// The importance of the message.
20 | public ExtendedPropertyInitialValueSetEventArgs(
21 | string propertyName,
22 | string propertyValue,
23 | string propertySource,
24 | string file,
25 | int line,
26 | int column,
27 | string message,
28 | string helpKeyword = null,
29 | string senderName = null,
30 | MessageImportance importance = MessageImportance.Low)
31 | : base(subcategory: null, code: null, file: file, lineNumber: line, columnNumber: column, 0, 0, message, helpKeyword, senderName, importance)
32 | {
33 | PropertyName = propertyName;
34 | PropertyValue = propertyValue;
35 | PropertySource = propertySource;
36 | }
37 |
38 | ///
39 | /// The name of the property.
40 | ///
41 | public string PropertyName { get; set; }
42 |
43 | ///
44 | /// The value of the property.
45 | ///
46 | public string PropertyValue { get; set; }
47 |
48 | ///
49 | /// The source of the property.
50 | ///
51 | public string PropertySource { get; set; }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/ExtendedPropertyReassignmentEventArgs.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 |
3 | namespace StructuredLogger.BinaryLogger
4 | {
5 | internal class ExtendedPropertyReassignmentEventArgs : BuildMessageEventArgs
6 | {
7 | ///
8 | /// Creates an instance of the class.
9 | ///
10 | /// The name of the property whose value was reassigned.
11 | /// The previous value of the reassigned property.
12 | /// The new value of the reassigned property.
13 | /// The file associated with the event.
14 | /// The line number (0 if not applicable).
15 | /// The column number (0 if not applicable).
16 | /// The message of the property.
17 | /// The help keyword.
18 | /// The sender name of the event.
19 | /// The importance of the message.
20 | public ExtendedPropertyReassignmentEventArgs(
21 | string propertyName,
22 | string previousValue,
23 | string newValue,
24 | string file,
25 | int line,
26 | int column,
27 | string message,
28 | string helpKeyword = null,
29 | string senderName = null,
30 | MessageImportance importance = MessageImportance.Low)
31 | : base(subcategory: null, code: null, file: file, lineNumber: line, columnNumber: column, 0, 0, message, helpKeyword, senderName, importance)
32 | {
33 | PropertyName = propertyName;
34 | PreviousValue = previousValue;
35 | NewValue = newValue;
36 | }
37 |
38 | ///
39 | /// The name of the property whose value was reassigned.
40 | ///
41 | public string PropertyName { get; set; }
42 |
43 | ///
44 | /// The previous value of the reassigned property.
45 | ///
46 | public string PreviousValue { get; set; }
47 |
48 | ///
49 | /// The new value of the reassigned property.
50 | ///
51 | public string NewValue { get; set; }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/FileUsedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Microsoft.Build.Framework
6 | {
7 | ///
8 | /// Arguments for the response file used event
9 | ///
10 | [Serializable]
11 | public class FileUsedEventArgs : BuildMessageEventArgs
12 | {
13 | public FileUsedEventArgs()
14 | {
15 | }
16 |
17 | ///
18 | /// Initialize a new instance of the ResponseFileUsedEventArgs class.
19 | ///
20 | public FileUsedEventArgs(string responseFilePath) : base()
21 | {
22 | FilePath = responseFilePath;
23 | }
24 |
25 | public string? FilePath { set; get; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/IExtendedBuildEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Microsoft.Build.Framework;
4 |
5 | ///
6 | /// Interface for Extended EventArgs to allow enriching particular events with extended data.
7 | /// Deriving from EventArgs will be deprecated soon and using Extended EventArgs is recommended for custom Event Args.
8 | ///
9 | public interface IExtendedBuildEventArgs
10 | {
11 | ///
12 | /// Unique string identifying type of extended data so receiver side knows how to interpret, deserialize and handle .
13 | ///
14 | string ExtendedType { get; set; }
15 |
16 | ///
17 | /// Metadata of .
18 | /// Example usage:
19 | /// - data which needed in custom code to properly routing this message without interpreting/deserializing .
20 | /// - simple extended data can be transferred in form of dictionary key-value per one extended property.
21 | ///
22 | IDictionary? ExtendedMetadata { get; set; }
23 |
24 | ///
25 | /// Transparent data as string.
26 | /// Custom code is responsible to serialize and deserialize this string to structured data - if needed.
27 | /// Custom code can use any serialization they deem safe - e.g. json for textual data, base64 for binary data...
28 | ///
29 | string? ExtendedData { get; set; }
30 | }
31 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/IsExternalInit.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | // This is a dummy class to make C# 9.0 init-only properties work in Full FW.
4 | // See https://developercommunity.visualstudio.com/t/error-cs0518-predefined-type-systemruntimecompiler/1244809
5 | internal static class IsExternalInit { }
6 | }
7 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/ArchiveFileEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger;
7 |
8 | ///
9 | /// Event arguments for event.
10 | ///
11 | public sealed class ArchiveFileEventArgs : EventArgs
12 | {
13 | public ArchiveFileEventArgs(ArchiveFile archiveFile) =>
14 | ArchiveFile = archiveFile;
15 |
16 | public ArchiveFile ArchiveFile { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/ArchiveFileEventArgsExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger;
7 |
8 | public static class ArchiveFileEventArgsExtensions
9 | {
10 | public static Action ToArchiveFileHandler(this Action stringHandler)
11 | {
12 | return args =>
13 | {
14 | var pathArgs = new StringReadEventArgs(args.ArchiveFile.FullPath);
15 | stringHandler(pathArgs);
16 | var contentArgs = new StringReadEventArgs(args.ArchiveFile.Text);
17 | stringHandler(contentArgs);
18 |
19 | args.ArchiveFile = new ArchiveFile(pathArgs.StringToBeUsed, contentArgs.StringToBeUsed);
20 | };
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/BinaryLogReaderErrorEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger
7 | {
8 | ///
9 | /// Materializes the error message.
10 | /// Until it's called the error message is not materialized and no string allocations are made.
11 | ///
12 | /// The error message.
13 | internal delegate string FormatErrorMessage();
14 |
15 | ///
16 | /// An event args for event.
17 | ///
18 | public sealed class BinaryLogReaderErrorEventArgs : EventArgs
19 | {
20 | private readonly FormatErrorMessage _formatErrorMessage;
21 |
22 | internal BinaryLogReaderErrorEventArgs(
23 | ReaderErrorType errorType,
24 | BinaryLogRecordKind recordKind,
25 | FormatErrorMessage formatErrorMessage)
26 | {
27 | ErrorType = errorType;
28 | RecordKind = recordKind;
29 | _formatErrorMessage = formatErrorMessage;
30 | }
31 |
32 | ///
33 | /// Type of the error that occurred during reading.
34 | ///
35 | public ReaderErrorType ErrorType { get; }
36 |
37 | ///
38 | /// Kind of the record that encountered the error.
39 | ///
40 | public BinaryLogRecordKind RecordKind { get; }
41 |
42 | ///
43 | /// Materializes the error message.
44 | /// Until it's called the error message is not materialized and no string allocations are made.
45 | ///
46 | /// The error message.
47 | public string GetFormattedMessage() => _formatErrorMessage();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/EmbeddedContentEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.IO;
6 |
7 | namespace Microsoft.Build.Logging.StructuredLogger
8 | {
9 | internal sealed class EmbeddedContentEventArgs : EventArgs
10 | {
11 | public EmbeddedContentEventArgs(BinaryLogRecordKind contentKind, Stream contentStream)
12 | {
13 | ContentKind = contentKind;
14 | ContentStream = contentStream;
15 | }
16 |
17 | public BinaryLogRecordKind ContentKind { get; }
18 | public Stream ContentStream { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/IBuildEventArgsReaderNotifications.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger
7 | {
8 | ///
9 | /// An interface for notifications from BuildEventArgsReader
10 | ///
11 | public interface IBuildEventArgsReaderNotifications
12 | {
13 | ///
14 | /// An event that allows the subscriber to be notified when a string is read from the binary log.
15 | /// Subscriber may adjust the string by setting property.
16 | /// The passed event arg can be reused and should not be stored.
17 | ///
18 | public event Action? StringReadDone;
19 |
20 | ///
21 | /// An event that allows the caller to be notified when an embedded file is encountered in the binary log.
22 | /// When subscriber is OK with greedy reading entire content of the file and is interested only in the individual strings (e.g. for sensitive data redaction purposes),
23 | /// it can simplify subscribing to this event, by using handler with same signature as handler for and wrapping it via
24 | /// extension.
25 | ///
26 | ///
27 | ///
28 | /// private void OnStringReadDone(StringReadEventArgs e)
29 | /// {
30 | /// e.StringToBeUsed = e.StringToBeUsed.Replace("foo", "bar");
31 | /// }
32 | ///
33 | /// private void SubscribeToEvents()
34 | /// {
35 | /// reader.StringReadDone += OnStringReadDone;
36 | /// reader.ArchiveFileEncountered += ((Action<StringReadEventArgs>)OnStringReadDone).ToArchiveFileHandler();
37 | /// }
38 | ///
39 | ///
40 | public event Action? ArchiveFileEncountered;
41 |
42 | ///
43 | /// Receives recoverable errors during reading.
44 | /// Communicates type of the error, kind of the record that encountered the error and the message detailing the error.
45 | /// In case of this is raised before returning the structured representation of a build event
46 | /// that has some extra unknown data in the binlog. In case of other error types this event is raised and the offending build event is skipped and not returned.
47 | ///
48 | event Action? RecoverableReadError;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/ReaderErrorType.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger;
5 |
6 | ///
7 | /// Type of the error that occurred during reading.
8 | ///
9 | public enum ReaderErrorType
10 | {
11 | ///
12 | /// The encountered event is completely unknown to the reader. It cannot interpret any part of it.
13 | ///
14 | UnknownEventType,
15 |
16 | ///
17 | /// The encountered event is known to the reader and reader is able to read the event as it knows it.
18 | /// However there are some extra data (append only extension to the event in future version), that reader cannot interpret,
19 | /// it can only skip it.
20 | ///
21 | UnknownEventData,
22 |
23 | ///
24 | /// The encountered event type is known to the reader, but the reader cannot interpret the data of the event.
25 | /// This is probably caused by an event definition changing more than just adding fields.
26 | /// The reader can only skip the event in full.
27 | ///
28 | UnknownFormatOfEventData,
29 | }
30 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/StreamChunkOverreadException.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger
7 | {
8 | public class StreamChunkOverReadException : Exception
9 | {
10 | public StreamChunkOverReadException()
11 | {
12 | }
13 |
14 | public StreamChunkOverReadException(string message) : base(message)
15 | {
16 | }
17 |
18 | public StreamChunkOverReadException(string message, Exception inner) : base(message, inner)
19 | {
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/Postprocessing/StringReadEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Microsoft.Build.Logging.StructuredLogger
7 | {
8 | ///
9 | /// An event args for callback.
10 | ///
11 | public sealed class StringReadEventArgs : EventArgs
12 | {
13 | ///
14 | /// The original string that was read from the binary log.
15 | ///
16 | public string OriginalString { get; private set; }
17 |
18 | ///
19 | /// The adjusted string (or the original string of none subscriber replaced it) that will be used by the reader.
20 | ///
21 | public string StringToBeUsed { get; set; }
22 |
23 | public StringReadEventArgs(string str)
24 | {
25 | OriginalString = str;
26 | StringToBeUsed = str;
27 | }
28 |
29 | internal void Reuse(string newValue)
30 | {
31 | OriginalString = newValue;
32 | StringToBeUsed = newValue;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/ProjectImportsCollectorExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 |
3 | namespace Microsoft.Build.Logging
4 | {
5 | // This functionality is separate from SourceFileCollector to avoid having SourceFileCollector
6 | // depend on MSBuild. It seems like a more standalone and useful utility and it feels better to
7 | // have the population from BuildEventArgs in a separate file.
8 | internal static class ProjectImportsCollectorExtensions
9 | {
10 | public static void IncludeSourceFiles(this ProjectImportsCollector projectImportsCollector, BuildEventArgs e)
11 | {
12 | if (e is TaskStartedEventArgs)
13 | {
14 | var taskArgs = (TaskStartedEventArgs)e;
15 | projectImportsCollector.AddFile(taskArgs.TaskFile);
16 | }
17 | else if (e is TargetStartedEventArgs)
18 | {
19 | var targetArgs = (TargetStartedEventArgs)e;
20 | projectImportsCollector.AddFile(targetArgs.TargetFile);
21 | }
22 | else if (e is ProjectStartedEventArgs)
23 | {
24 | var projectStarted = (ProjectStartedEventArgs)e;
25 | projectImportsCollector.AddFile(projectStarted.ProjectFile);
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/ResourceUtilities.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Shared
2 | {
3 | internal class ResourceUtilities
4 | {
5 | internal static string FormatResourceString(out string errorCode, out string helpKeyword, string text, string filePath, string message)
6 | {
7 | errorCode = "MSB0001";
8 | helpKeyword = "";
9 | return message;
10 | }
11 |
12 | internal static string FormatResourceStringStripCodeAndKeyword(out string errorCode, out string helpKeyword, string text, string filePath, string message)
13 | {
14 | errorCode = "MSB0001";
15 | helpKeyword = "";
16 | return message;
17 | }
18 |
19 | internal static string FormatResourceString(string v1, string v2)
20 | {
21 | return v1;
22 | }
23 |
24 | internal static string FormatResourceStringStripCodeAndKeyword(string v1, string v2)
25 | {
26 | return v1;
27 | }
28 |
29 | internal static string GetResourceString(string s) => s;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/TaskParameterEventArgs2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 |
4 | namespace Microsoft.Build.Framework
5 | {
6 | public class TaskParameterEventArgs2 : TaskParameterEventArgs
7 | {
8 | public TaskParameterEventArgs2(
9 | TaskParameterMessageKind kind,
10 | string parameterName,
11 | string propertyName,
12 | string itemType,
13 | IList items,
14 | bool logItemMetadata,
15 | DateTime eventTimestamp)
16 | : base(kind, itemType, items, logItemMetadata, eventTimestamp)
17 | {
18 | ParameterName = parameterName;
19 | PropertyName = propertyName;
20 | }
21 |
22 | // Added in MSBuild 17.11
23 | public string ParameterName { get; set; }
24 | public string PropertyName { get; set; }
25 |
26 | // the properties in the base class have an internal setter
27 | public new int LineNumber { get; set; }
28 | public new int ColumnNumber { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/StructuredLogger/BinaryLogger/TaskStartedEventArgs2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Framework
4 | {
5 | public class TaskStartedEventArgs2 : TaskStartedEventArgs
6 | {
7 | public TaskStartedEventArgs2(
8 | string message,
9 | string helpKeyword,
10 | string projectFile,
11 | string taskFile,
12 | string taskName,
13 | DateTime eventTimestamp)
14 | : base(
15 | message,
16 | helpKeyword,
17 | projectFile,
18 | taskFile,
19 | taskName,
20 | eventTimestamp)
21 | {
22 | }
23 |
24 | // the properties in the base class have an internal setter
25 | public new int LineNumber { get; set; }
26 | public new int ColumnNumber { get; set; }
27 | public string TaskAssemblyLocation { get; set; }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Construction/BuildStatistics.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class BuildStatistics
7 | {
8 | public int Tasks;
9 |
10 | public Dictionary> TaskParameterMessagesByTask = new Dictionary>();
11 | public Dictionary> OutputItemMessagesByTask = new Dictionary>();
12 |
13 | public int TimedNodeCount { get; set; }
14 |
15 | public void ReportTaskParameterMessage(Task task, string message)
16 | {
17 | Add(task.Name, message, TaskParameterMessagesByTask);
18 | }
19 |
20 | public void ReportOutputItemMessage(Task task, string message)
21 | {
22 | Add(task.Name, message, OutputItemMessagesByTask);
23 | }
24 |
25 | public void Add(string key, string value, Dictionary> dictionary)
26 | {
27 | if (!dictionary.TryGetValue(key, out var bucket))
28 | {
29 | bucket = new List();
30 | dictionary[key] = bucket;
31 | }
32 |
33 | bucket.Add(value);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/StructuredLogger/CustomAppDomainManager.cs:
--------------------------------------------------------------------------------
1 | #if !NETCORE
2 |
3 | using System;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Security.Policy;
7 |
8 | namespace Microsoft.Build.Logging.StructuredLogger
9 | {
10 | ///
11 | /// We want a callback when an appdomain is created, to install the MSBuildLocator
12 | /// into the XAML markup compilation appdomain.
13 | /// See https://github.com/KirillOsenkov/AppDomainManagerTest
14 | ///
15 | ///
16 | /// This class can't be in the entrypoint assembly because of a CLR bug.
17 | /// We need a way to callback the entrypoint assembly when a new appdomain
18 | /// is created. Let's just instantiate a well-known type from the
19 | /// entrypoint assembly.
20 | ///
21 | public class CustomAppDomainManager : AppDomainManager
22 | {
23 | ///
24 | /// This runs in the old appdomain when a new appdomain is created
25 | ///
26 | public override AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup appDomainInfo)
27 | {
28 | var result = base.CreateDomain(friendlyName, securityInfo, appDomainInfo);
29 | return result;
30 | }
31 |
32 | ///
33 | /// This runs in the new appdomain very early when it is being initialized
34 | ///
35 | public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
36 | {
37 | base.InitializeNewDomain(appDomainInfo);
38 | NotifyEntrypointAssembly();
39 | }
40 |
41 | private void NotifyEntrypointAssembly()
42 | {
43 | if (AppDomain.CurrentDomain.IsDefaultAppDomain())
44 | {
45 | return;
46 | }
47 |
48 | var assembly = Assembly.Load("TaskRunner");
49 | if (assembly == null)
50 | {
51 | return;
52 | }
53 |
54 | var type = assembly.GetType("TaskRunner.AppDomainInitializer");
55 | if (type == null)
56 | {
57 | return;
58 | }
59 |
60 | Activator.CreateInstance(type);
61 | }
62 | }
63 | }
64 |
65 | #endif
--------------------------------------------------------------------------------
/src/StructuredLogger/ErrorReporting.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class ErrorReporting
7 | {
8 | private static readonly string logFilePath = Path.Combine(PathUtils.RootPath, "LoggerExceptions.txt");
9 |
10 | public static string LogFilePath => logFilePath;
11 |
12 | public static void ReportException(Exception ex)
13 | {
14 | if (ex == null)
15 | {
16 | return;
17 | }
18 |
19 | try
20 | {
21 | Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
22 |
23 | // if the log has gotten too big, delete it
24 | if (File.Exists(logFilePath) && new FileInfo(logFilePath).Length > 10000000)
25 | {
26 | File.Delete(logFilePath);
27 | }
28 |
29 | File.AppendAllText(logFilePath, ex.ToString() + Environment.NewLine);
30 | }
31 | catch (Exception)
32 | {
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/AbstractDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class AbstractDiagnostic : TextNode, IHasSourceFile
6 | {
7 | public DateTime Timestamp { get; set; }
8 | public string Code { get; set; }
9 | public int ColumnNumber { get; set; }
10 | public int EndColumnNumber { get; set; }
11 | public int EndLineNumber { get; set; }
12 | public string File { get; set; }
13 | public int LineNumber { get; set; }
14 | public string ProjectFile { get; set; }
15 | public string Subcategory { get; set; }
16 |
17 | string IHasSourceFile.SourceFilePath => File;
18 |
19 | public override string TypeName => nameof(AbstractDiagnostic);
20 |
21 | public override string Title => ToString();
22 |
23 | public override string ToString()
24 | {
25 | File ??= "";
26 |
27 | string position = "";
28 | if (LineNumber != 0 || ColumnNumber != 0)
29 | {
30 | string column = "";
31 | if (ColumnNumber > 0)
32 | {
33 | column = "," + ColumnNumber.ToString();
34 | }
35 |
36 | position = $"({LineNumber}{column}):";
37 | }
38 |
39 | string code = "";
40 | if (!string.IsNullOrWhiteSpace(Code))
41 | {
42 | code = $" {this.GetType().Name.ToLowerInvariant()} {Code}:";
43 | }
44 |
45 | string text = Text;
46 | if (File.Length + position.Length + code.Length > 0)
47 | {
48 | text = " " + text;
49 | }
50 |
51 | string projectFile = "";
52 | if (!string.IsNullOrWhiteSpace(ProjectFile))
53 | {
54 | projectFile = $" [{ProjectFile}]";
55 | }
56 |
57 | return $"{File}{position}{code}{text}{projectFile}";
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/AddItem.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class AddItem : NamedNode, IHasLineNumber
4 | {
5 | public AddItem()
6 | {
7 | DisableChildrenCache = true;
8 | }
9 |
10 | public override string TypeName => nameof(AddItem);
11 |
12 | public int? LineNumber { get; set; }
13 | }
14 |
15 | public class TaskParameterItem : AddItem
16 | {
17 | public string ParameterName { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/ArchiveFile.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class ArchiveFile
7 | {
8 | public ArchiveFile(string fullPath, string text)
9 | {
10 | FullPath = fullPath;
11 | Text = text;
12 | }
13 |
14 | public string FullPath { get; }
15 | public string Text { get; }
16 |
17 | public static ArchiveFile From(ZipArchiveEntry entry)
18 | => From(entry, adjustPath: true);
19 |
20 | public static ArchiveFile From(ZipArchiveEntry entry, bool adjustPath)
21 | {
22 | var filePath = adjustPath ? CalculateArchivePath(entry.FullName) : entry.FullName;
23 | var text = GetText(entry);
24 | var file = new ArchiveFile(filePath, text);
25 | return file;
26 | }
27 |
28 | public static string GetText(ZipArchiveEntry entry)
29 | {
30 | using (var contentStream = entry.Open())
31 | using (var reader = new StreamReader(contentStream))
32 | {
33 | var text = reader.ReadToEnd();
34 | return text;
35 | }
36 | }
37 |
38 | public static string CalculateArchivePath(string filePath)
39 | {
40 | string archivePath = filePath;
41 |
42 | if (archivePath.Length > 1 && archivePath[1] == '\\' && archivePath[0] != '\\' && archivePath[0] != '/')
43 | {
44 | archivePath = archivePath[0] + ":" + archivePath.Substring(1);
45 | }
46 |
47 | archivePath = TextUtilities.NormalizeFilePath(archivePath);
48 |
49 | return archivePath;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/CriticalBuildMessage.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class CriticalBuildMessage : AbstractDiagnostic
4 | {
5 | public override string TypeName => nameof(CriticalBuildMessage);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/EntryTarget.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class EntryTarget : NamedNode, IHasRelevance
6 | {
7 | private Project? project;
8 | private Target? target;
9 | public override string TypeName => nameof(EntryTarget);
10 | private Project? Project => project ??= GetNearestParent();
11 | public Target? Target => target ??= Project?.FindFirstChild(t => t.Name == Name);
12 | public bool IsLowRelevance => Target?.IsLowRelevance ?? true;
13 | public string? DurationText => Target?.DurationText;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Error.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Error : AbstractDiagnostic
4 | {
5 | public override string TypeName => nameof(Error);
6 | }
7 |
8 | public class BuildError : Error
9 | {
10 | public override string TypeName => "Build Error";
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/EvaluationProfileEntry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Microsoft.Build.Framework.Profiler;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class EvaluationProfileEntry : TreeNode, IHasSourceFile, IHasLineNumber
7 | {
8 | public string ElementName { get; set; }
9 | public string ElementDescription { get; set; }
10 | public string EvaluationPassDescription { get; set; }
11 | public EvaluationLocationKind Kind { get; set; }
12 | public string SourceFilePath { get; set; }
13 | public int? LineNumber { get; set; }
14 | public string NumberOfHits => ProfiledLocation.NumberOfHits > 0 ? ProfiledLocation.NumberOfHits.ToString() : "";
15 |
16 | public override string TypeName => nameof(EvaluationProfileEntry);
17 |
18 | public ProfiledLocation ProfiledLocation { get; private set; }
19 |
20 | public void AddEntry(ProfiledLocation result)
21 | {
22 | ProfiledLocation = result;
23 | }
24 |
25 | public string DurationText
26 | {
27 | get
28 | {
29 | if (ProfiledLocation.InclusiveTime.TotalMilliseconds == 0)
30 | {
31 | return "";
32 | }
33 |
34 | var result = TextUtilities.DisplayDuration(ProfiledLocation.InclusiveTime);
35 |
36 | var hits = ProfiledLocation.NumberOfHits;
37 | if (hits == 1)
38 | {
39 | result += " (1 hit)";
40 | }
41 | else if (hits > 1)
42 | {
43 | result += " (" + hits + " hits)";
44 | }
45 |
46 | return result;
47 | }
48 | }
49 |
50 | public string FileName => Path.GetFileName(SourceFilePath);
51 |
52 | public override string Title
53 | {
54 | get
55 | {
56 | if (SourceFilePath == null)
57 | {
58 | return EvaluationPassDescription;
59 | }
60 |
61 | if (Kind == EvaluationLocationKind.Element)
62 | {
63 | return $"{FileName}:{LineNumber}";
64 | }
65 |
66 | return $"{FileName}:{LineNumber} {Kind}";
67 | }
68 | }
69 |
70 | public string ShortenedElementDescription => TextUtilities.ShortenValue(ElementDescription, "...", maxChars: 80);
71 |
72 | public double Value { get; set; }
73 |
74 | public override string ToString()
75 | {
76 | return $"{Title}";
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Folder.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Folder : NamedNode, IHasRelevance
4 | {
5 | public bool IsLowRelevance
6 | {
7 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected;
8 | set => SetFlag(NodeFlags.LowRelevance, value);
9 | }
10 |
11 | public override string TypeName => nameof(Folder);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/IHasExtendedData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger;
4 |
5 | public interface IHasExtendedData
6 | {
7 | string ExtendedType { get; }
8 | IDictionary? ExtendedMetadata { get; }
9 | string? ExtendedData { get; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/IHasLineNumber.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public interface IHasLineNumber
4 | {
5 | int? LineNumber { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/IHasRelevance.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public interface IHasRelevance
4 | {
5 | bool IsLowRelevance { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/IHasSourceFile.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public interface IHasSourceFile
4 | {
5 | string SourceFilePath { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/IPreprocessable.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public interface IPreprocessable
4 | {
5 | string RootFilePath { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Import.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Import : TextNode, IHasRelevance, IPreprocessable, IHasSourceFile, IHasLineNumber
4 | {
5 | public string ProjectFilePath { get; set; }
6 | public string ImportedProjectFilePath { get; set; }
7 | public int Line { get; set; }
8 | public int Column { get; set; }
9 |
10 | public Import()
11 | {
12 | }
13 |
14 | public Import(
15 | string projectFilePath,
16 | string importedProjectFilePath,
17 | int line,
18 | int column)
19 | {
20 | ProjectFilePath = projectFilePath;
21 | ImportedProjectFilePath = importedProjectFilePath;
22 | Line = line;
23 | Column = column;
24 |
25 | Text = importedProjectFilePath;
26 | }
27 |
28 | public string Location => $" at ({Line};{Column})";
29 |
30 | public override string TypeName => nameof(Import);
31 |
32 | public bool IsLowRelevance
33 | {
34 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected;
35 | set => SetFlag(NodeFlags.LowRelevance, value);
36 | }
37 |
38 | string IPreprocessable.RootFilePath => ImportedProjectFilePath;
39 |
40 | string IHasSourceFile.SourceFilePath => ProjectFilePath;
41 | int? IHasLineNumber.LineNumber => Line;
42 |
43 | public override string ToString() => $"Import: {Text}{Location}";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Item.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | ///
4 | /// Class representation of an item/property with associated metadata (if any).
5 | ///
6 | public class Item : NamedNode
7 | {
8 | public Item()
9 | {
10 | DisableChildrenCache = true;
11 | }
12 |
13 | public override string TypeName => nameof(Item);
14 |
15 | public string Text
16 | {
17 | get => Name;
18 | set => Name = value;
19 | }
20 | }
21 |
22 | public class FileCopy : Item
23 | {
24 | public string Kind { get; set; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Metadata.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Metadata : NameValueNode
4 | {
5 | public override string TypeName => nameof(Metadata);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/NameValueNode.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class NameValueNode : BaseNode
4 | {
5 | public string Name { get; set; }
6 | public string Value { get; set; }
7 | public string NameAndEquals => Name + " = ";
8 | public string ShortenedValue => TextUtilities.ShortenValue(Value);
9 | public bool IsValueShortened => Value != ShortenedValue;
10 |
11 | public override string TypeName => nameof(NameValueNode);
12 | public override string Title => Name;
13 |
14 | public override string ToString() => Name + " = " + Value;
15 | public override string GetFullText() => ToString();
16 | public bool IsVisible { get => true; set { } }
17 | public bool IsExpanded { get => true; set { } }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/NamedNode.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class NamedNode : TreeNode
4 | {
5 | public virtual string Name { get; set; }
6 | public string ShortenedName => TextUtilities.ShortenValue(Name);
7 | public bool IsNameShortened => Name != ShortenedName;
8 |
9 | public override string TypeName => nameof(NamedNode);
10 | public override string Title => Name;
11 |
12 | public override string ToString() => Name;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/NoImport.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class NoImport : TextNode, IHasRelevance, IHasSourceFile, IHasLineNumber
4 | {
5 | public string ProjectFilePath { get; set; }
6 | public string ImportedFileSpec { get; set; }
7 | public string Reason { get; set; }
8 | public int Line { get; set; }
9 | public int Column { get; set; }
10 |
11 | public NoImport()
12 | {
13 | }
14 |
15 | public override string TypeName => nameof(NoImport);
16 |
17 | public NoImport(
18 | string projectFilePath,
19 | string importedFileSpec,
20 | int line,
21 | int column,
22 | string reason)
23 | : this()
24 | {
25 | ProjectFilePath = projectFilePath;
26 | Text = importedFileSpec;
27 | Line = line;
28 | Column = column;
29 | Reason = reason;
30 | }
31 |
32 | public string Location => $" at ({Line};{Column})";
33 |
34 | public bool IsLowRelevance
35 | {
36 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected;
37 | set => SetFlag(NodeFlags.LowRelevance, value);
38 | }
39 |
40 | string IHasSourceFile.SourceFilePath => ProjectFilePath;
41 | int? IHasLineNumber.LineNumber => Line;
42 |
43 | public override string ToString() => $"NoImport: {Text}{Location} {Reason}";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/NodeFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | [Flags]
6 | internal enum NodeFlags : byte
7 | {
8 | None = 0,
9 | Hidden = 1 << 0,
10 | Expanded = 1 << 1,
11 | SearchResult = 1 << 2,
12 | ContainsSearchResult = 1 << 3,
13 | LowRelevance = 1 << 4,
14 | DisableChildrenCache = 1 << 5
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Note.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Note : TextNode
4 | {
5 | protected override bool IsSelectable => false;
6 |
7 | public override string TypeName => nameof(Note);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/ObservableObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.ComponentModel;
3 | using System.Runtime.CompilerServices;
4 |
5 | namespace Microsoft.Build.Logging.StructuredLogger
6 | {
7 | public abstract class ObservableObject : INotifyPropertyChanged
8 | {
9 | public event PropertyChangedEventHandler PropertyChanged;
10 |
11 | protected bool SetField(ref TField field, TField newValue, [CallerMemberName] string propertyName = null)
12 | {
13 | if (EqualityComparer.Default.Equals(field, newValue))
14 | {
15 | return false;
16 | }
17 |
18 | field = newValue;
19 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
20 |
21 | return true;
22 | }
23 |
24 | protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
25 | => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Package.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Package : NamedNode
4 | {
5 | public override string TypeName => nameof(Package);
6 |
7 | public string Version { get; set; }
8 |
9 | public string VersionSpec { get; set; }
10 |
11 | public override string ToString()
12 | {
13 | string result = Name;
14 | if (!string.IsNullOrEmpty(Version))
15 | {
16 | result = $"{result} {Version}";
17 | }
18 |
19 | if (!string.IsNullOrEmpty(VersionSpec))
20 | {
21 | result = $"{result} {VersionSpec}";
22 | }
23 |
24 | return result;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Parameter.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Parameter : NamedNode
4 | {
5 | public override string TypeName => nameof(Parameter);
6 |
7 | public string ParameterName { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Property.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Property : NameValueNode
4 | {
5 | public override string TypeName => nameof(Property);
6 | }
7 |
8 | public class TaskParameterProperty : Property
9 | {
10 | public string ParameterName { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/RemoveItem.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class RemoveItem : NamedNode, IHasLineNumber
4 | {
5 | public RemoveItem()
6 | {
7 | DisableChildrenCache = true;
8 | }
9 |
10 | public override string TypeName => nameof(RemoveItem);
11 |
12 | public int? LineNumber { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/SearchableItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Microsoft.Build.Logging.StructuredLogger
6 | {
7 | public class SearchableItem : Item
8 | {
9 | public SearchableItem() : base() { }
10 |
11 | public string SearchText
12 | {
13 | get { return _searchText ?? this.Text; }
14 | set { _searchText = value; }
15 | }
16 |
17 | private string _searchText;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/SourceFile.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class SourceFile : NamedNode, IHasSourceFile
4 | {
5 | public string SourceFilePath { get; set; }
6 | public override string TypeName => nameof(SourceFile);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/SourceFileLine.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class SourceFileLine : TreeNode
4 | {
5 | public int LineNumber { get; set; }
6 | public string LineText { get; set; }
7 |
8 | public override string TypeName => nameof(SourceFileLine);
9 |
10 | public override string ToString()
11 | {
12 | return LineNumber.ToString().PadRight(5, ' ') + LineText;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Task.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class Task : TimedNode, IHasSourceFile, IHasLineNumber
7 | {
8 | public string FromAssembly { get; set; }
9 | public string CommandLineArguments { get; set; }
10 | public string SourceFilePath { get; set; }
11 |
12 | public override string TypeName => nameof(Task);
13 |
14 | public virtual bool IsDerivedTask => this.GetType() != typeof(Task);
15 |
16 | public int? LineNumber { get; set; }
17 |
18 | public IReadOnlyList GetMessages()
19 | {
20 | TreeNode node = this;
21 | if (this.FindChild(Strings.Messages) is Folder messagesFolder)
22 | {
23 | node = messagesFolder;
24 | }
25 |
26 | return node.Children.OfType().ToArray();
27 | }
28 | }
29 |
30 | public class MSBuildTask : Task
31 | {
32 | //public override string TypeName => nameof(MSBuildTask);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/TaskItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using Microsoft.Build.Framework;
5 |
6 | namespace Microsoft.Build.Logging
7 | {
8 | public class TaskItem : ITaskItem2
9 | {
10 | public string ItemSpec { get; set; }
11 | public Dictionary Metadata { get; } = new Dictionary();
12 | public string EvaluatedIncludeEscaped { get; set; }
13 |
14 | public int MetadataCount => Metadata.Count;
15 | public ICollection MetadataNames => Metadata.Keys;
16 |
17 | public TaskItem()
18 | {
19 | }
20 |
21 | public TaskItem(string itemSpec)
22 | {
23 | ItemSpec = itemSpec;
24 | }
25 |
26 | public IDictionary CloneCustomMetadata()
27 | {
28 | return Metadata;
29 | }
30 |
31 | public IDictionary CloneCustomMetadataEscaped()
32 | {
33 | throw new NotImplementedException();
34 | }
35 |
36 | public void CopyMetadataTo(ITaskItem destinationItem)
37 | {
38 | foreach (var kvp in Metadata)
39 | {
40 | destinationItem.SetMetadata(kvp.Key, kvp.Value);
41 | }
42 | }
43 |
44 | public string GetMetadata(string metadataName)
45 | {
46 | if (!Metadata.TryGetValue(metadataName, out var result))
47 | {
48 | if (metadataName == "FullPath")
49 | {
50 | return ItemSpec;
51 | }
52 | }
53 |
54 | return result ?? "";
55 | }
56 |
57 | public string GetMetadataValueEscaped(string metadataName)
58 | {
59 | throw new NotImplementedException();
60 | }
61 |
62 | public void RemoveMetadata(string metadataName)
63 | {
64 | throw new NotImplementedException();
65 | }
66 |
67 | public void SetMetadata(string metadataName, string metadataValue)
68 | {
69 | Metadata[metadataName] = metadataValue;
70 | }
71 |
72 | public void SetMetadataValueLiteral(string metadataName, string metadataValue)
73 | {
74 | SetMetadata(metadataName, metadataValue);
75 | }
76 |
77 | public override string ToString() => ItemSpec;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/CopyTask.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace Microsoft.Build.Logging.StructuredLogger
6 | {
7 | public class CopyTask : Task
8 | {
9 | //public override string TypeName => nameof(CopyTask);
10 |
11 | private IEnumerable fileCopyOperations;
12 | public IEnumerable FileCopyOperations => fileCopyOperations ??= GetFileCopyOperations();
13 |
14 | protected virtual IEnumerable GetFileCopyOperations()
15 | {
16 | if (!HasChildren)
17 | {
18 | return Enumerable.Empty();
19 | }
20 |
21 | List list = new List();
22 |
23 | Match match;
24 | foreach (var message in this.GetMessages())
25 | {
26 | var text = message.Text;
27 |
28 | match = Strings.CopyingFileFromRegex.Match(text);
29 | if (match.Success && match.Groups.Count > 2)
30 | {
31 | var operation = ParseCopyingFileFrom(match);
32 | operation.Node = message;
33 | list.Add(operation);
34 | continue;
35 | }
36 |
37 | match = Strings.DidNotCopyRegex.Match(text);
38 | if (match.Success && match.Groups.Count > 2)
39 | {
40 | var operation = ParseCopyingFileFrom(match, copied: false);
41 | operation.Node = message;
42 | list.Add(operation);
43 | continue;
44 | }
45 |
46 | match = Strings.CreatingHardLinkRegex.Match(text);
47 | if (match.Success && match.Groups.Count > 2)
48 | {
49 | var operation = ParseCopyingFileFrom(match);
50 | operation.Node = message;
51 | list.Add(operation);
52 | continue;
53 | }
54 | }
55 |
56 | return list;
57 | }
58 |
59 | protected static FileCopyOperation ParseCopyingFileFrom(Match match, bool copied = true) => new FileCopyOperation
60 | {
61 | Source = match.Groups["From"].Value,
62 | Destination = match.Groups["To"].Value,
63 | Copied = copied
64 | };
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/CscTask.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class CscTask : ManagedCompilerTask
4 | {
5 | //public override string TypeName => nameof(CscTask);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/FileCopyOperation.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class FileCopyOperation
4 | {
5 | public string Source { get; set; }
6 | public string Destination { get; set; }
7 |
8 | ///
9 | /// We need to represent both "Copied" and "Did not copy" cases
10 | ///
11 | public bool Copied { get; set; }
12 |
13 | ///
14 | /// Node in the tree where the copy operation happened
15 | /// (usually the Message under a Copy task)
16 | ///
17 | public TreeNode Node { get; set; }
18 |
19 | public override string ToString()
20 | {
21 | return $"{Source} ➔ {Destination}";
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/FscTask.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class FscTask : ManagedCompilerTask
4 | {
5 | //public override string TypeName => nameof(FscTask);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/ManagedCompilerTask.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class ManagedCompilerTask : Task
4 | {
5 | //public override string TypeName => nameof(ManagedCompilerTask);
6 |
7 | private CompilationWrites? compilationWrites;
8 | public CompilationWrites? CompilationWrites
9 | {
10 | get
11 | {
12 | if (!HasChildren)
13 | {
14 | return null;
15 | }
16 |
17 | if (!compilationWrites.HasValue)
18 | {
19 | compilationWrites = Logging.StructuredLogger.CompilationWrites.TryParse(this);
20 | if (compilationWrites == null)
21 | {
22 | return null;
23 | }
24 | }
25 |
26 | return compilationWrites.Value;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/ResolveAssemblyReferenceTask.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class ResolveAssemblyReferenceTask : Task
4 | {
5 | //public override string TypeName => nameof(ResolveAssemblyReferenceTask);
6 |
7 | public Folder Inputs { get; set; }
8 | public Folder Results { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/RobocopyTask.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace Microsoft.Build.Logging.StructuredLogger
6 | {
7 | public class RobocopyTask : CopyTask
8 | {
9 | //public override string TypeName => nameof(RobocopyTask);
10 |
11 | protected override IEnumerable GetFileCopyOperations()
12 | {
13 | if (!HasChildren)
14 | {
15 | return Enumerable.Empty();
16 | }
17 |
18 | List list = new List();
19 |
20 | Match match;
21 | foreach (var message in GetMessages())
22 | {
23 | var text = message.Text;
24 |
25 | match = Strings.RobocopyFileCopiedRegex.Match(text);
26 | if (match.Success && match.Groups.Count > 2)
27 | {
28 | var operation = ParseCopyingFileFrom(match);
29 | operation.Node = message;
30 | list.Add(operation);
31 | continue;
32 | }
33 |
34 | match = Strings.RobocopyCreatedCopyOnWriteLinkRegex.Match(text);
35 | if (match.Success && match.Groups.Count > 2)
36 | {
37 | var operation = ParseCopyingFileFrom(match);
38 | operation.Node = message;
39 | list.Add(operation);
40 | continue;
41 | }
42 |
43 | match = Strings.RobocopyFileSkippedRegex.Match(text);
44 | if (match.Success && match.Groups.Count > 2)
45 | {
46 | var operation = ParseCopyingFileFrom(match, copied: false);
47 | operation.Node = message;
48 | list.Add(operation);
49 | continue;
50 | }
51 |
52 | match = Strings.RobocopyFileSkippedAsDuplicateRegex.Match(text);
53 | if (match.Success && match.Groups.Count > 2)
54 | {
55 | var operation = ParseCopyingFileFrom(match, copied: false);
56 | operation.Node = message;
57 | list.Add(operation);
58 | continue;
59 | }
60 |
61 | match = Strings.RobocopyFileFailedRegex.Match(text);
62 | if (match.Success && match.Groups.Count > 2)
63 | {
64 | var operation = ParseCopyingFileFrom(match, copied: false);
65 | operation.Node = message;
66 | list.Add(operation);
67 | continue;
68 | }
69 | }
70 |
71 | return list;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Tasks/VbcTask.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class VbcTask : ManagedCompilerTask
4 | {
5 | //public override string TypeName => nameof(VbcTask);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/TextNode.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class TextNode : TreeNode
4 | {
5 | public string Text { get; set; }
6 | public string ShortenedText => TextUtilities.ShortenValue(Text);
7 | public bool IsTextShortened => Text != null && Text.Length != TextUtilities.GetShortenLength(Text);
8 |
9 | public override string TypeName => nameof(TextNode);
10 | public override string Title => Text ?? TypeName;
11 |
12 | public override string ToString()
13 | {
14 | return Title;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/TimedNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class TimedNode : NamedNode
6 | {
7 | ///
8 | /// The Id of a Project, ProjectEvaluation, Target and Task.
9 | /// Corresponds to ProjectStartedEventsArgs.ProjectId, TargetStartedEventArgs.TargetId, etc.
10 | ///
11 | public int Id { get; set; }
12 |
13 | ///
14 | /// Corresponds to BuildEventArgs.BuildEventContext.NodeId,
15 | /// which is the id of the MSBuild.exe node process that built the current project or
16 | /// executed the given target or task.
17 | ///
18 | public int NodeId { get; set; }
19 |
20 | ///
21 | /// Unique index of the node in the build tree, can be used as a
22 | /// "URL" to node
23 | ///
24 | public int Index { get; set; }
25 |
26 | public DateTime StartTime { get; set; }
27 | public DateTime EndTime { get; set; }
28 | public TimeSpan Duration
29 | {
30 | get
31 | {
32 | if (EndTime >= StartTime)
33 | {
34 | return EndTime - StartTime;
35 | }
36 |
37 | return TimeSpan.Zero;
38 | }
39 | }
40 |
41 | public string DurationText => TextUtilities.DisplayDuration(Duration);
42 |
43 | public override string TypeName => nameof(TimedNode);
44 |
45 | public string GetTimeAndDurationText(bool fullPrecision = false)
46 | {
47 | var duration = DurationText;
48 | if (string.IsNullOrEmpty(duration))
49 | {
50 | duration = "0";
51 | }
52 |
53 | return $@"Start: {TextUtilities.Display(StartTime, displayDate: true, fullPrecision)}
54 | End: {TextUtilities.Display(EndTime, displayDate: true, fullPrecision)}
55 | Duration: {duration}";
56 | }
57 |
58 | public override string ToolTip => GetTimeAndDurationText();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/StructuredLogger/ObjectModel/Warning.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class Warning : AbstractDiagnostic
4 | {
5 | public override string TypeName => nameof(Warning);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/StructuredLogger/PlatformUtilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger;
5 |
6 | public class PlatformUtilities
7 | {
8 | public static bool HasThreads => !_isBrowser && !_isWasi;
9 |
10 | public static bool HasTempStorage => !_isWasi;
11 | public static bool HasColor => !_isWasi;
12 |
13 | private static readonly bool _isBrowser = RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
14 | private static readonly bool _isWasi = RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI"));
15 | }
16 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Progress.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public class Progress : IProgress
7 | {
8 | public virtual CancellationToken CancellationToken { get; set; } = CancellationToken.None;
9 |
10 | public event Action Updated;
11 |
12 | public virtual void Report(double ratio)
13 | {
14 | Report(new ProgressUpdate { Ratio = ratio });
15 | }
16 |
17 | public virtual void Report(ProgressUpdate progressUpdate)
18 | {
19 | Updated?.Invoke(progressUpdate);
20 | }
21 | }
22 |
23 | public struct ProgressUpdate
24 | {
25 | public double Ratio;
26 | public int BufferLength;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "StructuredLogger": {
4 | "commandName": "Executable",
5 | "executablePath": "$(MSBuildToolsPath)\\MSBuild.exe",
6 | "commandLineArgs": "$(SolutionDir)src\\StructuredLogger.Tests\\StructuredLogger.Tests.csproj /t:Rebuild /v:diag /noconlog /logger:StructuredLogger,$(TargetPath);test.buildlog",
7 | "workingDirectory": "$(SolutionDir)"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/StructuredLogger/ReaderSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | ///
4 | /// Settings for the .
5 | ///
6 | public class ReaderSettings
7 | {
8 | public static ReaderSettings Default { get; } =
9 | new() { UnknownDataBehavior = UnknownDataBehavior.Warning };
10 |
11 | ///
12 | /// Indication of how the unknown data in future versions of binlogs should be handled.
13 | ///
14 | public UnknownDataBehavior UnknownDataBehavior { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Search/HighlightedText.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public class HighlightedText
4 | {
5 | public string Text { get; set; }
6 | public string Style { get; set; }
7 |
8 | public override string ToString() => Text;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Search/ISearchExtension.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using StructuredLogViewer;
3 |
4 | namespace Microsoft.Build.Logging.StructuredLogger
5 | {
6 | public interface ISearchExtension
7 | {
8 | bool AugmentOtherResults { get; }
9 | bool TryGetResults(NodeQueryMatcher nodeQueryMatcher, IList resultCollector, int maxResults);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/StructuredLogger/Search/SearchResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Build.Logging.StructuredLogger;
4 |
5 | namespace StructuredLogViewer
6 | {
7 | public class SearchResult
8 | {
9 | public BaseNode Node { get; }
10 | public List<(string field, string match)> WordsInFields = new List<(string, string)>();
11 |
12 | public IList FieldsToDisplay { get; set; }
13 |
14 | public bool MatchedByType { get; private set; }
15 | public TimeSpan Duration { get; set; }
16 | public DateTime StartTime { get; set; }
17 | public DateTime EndTime { get; set; }
18 |
19 | public string RootFolder { get; set; }
20 |
21 | public FileCopyInfo AssociatedFileCopy { get; set; }
22 |
23 | public static SearchResult EmptyQueryMatch { get; } = new SearchResult();
24 |
25 | public SearchResult()
26 | {
27 | }
28 |
29 | public SearchResult(BaseNode node, bool includeDuration = false, bool includeStart = false, bool includeEnd = false)
30 | {
31 | Node = node;
32 |
33 | if (node is TimedNode timedNode)
34 | {
35 | if (includeDuration)
36 | {
37 | Duration = timedNode.Duration;
38 | }
39 |
40 | if (includeStart)
41 | {
42 | StartTime = timedNode.StartTime;
43 | }
44 |
45 | if (includeEnd)
46 | {
47 | EndTime = timedNode.EndTime;
48 | }
49 | }
50 | }
51 |
52 | public void AddMatch(string field, string word, bool addAtBeginning = false)
53 | {
54 | if (addAtBeginning)
55 | {
56 | WordsInFields.Insert(0, (field, word));
57 | }
58 | else
59 | {
60 | WordsInFields.Add((field, word));
61 | }
62 | }
63 |
64 | public void AddMatchByNodeType()
65 | {
66 | MatchedByType = true;
67 | }
68 |
69 | public override string ToString()
70 | {
71 | return Node?.ToString();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Serialization/AttributeNames.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | public enum AttributeNames
4 | {
5 | Name,
6 | IsLowRelevance,
7 | Text,
8 | ProjectFile,
9 | DependsOnTargets,
10 | FromAssembly,
11 | CommandLineArguments,
12 | Code,
13 | File,
14 | LineNumber,
15 | ColumnNumber,
16 | EndLineNumber,
17 | EndColumnNumber,
18 | StartTime,
19 | EndTime,
20 | Succeeded,
21 | Timestamp,
22 | IsAnalyzed,
23 | NodeId
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Serialization/Binary/BetterBinaryReader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class BetterBinaryReader : BinaryReader
6 | {
7 | public BetterBinaryReader(Stream input) : base(input)
8 | {
9 | }
10 |
11 | public override int ReadInt32()
12 | {
13 | return Read7BitEncodedInt();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Serialization/Binary/BetterBinaryWriter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class BetterBinaryWriter : BinaryWriter
6 | {
7 | public BetterBinaryWriter(Stream output) : base(output)
8 | {
9 | }
10 |
11 | public override void Write(int value)
12 | {
13 | Write7BitEncodedInt(value);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Serialization/StringWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public class StringWriter
6 | {
7 | public static int MaxStringLength = 100_000_000;
8 |
9 | public static string GetString(BaseNode rootNode)
10 | {
11 | var sb = new StringBuilder();
12 |
13 | WriteNode(rootNode, sb, 0);
14 |
15 | return sb.ToString();
16 | }
17 |
18 | private static void WriteNode(BaseNode node, StringBuilder sb, int indent = 0)
19 | {
20 | if (node == null)
21 | {
22 | return;
23 | }
24 |
25 | if (sb.Length > MaxStringLength)
26 | {
27 | return;
28 | }
29 |
30 | Indent(sb, indent);
31 |
32 | var text = node.GetFullText();
33 |
34 | sb.AppendLine(text);
35 |
36 | var treeNode = node as TreeNode;
37 | if (treeNode != null && treeNode.HasChildren)
38 | {
39 | foreach (var child in treeNode.Children)
40 | {
41 | WriteNode(child, sb, indent + 1);
42 | }
43 | }
44 | }
45 |
46 | private static void Indent(StringBuilder sb, int indent)
47 | {
48 | for (int i = 0; i < indent * 4; i++)
49 | {
50 | sb.Append(' ');
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Span.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Build.Logging.StructuredLogger
4 | {
5 | public struct Span
6 | {
7 | public int Start;
8 | public int Length;
9 | public int End => Start + Length;
10 |
11 | public static readonly Span Empty = new Span();
12 |
13 | public Span(int start, int length) : this()
14 | {
15 | Start = start;
16 | Length = length;
17 | }
18 |
19 | public override string ToString()
20 | {
21 | return $"({Start}, {Length})";
22 | }
23 |
24 | public Span Skip(int length)
25 | {
26 | if (length > Length)
27 | {
28 | return new Span();
29 | }
30 |
31 | return new Span(Start + length, Length - length);
32 | }
33 |
34 | public bool ContainsEndInclusive(int position)
35 | {
36 | return position >= Start && position <= End;
37 | }
38 |
39 | public bool Contains(int position)
40 | {
41 | return position >= Start && position < End;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Strings/ResourcesCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using ResourcesDictionary = System.Collections.Generic.Dictionary>;
4 | #if NET8_0_OR_GREATER
5 | using System.Text.Json;
6 | using System.Text.Json.Serialization;
7 | #endif
8 |
9 | namespace Microsoft.Build.Logging.StructuredLogger
10 | {
11 | public partial class StringsSet
12 | {
13 | private Dictionary currentSet;
14 | public string Culture { get; set; }
15 |
16 | public StringsSet(string culture)
17 | {
18 | Culture = culture;
19 | currentSet = ResourcesCollection[culture];
20 | }
21 |
22 | private static readonly object lockObject = new object();
23 |
24 | private static ResourcesDictionary resourcesCollection;
25 | public static ResourcesDictionary ResourcesCollection
26 | {
27 | get
28 | {
29 | if (resourcesCollection == null)
30 | {
31 | lock (lockObject)
32 | {
33 | if (resourcesCollection == null)
34 | {
35 | var assembly = typeof(StructuredLogger).Assembly;
36 | using var stream = assembly.GetManifestResourceStream(@"Strings.json");
37 | #if NET8_0_OR_GREATER
38 | resourcesCollection = JsonSerializer.Deserialize(stream, JsonSourceGenerationContext.Default.ResourcesDictionary);
39 | #else
40 | using var reader = new StreamReader(stream);
41 | var text = reader.ReadToEnd();
42 | resourcesCollection = TinyJson.JSONParser.FromJson(text);
43 | #endif
44 | }
45 | }
46 | }
47 |
48 | return resourcesCollection;
49 | }
50 | }
51 |
52 | public string GetString(string key)
53 | {
54 | if (currentSet.TryGetValue(key, out var value))
55 | {
56 | return value;
57 | }
58 |
59 | return string.Empty;
60 | }
61 |
62 | #if NET8_0_OR_GREATER
63 | [JsonSerializable(typeof(ResourcesDictionary), TypeInfoPropertyName = "ResourcesDictionary")]
64 | partial class JsonSourceGenerationContext : JsonSerializerContext
65 | {
66 | }
67 | #endif
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/StructuredLogger/Strings/readme.txt:
--------------------------------------------------------------------------------
1 | Strings.json is a generated file containing all localized resource strings from MSBuild that we want to use.
2 |
3 | To regenerate Strings.json, add the resource string ids you need to ResourcesGenerator project -> ResourceCreator.ResourceNames,
4 | then run the ResourcesGenerator tool.
--------------------------------------------------------------------------------
/src/StructuredLogger/UnknownDataBehavior.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.Build.Logging.StructuredLogger
2 | {
3 | ///
4 | /// Indication of how the unknown data in future versions of binlogs should be handled.
5 | ///
6 | public enum UnknownDataBehavior
7 | {
8 | ///
9 | /// When unknown data encountered - emit a single synthetic error message for the entire build.
10 | ///
11 | Error,
12 |
13 | ///
14 | /// When unknown data encountered - emit a single synthetic warning message for the entire build.
15 | ///
16 | Warning,
17 |
18 | ///
19 | /// When unknown data encountered - emit a single synthetic message for the entire build.
20 | ///
21 | Message,
22 |
23 | ///
24 | /// Ignore the unknown data and continue reading the rest of the build.
25 | ///
26 | Ignore,
27 |
28 | ///
29 | /// Throw an exception when unknown data is encountered.
30 | ///
31 | ThrowException
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/StructuredLogger/key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogger/key.snk
--------------------------------------------------------------------------------
/src/TaskRunner/AppDomainInitializer.cs:
--------------------------------------------------------------------------------
1 | namespace TaskRunner
2 | {
3 | public class AppDomainInitializer
4 | {
5 | ///
6 | /// This is called everytime a new appdomain is created in the current process.
7 | ///
8 | public AppDomainInitializer()
9 | {
10 | Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/TaskRunner/MissingPropertyException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TaskRunner
4 | {
5 | public class MissingPropertyException : MissingMemberException
6 | {
7 | public MissingPropertyException(string className, string propertyName) : base(className, propertyName)
8 | {
9 | }
10 |
11 | public string MSBuildVersion { get; set; }
12 |
13 | public override string Message
14 | => $"Property {MemberName} was not found on type {ClassName}. " +
15 | $"This probably means that the task being run was recorded with a newer version of MSBuild ({MSBuildVersion ?? "N/A"}) " +
16 | $"than the version used by MSBuild Structured Log Viewer ({Microsoft.Build.Evaluation.ProjectCollection.Version}).";
17 | }
18 | }
--------------------------------------------------------------------------------
/src/TaskRunner/TaskRunner.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net472;net8.0
6 | app.manifest
7 |
8 |
9 |
10 | true
11 |
12 |
13 |
14 | false
15 | LatestMajor
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/TaskRunner/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | amd64 arm64
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Tests/1.proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Prop1Value
7 | Prop2Value
8 | Prop1ValueReassigned
9 |
10 |
11 |
12 |
13 | Metadata1Value
14 |
15 |
16 |
17 |
18 |
19 | Metadata2Value
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | PropInTargetValue
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Metadata3Value
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/Tests/2.proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
3 | "version": "2.2",
4 | "publicReleaseRefSpec": [
5 | "^refs/heads/main$"
6 | ],
7 | "cloudBuild": {
8 | "buildNumber": {
9 | "enabled": true
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------