We just had an issue with the build of one of our applications on our CI server. The build in question no longer contained an assembly in the bin folder where previously it had.
Pinpointing the issue
At first I couldn’t understand why the assembly was not in the bin folder as the assembly was clearly marked in Visual Studio as “Copy Local = true”. I then looked through the build history to see when the assembly was last published but instead of finding a point in time that it stopped working I found that the issue only occurred on one of our build agents. Two of the three build agents correctly publish the assembly but the other one did not.
The next step was to compare the configuration of the three build agents. The difference was that the two build agents that worked did not have the specific assembly in the GAC where the failing one did. But hang on a minute “Copy Local = true” should still copy the assembly into the bin folder whether referencing a assembly on the file system or in the GAC (try setting the System assembly to “Copy Local = true”).
Investigating the Issue
My first thought was to remove the assemblies from the GAC on the failing build agent but the voice in my head kept saying “This should work dam it” so instead I cracked open the project file. The first thing I noticed was that on some of the assembly references there was an inner element called private which was set to true where as others, including the affected assembly, had no element:
BUT they both show up Visual Studio as “Copy Local = true”. So to test how the project file works I set the System assembly to be local as well and sure enough the inner element “Private” with a value of true appears in the project file where before there was none. As expected when built the System.dll was output to the bin folder. However the curious thing was after reverting the System assembly back to “Copy Local = false” the element was not removed instead the value was changed to false.
So my final step was to add the “Private” element to the affected assembly reference and set its value to true, commit the changes to code repository and force a build on the build agent in question and low and behold it worked.
To see how the project file got into this state I tried adding assembly references to a dummy project where some assemblies were in the GAC and some were not. My results show that if you add an assembly reference from the file system then the Copy Local value is implied as no Private element is added to the assembly reference. The Visual Studio UI will show a value of false if the assembly is in the GAC and true if it is not. What this means is that the build output will be different for different environments.
The work around
The only way round this is either to edit the project file manually or to set the Copy Local value to false and then back to true again when adding a reference to the project which you do want included in the build output.