Executing Visual Studio from Team Foundation Server - tfs

I am working on a project to convert a large application (Database, SSIS packages, some class libraries, PowerShell scripts) from building using a batch file to TFS build and possibly deploy. Currently, how the process works is that a Batch file is ran, and some inputs are taken in, which get stored as variables (environment which helps determine which build to kick off), than MSBuild is called, like so
MSBUILD BuildAll.proj /t:Clean,Build,Package /p:Configuration=%CONFIGURATION% /fl /flp:Summary;LogFile=%LOGFILE% /fl1 /flp1:ErrorsOnly;LogFile=%ERRORLOGFILE% /tv:4.0
The BuildAll.proj is a custom project file that sets some variables, but most importantly (and the part that is failing) executes a command to call Visual Studio to build the solution
<Target Name="Build">
<Exec Condition=" '%(Application.Solution)' != '' " Command='"$(DevEnv10InstallDir)Devenv.com" %(Application.Solution) /Rebuild $(Configuration)' />
</Target>
Here is the log that gets built out
Target "Build" in project "C:\Source\MBFinancial\Development\Trunk\Source\BuildAll.proj" (entry point):
Using "Exec" task from assembly "Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "Exec"
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Devenv.com" Application.sln /Rebuild LocalDevelopment
When I look at the Build Events, it just hangs there, and the Build Folder on the TFS server is never populated as it should. The build process runs just fine if I run it from the TFS server, running as the TFS Build Account, so I know all the proper installs are on the Build Server, but when I execute through Visual Studio Team Explorer, it hangs. I can provide additional details if needed.

you can add a /out switch to devenv.exe (pretty sure you can to devenv.com too), I think you're only looking at teambuild's output and not devenv's, which hopefully will shed light on what's up.
FWIW here's my exec task:
<Exec WorkingDirectory="$(MSBuildProjectDirectory)\Xxx" Command='"$(VS2010)"\devenv.exe xxx.sln /REBUILD "$(Configuration)|x86" /OUT $(MakeLog) /LOG C:\tmp\ActivityLog.xml' ContinueOnError="false" />
I can't remember why I use .exe and an outfile rather than .com.

Related

Buildiling .net multiple Solutions in Jenkins using MSBuild plugin

I am trying to build .net multiple Solutions files in Jenkins using MSBuild plugin. I have installed and configured MSBuild. In the Build step - 'Build a Visual Studio project or Solution using MSBuild', under 'MSBuild Build FIle' how do we reference the build file(ex, test.txt) which have 5 solution file paths in it. When i give the path directly C:\test.txt, the build is failing with error
C:\test.txt(1,1): error MSB4025: The project file could not be loaded. Data at the root level is invalid. Line 1, position 1.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:02.22
Build step 'Build a Visual Studio project or solution using MSBuild' marked build as failure
Finished: FAILURE
Below are the solution file paths mentioned in my test.txt file
C:\workspace\DotNet\Constants.sln
C:\workspace\DotNet\Security.sln
C:\workspace\DotNet\Library.sln
C:\workspace\DotNet\Mapping.sln
C:\workspace\DotNet\MapForce.sln
Could I build by mentioning five solution file paths in .txt file and referring the .txt file path by just using one 'Build a Visual Studio project or solution using MSBuild section'?
I have tried with command prompt manually it builds fine. Also, tried with 'Execute Windows batch command' option by batch commands it works fine and builds successfully, facing problem with 'Build a Visual Studio project or solution using MSBuild' when spefiying .txt file which has many solution file paths in it.
Appreciate your help on this.
MSBuild builds MSBuild files, written in xml, not text files (hence the error MSB4025). Luckily creating a file which can be used to build x other files is fairly simple, here is an example:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<Sln Include="C:\workspace\DotNet\Constants.sln"/>
<Sln Include="C:\workspace\DotNet\Security.sln"/>
<Sln Include="C:\workspace\DotNet\Library.sln"/>
<Sln Include="C:\workspace\DotNet\Mapping.sln"/>
<Sln Include="C:\workspace\DotNet\MapForce.sln"/>
</ItemGroup>
<Target Name="Build">
<MsBuild Projects="#(Sln)" Targets="Build" Properties="Configuration=Release;Platform=Win32" BuildInParallel="True" />
</Target>
</Project>
This is just written in a straightforward way, shorter but possibly more complicated code might apply. E.g. to just build all solutions in the C:\workspace\DotNet directory you'd just write <Sln Include="C:\workspace\DotNet\*.sln"/> instead of manually spelling out each of them. Or if all of them reside in that directory you could include them by filename only, e.g. <Sln Include="Constants.sln"/> and then add the directory when expanding the list like Projects="#(Sln->'c:\workspace\dotnet\%(Identity)')".
As you can see this builds the Win32|Release version, change that by altering the properties. Also not the BuildInParallel switch: only use this if the solutions don't depend on each other.

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.

How to use MSBuild with TFS and SSIS

I'm a complete novice in using TFS Build definition and MSBuild scripts.
I want to automate my SSIS build and deployments and create a build definition which will build and deploy my SSIS project whenever I queue it up.
I found this project: http://sqlsrvintegrationsrv.codeplex.com/releases/view/82369
which allows you to create a DLL which you can place in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies
Then you can call the SSIS.MSBuild.proj (See end for this) with certain parameters like this in a visual studio command line:
MSBuild SSIS.MSBuild.proj /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="DEV",,SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
or I can put it in a BAT file like this:
%systemroot%\Microsoft.Net\Framework\v4.0.30319\MSBuild.exe SSIS.MSBuild.proj /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="DEV",,SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
It works fine when you run the BAT file, it builds and deploys the SSIS project.
Questions:
How can I use this so it is automated, so I can manually kick off a build and deployment from within VS/TFS? Using a build definition.
How can I ensure the correct configurations are selected, and the correct destination server? For example we have SSIS configurations for DEV, SIT, SYS, UAT, PRD. Each with its own server name. Do I need a separate build definition for each environment or is there a way to use one build definition?
Anything useful in using powershell somehow?
Here is SSIS.MSBuild.proj:
<?xml version="1.0" encoding="Windows-1252"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="SSISBuild;SSISDeploy">
<!--Requires a property called $(SSISProj) to be defined when this script is called-->
<UsingTask TaskName="DeploymentFileCompilerTask" AssemblyFile="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.SqlServer.IntegrationServices.Build.dll" />
<Target Name="SSISBuild" Condition="'$(SSISProj)' != ''">
<PropertyGroup>
<SSISProjPath>$(SSISProj)\$(SSISProj).dtproj</SSISProjPath>
</PropertyGroup>
<Message Text="**************Building SSIS project: $(SSISProjPath) for configuration: $(CONFIGURATION)**************" />
<DeploymentFileCompilerTask
InputProject="$(SSISProjPath)"
Configuration="$(CONFIGURATION)"
ProtectionLevel="DontSaveSensitive">
</DeploymentFileCompilerTask>
</Target>
<UsingTask TaskName="DeployProjectToCatalogTask" AssemblyFile="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.SqlServer.IntegrationServices.Build.dll" />
<Target Name="SSISDeploy" Condition="'$(SSISProj)' != ''">
<Message Text="**************Publishing SSIS project: $(SSISProj) to: $(SSISServer) to folder: $(PROJECTNAME)**************" />
<PropertyGroup>
<ISPac>$(SSISProj)\bin\$(CONFIGURATION)\$(SSISProj).ispac</ISPac>
</PropertyGroup>
<DeployProjectToCatalogTask
DeploymentFile="$(ISPac)"
Instance="$(SSISServer)"
Folder="$(PROJECTNAME)"
CreateFolder="true"/>
</Target>
</Project>
EDIT I tried adding some MSBuild Arguments to the TFS Build Definition. I tried various combinations of arguments, some with quotes, some without. I couldn't get it to work.
"C:\Users\me\Desktop\Buildssis\SSIS.MSBuild.proj" /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="SIT",SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
But I always get this error:
MSBUILD : error MSB1008: Only one project can be specified.
Switch: C:\Users\me\Desktop\Buildssis\SSIS.MSBuild.proj
For switch syntax, type "MSBuild /help"
I think you are prety close about the solution. you can call your package in console application and set the variables there. And for different DEV, SIT, SYS, UAT, PRD. you can have configuration file for the the console application. and then you can set the package variables in the console app. I hope it solve the preoblem. As much i could understand.
Please let me know if this is not related to your problem then explain your question a bit more.
To answer your question, the best way would be to use an UpgradeBuildTemplate for your team build.
Modify the build script to calls the tasks that you have created in the "AfterCompile" target of the build. See below
http://msdn.microsoft.com/en-us/library/aa337604(v=vs.100).aspx
You can pass build parameters in your team build definition. If you edit your build definition and edit Process, you will see option to pass MSBuild Arguments.

Specifying Tf.exe and MsBuild version in CC.Net

The problem
I am porting a build to a newer version of CC.Net (from 1.4.4 to a very recent version of 1.6). Previously the build mixed <msbuild /> tags with <exec /> tags that call batch files with tf.exe commands in them.
The build must have relied on the source control version being the same between the msbuild and tf.exe commands, because now I get an error on the tf.exe command:
Unable to determine the workspace.
When I launch a command prompt, with runas as the same user the service is using, and call the same command the batch file used (from the same directory), I get the same error.
However if I launch a Visual Studio 2010 command prompt, with runas, and call that command (from the same directory), I don't get the error.
This also makes sense, because the %UserProfile%\Local Settings\Application Data\Microsoft\Team Foundation\3.0\Cache\VersionControl.config file has the workspace mapping, but the ...\1.0\Cache\VersionControl.config file does not.
The solution I'd like to try
I can't simply change the PATH for the batch file because it actually needs to build against an older version of Visual Studio, due to licensing.
I think I could solve this by specifying the TFS/msbuild version used with the <msbuild /> tag. But I'm not sure how to go about this. I think it would be some sort of server configuration, but I haven't found anything about this in the docs.
My question is: How do I specify the version of TFS that Cruise Control uses for <msbuild /> tags? Or is there another way to solve this?
As far as I know, there is no way to specify this directly with msbuild. The way it is solved at our place is that we set the executable for msbuild to a bat-file that's basically a copy of the desired vcvars-script (used by Visual Studio Command Prompt to set up the environment).
<tasks>
<msbuild>
<executable>vcvars_VC10_amd64.bat</executable>
<workingDirectory>SomeFolder</workingDirectory>
<environment>
<variable name="foo" value="bar" />
</environment>
<projectFile>msbuild.proj</projectFile>
<buildArgs>/m:2 /p:Configuration=Release /p:Platform=x64 /v:normal</buildArgs>
<targets>Clean;Build;Test</targets>
<timeout>3600</timeout>
</msbuild>
</tasks>
Maybe you could get away with setting up your desired paths using the <environment> property as well.
I won't accept this solution, in case people have other solutions (or can solve the problem I directly asked about), but here's how I ended up solving my problem:
I added a workspace sync task to the top of the build tasks:
<tasks>
<exec>
<description>Sync the workspaces with the TFS server, for the version of TFS used in exec commands</description>
<executable>Tf.exe</executable>
<buildArgs>workspaces /s:$(TfsServer)</buildArgs>
</exec>
This works because in my scenario, I don't care about the versioning as much as I do about the workspaces getting out of sync.

Using MSBuild task for ftp a directory in TFS 2010

Is there any tutorial to show how can I use MSBuild tasks like FtpUploadDirectoryContent to copy file/directory to a remote host using FTP in Team Build 2010? I never used a MSBuild task in TFS 2010.
Just put it in the AfterBuild target of one of your projects - probably best to put it in the project that's at the top of your dependency graph. You can add a condition if you don't want it to run in Visual Studio, or if you only want to do the FTP transfer for a particular build configuration. For example:
<Project>
...
<Target Name="AfterBuild" Condition="'$(BuildingInsideVisualStudio)'!='true'" >
<!-- Insert your FTP task here -->
</Target>
</Project>
See How to: Extend the Visual Studio Build Process
You might consider modifying your build process template (WF) and using the InvokeProcess activity call out to FTP.exe.
There are also a handful of FTP activities and command line utilties if the built in Windows FTP command line client doesn't work for you.

Resources