Mixing C# and VB.NET in one assembly
A couple of weeks ago Jayson Knight invited me to join the CSMVP's' CSModules package. This project was created to incorporate several CSModules written by CS MVP's. So I bought into that project by bringing along my Akismet spam rule.
At that point in time, for each CSModule there was a single Visual Studio project, all bound together in a Visual Studio solution. But our goal was to incorporate all modules into a single assembly, so the user only has to copy one DLL to his /bin web folder.
My first idea was to put all sources into a single project. However, since every CS MVP wrote his module in his favorite language, there were both C# and VB.NET projects. Unfortunately, in Visual Studio projects have a certain type supporting only a single language.
But luckily, even if there's no support by the IDE, you still can compile different .NET languages into one assembly. The solution are modules. A modules is a unit of compilation, comparable to .obj files. It can't stand by its own, but must be linked into an assembly before it can be used. Basically, a module is an assembly without a manifest. You can get more details on MSDN at .netmodule Files as Linker Input. Additionally I recommend reading Junfeng Zhang's Netmodule vs. Assembly and Multimodule Assemblies.
Knowing the concept of modules, I was able to write a MSBuild project file to compile all CSModules into a single assembly. Here's an (extremely) simplified version:
| <Project |
| DefaultTargets="build" |
| xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| |
| <!-- Target folders --> |
| <PropertyGroup> |
| <SourcePath>src</SourcePath> |
| </PropertyGroup> |
| |
| <!-- Specify the sources to include, excluding any assembly info's --> |
| <ItemGroup> |
| <CSFiles |
| Include="$(SourcePath)/**/*.cs" |
| Exclude="$(SourcePath)/**/assemblyinfo.cs"/> |
| <VBFiles |
| Include="$(SourcePath)/**/*.vb" |
| Exclude="$(SourcePath)/**/assemblyinfo.vb;$(SourcePath)/**/My Project/*"/> |
| </ItemGroup> |
| |
| <!-- Specify all referenced assembly --> |
| <ItemGroup> |
| <References Include="lib/2.1 RTM/*.dll" /> |
| </ItemGroup> |
| |
| <!-- Target files --> |
| <PropertyGroup> |
| <OutputModule>MyModule</OutputModule> |
| </PropertyGroup> |
| |
| <!-- builds the CSMVPs.CSModules assembly --> |
| <Target |
| Name="build" |
| Inputs="@(CSFiles);@(VBFiles);@(References)" |
| Outputs="$(OutputModule).dll"> |
| |
| <!-- compile C# sources --> |
| <CSC |
| Sources="@(CSFiles)" |
| References="@(References)" |
| OutputAssembly="$(OutputModule).CS.netmodule" |
| TargetType="module"> |
| </CSC> |
| |
| <!-- compile VB.NET sources --> |
| <VBC |
| Sources="@(VBFiles)" |
| References="@(References)" |
| OutputAssembly="$(OutputModule).VB.netmodule" |
| TargetType="module"> |
| </VBC> |
| |
| <!-- link the C# and VB.NET modules --> |
| <Exec |
| Command="link /dll /ltcg /out:$(OutputModule).dll *.netmodule" /> |
| |
| </Target> |
| </Project |