├── .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 | }
--------------------------------------------------------------------------------