TFS SourcesDirectory is null/empty when used in Expression Editor - tfs

I have a build definition which has two working folders specified. One of these has the following columns:
Status: Active
Source Control Folder: $/MyCompany/MyDivision/Solution2
Build Agent Folder: $(SourceDir)\Solution2
I am trying to access this Build Agent Folder from within the Expression editor in TFS 2012?
I've tried SourcesDirectory but when it gets evaluated it is null/empty. Is there something specific I need to do to get SourcesDiretory to work in the expression editor? Or is there a better way to reference this specific build agent folder in it's entirety?

The SourcesDirectory and indeed the BinariesDirectory are not set till later in the build. They are not immediately available at the start of the build.

Related

Modify setting in web.config on TFS build

Is it possible to modify a web.config file in one of my projects during a Team Foundation Server build? Inside of my web.config file I have two setting keys: VersionNumber and BuildNumber.
Is it possible to change the value of BuildNumber based on the ID of the build in TFS?
Since you're using TFS 2013 you have an easier option than the old way of modifying the workflow - Use PowerShell.
If you're using the build template TfvcTemplate.12.xaml (which is the new default in TFS 2013), then you have some extra build definition parameters you can set to specify Powershell scripts to run. This way you just write a little bit of powershell code that gets the TFS Build Number (from an environment variable), then finds your web.config file(s) and changes them.
There is a sample script published in the TFS Community Build Extensions project that will do something very similar that you could modify. It finds all AssemblyInfo.cs files, then uses RegEx to modify them to update the build number based on the TFS Build number.
See the sample PS script here: https://tfsbuildextensions.codeplex.com/SourceControl/latest#Scripts/ApplyVersionToAssemblies.ps1
See some docs on how to modify the build to run PowerShell here: http://msdn.microsoft.com/en-us/library/dn376353.aspx
The answer is yes, though it's non-trivial. I've done this in the past using two methods.
Method 1: Use CodeActivities and alter the TFS Build workflow XAML file to include the new CodeActivity as a workflow step.
Here's an article on creating custom workflows with custom code activities (it's a little dated but still relevant):
http://blogs.msdn.com/b/jimlamb/archive/2010/02/12/how-to-create-a-custom-workflow-activity-for-tfs-build-2010.aspx
Basically, you create a new object that inherits from CodeActivity, create properties for your inputs (BuildNumber,VersionNumber, TargetFile), perform your actions on the TargetFile and save it. Then you wire up this new DLL with your activity to the TFS Workflow XAML, injecting your activity in the desired build step (post-build & pre-package in your case I bet to ensure xml transforms are applied).
Method 2:
Tag the desired properties onto the MSBuild invocation inside the TFS XAML file so they are passed in to MSBuild and available for your to use as $(BuildNumber) and $(VersionNumber). Then you can use MSBuild tasks to inject the properties into the file at the right time.

How can I refer to the workspace path from msbuild in team build?

I've got a boatload of legacy project files that use a $(ProjectRoot) variable in include paths to refer to the root of the source tree. We define the variable in our visual studio environment so desktop builds can find their way to the tfs workspace in use.
I would like to define this variable in the Team Build environment via an MSBuild command line option in my tfs build definition, but I can't seem to find a way that works. I found an answer (List of msbuild properties TFS passes to a build script) with a list of variables and SolutionRoot looked promising.
When I put /p:ProjectRoot=$(SolutionRoot) in the MSBuild Arguments for my build definition, ProjectRoot ends up empty. I want it to expand to something like D:\Builds\18\TfsProject\BuildDef\src\
Is there a way to define my variable with the value of the root of the workspace sources in Team Build?
Note: I tried to figure this out in TFS2010 and finally gave up and modified the BuildProcessTemplate to define the variable using the SourcesDirectory variable that's available inside that context. I thought I'd take another run at it in the hopes of using the new template in TFS2013 without modification, but I'm still stumped.
UPDATE: Turns out the syntax I had in my MSBuild Arguments would work if the variable I'm dereferencing were actually being passed to msbuild. The list from the other post I reference seems to no longer be valid. Looking at the detailed Team Build logs, it looks like the only variables being passed in to msbuild in Team Build are OutDir, BuildId, BuildLabel, BuildTimestamp, BuildSourceVersion, and BuildDefinition. None of those gives me the info my current msbuild scripts need at build time so I'm stuck with either reworking all our project files or continuing with the BuildProcessTemplate modifications I've been using. Unless someone comes along with a better answer here.
You should be able to specify /p:ProjectRoot=$(TF_BUILD_SOURCESDIRECTORY) in the MSBuild Arguments of your build definition when using TFS2013.
See the Team Foundation Build environment variables documentation for more information.
Set the property using the other property. Use condition to only set it if its empty.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="xxxxxxxxxxxx">
<PropertyGroup>
<ProjectRoot Condition="$(ProjectRoot)==''"/>$(SolutionRoot)</ProjectRoot>
</PropertyGroup>

What happened to the "Reason" property in TFS 2010 Builds

When we used TFS 2008 (SP1) we used to be able to access the "Reason" MSBuild property, which contained the reason the build was running. This was available as soon as the build started, so no task/target needed to fetch it. Now we're using TFS 2010 (and MSBuild / Upgrade Template, not Workflow) and the property is no longer populated. I could write a custom task to retrieve the info from IBuildDetail, but isn't there a property immediately available to my MSBuild scripts? I used to be able to do conditional MSBuild script imports based on the build reason and this will not work if I have to use a task/target to fetch the reason.
You are still using MSBuild scripts, so I assume you are using the Upgrade Template. You can clone/edit or modify the Upgrade Template so that the template takes the current build reason and passes it to MSBuild via the Run TfsBuild for Configuration Folder activity. Open the template for edit in Visual Studio and do the following:
Select the Run On Agent activity and go to the Variables tab. Create a new variable to store the build reason and populate its default value using info from the IBuildDetail. Add something like the following to the list of variables:
Name: currentBuildReason
Variable type: String
Scope: Run On Agent
Default: BuildDetail.Reason.ToString()
Next, click on the Properties dialog for the Run TfsBuild for Configuration Folder activity. Select the CommandLineArgument elipses to open the dialog that passes arguments to TFSBuild. You probably only see one variable listed:
MSBuildArguments
You can now add the current build reason to the command line arguments (and still pass any existing MSBuildArguments), similar to this:
String.Format("/p:CurrentBuildReason={0} {1}", currentBuildReason, MSBuildArguments)
Click OK to close the dialog. That will pass the current build reason as a new argument named CurrentBuildReason and it can be referenced as $(CurrentBuildReason) in your MSBuild scripts.
Save and check-in the Upgrade Template.
Assign the template to a Build Definition. The MSBuild scripts used by that definition can now use the variable as described above.

Retrieving path to solution(s)?

We are currently setting up Team Build 2010 for our company, and I am trying to use workflow activities to retrieve the exact local path to the current solution being built. I haven't found a way to get this value, does anybody know how (without writing a custom activity)?
Either one of server or local path would suffice (i.e $/TeamProject/Branch/OurProject or C:\TeamBuild\src\path\to\branch\OurProject) since we can use the conversion activities on the server item.
The reason we need this path is for updating version info files, and that needs to be done for only the current solution being built, and the files have the same names (AssemblyInfo.cs, for example).
In this similar question, the solution is to define a parameter, but since this information is particular to the solution being built and not some external path, we were hoping that this info would retrievable.
You can retrieve this particular info without adding anything. If you navigate within your Build Process Template to the position where MSBuild breaks out, you will see that the solution that shall be build is set as a string named localProject. This will contain the local path where TFS has downloaded your SLN, something like C:\TeamBuild\src\path\to\branch\OurProject\OurProject.sln.Open the XAML and navigate to:
Run On Agent
Try Compile, Test, and Associate Changesets and Work Items
Compile, Test, and Associate Changesets and Work Items
Try Compile and Test
Compile and Test
For Each Configuration in BuildSettings.PlatformConfigurations
Compile and Test for Configuration
If BuildSettings.HasProjectsToBuild
For Each Project in BuildSettings.ProjectsToBuild
Try to Compile the Project
Compile the Project
Run MSBuild for Project
if you select Run MSBuild for Project & hit F4 you see it.
In order to retrieve what you are after you can define another string-variable solutionPath in your Build Process Template & insert under the Run MSBuild for Project a new Assign activity withTo : solutionPath andValue : Path.GetDirectoryName(localProject)
Have you looked at the TFS Community Build Extensions, they give you a assembly versioning out of the box?
You can use the variable called SourcesDirectory to get the current Source Directory on the Build Server. You can also use an ConvertWorkspaceItem activity to convert between server and local paths.
There's also a blog post that cover's all of this here.

TFS 2010 Build Definition

In the TFS 2010 build definition window, under “Process” there are two required items. They are “Configurations to build” and “Projects to build”. Under projects to build, it will allow me to enter something like:
$/TeamProject/Area1/Area2/*
However, this doesn’t seem to do what I expect. The build fails because it’s looking for:
$/TeamProject/Area1/Area2/Sources/*
What I am trying to achieve by this is to build all the solutions held under this area. For example, I have:
$/TeamProject/Area1/Area2/Solution1/Solution1.sln
$/TeamProject/Area1/Area2/Solution2/Solution2.sln
$/TeamProject/Area1/Area2/Solution3/Solution3.sln
There are many more solutions than this, which is why I’m looking for a way to build all solutions under the specified path recursively. Is there a way to do this in TFS 2010?
You can modify the process template. Expand it with the Matching files (I don't have the exact naming now) activity. Add a parameter that passes the information you set in the build defintion to the MachingFiles actvity. Then pass into the build solution activity instead of the argument that you enter in the build definition the files that is found by the MatchingFiles activity.
Now add a dummy solution in the build definition for the solution to build (it is not used anymore).
See the blog post series on the build customization for more information on customizing the build process template.
FWIW,
I've got: "configurations to build" blank
and under "projects to build" I've added my solutions via the ellipsis button
I would setup mappings for
$/TeamProject/Area1/Area2/Solution1/
$/TeamProject/Area1/Area2/Solution2/
$/TeamProject/Area1/Area2/Solution3/
Then in the build definitions enter the three projects to build
$/TeamProject/Area1/Area2/Solution1/Solution1.sln
$/TeamProject/Area1/Area2/Solution2/Solution2.sln
$/TeamProject/Area1/Area2/Solution3/Solution3.sln
You can leave the configurations to build as blank, or if you want to do a certain build you can set it to (for example) something like Debug|Mixed Platforms (check your Configuration Manager... for the solutions you are building to see what is valid)
Alternatively, you can just map the following (depending on how much you have in this folder, if you have Solutions 4+ that you don't want to trigger builds on, don't do it at this level)
$/TeamProject/Area1/Area2
And have one solution which contains the Solution1, Solution2 and Solution3, and build that instead.
By default building your Solution1 which is mapped to
$/TeamProject/Area1/Area2
On a build agent with a working directory that is going to looks something like:
$(SystemDrive)\Builds\$(BuildAgentId)\$(BuildDefinitionPath)
You'll end up with it being build under
C:\Builds\1\Solution1\Binaries
C:\Builds\1\Solution1\Sources
C:\Builds\1\Solution1\TestResults
Which is why you want to make sure that your OutDir's etc are all correct and not hard coded!
If you have a look while building, you'll see the build agent populating the Sources folder, and it should (if configured correctly) put all outputs into the Binaries folder (and then copy them to the Drop Folder configured under Build Defaults in TFS.

Resources