Generate Reference Assemblies With Refasmer
With each passing day, the .NET ecosystem introduces new target environments. In addition to Windows environments, we can now deploy to Linux, macOS, and all popular mobile operating systems. Having these options is excellent, as it allows us to reach a broader audience, but each new environment comes with its own set of challenges.
.NET Framework has solved the multiple-environments issue by creating the .NET Standard. The .NET Standard exposes a common API we can compile against with the understanding that our target environments have a context-specific implementation. In this post, we’ll see how we can take one of our own common assemblies and build a reference assembly.
What are reference assemblies?
According to the Microsoft documentation:
Reference assemblies are a special type of assembly that contains only the minimum amount of metadata required to represent the library’s public API surface. — Microsoft Docs
Reference assemblies allow us to build for a target environment within a different environment. For example, we may want to build our assemblies to target mobile devices from our Windows continuous integration environment. The most common reference assembly being the .NET Standard itself. In general, we can think of reference assemblies as contracts that concrete implementations can support.
Who can benefit?
Who benefits the most from using reference assemblies? That would be folks building APIs that target multiple environments. For example, we may be building a cross-platform microphone API. Our new microphone library shares a standard interface across environments, but because of the target operating systems like Windows, macOS, and Linux, we need unique implementations to make our API work.
Additionally, Reference assemblies allow platform developers to publish only the API surface, holding the implementation back at the time it is needed by developers. Reference assemblies are an ideal solution for products with a plugin system (all JetBrains products for example).
Product developers can publish reference assemblies to help plug-in authors understand the API surface while retaining the implementation details within the product. The product can continue to iterate with enhancements and bugfixes, changing internal implementation details without plug-in authors needing to update any references.
In the case of JetBrains, we required reference assemblies to target .NET Framework 3.5, which we were able to create with our own tooling. The .NET Framework 3.5 reference assemblies are made available on NuGet for anyone still targeting older versions of .NET Framework. This allowed us to support our loyal developer community working on older versions of .NET while still pushing to the future. The .NET Framework 3.5 reference assembly is a rebuild of the public API surface built from scratch and not just a stripped original assembly.
JetBrains developers Sergey Zabodalov and Mikhail Filippov have developed Refasmer, a command-line tool that can take an assembly and generate a reference assembly. As stated before, our newly created reference assembly is a contract that can help us target new environments.
You could download refasmer from GitHub: https://github.com/JetBrains/Refasmer/releases or install as .NET Tool:
dotnet tool install -g JetBrains.Refasmer.CliTool
To use Refasmer, we need to invoke RefasmerExe.exe with specific arguments found under the help menu.
RefasmerExe.exe -v -O ref -c *.dll
The command above will handle all DLLs in the current directory, continue on errors, and try to strip native resources. Refasmer will place output DLLs in the ./ref directory.
There is a wealth of options, which we can find in the help menu of Refasmer.
RefasmerExe.exe [options] <dll> [<dll> ...] Options: -v increase verbosity -q, --quiet be quiet -h, --help show help -c, --continue continue on errors -O, --outputdir=VALUE set output directory -o, --output=VALUE set output file, for single file only -r, --refasm make reference assembly, default action -w, --overwrite overwrite source files -l, --list make file list xml -a, --attr=VALUE add FileList tag attribute
Refasmer has the advantage of being a stand-alone executable, making it easy to integrate into continuous integration environments.
Refasmer is available on GitHub for folks looking to make the process of building reference assemblies easier. Sergey and Mikhail have done a great job and released the code for the benefit of the .NET community. Contributions and feedback are welcome.