Home > Net >  Create a reusable build task in .NET
Create a reusable build task in .NET

Time:09-17

I want to copy an assembly (let's call it Plugin1.dll) into several different applications that can make use of this plugin. Currently I'm doing something like this in the plugins .csproj/.fsproj file:

  <ItemGroup>
    <Applications Include="..\..\..\Applications\AppA\bin\$(Configuration)" />
    <Applications Include="..\..\..\Applications\AppB\bin\$(Configuration)" />
    <Applications Include="..\..\..\Applications\AppC\bin\$(Configuration)" />
 </ItemGroup>
 <Target Name="CopyPlugin" AfterTargets="Build">
   <ItemGroup>
     <ApplicationOutDirs Include="$([System.IO.Directory]::GetDirectories('%(Applications.Identity)'))" />
   </ItemGroup>
   <Copy SourceFiles="$(TargetDir)$(TargetFileName)" DestinationFolder="%(ApplicationOutDirs.Identity)\Plugins\" SkipUnchangedFiles="true" />
 </Target>

Like this I can copy my Plugin1.dll into AppA, AppB and AppC.

However I would need to copy 11 lines of code into all of my 200 plugin project files.

Is there any way to create a build task that I could reuse here. I'm thinking of something like this:

<CopyPlugin Application="AppA" Directory="Plugins" />
<CopyPlugin Application="AppB" Directory="Plugins" />
<CopyPlugin Application="AppC" Directory="Plugins" />

I saw there are ways to add tasks with UsingTask and specifying C# code inline to implement it, but I'm looking for an easier way...

FYI: I'm using ApplicationOutDirs (target batching) here because the apps can be built for .NET Framework 4.8 and .NET 6 at the same time. So there are sub-folders like net48 and net6.0 in bin/Debug or bin/Release respectively.

CodePudding user response:

What you can do is move the Target into a separate MSBuild file. Create a new text file, wrap your target with in a Project element and paste it into the file:

<Project>
  <Target Name="CopyPlugin" AfterTargets="Build">
    <ItemGroup>
      <ApplicationOutDirs Include="$([System.IO.Directory]::GetDirectories('%(Applications.Identity)'))" />
    </ItemGroup>
    <Copy SourceFiles="$(TargetDir)$(TargetFileName)" DestinationFolder="%(ApplicationOutDirs.Identity)\Plugins\" SkipUnchangedFiles="true" />
  </Target>
</Project>

If the copy targets are the same for all of your plugins you could move the Applications item into that separate file as well. If it varies by plugin you would have to store it in the .csproj file of each plugin.

The traditional file extension for such files is .targets but that is not enforced by MSBuild so you could use whatever file extension you want. For this example, the name is CopyPlugin.targets.

Then, you would need to import that target into each plugin .csproj file as follows:

  <!--
  Content of the plugin .csproj file
  -->
  <Import Project="<relative_path_to_the_targets_file>\CopyPlugin.targets" />
</Project>

Building your plugins will now call the same target for each plugin .csproj file. If you add the Application (better use the singular than the plural) item to the .csproj file the target in the separate file will find it nonetheless. If you put it into the separate file it will not vary between the plugins.

You could also name that separate MSBuild file Directory.Build.targets and put it into the root folder of your plugins. This would make the Import statement obsolete because MSBuild automatically imports files with that name (see the link for more details). However, that would import the file for each and every .csproj file in the folder that contains this .targets file.

  • Related