Search code examples
visual-studiomsbuildaspnetboilerplate

Copy plugin dlls to folder after successful build only when output is changed


I have dynamically loaded plugin libraries that I'd like to push to running web host with new build while in development. To avoid "file in use" error on copy I create app_offline.htm using msbuild in csproj to recycle the web app.

<ItemGroup>
  <ModuleFiles Include="$(TargetDir)*.dll" />
</ItemGroup>

<Target Name="PublishModule" AfterTargets="PostBuildEvent">
  <WriteLinesToFile File="$(SolutionDir)src\FooBar.Web.Mvc\app_offline.htm" />
  <Copy SourceFiles="@(ModuleFiles)" DestinationFolder="$(SolutionDir)src\FooBar.Web.Mvc\Modules\FooBar.Blog\" SkipUnchangedFiles="true" />
  <Delete Files="$(SolutionDir)src\FooBar.Web.Mvc\app_offline.htm" />
</Target>

It works as expected, but I'm wondering if it can be improved by wrapping PublishModule target to only execute when output is changed. So If I build plugin that didn't change it won't unnecessarily recycle the web host. SkipUnchangedFiles prevents any unchanged DLLs from being copied, but app_offline is still created.

I need something like this, but it doesn't seem to do anything.

<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>

Solution

  • You can make your target incremental so it only builds when the inputs change or the expected outputs are missing:

    <ItemGroup>
      <ModuleFiles Include="$(TargetDir)*.dll" DestinationPath="$(SolutionDir)src\FooBar.Web.Mvc\Modules\FooBar.Blog\%(Filename)%(Extension)" />
    </ItemGroup>
    
    <Target Name="PublishModule" AfterTargets="PostBuildEvent" Inputs="@(ModuleFiles)" Outputs="@(ModuleFiles->'%(DestinationPath)')">
      <WriteLinesToFile File="$(SolutionDir)src\FooBar.Web.Mvc\app_offline.htm" />
      <Copy SourceFiles="@(ModuleFiles)" DestinationFiles="@(ModuleFiles->'%(DestinationPath)')"/>
      <Delete Files="$(SolutionDir)src\FooBar.Web.Mvc\app_offline.htm" />
    </Target>
    

    See How to: Build Incrementally for details on incremental builds.