Overriding the PostBuildEvent in TFSBuild.proj - tfs

I'm using currently VS 2010 and TFS 2008. In one of our solutions we have a .csproj file with the following:
<Target Name="BeforeBuild">
<TransformXml Source="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.config" Transform="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config" Destination="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config.transformed" />
</Target>
<PropertyGroup>
<PostBuildEvent>xcopy "C:\Source\Projects\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config.transformed" "C:\Source\Projects\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.config" /R /Y</PostBuildEvent>
</PropertyGroup>
This works fine when building locally, but TFS is failing when it reaches <PostBuildEvent>, because this path is not available on the build machine. How can I get TFS to either skip the entire "BeforeBuild" or just set the <PostBuildEvent> to an empty string, so that the build will work successfully when built locally in VS2010 and via TFS 2008? I've tried within TFSBuild.proj to set <Target Name="BeforeBuild" />, but this doesn't work.

I'd recommend against using absolute pathnames in your projects - relative paths will make them relocatable and headaches like this will disappear.
i.e. In the post-build xcopy, just change C:\Source\Projects\ProjectX to ..\..\..\ProjectX
(You can't rely on always being able to put your code on C: - I've had to move my code from C: to D: or E: due to lack of disk space or installation of a new drive on several occasions, and using relative paths has made this process very easy. It's also very handy at times to be able to have multiple copies (different versions or branches) of your source code on your PC at once and still be able to build them all)

Related

sfproj - outpath Build error

Iam trying to build the sfproj using msbuild on my build machine , This is what Iam doing.
<target ="package">
<foreach item="File" property="sfproj">
<in>
<items refid="servicefabric.files.sfproj" />
</in>
<do>
<exec program="${msbuild14.exe}">
<arg value="${sfproj}" />
<arg value="/p:Configuration=${config}" />
<arg value="/p:Platform=x64" />
<arg value="/target:Package" />
</exec>
</do>
</foreach>
</target>
The error Iam getting on the build machine is
(_CheckForInvalidConfigurationAndPlatform target) ->
10:25:10 [exec] C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(724,5): error : The OutputPath property is not set for project 'App.sfproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Debug' Platform='x64'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project.
It appears that the Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.1.0 Nuget package contains an MSBuild target that skips building of the sfproj itself (that or makes certain options present in order for it to succeed).
In my case I had accidentally checked in some Nuget package folders into source control that were incomplete. Visual Studio saw the presence of the folder so it didn't try to redownload it. You need to make sure that the build folder is present and it contains an MSBuild target file. Your best bet is just to delete the packages folder entirely to ensure you're starting off with a fresh start.
https://ijustwrite.software/2016/07/20/ouputpath-property-not-set/
I ran into a problem with the same symptoms. It happened to me after upgrading the Azure SDK, but it seems to me that it could easily happen in a few ways. The trouble is that there are two paths in the sfproj file that lead into the directory where nuget packages are stored, oddly enough on the first and last line of the file. This causes problems because you may have overridden where nuget packages are stored (this is not at all uncommon). Since these are relative paths, it's easy for nuget to get the packages to where you have specified, but for the lines in the project to point to "..\packages" instead, which makes Visual Studio want to restore the packages, but to never think they have been restored as it's looking for them in the wrong place.
To fix it all you need to do is modify the paths in the sfproj file to point to wherever you have your nuget packages set to download (this setting is in the nuget.config file, which can be in any directory above your project directory).
These are the two broken lines in my sfproj file:
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" />
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" />
Since I have the value ThirdPartyLibraries\NuGetPackages in my nuget.config file (which is two directories above my project file), these lines had to be modified to be the following in order to work again :
<Import Project="..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props" Condition="Exists('..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" />
<Import Project="..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets" Condition="Exists('..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" />
And that fixes it right up. I was lucky: I had two sfproj files, one with this issue, and one without, so all I had to do was diff to see the trouble.

How to include config transform files in web application filesystem publish output

I've been pulling my hair out on this for a while now. I'm trying to implement a continuous integration and deployment pipeline using TeamCity and Octopus Deploy. I am 99% there, except for one problem. I am using the standard msbuild runner of teamcity, configured to use the version 12 of msbuild.
I need to include the web.config transforms in the published output so they can be packaged into a nuget package for octopus deploy. I do not want the transforms to be applied by msbuild.
I am not using Octopack to create packages. I'm using the built-in teamcity nuget packager. So I'm publishing the website to a filesystem folder and then creating the package from the files in this folder. However, no matter what I do I cannot get msbuild to include the web.config transform files in the publish (I am using Octopus Deploy to perform the transforms, so I don't want msbuild to perform them).
I have verified that all the transform files (Web.Release.config, etc..) are marked as "Content". I have NOT marked them to copy always, because doing this copies them to the bin folder, not the root folder where they belong.
I have removed the /p:Configuration= property from the msbuild command line as I've read that is required for transforms to be applied. my parameters to msbuild look like this:
/p:DeployOnBuild=true /p:PublishProfile=Deployment
There is nothing in the publish profile that seems to relate to transforms. The publish profile contains the filesystem location to publish to.
Any suggestions here?
Note: I've given up and found a different solution, but I'm leaving this open in case anyone has any input.
You could create a custom .nuspec file and reference the files that you want to include from there.
My suggestion would be to have the .nuspec file in the same directory as the web.config / web.release.config files, and make the paths relative from there.
So if you publish to a directory called /output you could use rules like this
<files>
<file src="*.config" target="\" />
<file src="publish\*.*" target="\" />
</files>
So nuget pack nuspecPath would become the way to pack the project
NuSpec Reference
Hope this helps

TFSBuild.proj import common targets after getting from source control

I am currently rewriting/cleaning up our TFS Builds, and I have noticed that we have a lot of duplication. I was wanting to create a "Common.targets" file which all our tfs builds (about 30) would then import.
I have tried a few things along these lines of:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft.Sdc.Common.Tasks"/>
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(BuildDefinition).proj"/>
<PropertyGroup>
<ProjectName>Felix.LincsPublishingHandlers</ProjectName>
</PropertyGroup>
<Import Project="$(BuildDirectory)/src/BuildScripts/Common/CommonTargets.targets"/>
</Project>
Where:
$(BuildDefinition).proj -> contains project/environment specific properties (eg db connection strings, IP addresses)
CommonTargets.targets -> used across all the various projects. Defined in the Build Definition > Source Settings
However when I try to run on the TFS Build agent (version 2010 if anyone needs to know), it tries to run the statements before it has copied down the source files. So I get an error saying that the CommonTargets.targets file doesn't exist.
I've had a look around the web, and the solutions I have come across so far have suggestions that I would prefer not to use:
Copy the Common.targets file under into the same folder that has the TFSBuild.proj file
I have 20+ builds in different folders
I would like them all to use a single common.targets file (not 20 copies of the same file)
Copy the Common.targets file to a location on the build server(s) that the TFS Build Agents can access
I want to have it in source control with the rest of the build scripts and code.
As we are doing a sort of migration project, the Common.targets will change a bit over time, so best to have in source control
Has come across any similar issue of trying to import a Common.targets file that is kept in TFS source control?
Thanks for any help
First, you should consider upgrading TFS as mainstream support for TFS 2010 ends in a few months.
http://nakedalm.com/its-that-time-again-get-ready-to-upgrade-to-tfs-2015/
Second, you should consider updating your build system to use a modern method. Use the TFS 2013 template and PowerShell.

MSBUILD script should zip all assemblies in solution after build, but only gets some DLLs (under local and TFS build)

I want to set up my VS solution so at the end of the build, the installable files are zipped up for easy distribution. This should work under either a local build, or a TFS build. This is set up as follows:
There is one project (called MyApp.Packaging) which contains no code, just an MSBUILD .targets script
The project has references to all other projects, so builds last (confirmed by looking at the Project Build Order)
The build script contains the following to identify and zip (using MSBUILD Community Tasks' ZIP task) the EXE and DLLs into two different packages (there is other code to pull the version number from a version.txt file using MSBUILD Community Tasks - omitted for clarity)
<!-- Set package name and input/output folders -->
<PropertyGroup>
<PackageName>MyAppService</PackageName>
<BuildTargetFolder>$(TargetDir)</BuildTargetFolder>
<PackageOutputFolder>$(OutDir)</PackageOutputFolder>
</PropertyGroup>
<!-- Set location of files -->
<ItemGroup>
<MyAppBinaries Include="$(BuildTargetFolder)*.exe$(BuildTargetFolder)*.dll;" Exclude="$(BuildTargetFolder)MyApp.Packaging.dll" />
<MyAppOtherFiles Include="$(SolutionDir)MyApp.Packaging\InstallService.bat;$(SolutionDir)MyApp.Packaging\UnInstallService.bat;$(BuildTargetFolder)MyApp.HostService.exe.config" />
<MyAppContracts Include="$(BuildTargetFolder)MyApp.Common.DataContext.dll;$(BuildTargetFolder)MyApp.Common.Shared.dll" />
</ItemGroup>
<!-- After building (in Release mode only), build the installation package -->
<Target Name="AfterBuild">
<CallTarget Targets="BuildPackage" Condition="'$(Configuration)'=='Release'" />
</Target>
<!-- Build the package -->
<Target Name="BuildPackage">
<!-- Package for installing the MyApp Service -->
<Zip Files="#(MyAppBinaries);#(MyAppOtherFiles)" Flatten="True" WorkingDirectory="$(MSBuildProjectDirectory)" ZipFileName="$(PackageOutputFolder)\$(PackageName)_$(Major).$(Minor).$(Revision)_Install.zip" />
<!-- Package for MyApp Contracts -->
<Zip Files="#(MyAppContracts)" Flatten="True" WorkingDirectory="$(MSBuildProjectDirectory)" ZipFileName="$(PackageOutputFolder)\$(PackageName)_MyAppContracts_$(Major).$(Minor).$(Revision)_Install.zip" />
</Target>
The ZIP files are created in the TFS drop location when TFS does the build, or the Packaging project's bin folder for a local build.
The second ZIP (containing 2 DLLs) always gets created OK, under local and TFS build.
The problem is that when TFS does the build, the first ZIP contains no EXE and only 2 of the 23 DLLs (and all 3 of the files identified by MyAppOtherFiles). When the build is done locally (and the the Packaging project's bin folder is emptied first), the first ZIP contains no EXE or DLLs, and only the 2 .bat files identified by MyAppOtherFiles.
If I change BuildTargetFolder from $(TargetDir) to $(OutDir), I get the same result.
The TFS build definition uses an unmodified Default Template.
It is as if when TFS does the build, the Packaging project is the 3rd project to be built, rather than the last, therefore only zipping 2 DLLs. However, the solution checked into TFS is exactly the same as what I build locally, and in that case it seems the script cannot see ANY of the binaries. If the local build is done again (without emptying the Packaging project's bin folder), the ZIPs then contain all the required files, but this is obviously because after the 1st build the bin folder now contains the EXE & all the DLLs.
Its also confusing that under a TFS build the MyApp.HostService.exe.config (which is produced by the build) is zipped, but not the MyApp.HostService.exe. And why is the 2nd ZIP always created OK, when it contains DLLs that are skipped in the 1st ZIP ????? I have tried swapping the order the ZIPs are created, but it makes no difference!
What can I do to ensure that the zipping is always done after all the projects are built, under both local and a TFS build?
Thanks
Make sure you set the packaging project dependency on the solution depend on the other projects this way it will always build the others first, thus leaving you with the packaging last.
We do something similar but we have our tfs build definition setup to build the targets/proj file instead of the sln. In the targets/proj file we have a target that compiles our application and then uses wix to create a msi. In your case, you should create a target that uses the msbuild target to compile your exe project and then call the zip target to compress the output. You can leave the outdir parameter as is our you can set the property so the output goes to a directory of your choosing. Building this easy will work on both the tfs server and locally.

Xml file transforms using One-click publish, but not Web Deploy

I have two different ways to deploy my web app - one via IIS Web Deploy, hooked up via MSBuild arguments in a custom build template, and using the One-click publish built into Visual Studio. I have also installed the Slow Cheetah transforms, and can successfully transform Web.config files.
However, I'm stuck on the Nlog.config files. If I use One-click publish, the webapp deploys fine with the updated config file. However, using the actual build process, nothing gets transformed. The source Nlog.config file is copied to the drop locations, the deployment package, and the output folder on the remote server.
The .csproj file has Nlog set up the same way as web.config, i.e.
<Content Include="NLog.config">
<TransformOnBuild>true</TransformOnBuild>
<Content Include="Web.config">
<SubType>Designer</SubType>
<TransformOnBuild>true</TransformOnBuild>
<Content Include="NLog.Debug.config">
<DependentUpon>NLog.config</DependentUpon>
<IsTransformFile>True</IsTransformFile>
The SlowCheetah preview function lets me know that my transform files are well-formed, as well. Not sure what I could be missing.
Well, I really biffed that one. After much poking, I found that I needed to include the following -
<Import Project="TransformsFiles.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
right above the final Project tag, as Sayed mentions here. This will actually transform the files, but they still won't deploy successfully. I'll have to add in some post-build events or something to take care of that. Not the best solution, but it's working, at least.
UPDATE: Just for comprehension, to deploy the files, I had to edit the .csproj file to include a new target and dump them manually to the remote server, but only after the transformation had completed. Take a look at your log file to see what's going on, then just pick up the transformed file and move it to the remote server. That part of the code looks like this -
<Target Name="PostTransformNLogConfig" AfterTargets="TransformAllFiles">
<Copy Condition="Exists('d:\Builds\Binaries\NLog.config' )"
SourceFiles="d:\Builds\NLog.config"
DestinationFiles="\\remoteserver\NLog.config" />
</Target>

Resources