Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>In a previous project I worked with one big solution with project references and bumped into a performance problem as well. The solution was three fold:</p> <ol> <li><p>Always set the Copy Local property to false and enforce this via a custom msbuild step</p></li> <li><p>Set the output directory for each project to the same directory (preferably relative to $(SolutionDir)</p></li> <li><p>The default cs targets that get shipped with the framework calculate the set of references to be copied to the output directory of the project currently being built. Since this requires calculating a transitive closure under the 'References' relation this can become <strong>VERY</strong> costly. My workaround for this was to redefine the <code>GetCopyToOutputDirectoryItems</code> target in a common targets file (eg. <code>Common.targets</code> ) that's imported in every project after the import of the <code>Microsoft.CSharp.targets</code>. Resulting in every project file to look like the following: </p> <pre><code>&lt;Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"&gt; &lt;PropertyGroup&gt; ... snip ... &lt;/ItemGroup&gt; &lt;Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /&gt; &lt;Import Project="[relative path to Common.targets]" /&gt; &lt;!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. &lt;Target Name="BeforeBuild"&gt; &lt;/Target&gt; &lt;Target Name="AfterBuild"&gt; &lt;/Target&gt; --&gt; &lt;/Project&gt; </code></pre></li> </ol> <p>This reduced our build time at a given time from a couple of hours (mostly due to memory constraints), to a couple of minutes.</p> <p>The redefined <code>GetCopyToOutputDirectoryItems</code> can be created by copying the lines 2,438&ndash;2,450 and 2,474&ndash;2,524 from <code>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets</code> into <code>Common.targets</code>.</p> <p>For completeness the resulting target definition then becomes:</p> <pre><code>&lt;!-- This is a modified version of the Microsoft.Common.targets version of this target it does not include transitively referenced projects. Since this leads to enormous memory consumption and is not needed since we use the single output directory strategy. ============================================================ GetCopyToOutputDirectoryItems Get all project items that may need to be transferred to the output directory. ============================================================ --&gt; &lt;Target Name="GetCopyToOutputDirectoryItems" Outputs="@(AllItemsFullPathWithTargetPath)" DependsOnTargets="AssignTargetPaths;_SplitProjectReferencesByFileExistence"&gt; &lt;!-- Get items from this project last so that they will be copied last. --&gt; &lt;CreateItem Include="@(ContentWithTargetPath-&gt;'%(FullPath)')" Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(ContentWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" &gt; &lt;Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways" Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='Always'"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory" Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/&gt; &lt;/CreateItem&gt; &lt;CreateItem Include="@(_EmbeddedResourceWithTargetPath-&gt;'%(FullPath)')" Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" &gt; &lt;Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways" Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='Always'"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory" Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/&gt; &lt;/CreateItem&gt; &lt;CreateItem Include="@(Compile-&gt;'%(FullPath)')" Condition="'%(Compile.CopyToOutputDirectory)'=='Always' or '%(Compile.CopyToOutputDirectory)'=='PreserveNewest'"&gt; &lt;Output TaskParameter="Include" ItemName="_CompileItemsToCopy"/&gt; &lt;/CreateItem&gt; &lt;AssignTargetPath Files="@(_CompileItemsToCopy)" RootFolder="$(MSBuildProjectDirectory)"&gt; &lt;Output TaskParameter="AssignedFiles" ItemName="_CompileItemsToCopyWithTargetPath" /&gt; &lt;/AssignTargetPath&gt; &lt;CreateItem Include="@(_CompileItemsToCopyWithTargetPath)"&gt; &lt;Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways" Condition="'%(_CompileItemsToCopyWithTargetPath.CopyToOutputDirectory)'=='Always'"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory" Condition="'%(_CompileItemsToCopyWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/&gt; &lt;/CreateItem&gt; &lt;CreateItem Include="@(_NoneWithTargetPath-&gt;'%(FullPath)')" Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_NoneWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" &gt; &lt;Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways" Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='Always'"/&gt; &lt;Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory" Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/&gt; &lt;/CreateItem&gt; &lt;/Target&gt; </code></pre> <p>With this workaround in place I found it workable to have as much as > 120 projects in one solution, this has the main benefit that the build order of the projects can still be determined by VS instead of doing that by hand by splitting up your solution.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload