Skip to content
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

Persisting and reading dg spec for unloaded or out of current solution projects #2521

Merged
merged 4 commits into from
Jan 8, 2019

Conversation

jainaashish
Copy link
Contributor

@jainaashish jainaashish commented Nov 6, 2018

Bug

Fixes: NuGet/Home#5820
Regression: Yes/No
If Regression then when did it last work:
If Regression then how are we preventing it in future:

Fix

Details: Currently restore inside VS fails when there is a unloaded project in solution or some projects are not part of current solution, even though those projects are already restored and up to date. So with this PR, we'll persist the dependency graph with each restore for every project in the build intermediate output directory (/obj) and use that persisted graph to complete restore when there are some missing projects.

Spec - https://github.com/NuGet/Home/wiki/Allow-restore-to-succeed-for-unloaded-projects-in-Visual-Studio

Testing/Validation

Tests Added: Yes
Reason for not adding tests:
Validation done:

@jainaashish jainaashish changed the title [wip] persisting and reading dg spec for unloaded or out of current solution projects [poc] persisting and reading dg spec for unloaded or out of current solution projects Nov 21, 2018
@jainaashish jainaashish force-pushed the dev-asja-PersistDGSpec branch from 7597d58 to d3dc97e Compare December 6, 2018 06:28
@jainaashish jainaashish force-pushed the dev-asja-PersistDGSpec branch from d3dc97e to f5a9391 Compare December 19, 2018 18:48
@jainaashish jainaashish changed the title [poc] persisting and reading dg spec for unloaded or out of current solution projects Persisting and reading dg spec for unloaded or out of current solution projects Dec 20, 2018
@jainaashish jainaashish requested a review from dtivel January 4, 2019 17:33
@jainaashish
Copy link
Contributor Author

@NuGet/nuget-client 🔔

@@ -14,6 +14,8 @@ namespace NuGet.ProjectModel
{
public class DependencyGraphSpec
{
public static readonly string DGSpecFileName = "{0}.nuget.dgspec.json";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the file name. It's a format string. Instead of exposing this as a static readonly field (or better yet const) and requiring callers to format it, how about moving this format string inside a static method:

public static string GetDgSpecFileName(string projectName)
{
    ...
}

Then callers won't have to know how to format it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it, will change it accordingly

new Configuration.PackageSource(packageSource.Path)
});

using (var testSolutionManager = new TestSolutionManager(true))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a named parameter to explain the pesky true literal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named parameter is foo which is not even used. I dont want to refactor it with this PR. But we should just delete this parameter. For now, this doesn't make any sense to even make it a named parameter

testLogger,
CancellationToken.None);

// Assert
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should first assert:

Assert.NotEmpty(restoreSummaries);

sourceRepositoryProvider.GetRepositories(),
Guid.Empty,
false,
true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add named parameters for false and true.

true,
testLogger,
CancellationToken.None);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.NotEmpty(restoreSummaries);

foreach (var project in projects)
var projects = ((await solutionManager.GetNuGetProjectsAsync()).OfType<IDependencyGraphProject>()).ToList();

for (var i=0; i< projects.Count; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simply foreach here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Foreach on a list still creates a IEnumable instance and waste double memory. This might not make a huge difference but a good practice to use for for list instead of foreach


foreach (var dependentPackageSpec in persistedDGSpec.GetClosure(packageSpec.RestoreMetadata.ProjectUniqueName))
{
if (!projects.Any(p => PathUtility.GetStringComparerBasedOnOS().Equals(p.MSBuildProjectPath, dependentPackageSpec.RestoreMetadata.ProjectPath)) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If you have n projects and all projects reference the same unloaded project, we'll perform this check up to n^2 times. I would add an initial check:

     !uniqueProjectDependencies.Contains(dependentPackageSpec.RestoreMetadata.ProjectUniqueName) &&
    
  2. Outside these loops (like here you can memoize PathUtility.GetStringComparerBasedOnOS() with a local and use it here and here.

@jainaashish jainaashish force-pushed the dev-asja-PersistDGSpec branch from 32754ac to 8c08217 Compare January 7, 2019 19:58
@jainaashish jainaashish merged commit aee50ea into dev Jan 8, 2019
@natemcmaster
Copy link
Contributor

Yay!

foreach (var project in projects)
var uniqueProjectDependencies = new HashSet<string>(stringComparer);

var projects = ((await solutionManager.GetNuGetProjectsAsync()).OfType<IDependencyGraphProject>()).ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How this behave in the case in which we are still waiting for a nomination?

Didn't we want to distinguish between the NU1105 (the project is not in the solution & you have never restored) and NU110, the project-system is slow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a persisted DGSpec then it will consume it and continue with the restore. And when actual nomination happens for that project, then restore will either be NoOp (if there was no change in the project) or restore will consume the new changes. So IMO this experience is better than showing bunch of errors and the final restore being successful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants