I'm having a lot of trouble trying to identify why MSBuild is blocking access to a dll used inside a new T4 template I just created.
The problem is kind of hard to explain (and even ask, as is evident by the title).
I created a T4 template to generate a c# class that is a wrapper to N other classes we have. This was the solution I came up with to expose multiple WCF Services over the same endpoint.
The template code itself uses an assembly (Mobiltec.Framework.dll) that contains various extension methods to simplify the template code. At first, I just added a .tt file to the project and went with it: it updated the generated file whenever the .tt changed, as expected.
Since this template reads other assemblies and generates a class based on them, I wanted to make sure it transformed the output on every build, as to prevent developers from forgetting to update the output and things like that.
After a few days of searching, I finally came to a fairly decent solution and the templates were being transformed on each build 'almost' perfectly. The problem is that, whenever I build the project and the template is transformed, it locks access to the aforementioned dll, preventing me from deleting or updating the file afterwards. I have to close Visual Studio for it to release the file.
Now, this is not a huge problem on local developers machines, but it fails our build on the build server because the output there is all in a single folder:
- The build process starts compiling our master solution
- The project containing the T4 template is compiled, and the class is generated
- Another project is compiled and tries to change the dll in the output folder, failing the build after trying 10 times
Is this a bug in the Text Templating system or am I possibly making some mistake that is causing the file to be locked?
Update
I'll try to explain a bit better how everything is setup here, so that maybe you guys can have a better understanding of the problem.
There is a .tt template file, one for each of our web applications (there are 4 in total). This template is responsible for automatically generating a wrapper class for all of our WCF services contracts. The contracts themselves are separated in another assembly (also one for each of the four components) so that they can be shared with clients, without carrying all the other dependencies.
I had to implement a bunch of methods to print, using reflection, the signatures, members and types from each assembly, to generate text that could compile as a valid class. I implemented these methods on a common assembly.
Each tt file then references this common assembly, along with the service contracts assembly, to generate this wrapper class. The template has a bunch of lines like these for instance:
<#@ assembly name="Mobiltec.M3.EG.Services.dll" #>
<#@ assembly name="Mobiltec.M3.Common.dll" #>
Since we need these templates to be transformed on every build, I've changed the project files to enable this and installed the Visual Studio SDK and Visualization and Modeling SDK, following this advice.
Now, whenever I make any changes to code in any of the assemblies or their dependencies, I get messages like these:
Error 1516 Compiling transformation: Metadata file 'Mobiltec.M3.EG.Services.dll' could not be found. Line=0, Column=0 Mobiltec.M3.EG
Error 1517 Compiling transformation: Metadata file 'Mobiltec.M3.Common.dll' could not be found. Line=0, Column=0 Mobiltec.M3.EG
Or:
Error 395 Could not copy "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.Common\Mobiltec.M3.Common.Desktop\bin\Mobiltec.M3.Common.dll" to "bin\Mobiltec.M3.Common.dll". Exceeded retry count of 10. Failed. Mobiltec.M3.EG
Error 396 Unable to copy file "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.Common\Mobiltec.M3.Common.Desktop\bin\Mobiltec.M3.Common.dll" to "bin\Mobiltec.M3.Common.dll". The process cannot access the file 'bin\Mobiltec.M3.Common.dll' because it is being used by another process. Mobiltec.M3.EG
Error 407 Could not copy "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.EG\Mobiltec.M3.EG.Services\bin\Debug\Mobiltec.M3.EG.Services.dll" to "bin\Mobiltec.M3.EG.Services.dll". Exceeded retry count of 10. Failed. Mobiltec.M3.EG
Error 408 Unable to copy file "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.EG\Mobiltec.M3.EG.Services\bin\Debug\Mobiltec.M3.EG.Services.dll" to "bin\Mobiltec.M3.EG.Services.dll". The process cannot access the file 'bin\Mobiltec.M3.EG.Services.dll' because it is being used by another process. Mobiltec.M3.EG
Also, when I try to delete the bin/obj folders though Visual Studio, I get this error:
If I manually try to delete the files on the bin folder, in Windows Explorer, I get this error:
Basically, it all boils down to MSBuild locking the files (all of the explicitly referenced ones in the .tt) in the bin folders of the web application projects. Whenever the project is rebuilt, the referenced dlls are copied to the project's own output folder again, and this fails since windows cannot overwrite the files that are 'in use'.
The only thing that releases the lock is completely closing down Visual Studio and reopening it. I tried manually killing all MSBuild processes on the machine, or closing and reopening the solution, but nothing works. Even tools like Unlocker are unable to remove the lock on the files (it doesn't even detect it).
The initial problem on the build server was caused by the very same issue, but is now 'solved' because I renamed one of the dlls. Two different projects (Mobiltec.Framework.Desktop
and Mobiltec.Framework.WindowsMobile
) had the same output dll name (Mobiltec.Framework.dll
), and the second one was trying to overwrite the first on the global output folder on the build, after the transformation took place for the .tt files (locking the original dll) failing the whole process.