1

The scenario is this one:

We need to reduce the time of building a solution that contains several projects. We have the constraint that we cannot consolidate projects, thus we are having around 50 projects. The build time at this moment is around 3 minutes. A quick test by setting manually all project references property CopyLocal to False and changing the output directory to a central one improved the building performance by more than 50%. However, the problem is by doing so, when we deploy or run out test on CI, DLLs are missing (I suspect it builds by using the main project and not all projects of the solution).

I thought that I can have 2 sets of build directives. One when developing that will set CopyLocal to false and output into a single directory all DLL, and one when deploying in the CI and Azure website (which preserve the normal DLL location).

I have read in previous post and here that is it possible to the CopyLocal with MsBuild by using a target file (not sure yet about the output directory). Thus, I could have this one use the targets file on the local machine and not when deploying.

My question is how can I have Visual Studio's Build action to use a specific targets file when developing and not when deploying with the IDE to Azure or the CI environment?

Community
  • 1
  • 1
Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341
  • CopyLocal cannot affect build speed unless the machine is severely RAM constrained. Except for one case: disable your anti-malware and try again. – Hans Passant Mar 14 '16 at 17:37
  • Yesterday I did some benchmark and changing CopyLocal and having all the DLL to go in the same folder made MsBuild to write 58 megs on disk instead of +800 megs. So, yes, the overhead of having local copy can change the build speed. Anti-malware is not scanning the src folder, ram is at 8 gig. – Patrick Desjardins Mar 14 '16 at 17:41
  • Copy local surely can impact build speeds. especially if you have many projects. Having SSDs or RAMdrives in your build server can fix that though... Few more nice tricks can be found here: https://blog.jetbrains.com/dotnet/2015/11/18/resharper-build-webinar-recording-and-qa/ – jessehouwing Mar 14 '16 at 18:19
  • @jessehouwing I am running on a SSD drive. Thank you for the link. See my other comments under your answer. – Patrick Desjardins Mar 14 '16 at 19:49

1 Answers1

1

You can use a itemgroup with a condition to optionally override the output directory. Or use it to remove the copy-local flag from your Reference items. The same trick works for ProjectReferences.

See:

Then make this group conditional using a number of flags:

 IsDesktopBuild             Is true for a build that's running outside of a build server
 BuildingInsideVisualStudio Is true for a build that's running inside VS

See:

Putting it all together:

<Target Name="BeforeBuild" Condition="'$(IsDesktopBuild)' != 'true'">
  <ItemGroup>
        <ProjectReferenceNew Include="@(ProjectReference)">
           <Private>False</Private>
        </ProjectReferenceNew>
        <ProjectReference Remove="@(ProjectReference)"/>
        <ProjectReference Include="@(ProjectReferenceNew)"/>
  </ItemGroup>

  <ItemGroup>
        <ReferenceNew Include="@(Reference)">
           <Private>False</Private>
        </ReferenceNew>
        <Reference Remove="@(Reference)"/>
        <Reference Include="@(ReferenceNew)"/>
  </ItemGroup>
</Target>

To improve performance of this translation, you'll want to specify the Input and Output parameters of the Target:

<Target Name="BeforeBuild" Condition="'$(IsDesktopBuild)' != 'true'"
    Inputs="@(Reference);@(ProjectReference)" 
    Outputs="@(Reference);@(ProjectReference)"
>
    ....
</Target>
Community
  • 1
  • 1
jessehouwing
  • 106,458
  • 22
  • 256
  • 341
  • Thank you @jessehouwing. Concerning the first part, I suppose that this need to be put in all projects. What I did was to add at the bottom of every projects and the targets file : https://gist.github.com/MrDesjardins/569f34022a8a5d84ec9c I can use the same principle to avoid repetition. What do you think? – Patrick Desjardins Mar 14 '16 at 19:46
  • Concerning the second part of your answer, I am not really sure what you are doing with the Inputs and Outputs property. Can you elaborate? – Patrick Desjardins Mar 14 '16 at 19:48
  • I used the code between the Target node of your Xml file into my .targets file and I am getting "The attribute "Remove" in element is unrecognized." – Patrick Desjardins Mar 14 '16 at 20:18
  • What version of MsBuild are you calling? teh ability to remove project items was added with MsBuild 4.0 => https://msdn.microsoft.com/en-us/library/bb651786(v=vs.90).aspx – jessehouwing Mar 14 '16 at 20:38
  • The Input/Output tag ensure that MsBuild knows which items you're about to manipulate and what you intend to do with them. It helps reduce time further by supporting incremental builds. => https://msdn.microsoft.com/en-us/library/ee264087.aspx – jessehouwing Mar 14 '16 at 20:40
  • If your current projects have custom targets or pre/postbuild scripts, then it's important that you add these parameters, it can speed up things tremendously by just skipping stuff that can be skipped. – jessehouwing Mar 14 '16 at 20:41
  • Your Gist only sets the default value, but it doesn't override the value specified in the project file in any way. – jessehouwing Mar 14 '16 at 20:51
  • -msbuild /version show me : 12.0.31101.0 -The input and output make sense, thank you I get it. -Weird that is doesn't override it, I can clearly see that it reduces drastically the amount of write and also does not produce all additional DLL in bin folders. – Patrick Desjardins Mar 14 '16 at 20:58