Obviously, I wrote about this topic some time ago. Here’s the original post: Post. Basically, the issue was that I needed to maintain multiple versions of my Revit plugin, to match multiple versions of Revit that I wanted to support. At the time I thought that Configurations was the right way to go, but as you can guess, I no longer think that’s true. The only reason I am even touching this subject is because I have recently saw a post on Speckle Blog, that discussed this same approach, and they opted for Configurations. Now, don’t take this as I am telling you that Matteo is wrong. Far from it. This in fact might be the best solution for their unique problem, but I thought that I share another approach that I have learned recently.
Shared Projects. This has been added as a new feature back in Visual Studio 2015, but I haven’t really heard of it until few months ago. Let’s first examine what a Shared Project is. From the sound of it, you might think that it’s some DLL that is shared across multiple projects, and that it contains code that would be used by these projects. That’s not the case. A Shared Project, is basically shared source code. The way it works is that each project (DLL) that references the Shared Project would have the code contained in it, incorporated right into it. How is that different than Configurations:
- Configurations build different versions of the same code, based on the selected Configuration. There is still just a single project (DLL), that is compiled and post processed based on whatever Configuration is currently selected.
- Shared Project approach actually allows you to build different DLLs for each version, where each DLL has the Shared Project embedded into it, but now instead of using Configurations to control Post Build events, References, Dependencies, Setup projects etc. you can just do that in different projects.
Let me show you an example. I have recently switched Archi-lab.net Dynamo package to use this approach. Now, whether this is the “correct” approach for Dynamo is a completely different discussion, because as we all know it, Dynamo doesn’t make anything easy, but let’s assume it was the correct approach. What does that look like:
As you can see above there is an “archilabSharedProject” project, and there are different projects for “archilab2021”, “archilab2020” etc. The idea is that all shared code, is contained in the archilabSharedProject, but all resources that are unique to specific version would be contained in that version’s project. This approach makes it easy for me to have post build events that copy resources (*.addin manifest files) to specific location, and I don’t have to use “if Configuration == 2020 copy to 2020 location” type code. Someone might say: “Well that’s nothing special. If statements are not that hard. Is there a real benefit to this approach?”
The answer is, YES! OK, so I agree, a Post Build event or Target FrameworkVersion configuration that looks like the one below might not be sexy, but it’s not end of the world.
The real benefit of Shared Projects comes into play when we start dealing with things like Setup projects aka. installers (MSIs). These allow you to reference an asset or a project. If you are using things like Configurations, then you really have just a single DLL/project, but you might want to have your installer reference different versions as you would want it to copy them to different locations. That’s the whole point of an installer. Now, you have a pickle because you have to do that in two steps:
- build all of your Configurations, copy these DLLs to different locations based on a Configuration
- build your MSI, referencing assets (DLLs) from folders on a drive, rather then dynamically linking them from Visual Studio solution directly.
Another thing that greatly annoyed me with Configurations was the fact that now I basically either had to give up the idea of having a Debug/Release Configurations, or I had to create them for each version of Revit. That might be as many as 8 Configurations if I need to carry Debug/Release for 2021/2020/2019/2018. Why do I care for Debug/Release? You might want to do things like disable XML/PDB file creation for Release builds. If you are not planning on debugging them, then why clutter that bin folder with them? Also, every now and then I am asked to obfuscate the code (I know, don’t ask why), but if that’s part of the Release routine, then I CAN’T debug the Release code, and that necessitates that I have Debug and Release configurations.
Now, you might also be wondering, but can I still do the good old “#if Release2015 #else #endif” statements? The answer is, yes, of course. You can put all of the version specific code into the Shared Project. It works just like the old Configuration’s method.
In summary, the Shared Project approach has all of the same benefits that Configurations do, plus it avoids potential issues with things like MSI’s and Debug/Release Configurations. In my opinion it also makes it explicit that there is support for different versions of Revit, which then makes all version specific code and resources obvious and much easier to maintain. It might not be a revolutionary approach, but I think it’s a little bit better than Configurations.
Again, I used it with archi-lab.net project here. Please check it out, and let me know if you have any questions.