How to extend MSBuild to copy indirect references.

This article is a part of MSBuild series. Here is a list of all articles in this series:

  1. MSBuild basics for Sitecore devs
  2. MSBuild extension points
  3. MSBuild Build and Publish pipelines
  4. How to extend MSBuild publish pipeline to copy content files from all Helix modules to the output.
  5. How to extend MSBuild publish pipeline to apply transform files.
  6. How to extend MSBuild to execute Unicorn Sync action.
  7. VIDEO: Speed comparison of gulp vs MSBuild build & publish
  8. How to extend MSBuild to copy indirect references.
  9. VIDEO: How to configure local development environment step by step
  10. VIDEO: How to set up Build & Release pipelines on VSTS step by step (the easiest way)
  11. VIDEO: Speed comparison of gulp vs MSBuild build & publish.
  12. How to extend MSDeploy with custom providers for Unicorn and Transform Files
  13. VIDEO: How to set up Build & Release pipelines on VSTS step by step (to generate WDP package, apply transform files and sync unicorn by MSDeploy)
  14. Helix Publishing Pipeline by Richard Szalay

The compiler is smart enough to detect if a referenced assembly is used or not. So if in your root project, you add a Project Reference to other project and in that other project you have a NuGet package that is not used in the code, the compiler will not copy it to the output of the root project.

Usually it is a good thing, however, when developing a Sitecore website using Helix principles, it can be problematic. For example, you have Foundation.Serialization project with Unicorn package and you added it as a project reference in your WebRoot project. If you do a publish of WebRoot, the Unicorn.dll will not be published, because it's not used anywhere in the code. It's only mentioned in the config patch, but the compiler does not understand that.

This only happens with packages.config. If you use PackagesReferences it works fine without any fix. Fortunately, the fix is quite easy to implement and here is the code that we can add to the Helix.targets to fix it: 

<PropertyGroup>
  <ResolveAssemblyReferencesDependsOn>
    $(ResolveAssemblyReferencesDependsOn);
    CollectReferencesFromHelixModules;
  </ResolveAssemblyReferencesDependsOn>
</PropertyGroup>

<Target Name="CollectReferencesFromHelixModules">
  <MSBuild Projects="@(ProjectReference)" Targets="ResolveAssemblyReferences" BuildInParallel="$(BuildInParallel)">
    <Output TaskParameter="TargetOutputs" ItemName="ReferencesFromHelixModules" />
  </MSBuild>
  <ItemGroup>
    <Reference Include="%(ReferencesFromHelixModules.FusionName)" Condition="'%(ReferencesFromHelixModules.CopyLocal)'=='true'">
      <ResolvedFrom>%(ReferencesFromHelixModules.ResolvedFrom)%(ReferencesFromHelixModules.FullPath)</HintPath>
      <Redist>%(ReferencesFromHelixModules.Redist)</Redist>
      <CopyLocal>true</CopyLocal>
    </Reference>
  </ItemGroup>
</Target>