-
Notifications
You must be signed in to change notification settings - Fork 1.8k
C#: Use project.assets.json for package dependencies.
#14655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C#: Use project.assets.json for package dependencies.
#14655
Conversation
c0deb0c to
415638f
Compare
|
@tamasvajk : Could you take a look at this PR? This is the re-write that only includes the package logic based on |
tamasvajk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I've added some comments.
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
Outdated
Show resolved
Hide resolved
csharp/ql/integration-tests/windows-only/standalone_dependencies/Assemblies.expected
Show resolved
Hide resolved
csharp/ql/integration-tests/posix-only/standalone_dependencies/Assemblies.expected
Outdated
Show resolved
Hide resolved
737732e to
ef52629
Compare
…ore (needed to identify where assets file are located).
… project or solution restore.
660748c to
8cb4fd0
Compare
8cb4fd0 to
31f602c
Compare
|
@tamasvajk : Ready for review. I will also trigger a DCA run. |
tamasvajk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, I added some comments.
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Dependencies.cs
Outdated
Show resolved
Hide resolved
| return references | ||
| .Aggregate(dependencies, (deps, r) => | ||
| { | ||
| var info = r.Value; | ||
| var name = r.Key.ToLowerInvariant(); | ||
| if (info.Type != "package") | ||
| { | ||
| return deps; | ||
| } | ||
|
|
||
| // If this is a .NET framework reference then include everything. | ||
| return netFrameworks.Any(framework => name.StartsWith(framework)) | ||
| ? deps.Add(name, "") | ||
| : info | ||
| .Compile | ||
| .Aggregate(deps, (d, p) => d.Add(name, p.Key)); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this hard to read; dependencies, deps, and d all refer to the same variable, don't they?
Is this code equivalent to the below? (I find the below a lot easier to read)
foreach (var r in references)
{
var info = r.Value;
var name = r.Key.ToLowerInvariant();
if (info.Type != "package")
{
continue;
}
// If this is a .NET framework reference then include everything.
if (netFrameworks.Any(name.StartsWith))
{
dependencies.Add(name, "");
continue;
}
// Otherwise only include the files mentioned in the compile section.
foreach (var p in info.Compile)
{
dependencies.Add(name, p.Key);
}
}
return dependencies;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that Aggregate is not the most suitable way of doing this (I usually think in terms of functional programming and aggregation is just "folding", but since the data is collected as a side effect it doesn't make sense to use aggregate).
I know, we have a bit different preference when comes to implementing loops and I will try and do this using the ForeEach extension method instead - then the implementation also become similar to your suggestion :-)
Or should I just cave in and start using the foreach more? :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should probably try to convince me that I should start using ForEach more. :-)
I think it doesn't matter much which variant is used, I prefer readability over shortness. Lambdas oftentimes help with readability, but I'm not (yet) convinced that this is the case with simple foreach loops. Also, with ForEach I could always argue that there's an unnecessary lambda allocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, I will try 😸
I like to avoid using continue and break statements in foreach loops - it doesn't take that many of those before the readability starts to deteriorate and they have a tendency to start popping up as the code becomes more complicated. In that aspect I also prefer the return statement instead of a continue statement, which is what we will get, if we use lambdas (this also makes it easier to re-factor the entire lambda out into a separate method, which encourages refactors when needed).
Furthermore, it is not possible to use break inside ForEach (ForEach means for each (unless an exception is thrown 😄 )). It is my impression that break usually means that we are searching for a specific element or collecting elements up until a specific condition or something similar and at the same time we and may or may not map these elements as well (in one go): I generally prefer to do filtering and then processing (if possible). The approach of doing lots of things at the same time also has a tendency to make the code more complicated in the long run.
In terms of performance, I agree that it is sometimes better to use the native C# foreach construct for performance reasons and I am sure the performance difference between foreach and ForEach is measurable if it is a loop that is executed 100k+ times. However, in most cases the difference in performance is completely insignificant.
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Assets.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Assets.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Dependencies.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Dependencies.cs
Outdated
Show resolved
Hide resolved
csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Assets.cs
Outdated
Show resolved
Hide resolved
tamasvajk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Let's wait for the DCA analysis to finish, but even the previous run shows significant reduction in the count of cs/compilation-error tuples. There are some projects where the compilation error count increased, these are good candidates for investigation for upcoming improvements.
In the second DCA run, the Merging now. |
In this PR we try to narrow which dependencies to include in standalone compilation.
Prior to this PR we took all dll's in all the packages that were downloaded as a part of the
dotnet restoreprocess and included them in the standalone compilation.As a result
We try to solve this issue by inspecting the content of all
project.assets.jsonfiles afterdotnet restores have been executed:targetssection we try to find all package references and only include the dll's that are required for compilation (under thecompiletag). If a package reference is to a set of .NET framework reference assemblies we still include the entire package folder (for that package).The added testcases contain examples of
project.assets.jsoncontent (some context to the above).Please note, that this work is based on reverse engineering (guessing) what the content of a
project.assets.jsonfile means.Furthermore, this means that we have changed the logic in the DependencyManager to be addition based instead of subtraction based (that is we add packages, if we find information in the assets file about them otherwise we don't add them).
Implementation notes
dotnet restoreprocess we included the relevant NuGet package (based on a hardcoded list of priorities). If none of these packages have been downloaded, we add the assemblies from the .NET installation directory instead.project.assets.jsonfile there are sometimes a reference to a file named_._. This is an empty fail that may or may not exist and is sometimes there due to issues with empty directories (according to https://stackoverflow.com/questions/36338052/what-do-files-mean-in-nuget-packages). In this case we just add the entire directory as a dependency.With these changes we reduce the number of extra dll's that is taken into the compilation for the standalone extractor compared to the tracing extractor.
Before
After