DependenSee icon indicating copy to clipboard operation
DependenSee copied to clipboard

[FEATURE] IncludePackages should be recursive and package versions should be mentioned as well

Open mercenaryntx opened this issue 2 years ago • 4 comments

Hey there,

To me the most powerful feature of DependenSee is that it can include external Nuget packages.

However, Nuget packages can depend on other packages as well, so it would be nice to see them. Because sometimes those are more important than the topmost packages a given project references. And without that recursiveness the ExcludePackageNamespaces feature doesn't seem very useful to me, tbh.

For example, it could be crucial to show that if your solution depends on Microsoft.Azure.Cosmos, it's going to depend on Newtonsoft.Json as well.

image

And because of various reasons the package versions should be named as well, just as you can see on the image above.

Thanks in advance!

mercenaryntx avatar Jun 16 '22 18:06 mercenaryntx

Hey,

Thanks for the suggestion. I agree with your reasoning. However there are 2 issues regarding this.

Presentation

Displaying the versions in the html diagram view is currently a bit complicated. I don't want to show multiple bubbles for the same package due to 2 versions of it being used.

  • I can add a 'versions' tag/property to the JSON and XML outputs and allow external tooling to present it?
  • Also the version referenced for nuget packages can be a range. I.e you may specify something like 1.0.* and a max version of 1.0.7 and the resolved version during build may depend on what other projects are referenced as well. (if another project has a max of 1.0.6, it will may used for the final compilation)

Dependent Packages

Getting the dependent packages is a bit complicated as well. Currently DependenSee can be run without restoring packages or having compilation outputs.

Resolving packages either needs

  • The build output, which may be feasible/preferred as this offloads many of the complications to the existing msbuild/nuget/dotnet tooling. However this may lose the information regarding where a DLL came from. Especially on a release build, I don't think there's nothing to distinguish a nuget package vs a project output? This may also force users to run DependenSee against a specific run target instead of a solution folder. Currently if you have a repository with multiple services, which only rely on a shared DLL, the output can show all of them since they may live in the same source folder.

  • or the current store of nuget packages which relies on a number of configurations, and/or currently configured nuget servers and the precedence of them. This well out of scope for DependenSee as nuget configuration can be quite complex and may require credentials .etc. and local stores/caches of nuget packages can also be configured in numerous ways.

Is there any other approach I'm missing that can accomplish this?

Please note that the goal of DependenSee is not to provide a comprehensive view. This can be done with Visual Studio tooling and commercial products like NDepend (for a fee of course). DependenSee instead provides a quick diagram of your dependencies, packages, and how your project is composed, for free. This is helpful during onboarding of a new member to the team, making architectural changes, and understanding how things come together/documenting your project structure. Console outputs are provided for further processing and extensibility.

Happy to discuss any other approaches I'm missing.

madushans avatar Jun 18 '22 07:06 madushans

Hi,

Thanks for the quick reply!

I agree about the presentation, it's more important to have the version numbers as additional properties in the JSON/XML than appended to the package name. And I get your concern about the multiple bubbles, but what if it's configurable? In smaller graphs it could be useful, however that just drives us back to the version number presentation. :) But that's just a nice to have feature anyway, tho.

Unfortunately building the app will do no good, because assembly dependencies are different than package dependencies, and this is where all the other tools fail.

For example, this is what VS Assembly Explorer generates for one of our DLLS, because it has no filtering capabilities, and I cannot filter out .NET assemblies: MicrosoftTeams-image (9)

And the lines are all wrong too, because .NET does a lot of optimization during build. For example, you have 5 package references in the csproj, yet you'll see more than a dozen assembly references coming from Nuget packages in the compiled binary.

Your solution is the closest I'm looking for (and the VS Solution Explorer tree, but sadly that cannot export the data) But I get your concerns, it's not an easy task to deliver. Maybe, just maybe, we could utilize Nuget cache here because it has all the nuspec files we need, and perhaps a preceding "dotnet restore" could serve us justice to deal with all that complexities you mentioned. Perhaps I'll try to tinker with it in my free time and I'll get back to you with a PR if I find an acceptable solution.

Cheers!

mercenaryntx avatar Jun 21 '22 08:06 mercenaryntx

Yea actually I'm not opposed to a configurable option to show version numbers. That way the user opts in to accepting multiple bubbles, so they know to expect it. If we stop at adding just the version numbers to immediately referenced packages, this can work quite well.

But it does have a significant complexity, in order to make it useful for transient packages. And there are a lot of decisions we have to make correctly, to make the results correct.

One way (and the problems we need to solve for it) is as below.

  • Since the project files has the package name and the version we can start there
  • to keep things simple, we can expect the user to have run dotnet restore (or we could run it ourselves)
  • then run dotnet nuget list source --format short to get a list of configured nuget sources. (assume dotnet is in path?) Hopefully this contains a local folder. If it has more, I suppose we can search in each folder in order.

Output for my machine shows as below

E https://api.nuget.org/v3/index.json
EM C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\

I don't know what the E and EM means. but by looking at this, we can find local ones.

  • Assume we have read access to these folders
  • Then find packages mentioned in project files
    • some packages have targeting processor .etc. in the name. i.e System.IO.Compression.clrcompression-arm so we have to decide which one to pick, as different ones could have different other packages referenced? We don't have any way of knowing which one to take, since we don't have any build config. We could ask the user this?
  • Now we have to figure out which version we want, and hopefully the version we want is available in the folder. (If its a version range, it would be hard to decide which to take, and may need to do multiple passes to resolve the one we want.)
  • Find the .nuspec file in that folder, open and parse as xml, find the dependencies node. (if no dependencies node, we're at a leaf node)
  • Many have dependencies listed, but others, have groups, defining which target framework has which dependencies. We have to decide this in order to decide which dependency subtree we want to go down. (Don't know this either)
  • For each dependency, it can define which version it would be happy with. Hopefully our store also has this version available. (nuget restore should bring all of them?)
  • For each dependency, do the above again, until we all packages have no more dependencies.

There's also <frameworkAssemblies> in the .nuspec files. Think these are DLLs in the .NET Framework GAC.

Have a go at the above. I'm not entirely convinced if the above problems can be solved with compile time artefacts to a point where we can create consistent and accurate results.

Also as another approach, may be we can use the MSBuild API? I had a brief look at it quite early on, but didn't decide on it due to complexity (and also I've never used it 🙂). Wonder if its some DLL we can reference, point to a folder or csproj files and get answers. May be it has some answers to the above.

madushans avatar Jun 21 '22 10:06 madushans

TBH, visualising transitive dependencies is the most important feature for me as well..

One idea would be to include nuget packages.lock.json file, if present. This:

  • includes the entire dependency tree for said host
  • does not require building the project,
  • does not care where the packages were resolved from,
  • retains strong connection to projects
  • contains info about version number requirements for dependencies

ImrePyhvel avatar Jul 03 '23 11:07 ImrePyhvel