├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── AnalyzeDotNetProject.csproj ├── AnalyzeDotNetProject.sln ├── DependencyGraphService.cs ├── DotNetRunner.cs ├── LICENSE ├── LockFileService.cs ├── Program.cs └── RunStatus.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | .vscode/* 332 | !.vscode/settings.json 333 | !.vscode/tasks.json 334 | !.vscode/launch.json 335 | !.vscode/extensions.json 336 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": ".NET Core Launch (console)", 10 | "type": "coreclr", 11 | "request": "launch", 12 | "preLaunchTask": "build", 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.1/AnalyzeDotNetProject.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "console": "internalConsole", 17 | "stopAtEntry": false, 18 | "internalConsoleOptions": "openOnSessionStart" 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AnalyzeDotNetProject.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /AnalyzeDotNetProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /AnalyzeDotNetProject.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnalyzeDotNetProject", "AnalyzeDotNetProject.csproj", "{71E93958-4D00-405B-A3A8-E3C999FCF478}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {71E93958-4D00-405B-A3A8-E3C999FCF478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {71E93958-4D00-405B-A3A8-E3C999FCF478}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {71E93958-4D00-405B-A3A8-E3C999FCF478}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {71E93958-4D00-405B-A3A8-E3C999FCF478}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B9342362-98E1-4CD7-8250-104A96E314CE} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DependencyGraphService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using NuGet.ProjectModel; 6 | 7 | namespace AnalyzeDotNetProject 8 | { 9 | /// 10 | /// Credit for the stuff happening in here goes to the https://github.com/jaredcnance/dotnet-status project 11 | /// 12 | public class DependencyGraphService 13 | { 14 | public DependencyGraphSpec GenerateDependencyGraph(string projectPath) 15 | { 16 | var dotNetRunner = new DotNetRunner(); 17 | 18 | string dgOutput = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); 19 | 20 | string[] arguments = new[] {"msbuild", $"\"{projectPath}\"", "/t:GenerateRestoreGraphFile", $"/p:RestoreGraphOutputPath={dgOutput}"}; 21 | 22 | var runStatus = dotNetRunner.Run(Path.GetDirectoryName(projectPath), arguments); 23 | 24 | if (runStatus.IsSuccess) 25 | { 26 | string dependencyGraphText = File.ReadAllText(dgOutput); 27 | return new DependencyGraphSpec(JsonConvert.DeserializeObject(dependencyGraphText)); 28 | } 29 | else 30 | { 31 | throw new Exception($"Unable to process the the project `{projectPath}. Are you sure this is a valid .NET Core or .NET Standard project type?" + 32 | $"\r\n\r\nHere is the full error message returned from the Microsoft Build Engine:\r\n\r\n" + runStatus.Output); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /DotNetRunner.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace AnalyzeDotNetProject 7 | { 8 | /// 9 | /// Credit for the stuff happening in here goes to the https://github.com/jaredcnance/dotnet-status project 10 | /// 11 | public class DotNetRunner 12 | { 13 | public RunStatus Run(string workingDirectory, string[] arguments) 14 | { 15 | var psi = new ProcessStartInfo("dotnet", string.Join(" ", arguments)) 16 | { 17 | WorkingDirectory = workingDirectory, 18 | UseShellExecute = false, 19 | CreateNoWindow = true, 20 | RedirectStandardOutput = true, 21 | RedirectStandardError = true 22 | }; 23 | 24 | var p = new Process(); 25 | try 26 | { 27 | p.StartInfo = psi; 28 | p.Start(); 29 | 30 | var output = new StringBuilder(); 31 | var errors = new StringBuilder(); 32 | var outputTask = ConsumeStreamReaderAsync(p.StandardOutput, output); 33 | var errorTask = ConsumeStreamReaderAsync(p.StandardError, errors); 34 | 35 | var processExited = p.WaitForExit(20000); 36 | 37 | if (processExited == false) 38 | { 39 | p.Kill(); 40 | 41 | return new RunStatus(output.ToString(), errors.ToString(), exitCode: -1); 42 | } 43 | 44 | Task.WaitAll(outputTask, errorTask); 45 | 46 | return new RunStatus(output.ToString(), errors.ToString(), p.ExitCode); 47 | } 48 | finally 49 | { 50 | p.Dispose(); 51 | } 52 | } 53 | 54 | private static async Task ConsumeStreamReaderAsync(StreamReader reader, StringBuilder lines) 55 | { 56 | await Task.Yield(); 57 | 58 | string line; 59 | while ((line = await reader.ReadLineAsync()) != null) 60 | { 61 | lines.AppendLine(line); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jerrie Pelser Blog 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 | -------------------------------------------------------------------------------- /LockFileService.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NuGet.ProjectModel; 3 | 4 | namespace AnalyzeDotNetProject 5 | { 6 | public class LockFileService 7 | { 8 | public LockFile GetLockFile(string projectPath, string outputPath) 9 | { 10 | // Run the restore command 11 | var dotNetRunner = new DotNetRunner(); 12 | string[] arguments = new[] {"restore", $"\"{projectPath}\""}; 13 | var runStatus = dotNetRunner.Run(Path.GetDirectoryName(projectPath), arguments); 14 | 15 | // Load the lock file 16 | string lockFilePath = Path.Combine(outputPath, "project.assets.json"); 17 | return LockFileUtilities.GetLockFile(lockFilePath, NuGet.Common.NullLogger.Instance); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using NuGet.ProjectModel; 4 | 5 | namespace AnalyzeDotNetProject 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | // Replace to point to your project or solution 12 | string projectPath = @"c:\development\jerriep\dotnet-outdated\DotNetOutdated.sln"; 13 | 14 | var dependencyGraphService = new DependencyGraphService(); 15 | var dependencyGraph = dependencyGraphService.GenerateDependencyGraph(projectPath); 16 | 17 | foreach(var project in dependencyGraph.Projects.Where(p => p.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference)) 18 | { 19 | // Generate lock file 20 | var lockFileService = new LockFileService(); 21 | var lockFile = lockFileService.GetLockFile(project.FilePath, project.RestoreMetadata.OutputPath); 22 | 23 | Console.WriteLine(project.Name); 24 | 25 | foreach(var targetFramework in project.TargetFrameworks) 26 | { 27 | Console.WriteLine($" [{targetFramework.FrameworkName}]"); 28 | 29 | var lockFileTargetFramework = lockFile.Targets.FirstOrDefault(t => t.TargetFramework.Equals(targetFramework.FrameworkName)); 30 | if (lockFileTargetFramework != null) 31 | { 32 | foreach(var dependency in targetFramework.Dependencies) 33 | { 34 | var projectLibrary = lockFileTargetFramework.Libraries.FirstOrDefault(library => library.Name == dependency.Name); 35 | 36 | ReportDependency(projectLibrary, lockFileTargetFramework, 1); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | private static void ReportDependency(LockFileTargetLibrary projectLibrary, LockFileTarget lockFileTargetFramework, int indentLevel) 44 | { 45 | Console.Write(new String(' ', indentLevel * 2)); 46 | Console.WriteLine($"{projectLibrary.Name}, v{projectLibrary.Version}"); 47 | 48 | foreach (var childDependency in projectLibrary.Dependencies) 49 | { 50 | var childLibrary = lockFileTargetFramework.Libraries.FirstOrDefault(library => library.Name == childDependency.Id); 51 | 52 | ReportDependency(childLibrary, lockFileTargetFramework, indentLevel + 1); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RunStatus.cs: -------------------------------------------------------------------------------- 1 | namespace AnalyzeDotNetProject 2 | { 3 | public class RunStatus 4 | { 5 | public string Output { get; } 6 | public string Errors { get; } 7 | public int ExitCode { get; } 8 | 9 | public bool IsSuccess => ExitCode == 0; 10 | 11 | public RunStatus(string output, string errors, int exitCode) 12 | { 13 | Output = output; 14 | Errors = errors; 15 | ExitCode = exitCode; 16 | } 17 | 18 | } 19 | } --------------------------------------------------------------------------------