简体   繁体   中英

Linking netmodules to a single file in msbuild

I want to ship a single .NET assembly generated from several C# projects via .netmodules.

I've experimented with ILmerge, but it has other problems. I also had a look at the AssemblyResolve way, but I don't really understand it (both covered here: How to merge multiple assemblies into one? ).

I've found a possible solution that would work fine for the task via .netmodules. No external programs, standard tools, the resulting assembly looks like it came from one project only (in ildasm).

Here's a MWE: Lib.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OutputType>Module</OutputType>
    <OutputPath>bin\</OutputPath>
    ...
  </PropertyGroup>
  ...
  <ItemGroup>
    <Compile Include="Lib.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Exe.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OutputType>Module</OutputType>
    <OutputPath>bin\</OutputPath>
    ...
  </PropertyGroup>
  ...
  <ItemGroup>
    <AddModules Include="..\Lib\bin\Lib.netmodule" />
    <Compile Include="Program.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

The output type of both projects is set to module. The "Exe" project uses the "Lib" netmodule via the AddModules switch (required for compilation). This results in two .netmodules in the Exe output directory.

In the final step, the linker is used to link all .netmodules into one Assembly (see https://docs.microsoft.com/en-us/cpp/build/reference/netmodule-files-as-linker-input?view=vs-2017 ):

link Lib.netmodule Exe.netmodule -subsystem:console -out:Exe.exe -ltcg -entry:Exe.Program.Main

The question: Can this last step be performed by MSBuild? A CMake solution would also be appreciated, but I could not get output type "Module" from CMake.

I would approach it in one of two ways.

Solution 1: Create one more project to bring them all and with a task bind them.

In the .csproj of this project it should be enough to add:

<ItemGroup>
    <AddModules Include="Lib.netmodule" />
    <AddModules Include="Exe.netmodule" />
</ItemGroup>

This should pass those files to the compiler task as the AddModules parameter (see usage of Csc task in Microsoft.CSharp.CurrentVersion.targets , line 250).

This will result in one assembly. That assembly will span across the .netmodule files and the file resulting from compiling the third project. It means you need to copy/distribute all of them for that assembly to work.

But you really did that yourself, you already have AddModule items in your Exe.csproj so I'm probably missing something.

Solution 2: Let the second project build the assembly.

It could be done like so:

<ItemGroup>
  <ModulesToInclude Include="Lib.netmodule" />
</ItemGroup>

<Target Name="LordOfTheRings">
  <!-- The below uses the netmodule generated from VB code, together with C# files, to generate the assembly -->
  <Csc Sources="@(Compile)"
       References="@(ReferencePath)"
       AddModules="@(ModulesToInclude)"
       TargetType="exe" />
</Target>

<Target Name="AfterBuild" DependsOnTargets="LordOfTheRings">
  <!-- This target is there to ensure that the custom target is executed -->
</Target>

I have a very similar solution. The above is more a hint at how to approach it that a copy-paste solution.


Disclaimer: I just started experimenting with msbuild recently, I'm happy to improve this answer if something doesn't work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM