30

I'm programming a project with plugin support. Since many of the plugins are relatively small (only one source-file/class) I would like to have them all in one project in visual studio, but to successfully do this I would need each source-file/class to be compiled into its own dll file, is this possible using visual studio?

If this is not possible with visual studio, would it be possible using another build system, while still coding and debugging with visual studio?

Currently I've set the plugins project output type to console, and programmed a main() method that will compile all .cs files in the source directory to dlls and copy those to the proper directory. Then I set that console app to be the post-build event of the plugins project. It works, but it seems like a very ugly hack.

Using visual studio 2010.

Aleksi
  • 352
  • 1
  • 5
  • 11

5 Answers5

42

You could create one project for each plugin and group all projects in a solution.

If you don't want to have one project per plugin, you could create a custom build with MSBuild using CSC task

How to generate a dll for each plugin file

  1. In a project you add all plugins files

  2. Edit the project file to specify which class will generate a plugin library :

    <ItemGroup>
      <Compile Include="Class1.cs">
        <Plugin>true</Plugin>
      </Compile>
      <Compile Include="Class2.cs" />
      <Compile Include="Class3.cs">
        <Plugin>true</Plugin>
      </Compile>
      <Compile Include="Program.cs" />
      <Compile Include="Properties\AssemblyInfo.cs" />
    </ItemGroup>
    
  3. Add a new target in your project file to generate the plugins library

    <Target Name="BuildPlugins">
      <CSC Condition="%(Compile.Plugin) == 'true'"
           Sources="%(Compile.FullPath)"
           TargetType="library"
           OutputAssembly="$(OutputPath)%(Compile.FileName).dll"
           EmitDebugInformation="true" />
    </Target>
    
  4. If you want to create the plugins library after each build, add an after build target :

    <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
    </Target>
    
Community
  • 1
  • 1
Julien Hoarau
  • 48,964
  • 20
  • 128
  • 117
  • 1
    Hey thanks for above answer....it solves my half of the problems. but I want version number and other assambly related information to those newly generated DLLs and also would like to combine multiple file in one dll not the single file. Please help if anyone know regarding the same.....would be great help to me.... – Denish Oct 12 '11 at 03:48
  • simply awesoooooooooooome, where did you get all that knowledge? great, just great ,exactly what I was looking for – franko_camron Dec 06 '11 at 19:42
  • 2
    Is it just multifile assembly in result? Could I add as reference any result dll (without manifest) to another project separately? – SerG Jan 21 '14 at 13:12
  • This is awesome logic but I face difficulty If my project has some reference of other project. Please provide me idea to compile that project too. – Saroop Trivedi Oct 02 '14 at 12:13
  • 1
    As others have mentioned - this does not work when project references another project within the same solution... – Alex Jul 23 '20 at 17:13
12

You just have to create a Solution then add as many projects you want.

You can have like 5 Class Library projects and compile them, generating 5 DLLs.

BrunoLM
  • 97,872
  • 84
  • 296
  • 452
  • 1
    Yes, but creating 10 projects with each having one source-file seems like a terrible waste, I was hoping there was a way of compiling all the source-files in one project into separate dlls. – Aleksi Oct 05 '10 at 19:46
  • 10
    A waste of perhaps several kilobytes for the Project and AssemblyInfo files? Respectfully, we're talking false economy, here. This is what projects are *for*, to encapsulate your source code into assemblies. :) – Dan J Oct 05 '10 at 19:48
  • 1
    So there isn't an approach for you! – Sadegh Oct 05 '10 at 19:49
4

To expand upon Julien Hoarau's answer above, here is a solution that allows you to compile multiple DLL files from within a single project, and have those DLL files be compiled from multiple CS files. Just open your csproj file and add this before the </Project> tag:

  <!-- Plugin Building -->
  <!-- 1. Hardlink to NuGet References - CSC does not read HintPaths, so you will have to create these for all your packages -->
  <ItemGroup>
    <PluginReference Include="..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll" ><InProject>false</InProject></PluginReference>       
  </ItemGroup>
  <!-- 2. Each Plugin CS Files -->
  <!-- You see that each tag in here has it's own name starting with Plugin -->
  <!-- We can reference that later e.g. as @(PluginBlue) to get an array list to pass to the CSC sources, allowing us to have multiple files -->
  <!-- Plugin.Blue\**\*.cs gets all the files in the "Plugin.Blue" folder -->
  <!-- Plugin.Green just has a specific file list -->
  <ItemGroup>
    <PluginBlue Include="Plugin.Blue\**\*.cs"><InProject>false</InProject></PluginBlue>
    <PluginGreen Include="Plugin.Green\File1.cs"><InProject>false</InProject></PluginGreen>
    <PluginGreen Include="Plugin.Green\File2.cs"><InProject>false</InProject></PluginGreen>
  </ItemGroup>
  <!-- 3. Build each Plugin -->
  <Target Name="BuildPlugins">
    <!-- Plugin Blue -->
    <CSC Sources="@(PluginBlue)" References="@(PluginReference)" TargetType="library" OutputAssembly="$(OutputPath)Plugin.Blue.dll" EmitDebugInformation="true" />
    <!-- Plugin Green -->
    <CSC Sources="@(PluginGreen)" References="@(PluginReference)" TargetType="library" OutputAssembly="$(OutputPath)Plugin.Green.dll" EmitDebugInformation="true" />
  </Target>

  <!-- 4. Require Build on Solution Compile -->
  <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
  </Target>

This is my approach - it lets you keep everything organized at the bottom instead of spread out throughout the project file. Using

<InProject>false</InProject>

allows us to hide the files from the SolutionExplorer, and have a separate defined list of files instead of simply adding the plugin tag to the files we want. In your main solution, be sure to set the Build Action to "none" on all the files you are compiling in the plugin so there is no duplication in the main project file.

Some more reading about CSC:

https://msdn.microsoft.com/en-us/library/78f4aasd.aspx Command-line Building With csc.exe

https://msdn.microsoft.com/en-us/library/ms379563(v=vs.80).aspx Working with the C# 2.0 Command Line Compiler

https://msdn.microsoft.com/en-us/library/s5c8athz.aspx Csc Task

https://msdn.microsoft.com/en-us/library/7szfhaft.aspx MSBuild Conditions

I hope this is useful to someone.

Qjimbo
  • 377
  • 2
  • 11
  • The registration tool tells me the key file is not include, when I try to add the key file to the project file I get an error: Metadata file 'mykey.snk' could not be opened -- PE image doesn't contain managed metadata. How can I fix this? – Tony Oct 27 '17 at 20:52
2

Some poetry:

In my case - I needed to have plugin dlls built for the unit test project specifically.. If you do it the "normal way", by creating separate project for each plugin - then you will end up having more unit-test related projects than core assemblies.. So I think that in some cases it is worth doing multi-builds within same project. With that being said - I would like to provide an improved version of the accepted answer.

New approach improvements:

  • support for new SDK projects (used in .net Core)
  • support to build assemblies that are referencing another projects within solution!

Create a new empty library project, make sure that the structure is following:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net472</TargetFramework>
  </PropertyGroup>
</Project>

hint: you can create .Net core project and change the target to net472, for example.

Add a plugin and some references if needed, then, per following documentation, add: https://learn.microsoft.com/en-us/dotnet/core/tools/csproj

<EnableDefaultCompileItems>false</EnableDefaultCompileItems>

This will make it possible to manually include files for building.. otherwise the build will include all files by default.

Then explicitly add the item to be compiled:

<ItemGroup>
<Compile Include="TestApp1.cs">
  <Plugin>true</Plugin>
</Compile>
</ItemGroup>

Then, as mentioned in the following source - you can aggregate all references into a single string: How to get paths to all referenced DLLs in MSBuild?

  <Target Name="GatherReferences" DependsOnTargets="ResolveReferences">
    <ItemGroup>
      <MyReferencedAssemblies Include="@(ReferencePath)" />
    </ItemGroup>
  </Target>

To test:

  <Target Name="TestMessage" AfterTargets="Build" >
    <Message Text="Referenced assemblies: @(ReferencePath)" Importance="high"/>
  </Target>

Then you have to append a build target that is triggered on PostBuild:

  <Target Name="BuildPlugins" AfterTargets="PostBuildEvent">
    <CSC Condition="%(Compile.Plugin) == 'true'"
         Sources="%(Compile.FullPath)"
         TargetType="library"
         References="@(ReferencePath)"
         OutputAssembly="$(OutputPath)%(Compile.FileName).dll"
         EmitDebugInformation="true" />
  </Target>

The difference here you will notice is a References property which basically translates the value into appropriate -reference argument for CSC compiler: https://learn.microsoft.com/en-us/visualstudio/msbuild/csc-task?view=vs-2019

bonus: you might incorporate this logic into your project even without having to explicitly define the Plugin property..

According to this topic here: MsBuild Condition Evaluate Property Contains

It is possible to use Regex expression on build metadata: https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata?view=vs-2019

So basically, something like this is allowed:

Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Compile.Filename)', 'Plugin'))"

You can make it so, that if the Class filename contains a specific keyword, then you can trigger a separate compilation for such file.. Or if the file is located in specific folder! In example above if the file has word "Plugin" in it, then it will get picked up by CSC task.. I recommend checking metadata page to see all options.

bonus: If you just like me like being able to step up into the code and also to be able to output into the Debug output window, you can also define:

  DefineConstants="DEBUG;TRACE"
  DebugType="full"

My latest configuration looks like this:

  <Target Name="BuildPlugins" AfterTargets="PostBuildEvent">
    <CSC 
      Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Compile.FullPath)', '.*\\AppUnitTests\\Plugins\\.*.cs'))"
      Sources="%(Compile.FullPath)" 
      TargetType="library" 
      References="@(ReferencePath)"
      OutputAssembly="$(OutputPath)Plugins\%(Compile.FileName).dll" 
      EmitDebugInformation="true"
      DefineConstants="DEBUG;TRACE"
      DebugType="full" />
  </Target>

Hopefully it was useful for someone ;)

p.s. my thanks to original author for giving an example that I could improve upon.

Alex
  • 4,607
  • 9
  • 61
  • 99
-1

If you want each code file to be it's own DLL, then you have to make a new project for each code file. But you can put multiple project files (.csproj) into one big solution.

If this doesn't suite you, then you could always compile your project using the command line. That would allow you to customize your build to suite your needs. For instance you could write a batch scrip, or a powershell script that would compile all your code files into a separate DLL.

C.J.
  • 15,637
  • 9
  • 61
  • 77
  • 1
    Please post examples and/or links with your answer, telling people what to do but not HOW is not what this site is about. – Hannish Feb 28 '17 at 12:33