Visual Studio一个项目有多个dll作为输出?

时间:2010-10-05 19:43:20

标签: c# visual-studio

我正在编写一个支持插件的项目。 由于许多插件相对较小(只有一个源文件/类),我希望将它们全部放在visual studio中的一个项目中,但为了成功完成这个,我需要将每个源文件/类编译到其中自己的dll文件,这可能使用visual studio吗?

如果使用visual studio无法实现这一点,是否可以使用其他构建系统,同时仍可使用visual studio进行编码和调试?

目前我已将插件项目输出类型设置为console,并编写了一个main()方法,该方法将源目录中的所有.cs文件编译为dll并将其复制到正确的目录中。然后我将该控制台应用程序设置为插件项目的后期构建事件。它有效,但它似乎是一个非常难看的黑客。

使用visual studio 2010。

5 个答案:

答案 0 :(得分:36)

您可以为每个插件创建一个项目,并将解决方案中的所有项目分组。

如果您不希望每个插件都有一个项目,可以使用CSC task

使用MSBuild创建自定义版本

如何为每个插件文件

生成一个dll
  1. 在项目中添加所有插件文件
  2. 编辑项目文件以指定哪个类将生成插件库:

    <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. 在项目文件中添加新目标以生成插件库

    <Target Name="BuildPlugins">
      <CSC Condition="%(Compile.Plugin) == 'true'"
           Sources="%(Compile.FullPath)"
           TargetType="library"
           OutputAssembly="$(OutputPath)%(Compile.FileName).dll"
           EmitDebugInformation="true" />
    </Target>
    
  4. 如果要在每次构建后创建插件库,请添加后构建目标:

    <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
    </Target>
    

答案 1 :(得分:11)

您只需要create a Solution然后添加所需的项目。

您可以拥有5个Class Library个项目并进行编译,生成5个DLL。

答案 2 :(得分:3)

为了扩展Julien Hoarau的上述答案,这里有一个解决方案,允许您从单个项目中编译多个DLL文件,并从多个CS文件中编译这些DLL文件。只需打开您的csproj文件,然后在&lt; / Project&gt;之前添加它。标记:

  <!-- 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>

这是我的方法 - 它允许您将所有内容组织在底部,而不是在整个项目文件中展开。使用

<InProject>false</InProject>

允许我们隐藏SolutionExplorer中的文件,并拥有单独定义的文件列表,而不是简单地将插件标记添加到我们想要的文件中。在您的主要解决方案中,请务必将构建操作设置为&#34;无&#34;在您正在插件中编译的所有文件上,因此主项目文件中没有重复。

关于CSC的更多阅读:

https://msdn.microsoft.com/en-us/library/78f4aasd.aspx使用csc.exe进行命令行构建

https://msdn.microsoft.com/en-us/library/ms379563(v=vs.80).aspx使用C#2.0命令行编译器

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

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

我希望这对某人有用。

答案 3 :(得分:1)

一些诗歌:

在我的情况下-我需要专门为dlls项目构建插件unit test。如果您以“正常方式”进行操作,则为每个插件创建单独的项目-那么您将最终比核心程序集拥有更多的与单元测试相关的项目。因此,我认为在某些情况下,在同一项目中进行多次构建是值得的。话虽如此-我想提供接受答案的改进版本。

新方法的改进:

  • 支持新的SDK项目(在.net Core中使用)
  • 支持构建引用解决方案中其他项目的程序集!

创建一个新的空库项目,确保结构如下:

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

提示::例如,您可以创建.Net核心项目并将目标更改为net472

根据需要添加一个插件和一些参考,然后根据以下文档添加: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj

<EnableDefaultCompileItems>false</EnableDefaultCompileItems>

这将使得可以手动包括用于构建的文件。否则,默认情况下,构建将包括所有文件。

然后显式添加要编译的项目:

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

然后,如以下源中所述-您可以将所有引用聚合为一个字符串:How to get paths to all referenced DLLs in MSBuild?

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

要测试:

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

然后,您必须附加在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>

您将在这里注意到的区别是References属性,该属性基本上将值转换为CSC编译器的适当-reference参数:https://docs.microsoft.com/en-us/visualstudio/msbuild/csc-task?view=vs-2019

奖金::即使不必显式定义Plugin属性,您也可以将此逻辑合并到项目中。

根据此处的主题:MsBuild Condition Evaluate Property Contains

可以在构建元数据上使用正则表达式:https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata?view=vs-2019

所以基本上,这样的事情是允许的:

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

您可以这样做,如果类文件名包含特定的关键字,则可以触发该文件的单独编译。或者如果文件是特定的文件夹!在上面的示例中,如果文件中包含单词“ Plugin”,则它将由CSC任务提取。.我建议检查元数据页面以查看所有选项。

奖金::如果您像我一样喜欢能够进入代码并能够输出到Debug输出窗口,则还可以定义:

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

我的最新配置如下:

  <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>

希望它对某人有用;)

p.s。感谢原始作者提供了一个可以改进的示例。

答案 4 :(得分:-1)

如果您希望每个代码文件都是它自己的DLL,那么您必须为每个代码文件创建一个新项目。但是您可以将多个项目文件(.csproj)放入一个大解决方案中。

如果这不适合你,那么你总是可以使用命令行编译你的项目。这样您就可以自定义构建以满足您的需求。例如,您可以编写批处理脚本或PowerShell脚本,将所有代码文件编译为单独的DLL。