Declaring Transitive Dependencies

35 views
Skip to first unread message

Sebastian P.

unread,
Jun 14, 2017, 8:04:08 AM6/14/17
to resharper-plugins
Hey,

I'm currently struggling with a packaging question for a ReSharper plugin. The devguide at [1] states that "The package should not have a dependency on other NuGet packages" (btw. the same notes are broken/empty in the "stable" devguide [2]). What is meant by this statement?

As an example, assume that I build a plugin P, that depends on a NuGet package L1, which (transitively) depends on NuGet package L2. The dependency resolution works fine with NuGet in Visual Studio: once I add L1 to the plugin project, also L2 is added automatically. My intuitive assumption is that it works in the same way for P (I would simply add L1 as a dependency in the .nuspec file of P). However, the quote from the devguide makes it seems that this does not work.

What is the "canonical" way to declare (transitive) dependencies in P? It feels wrong to add <file ... /> entries to .nuspec of P for all .dlls that are contained in any directly or indirectly referenced NuGet packages.

best
Sebastian

Matt Ellis

unread,
Jun 14, 2017, 8:50:53 AM6/14/17
to resharper-plugins
Dependencies on third party libraries should be avoided, where possible. Any code you add will increase the memory and performance profile of ReSharper and Visual Studio. There is also a chance that the helper assembly you pull in will clash with the helper assembly of another plugin - all plugin files are copied directly to the install dir, so it would be easy to overwrite with conflicting versions.

If you can, rewrite your code so you don't need those dependencies (use existing framework or ReSharper code, or the assemblies that ReSharper ships with, e.g. Newtonsoft.Json). If you can't, either rename the dependencies to avoid potential clashes, or use ILMerge or similar to pull in just the code you need (although note that ILMerge means you lose any benefit of code sharing).

ReSharper extensions do support dependencies, but the idea is so that you can require installation of another plugin. For example, say I write a plugin that provides a new macro for Live Templates. Another extension author could add a dependency on my extension (package) so that she could use my macro in her Live Templates.

Technically, you can include third party libraries as NuGet package dependencies, but they would need to be uploaded to the ReSharper Gallery. This is not recommended, as you would become the owner of a package that isn't really yours (if another plugin author wished to use the same package, you two would need to negotiate updates, etc).

Also, ReSharper extensions aren't really NuGet packages - they're just fancy zip files that make use of NuGet infrastructure to get some nice features. One thing that isn't supported is resolving which assembly under lib is used - so if you upload a package, it must be a simple package with all assemblies living directly under lib, which means re-packaging most existing packages. Again, not recommended.

So, the choice is, in order of most recommended to least:
  • Rewrite to avoid dependencies.
  • Rename or ILMerge dependencies to avoid potential clashes.
  • Package dependencies (and transitive dependencies) directly into your extension package.
  • Add third party assemblies to the ReSharper Gallery, probably by repackaging to put assemblies directly under lib.
These last two are not really recommended, to be honest, although in reality, I suspect the chances of actual clashes would be fairly small. For example, the StyleCop plugin directly packages StyleCop.dll because frankly, no other package is going to use it. However, if it also included NHunspell.dll, then there could easily be a clash with an extension that also provided spell checking.

And thanks for pointing out the broken notes, I'll get that fixed.

Regards
Matt

Matt Ellis

unread,
Jun 14, 2017, 8:59:54 AM6/14/17
to resharper-plugins

Sebastian P.

unread,
Jun 14, 2017, 1:41:57 PM6/14/17
to resharper-plugins
I agree that point (4) is a very bad option and I fully understand your reservations regarding point (3) and the potential problems it creates. I'm not really sure what to say about the other two options though...

Point (1) stands against everything we teach in software engineering classes... why would one rewrite functionality that is already implemented, fully tested, established, and ready for reuse? I'm very sorry, but to me this really seems to be the least sensible option and it is completely off the table. Maybe big companies can afford such huge investments, but it is not safe to assume that everybody else has so much time on their hands to reimplement whole libraries.

Point (2) mentions ILMerge, which looks interesting. I assume that ILMerge already takes care of the "extern alias" stuff that would otherwise be needed to de-ambiguate classes that are defined in multiple dependencies? Or are type references always unambiguous on the CIL level? Never thought about this before... In addition, while point (2) generally seems to be a reasonable choice from the technical point of view, I'm also not sure about the legal implications. Doesn't merging .dlls break the license of the original releases? Are you sure this is legal?

I think that my question was sufficiently answered, but I'm not sure I like the answer. :) I'm also still struggling with my solution. So far, I always did (3), but I understand that -and why- (2) is the better solution... the legal stuff unsettles me though.

To me, it seems that -as of now- no "nice and simple" solution exists for using dependencies in ReSharper plugins... In a perfect world, one could simply define NuGet packages and rely on NuGet's dependency resolution to resolve these conflicts. I understand that the "ReSharper" NuGet does a lot of black magic internally and that it serves a completely different purpose, but I don't understand why you do not simply pull dependencies that are not available in the ReSharper gallery from the real NuGet repository... you could even "blacklist" packages that would collide with packages on which ReSharper relies.

Matt Ellis

unread,
Jun 15, 2017, 12:03:24 PM6/15/17
to resharper-plugins
 
Point (1) stands against everything we teach in software engineering classes... why would one rewrite functionality that is already implemented, fully tested, established, and ready for reuse? I'm very sorry, but to me this really seems to be the least sensible option and it is completely off the table. Maybe big companies can afford such huge investments, but it is not safe to assume that everybody else has so much time on their hands to reimplement whole libraries.

The point is that it's a trade off. Adding a third party dependency is a complication and a cost, as can be seen here. It complicates deployment, maintenance, and when you're a plugin, you're introducing a memory and performance overhead to a process that you don't even own. There is a cost to dependencies, so the sensible option is to minimise dependencies, where possible.

Of course, it's not always possible, and I wouldn't suggest writing your own spell checker, instead of using NHunspell or whatever. But similarly, if there's an existing way of doing something, even if the API isn't very nice or it requires a bit more code, think twice about introducing a dependency. I wouldn't bring in another JSON library because Newtonsoft.Json is already there. And I wouldn't bring in a WPF MVVM library for the handful of dialogs my plugin requires. Or logging, making web requests, talking to databases, etc.

Also, I've often found that when I do want to introduce a dependency, I'm not going to be using all of the functionality of that library, and yet the owning application still has to pay the price for the full library - and this is Visual Studio, where we want to have as small a memory footprint as possible. It's been better to rewrite my code to recreate this subset of functionality than it has to increase complexity and overheads by adding the dependency. But this isn't rewriting the whole library - I agree that would be crazy!

It's a trade off. One of your priorities as a plugin is to be as lightweight as possible. Add a dependency if it's worth the cost, avoid it if you can.
 
I don't understand why you do not simply pull dependencies that are not available in the ReSharper gallery from the real NuGet repository... you could even "blacklist" packages that would collide with packages on which ReSharper relies.

Basically because there's a cost to implementing that, and it wouldn't necessarily solve the issues at hand. It doesn't guarantee that you're not going to clash with another Visual Studio plugin, for example. And the nuget.org packages have no instructions for deployment, for example. And making it easier to add dependencies doesn't encourage the plugin author to be as lightweight as possible.

And also because there hasn't been any demand for it. :)
 
Point (2) mentions ILMerge, which looks interesting. I assume that ILMerge already takes care of the "extern alias" stuff that would otherwise be needed to de-ambiguate classes that are defined in multiple dependencies? Or are type references always unambiguous on the CIL level? Never thought about this before... In addition, while point (2) generally seems to be a reasonable choice from the technical point of view, I'm also not sure about the legal implications. Doesn't merging .dlls break the license of the original releases? Are you sure this is legal?

It depends on the license of the dependency. If it's an open source library, it's usually fine, but you will need to check. Quite often it's enough to note in READMEs, etc. that you're using and redistributing the project. Just like redistributing binaries, this is something you need to check before publishing.
 
I think that my question was sufficiently answered, but I'm not sure I like the answer. :) I'm also still struggling with my solution. So far, I always did (3), but I understand that -and why- (2) is the better solution... the legal stuff unsettles me though.

To me, it seems that -as of now- no "nice and simple" solution exists for using dependencies in ReSharper plugins…

This is the nature of plugins. You're introducing code into someone else's process (and in fact, you're a plugin to a plugin…) I think you'd be hard pressed to find any guidelines on this topic for any application.

But the pragmatic view is that you're probably going to be absolutely fine just bundling the files :) As long as you know the risks...

Regards
Matt

Sebastian P.

unread,
Aug 11, 2017, 4:53:22 PM8/11/17
to resharper-plugins
I would like to thank you a lot for the extensive answer. Thinking about the different options you mentioned (and trying them out) helped me a lot in understanding the problem and in improving my knowledge in an area of development, in which I had little prior experience. I highly appreciate the support you are providing in this forum!

The only remaining clarification question that I have left is whether it is possible to build R# plugins that depend on (and build upon) other R# plugins? If I interpret your answer and the (now updated?) documentation correctly, then adding the dependency in the .nuspec file will work as long as the other plugin is also published in the curated R# NuGet feed, right?

(While this is not the elaborated answer to this post that I had on my TODO list for quite some time now, I though I better write at least a quick one, before it is completely forgotten. :))

Matt Ellis

unread,
Aug 14, 2017, 5:19:33 AM8/14/17
to resharper-plugins
Yes, it was always a goal of the extension manager to allow plugins to depend on other plugins, even before choosing nuget as the packaging format/transport mechanism (where it's built in - that was a bonus). The idea was that you could have a plugin that relied on functionality from another plugin - e.g. one plugin provides new macros for Live Templates, and other plugins could use those macros.

And as long as both plugins are available in the NuGet feed, they will both be installed and loaded. You just need to add the other plugin as a dependency in the .nuspec file.

Regards
Matt
Reply all
Reply to author
Forward
0 new messages