tfsmsbuildmsbuild-buildengine

Can you use multiple working folders with TFS?


In projects in which the workspace has only one working folder, my build scripts work great. Now that I am working with a new project that required 2 working folders, all of the checkout and checkin commands of my previous script fail, with no files found.

Obviously, I'm not understanding a critical part of the implementation of the workspace here... I have a project which is dependant on other projects, the second working folder is basically a 3rd party folder with references to the various published DLL's and headers files needed to compile my project. There are 2 active folders and the local folders are:

$(SourceDir)\TEAM-MAIN\Address Finalizer
$(SourceDir)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party

The built code works fine, but the custom AfterGet fails on the following entry:

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(MSBuildProjectDirectory)\..\sources"
      ContinueOnError="false"/>

The project will of course work if I have a single working folder and move the source to a high enough point to get all the needed files, but I don't want to troll through 43 other projects todo what I want, let along mucking with their assembly files...

I have also tried :

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(SolutionRoot)"
      ContinueOnError="false"/>

Same problem, unable to find any assembly files... I have checked build log and I definately see assembly files check out during the build phase...

Task "Get"
  Get TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1430" Force=True Overwrite=False PopulateOutput=False Preview=False Recursive=True Version="C7564" Workspace="SBN01P-TFS03_61"
<snip>
  Getting C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\Address Finalizer\Address Finalizer\Properties\AssemblyInfo.cs;C7525.

If anyone has any ideas or can point me to some article to better explain how mutliple working folders work, I'd appreciate it.

Values for some of the build variables:

MSBuildProjectDirectory: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType

SolutionRoot: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources

To provide more information, I added the following command:

    <!-- Report what our working folders are -->
    <Exec 
      Command='$(TfCommand) workfold'
      WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\Address Finalizer"/>

The result was:

Task "Exec"
  Command:
  "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe" workfold
  ===============================================================================
  Workspace: SBN01P-TFS03_61 (tfsservice)
  Server   : http://pgpd-team01:8080/
   $/InfoTurn/TEAM-MAIN/Address Finalizer: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\TEAM-MAIN\Address Finalizer
   $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/3rd Party: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party

I have found that the following working directory will work:

WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\Address Finalizer"

But that the following two do not, note that the 2nd is my 2nd working folder:

WorkingDirectory="$(SolutionRoot)"
WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party"

The error that I get for the label task is the most useful:

Using "Label" task from assembly "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll".
Task "Label"
  Label TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1507" Name="Address Finalizer 2.0.1 Build 039" Recursive=True Comments="Automated build: Address Finalizer 2.0.1 Build 039" Version="W" Child="replace" Files="C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources"
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(310,5,310,5): error : Error: Unable to determine the workspace.

The actual error from the check out, which is not useful, is:

Task "Exec"
  Command:
  "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe" checkout AssemblyInfo.cs /recursive
  No matching items found in C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\AssemblyInfo.cs in your workspace.
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(280,5): error MSB3073: The command ""C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe" checkout AssemblyInfo.cs /recursive" exited with code 1.

Solution

  • It is possible to use disjoint workspace mappings + recursion at the same time. You just have to be careful.

    This gets all C# files in Foo & Bar:

    $/project/dir1/foo -> c:\code\foo
    $/project/dir2/bar -> c:\code\bar
    
    tf get c:\code\*.cs -recursive
    

    This usually gets everything under Foo & Bar, but will fail if the working directory cannot be resolved to a workspace unambiguously:

    $/project/foo -> c:\code1\foo
    $/project/bar -> c:\code2\bar
    
    tf get $/project/* -recursive
    

    This might work (same caveat as above), but it probably doesn't do what you expect!

    $/project/foo -> c:\code1\foo
    $/project/bar -> c:\code2\bar
    
    tf get c:\code* -recursive
    

    There are more variations; the behavior depends on how your workspace is defined, as well as the presence of other workspaces on the same machine with similar local paths. Can you spell out your build definition exactly? And can you add some debugging statements to verify exactly what $(SolutionRoot) and other MSBuild properties are equal to when your target is being executed?

    / EDIT /

    As I said, disjoint mappings + recursion is very tricky to get right. Turns out that behavior varies not only with the subtle details of your filespec vs the workspace definition, but also varies between commands! Unlike my first example, this fails:

    $/project/dir1/foo -> c:\code\foo
    $/project/dir2/bar -> c:\code\bar
    
    tf checkout c:\code\*.cs -recursive
    

    Confused yet? I am, and I worked on the TFS team for several years!

    Let me summarize my recommendations from the various comment threads: