I’m putting this here because there is a lot of google foo out there for this particular issue and none of the answers were from an authorative enough source for me to trust, so I just had to figure it out for myself.
We have a Visual Studio Web Application solution that has been continually upgraded in the business for at least the last 7 years. It has been through several architecture changes and Visual Studio project format upgrades up to Visual Studio 2010 and ASP.Net 4. Having started as an original n-tiered architecture we have multiple layers of references/dependencies between projects roughly ordered as such:
- Third Party Binary References
- DAL layer
- Business Logic Layer
- Application Settings Transport project
- Web Application Application Layer
There is some cross referencing from projects at different layers further down than the one below it (the Web App has to reference some of the Third Party dlls).
We have been publishing the application manually through VS 2010 by rebuilding the whole solution manually using specific Build Configurations and then right-clicking on the web project and using the publishing wizard.
We recently tried to get Jenkins to automatically build and publish (we ended up using package deployment, not publishing) the application using an msbuild task and were unable to get either the transforms, or the correct binary dlls in the publish folder. This was causing us no end of pain.
The multiple causes and fixes
Just finding the correct msbuild command line to get it to work was chore. We ended up settling on:
msbuild $WORKSPACE\Website\Website.sln /v:$MsBuildVerbosity /p:Configuration=$PublishEnvironment /p:Platform="Any CPU" /p:DeployOnBuild=True /p:DeployTarget=Package /p:AutoParameterizationWebConfigConnectionStrings=False /p:AllowedReferenceRelatedFileExtensions=".pdb" /p:ExcludeGeneratedDebugSymbol=false /p:_PackageTempDir="$WORKSPACE\Publish\$PublishEnvironment\Website"
Once we got that right we had to actually do the following to get the correct output published with correct configs:
- Move the config transform task into the pre-build section from post-build section
- There seems to be a race condition in msbuild between the transforms and the file publish activity. Sometimes they would make it in, sometimes they wouldn’t.
- Move any post-build tasks in pre-build.
- Update every single non-GACed assembly or project reference to ensure they had the node <Private>True</Private> (this is the xml behind the Copy Local property in the reference properties screen)
- This is because VS 2010 sees the absence of the Private attribute as a default of Copy Local = True whereas msbuild sees it’s absence as defaulting to false
- This is an absolutely nutso decision from MS. Why on earth would you have the command line build system treat project properties differently than your IDE?
- Ensure every single referenced project (or reference project of a referenced project) has a consistent default build Configuration and Platform defined, and then define a solution Configuration that sets the default Project Configs for your release configuration.
- This is critical for us because we have an upstream platform branch that is read-only and externaled into the build/reference path for customer projects.
- The platform projects only have Debug and Release configurations for Any CPU platforms but the customer projects have build configurations to ensure the config trasnforms are generated (Dev, Test, Staging, Production).
- To make this work we had to define these four configurations in the solution file and map them to their corresponding projects in the customer projects but also map them to the Release configuration in the platform projects.