.NET Tools
Essential productivity kit for .NET and game developers
ReSharper 8.1 EAP Guidance for Extensions
Now that we’ve released the ReSharper 8.1 EAP, and you’ve got the new SDK as a NuGet package, I want to provide some guidance for plugin and extension authors on how to update their extensions to be compatible with 8.1.
Traditionally, ReSharper plugins are not compatible across major and minor version releases (although they are for maintenance releases, such as 8.0.1 and 8.0.2). Plugins can access the full API that ReSharper has access to, rather than a limited subset “plugin API.” When we evolve the product and introduce new features, that API, by necessity, changes, requiring updates to plugins.
With 8.0, we introduced NuGet based extensions, and one of the goals of using this format was to make it easy to support multiple versions of ReSharper. The main way this is implemented is to realize that a NuGet package is essentially a zip file – we can easily put multiple copies of a plugin, each targeting different versions of ReSharper, into the same package. Also, ReSharper extensions are installed to a location that is shared between ReSharper versions, so ReSharper 8.1 will attempt to load any extensions that were installed for 8.0. If the extension doesn’t yet support 8.1, ReSharper will continue to check for updates for it, and will notify the user when one is available, allowing for a smooth migration of extensions from 8.0 to 8.1.
There are three steps to properly support extensions that can handle both 8.0 and 8.1:
- Follow the appropriate file layout
- Mark as pre-release
- Check NuGet dependencies
File layout
The file layout is very important, and key to supporting ReSharper 8.0 and 8.1 with a single extension package. When looking for files to load, ReSharper will look in a variety of locations within the package. It uses the following paths:
- ReSharper
$resharper_version$
$vs_version$
$file_type$
- ReSharper
$resharper_version$
$file_type$
- ReSharpervAny
$file_type$
Where $file_type$
is the type of file to be loaded (“plugins”, “settings” or “annotations”), $vs_version$
is the version of the current Visual Studio instance and $resharper_version$
is the current version of ReSharper. For example, ReSharper 8.0 running in Visual Studio 2012 will look in the following locations for plugins:
- ReSharperv8.0vs11.0plugins
- ReSharperv8.0plugins
- ReSharpervAnyplugins
It first looks at the most specific location, allowing for content that is both ReSharper and Visual Studio specific. This allows for a plugin that requires specific Visual Studio versions, by e.g. putting a Visual Studio 2012 build of the plugin in ReSharperv8.0vs11.0plugins
and at the same time, a Visual Studio 2013 build of the plugin in ReSharperv8.0vs12.0plugins
. By having both plugins in the same package, one extension can support both Visual Studio 2012 and Visual Studio 2013 with one install. This idea extends to support ReSharper 8.0 and 8.1 in the same package.
ReSharper 8.1 will look here:
- ReSharperv8.1vs11.0plugins
- ReSharperv8.1plugins
- ReSharpervAnyplugins
As you can see, the first two locations are different to where ReSharper 8.0 looks. If you want to support a plugin that has a build for 8.0 and a build for 8.1, the recommended locations are ReSharperv8.0plugins
and ReSharperv8.1plugins
, respectively. Only put your plugins in a Visual Studio specific folder if it actually requires a specific version of Visual Studio. The “vAny” location is shared between 8.0 and 8.1, and shouldn’t be used for plugins.
Settings and annotations files can be put in the ReSharper version specific or vAny folders – the file formats are common between ReSharper versions.
It is recommended that when adding support for 8.1, you continue to support 8.0. Do not remove your 8.0 content from the package, otherwise users of ReSharper 8.0 will lose functionality when they upgrade to the new version of your extension. Note that you aren’t required to rebuild or otherwise maintain your 8.0 content, you can simply include the content used in the previous package and redistribute it.
You can see the locations that ReSharper is searching, and what files it finds (or doesn’t find!) in the diagnostic dialog that is available from the “Additional Information” link in the Extension Manager when an extension is selected.
Mark as pre-release
Because 8.1 is still an EAP, and not stable, you should mark any extension packages that include 8.1 content as pre-release. This way, any updates to your existing package do not disturb users of the stable package for 8.0 – they won’t see updates that they aren’t interested in. As long as you continue to also ship the existing 8.0 content, then an 8.0 user can still update to your pre-release extension with no negative impact.
Once the EAP is over and 8.1 is released, you should make a new build of your extension that supports the final version and release it, removing the pre-release version identifier. Both 8.0 and 8.1 users will be prompted to install the new version, and it will simply work with either version of ReSharper.
Check your dependencies
You should also check the dependencies in your .nuspec file. All extensions require a dependency on a package called “ReSharper”. If this dependency isn’t included, or isn’t valid, ReSharper will not install or load the extension. This help us solve both a versioning issue, and a suitability issue – ReSharper won’t install or update to an extension that doesn’t declare support, and it won’t try to install the jQuery package from nuget.org! The “ReSharper” package isn’t real – it doesn’t exist on the gallery, and isn’t installed. The Extension Manager adds a hidden “fake” package with the file version of the currently running version of ReSharper – 8.0.14.856 for 8.0, 8.0.1000.2286 for 8.0.1, 8.0.2000.2660 for 8.0.2 and 8.1.8.449 for the EAP build released on November 3rd (this will of course, change as more builds are released).
You can take advantage of this in your .nuspec file, using standard NuGet versioning rules. The simplest dependency is “8.0”, which means at least ReSharper 8.0.0.0 – note that NuGet treats missing version parts as zeroes. This will match ReSharper 8.0 and above, including 8.1. You can of course be more specific than that, e.g. “[8.0.1000,8.1)”, meaning 8.0.1 and above, up to, but not including 8.1. Or “8.1”, meaning 8.1 and above, not including 8.0.
It is recommended that you keep the version specification simple, unless you have specific requirements otherwise. Use “8.0” if you support 8.0 and above, “8.1” if you don’t have any support for 8.0. Alternatively, you can be more specific while still handling maintenance releases, with “[8.0,8.1)” or “[8.0,8.2)”. Since all packages are installed to a single location, ReSharper will check for updates to unsupported packages, so if a user updates and you’re extension isn’t ready with a supported version, the user will be notified when it is available.
Update: It’s important to note that ReSharper uses the full file version of ReSharper in the dependency check, rather than the product version. Using “[8.0,8.1]” will not match the 8.1 EAP. NuGet adds missing version parts as zeroes, and treats this version specifier as (≥ 8.0.0.0 && ≤ 8.1.0.0) and this will NOT match ReSharper 8.1.8.449, so the package won’t be installed or loaded. This specifier should be used to target 8.0 only. To target just 8.0 and 8.1 and no higher, use “[8.0,8.2)”, which is the same as saying (≥ 8.0.0.0 && < 8.2.0.0) – note the closing parenthesis, meaning less than, rather than less than or equal to.
Finally, it is worth pointing out that the gallery URL has changed for 8.1. The ReSharper gallery uses “curated feeds” to provide supported feeds for the current version of ReSharper. It uses the ReSharper_v8.0 feed for 8.0, and the ReSharper_v8.1 feed for 8.1. It uses the “ReSharper” dependency to see if a package should be included in each feed. If the dependency is satisfied by ReSharper 8.0, it gets added to the ReSharper_v8.0 feed, if satisfied by ReSharper 8.1, it gets added to the ReSharper_v8.1 feed. A package can be in more than one feed at a time. There is nothing you need to do to use or support this, but it can be useful to know that if your package doesn’t appear in the Extension Manager gallery, you should check the “ReSharper” dependency is correct.
Update: Since a picture is worth a thousand words, here’s a look at how the xunitcontrib package is laid out to support both 8.0 and 8.1, using the very useful NuGet Package Explorer. You can see that the plugins are in 8.0 and 8.1 specific folders, while the external annotations file and the settings files are shared in the vAny folder.
As ever, more information can be found at the plugin development guide. Here’s the page for packaging, and we’ll be updating the migration guide shortly. We’re looking forward to seeing more extensions supported in 8.1 EAP soon!