Skip to content

Commit 003166a

Browse files
committed
C#: Collect information about used dependencies in a Dependencies object.
1 parent eb228b6 commit 003166a

File tree

3 files changed

+98
-46
lines changed

3 files changed

+98
-46
lines changed

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

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,11 @@ internal class Assets
2121
"netstandard.library.ref"
2222
};
2323

24-
2524
internal Assets(ProgressMonitor progressMonitor)
2625
{
2726
this.progressMonitor = progressMonitor;
2827
}
2928

30-
/// <summary>
31-
/// In most cases paths in asset files point to dll's or the empty _._ file, which
32-
/// is sometimes there to avoid the directory being empty.
33-
/// That is, if the path specifically adds a .dll we use that, otherwise we as a fallback
34-
/// add the entire directory (which should be fine in case of _._ as well).
35-
/// </summary>
36-
private static string ParseFilePath(string path)
37-
{
38-
if (path.EndsWith(".dll"))
39-
{
40-
return path;
41-
}
42-
return Path.GetDirectoryName(path) ?? path;
43-
}
44-
4529
/// <summary>
4630
/// Class needed for deserializing parts of an assets file.
4731
/// It holds information about a reference.
@@ -62,10 +46,11 @@ private class ReferenceInfo
6246
}
6347

6448
/// <summary>
65-
/// Gets the package dependencies from the assets file.
49+
/// Add the package dependencies from the assets file to dependencies.
6650
///
67-
/// Parse a part of the JSon assets file and returns the paths
68-
/// to the dependencies required for compilation.
51+
/// Parse a part of the JSon assets file and add the paths
52+
/// to the dependencies required for compilation (and collect
53+
/// information about used packages).
6954
///
7055
/// Example:
7156
/// {
@@ -88,12 +73,17 @@ private class ReferenceInfo
8873
/// }
8974
/// }
9075
///
91-
/// Returns {
92-
/// "castle.core/4.4.1/lib/netstandard1.5/Castle.Core.dll",
93-
/// "json.net/1.0.33/lib/netstandard2.0/Json.Net.dll"
94-
/// }
76+
/// Returns dependencies
77+
/// Required = {
78+
/// "castle.core/4.4.1/lib/netstandard1.5/Castle.Core.dll",
79+
/// "json.net/1.0.33/lib/netstandard2.0/Json.Net.dll"
80+
/// }
81+
/// UsedPackages = {
82+
/// "castle.core",
83+
/// "json.net"
84+
/// }
9585
/// </summary>
96-
private IEnumerable<string> GetPackageDependencies(JObject json)
86+
private Dependencies AddPackageDependencies(JObject json, Dependencies dependencies)
9787
{
9888
// If there are more than one framework we need to pick just one.
9989
// To ensure stability we pick one based on the lexicographic order of
@@ -108,49 +98,41 @@ private IEnumerable<string> GetPackageDependencies(JObject json)
10898
if (references is null)
10999
{
110100
progressMonitor.LogDebug("No references found in the targets section in the assets file.");
111-
return Array.Empty<string>();
101+
return dependencies;
112102
}
113103

114104
// Find all the compile dependencies for each reference and
115105
// create the relative path to the dependency.
116-
var dependencies = references
117-
.SelectMany(r =>
106+
return references
107+
.Aggregate(dependencies, (deps, r) =>
118108
{
119109
var info = r.Value;
120110
var name = r.Key.ToLowerInvariant();
121111
if (info.Type != "package")
122112
{
123-
return Array.Empty<string>();
113+
return deps;
124114
}
125115

126116
// If this is a .NET framework reference then include everything.
127117
return netFrameworks.Any(framework => name.StartsWith(framework))
128-
? new[] { name }
118+
? deps.Add(name, "")
129119
: info
130120
.Compile
131-
.Select(p => Path.Combine(name, ParseFilePath(p.Key)));
132-
})
133-
.ToList();
134-
135-
return dependencies;
121+
.Aggregate(deps, (d, p) => d.Add(name, p.Key));
122+
});
136123
}
137124

138125
/// <summary>
139-
/// Parse `json` as project.assets.json content and populate `dependencies` with the
140-
/// relative paths to the dependencies required for compilation.
126+
/// Parse `json` as project.assets.json content and add relative paths to the dependencies
127+
/// (together with used package information) required for compilation.
141128
/// </summary>
142129
/// <returns>True if parsing succeeds, otherwise false.</returns>
143-
public bool TryParse(string json, out IEnumerable<string> dependencies)
130+
public bool TryParse(string json, Dependencies dependencies)
144131
{
145-
dependencies = Array.Empty<string>();
146-
147132
try
148133
{
149134
var obj = JObject.Parse(json);
150-
var packages = GetPackageDependencies(obj);
151-
152-
dependencies = packages.ToList();
153-
135+
AddPackageDependencies(obj, dependencies);
154136
return true;
155137
}
156138
catch (Exception e)
@@ -160,13 +142,14 @@ public bool TryParse(string json, out IEnumerable<string> dependencies)
160142
}
161143
}
162144

163-
public static IEnumerable<string> GetCompilationDependencies(ProgressMonitor progressMonitor, IEnumerable<string> assets)
145+
public static Dependencies GetCompilationDependencies(ProgressMonitor progressMonitor, IEnumerable<string> assets)
164146
{
165147
var parser = new Assets(progressMonitor);
166-
return assets.SelectMany(asset =>
148+
return assets.Aggregate(new Dependencies(), (dependencies, asset) =>
167149
{
168150
var json = File.ReadAllText(asset);
169-
return parser.TryParse(json, out var dependencies) ? dependencies : Array.Empty<string>();
151+
parser.TryParse(json, dependencies);
152+
return dependencies;
170153
});
171154
}
172155
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
5+
namespace Semmle.Extraction.CSharp.DependencyFetching
6+
{
7+
/// <summary>
8+
/// Container class for dependencies found in the assets file.
9+
/// </summary>
10+
internal class Dependencies
11+
{
12+
private readonly List<string> required = new();
13+
private readonly HashSet<string> usedPackages = new();
14+
15+
/// <summary>
16+
/// In most cases paths in asset files point to dll's or the empty _._ file, which
17+
/// is sometimes there to avoid the directory being empty.
18+
/// That is, if the path specifically adds a .dll we use that, otherwise we as a fallback
19+
/// add the entire directory (which should be fine in case of _._ as well).
20+
/// </summary>
21+
private static string ParseFilePath(string path)
22+
{
23+
if (path.EndsWith(".dll"))
24+
{
25+
return path;
26+
}
27+
return Path.GetDirectoryName(path) ?? path;
28+
}
29+
30+
private static string GetPackageName(string package) =>
31+
package
32+
.Split("/")
33+
.First();
34+
35+
/// <summary>
36+
/// Dependencies required for Compilation.
37+
/// </summary>
38+
public IEnumerable<string> Required => required;
39+
40+
/// <summary>
41+
/// Packages that are used as a part of the required dependencies.
42+
/// </summary>
43+
public IEnumerable<string> UsedPackages => usedPackages;
44+
45+
/// <summary>
46+
/// Add a dependency inside a package.
47+
/// </summary>
48+
public Dependencies Add(string package, string dependency)
49+
{
50+
var path = Path.Combine(package, ParseFilePath(dependency));
51+
required.Add(path);
52+
usedPackages.Add(GetPackageName(package));
53+
return this;
54+
}
55+
56+
/// <summary>
57+
/// Add a dependency to an entire package
58+
/// </summary>
59+
public Dependencies Add(string package)
60+
{
61+
required.Add(package);
62+
usedPackages.Add(GetPackageName(package));
63+
return this;
64+
}
65+
}
66+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,12 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
104104
var dependencies = Assets.GetCompilationDependencies(progressMonitor, assets1.Union(assets2));
105105

106106
var paths = dependencies
107+
.Required
107108
.Select(d => Path.Combine(packageDirectory.DirInfo.FullName, d))
108109
.ToList();
109110

111+
// TODO: Log all packages that are not used as a dependency.
112+
110113
dllPaths.AddRange(paths);
111114
DownloadMissingPackages(allNonBinaryFiles, dllPaths);
112115
}

0 commit comments

Comments
 (0)