Skip to content

Commit 7b75a30

Browse files
committed
C#: Add framework detection to the assets.json parser.
1 parent 20b31d0 commit 7b75a30

File tree

3 files changed

+89
-29
lines changed

3 files changed

+89
-29
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Assets.cs

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ private record class ReferenceInfo(string? Type, Dictionary<string, object>? Com
6868
/// }
6969
/// }
7070
///
71-
/// Returns dependencies
72-
/// RequiredPaths = {
71+
/// Adds the following dependencies
72+
/// Paths: {
7373
/// "castle.core/4.4.1/lib/netstandard1.5/Castle.Core.dll",
7474
/// "json.net/1.0.33/lib/netstandard2.0/Json.Net.dll"
7575
/// }
76-
/// UsedPackages = {
76+
/// Packages: {
7777
/// "castle.core",
7878
/// "json.net"
7979
/// }
8080
/// </summary>
81-
private DependencyContainer AddPackageDependencies(JObject json, DependencyContainer dependencies)
81+
private void AddPackageDependencies(JObject json, DependencyContainer dependencies)
8282
{
83-
// If there are more than one framework we need to pick just one.
83+
// If there is more than one framework we need to pick just one.
8484
// To ensure stability we pick one based on the lexicographic order of
8585
// the framework names.
8686
var references = json
@@ -93,7 +93,7 @@ private DependencyContainer AddPackageDependencies(JObject json, DependencyConta
9393
if (references is null)
9494
{
9595
progressMonitor.LogDebug("No references found in the targets section in the assets file.");
96-
return dependencies;
96+
return;
9797
}
9898

9999
// Find all the compile dependencies for each reference and
@@ -111,7 +111,7 @@ private DependencyContainer AddPackageDependencies(JObject json, DependencyConta
111111
// If this is a .NET framework reference then include everything.
112112
if (netFrameworks.Any(framework => name.StartsWith(framework)))
113113
{
114-
dependencies.Add(name);
114+
dependencies.AddFramework(name);
115115
}
116116
else
117117
{
@@ -120,7 +120,69 @@ private DependencyContainer AddPackageDependencies(JObject json, DependencyConta
120120
}
121121
});
122122

123-
return dependencies;
123+
return;
124+
}
125+
126+
/// <summary>
127+
/// Add the framework dependencies from the assets file to dependencies.
128+
///
129+
/// Example:
130+
/// "project": {
131+
// "version": "1.0.0",
132+
// "frameworks": {
133+
// "net7.0": {
134+
// "frameworkReferences": {
135+
// "Microsoft.AspNetCore.App": {
136+
// "privateAssets": "none"
137+
// },
138+
// "Microsoft.NETCore.App": {
139+
// "privateAssets": "all"
140+
// }
141+
// }
142+
// }
143+
// }
144+
// }
145+
//
146+
/// Adds the following dependencies
147+
/// Paths: {
148+
/// "microsoft.aspnetcore.app.ref",
149+
/// "microsoft.netcore.app.ref"
150+
/// }
151+
/// Packages: {
152+
/// "microsoft.aspnetcore.app.ref",
153+
/// "microsoft.netcore.app.ref"
154+
/// }
155+
/// </summary>
156+
private void AddFrameworkDependencies(JObject json, DependencyContainer dependencies)
157+
{
158+
159+
var frameworks = json
160+
.GetProperty("project")?
161+
.GetProperty("frameworks");
162+
163+
if (frameworks is null)
164+
{
165+
progressMonitor.LogDebug("No framework section in assets.json.");
166+
return;
167+
}
168+
169+
// If there is more than one framework we need to pick just one.
170+
// To ensure stability we pick one based on the lexicographic order of
171+
// the framework names.
172+
var references = frameworks
173+
.Properties()?
174+
.MaxBy(p => p.Name)?
175+
.Value["frameworkReferences"] as JObject;
176+
177+
if (references is null)
178+
{
179+
progressMonitor.LogDebug("No framework references in assets.json.");
180+
return;
181+
}
182+
183+
references
184+
.Properties()
185+
.ForEach(f => dependencies.AddFramework($"{f.Name}.Ref".ToLowerInvariant()));
124186
}
125187

126188
/// <summary>
@@ -134,6 +196,7 @@ public bool TryParse(string json, DependencyContainer dependencies)
134196
{
135197
var obj = JObject.Parse(json);
136198
AddPackageDependencies(obj, dependencies);
199+
AddFrameworkDependencies(obj, dependencies);
137200
return true;
138201
}
139202
catch (Exception e)

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyContainer.cs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
99
/// </summary>
1010
internal class DependencyContainer
1111
{
12-
private readonly List<string> requiredPaths = new();
13-
private readonly HashSet<string> usedPackages = new();
12+
/// <summary>
13+
/// Paths to dependencies required for compilation.
14+
/// </summary>
15+
public List<string> Paths { get; } = new();
16+
17+
/// <summary>
18+
/// Packages that are used as a part of the required dependencies.
19+
/// </summary>
20+
public HashSet<string> Packages { get; } = new();
1421

1522
/// <summary>
1623
/// In most cases paths in asset files point to dll's or the empty _._ file, which
@@ -32,16 +39,6 @@ private static string GetPackageName(string package) =>
3239
.Split(Path.DirectorySeparatorChar)
3340
.First();
3441

35-
/// <summary>
36-
/// Paths to dependencies required for compilation.
37-
/// </summary>
38-
public IEnumerable<string> RequiredPaths => requiredPaths;
39-
40-
/// <summary>
41-
/// Packages that are used as a part of the required dependencies.
42-
/// </summary>
43-
public HashSet<string> UsedPackages => usedPackages;
44-
4542
/// <summary>
4643
/// Add a dependency inside a package.
4744
/// </summary>
@@ -51,19 +48,19 @@ public void Add(string package, string dependency)
5148
var d = dependency.Replace('/', Path.DirectorySeparatorChar);
5249

5350
var path = Path.Combine(p, ParseFilePath(d));
54-
requiredPaths.Add(path);
55-
usedPackages.Add(GetPackageName(p));
51+
Paths.Add(path);
52+
Packages.Add(GetPackageName(p));
5653
}
5754

5855
/// <summary>
59-
/// Add a dependency to an entire package
56+
/// Add a dependency to an entire framework package.
6057
/// </summary>
61-
public void Add(string package)
58+
public void AddFramework(string framework)
6259
{
63-
var p = package.Replace('/', Path.DirectorySeparatorChar);
60+
var p = framework.Replace('/', Path.DirectorySeparatorChar);
6461

65-
requiredPaths.Add(p);
66-
usedPackages.Add(GetPackageName(p));
62+
Paths.Add(p);
63+
Packages.Add(GetPackageName(p));
6764
}
6865
}
6966
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
119119
var dependencies = Assets.GetCompilationDependencies(progressMonitor, assets1.Union(assets2));
120120

121121
var paths = dependencies
122-
.RequiredPaths
122+
.Paths
123123
.Select(d => Path.Combine(packageDirectory.DirInfo.FullName, d))
124124
.ToList();
125125
dllPaths.UnionWith(paths);
@@ -356,7 +356,7 @@ private IEnumerable<string> GetAllPackageDirectories()
356356

357357
private void LogAllUnusedPackages(DependencyContainer dependencies) =>
358358
GetAllPackageDirectories()
359-
.Where(package => !dependencies.UsedPackages.Contains(package))
359+
.Where(package => !dependencies.Packages.Contains(package))
360360
.ForEach(package => progressMonitor.LogInfo($"Unused package: {package}"));
361361

362362
private void GenerateSourceFileFromImplicitUsings()

0 commit comments

Comments
 (0)