Child pages
  • Plugin Versioning
Skip to end of metadata
Go to start of metadata

This page covers versioning issues: what version numbers mean, which ones are significant and which ones are not, and in which cases ReSharper could refuse loading your plugin.

Plugin Versions

This is the most simple thing: ReSharper would not validate the version of the plugin DLL it loads and thus imposes no restrictions over your version numbering scheme. ReSharper does not currently have an automatic updates scheme for plugins, so versioning your plugin is up to you.

Visual Studio Versions

This also isn't complicated. If ReSharper can load into the given VS version, it will also try loading all of the eligible plugins. It is thus possible to write a single plugin DLL deployed per-machine to run in VS8, VS9 and VS10.

If your plugin does not use VS APIs directly and fully relies on the R# isolation layer, it is not likely to see any difference in the environments (but for the VsShell::VsVersion4 value).

If you have to call VS COM APIs directly, use the same precautions as when writing a VS extension: either use only the common subset of the APIs available in all VS versions, or do some runtime branching, or deploy separate plugin DLLs for each VS version (deployed per-user or per-machine in common app data).

ReSharper Versions

This is where versioning gets complicated.

Each ReSharper build, RTM or Early Access, has two version numbers:

  • Technical version, which is the standard Windows four-digit version number, for example 4.5.1257.6.
    Nearly all of the ReSharper assemblies have this version both as .NET Assembly Version and Windows File Version.
    The MSI package uses three-digit versions, and takes the first three digits out of this number.
    The first digit is the most significant, and the last digit is the least significant.
  • Marketing version, which is what you see on product splash screens, for example "4.5.1 Beta".
    This is just some freehand text, but its two first digits usually are the same as the technical version.

The technical version is what matters for versioning and plugins compatibility.

Generally, only the two first digits (major and minor version number) matter for plugins.

They affect deployment scenarios (if R# will be able to find your plugin) and .NET assembly binding rules (whether the usages of ReSharper classes in your plugin will be recognized by the .NET framework).

Plugin Deployment and ReSharper Versions

If you'd like ReSharper to load your plugin automatically, you would deploy it to special folders. All of these locations include ReSharper two-digit version number in their path (and most also do include Visual Studio's). This way your deployment process explicitly limits the ReSharper versions range available for your plugin.

For example, a plugin deployed into the folder with "/v4.0/" in its path will be loaded by R# 4.0 and R# 4.0.1 (marketing version, technical version smth like 4.0.1234.56), but will not be discovered by R# 4.1 (see below if you need to work this around).

Assembly Identity

ReSharper assemblies have strong name signatures. As you build your plugin DLLs against some pack of ReSharper DLLs, the version number of the latter gets written into your code and validated at load time.

Generally this means that if you built (in your labs) against R# 4.0.1234.56, and you're loading the plugin into a Visual Studio (on the user machine) which has R# 4.0.6789.1 installed, the plugin will not be recognized as referencing ReSharper classes (even though R# attempts loading it, because the two-digit version "4.0" is the same). Effectively, instead of specifying just the JetBrains.Application.Shell class, your DLL now requires "JetBrains.Application.Shell-from-assembly-v4.0.1234.56". The runtime has "JetBrains.Application.Shell-from-assembly-v4.0.6789.1" instead, which is a no match.

Fortunately, ReSharper installs special assembly binding rules which say that v4.0.6789.1 fits everywhere where v4.0.1234.56 is required. More specifically,

  • The first two digits must match. Only the last two components are ignored. R# v4.0.rx.ry could possibly load a plugin built for R# v4.0.px.py, but not v4.1.m.n.
  • The plugin must not be requiring a newer ReSharper than installed. In the above example, v4.0.rx.ry ≥ v4.0.px.py must be satisfied.
    The API Stability section expands on the reasoning behind these limitations.

This means that each Major.Minor ReSharper version requires a separate plugin DLL; and that you should be compiling against the earliest possible ReSharper build (of that same Major.Minor version, usually RTM) for the plugin to be loadable by any newer ReSharper. EAP builds, even though having lower version numbers than RTM, should not be used for production compilation, because API stability guarantee does not apply to EAP builds.

API Stability

Unlike WinAPI and Visual Studio APIs, ReSharper API is not invariant. It is currently not possible to write code against R# v4 and make it compile against v5 without any changes.

ReSharper evolves rapidly to meet the new language versions and technologies. On the other hand, ReSharper exposes most of the magic used by built-in functionality to plugin authors. Unfortunately, API invariance is the tradeoff, which makes it complicated to support multiple R# versions.

This is why plugins are not cross-loadable between different two-digit versions of ReSharper.

Within the same two-digit version, starting with RTM, the public API gets frozen and becomes backward compatible (this does not apply to pre-RTM builds - early access builds). This allows for assembly version redirection described in the Assembly Identity section.
However, the API is not forward-compatible: a class might be added in a later build, which would not break any existing ReSharper plugins from loading into that new ReSharper version; but a plugin built against the new version and using that class would not load into an older ReSharper. Hence the v4.0.rx.ry ≥ v4.0.px.py restriction from the Assembly Identity section.

Targeting multiple ReSharper versions

Sometimes it's necessary to support multiple ReSharper versions with a single plugin product installation. This is especially a case when the plugged-in product is fully functional on its own and only employs a ReSharper plugin for embedding some access to that functionality into ReSharper.

This section only covers assembly identity workarounds; for deployment scenarios, see Plugin Deployment and Plugin Integration.

B. If the API is believed to be stable

This is the case when the ReSharper API subset called by the plugin appears to be stable throughout the full range of ReSharper versions to support. This is highly probable for smaller plugins throughout the same major ReSharper version (e.g. 4.0 - 4.1 - 4.5). Thorough testing is required to make sure it is really so - fortunately, it could be easily automated.

The key is to reference ReSharper without strong names (see Assembly Identity section: from actual type references like "JetBrains.Application.Shell-from-assembly-v4.0.1234.56" we should remove the "-from-assembly-v4.0.1234.56" suffix to make it fit just any "JetBrains.Application.Shell" available).

This means that your plugin should not have strong name signatures, and that it should not specify strong name key tokens in references to ReSharper assemblies. The latter could be achieved by either editing the assembly manifest post-compile, or (probably) by setting SpecificVersion metadata to False on each Reference to a ReSharper DLL.

A. If the API appears to change

This is a highly probable case when different major versions of ReSharper are considered.

Mostly plugin. If your plugin integrates into ReSharper deeply (to provide code analysis, quick fixes, etc) and has little or no side functionality, then it could possibly mean you'll have to maintain separate branches for different ReSharper versions, or cease development on the branch for the previous ReSharper version.

Core + plugin. If ReSharper integration is but a part of your plugin, it is reasonable to split the plugin into a core DLL and ReSharper Integration DLL, of which the latter references the former and might exist in multiple versions. You might choose between loading your core DLL by the ReSharper plugin DLL in the course of loading ReSharper plugins, or, quite the other way, you might make your core a full-scale Visual Studio extension that uses the ReSharper Integration DLL to talk to ReSharper and plant your functionality into it when present. See Plugin Integration for details on the latter scenario.