Building solutions that reference to assemblies belonging to different team project

 

Scenario

 

Assume that we two projects (ConsoleApplication1 and ConsoleApplication2) under team project (TP1) and they are using the assembly (commonlibrary.dll) that is checked in under a different team project (TP2). Let us assume the corresponding paths under version control are

 

$/TP1/Framework/Common/v2.0/commonlibrary.dll

$/TP2/Main/ConsoleApplication1/ConsoleApplication1.sln

$/TP2/Main/ConsoleApplication1/ConsoleApplication1.sln

 

Note: please make sure that you have used File Reference to add the reference for commonlibrary.dll and CopyLocal option is set to true. Otherwise your desktop build scenario will be broken.

 

Custom steps to enable building the project

  • Create the BuiltType “Type1” for TP2.
  • Check out the Tfsbuild.proj file for “Type1” build type.
  • Reset the following property. This is done to skip invoking the default tasks to delete and recreate workspace on the build machine.

<SkipInitializeWorkspace>true</SkipInitializeWorkspace>

  • Define the new property. This is just for the user convenience and will be used in the custom target “BeforeGet” (mentioned below)

<TfCommand>$(TeamBuildRefPath)\..\tf.exe</TfCommand>

  • Define the folder mappings item group. (You are responsible for giving the correct local paths for the build machine. Please note that multiple mappings can not share the same local folder and will result in MappingConflictException in the CreateWorkspace task. Another recommendation is to avoid mapping to $(SolutionRoot). This is because if while creating workspace, task detects overlapping local paths, it will try to merge the local paths and screw up the relative paths. Please note that I have not explored the cloaking option.

<ItemGroup>

         <Map Include="$/TP2/Main">

             <LocalPath>$(SolutionRoot)\Main</LocalPath>

         </Map>

         <Map Include="$/TP1/Framework">

             <LocalPath>$(SolutionRoot)\Framework</LocalPath>

         </Map>

    </ItemGroup>

  • Override the “BeforeGet” target.

    <Target Name="BeforeGet">   

    <!— to remove any workspace that exist with the same name, from some previous build à

    <DeleteWorkspaceTask

        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"

        Name="$(WorkspaceName)" />   

   

 

    <!— create the workspace with default mapping that maps $(SolutionRoot) to $/ à

    <Exec

      WorkingDirectory="$(SolutionRoot)"

      Command="&quot;$(TfCommand)&quot; workspace /new $(WorkspaceName) /server:$(TeamFoundationServerUrl)"/>

 

   

    <!— task will add the folder mappings corresponding to items define in step 5 à

    <Exec

      WorkingDirectory="$(SolutionRoot)"

      Command="&quot;$(TfCommand)&quot; workfold /map /workspace:$(WorkSpaceName) /server:$(TeamFoundationServerUrl) &quot;%(Map.Identity)&quot; &quot;%(Map.LocalPath)&quot;"/>

 

 

    <!— remove the default mapping ( $(SolutionRoot) ß> $/)that was created initially. Otherwise all the team projects will be synced under the $(SolutionRoot) à  

    <Exec

     WorkingDirectory="$(SolutionRoot)"

     Command="&quot;$(TfCommand)&quot; workfold /unmap /workspace:$(WorkSpaceName) &quot;$(SolutionRoot)&quot;"/>

 

    </Target>

 

  •  Verify that sln paths (SolutionToBuild itemgroup) for correct relative paths

<SolutionToBuild Include="$(SolutionRoot)\Main\ConsoleApplication1\ConsoleApplication1.sln" />

<SolutionToBuild Include="$(SolutionRoot)\Main\ConsoleApplication2\ConsoleApplication2.sln" />

  • Similarly verify the vsmdi paths (MetaDataFile itemgroup) for correct relative paths.
  • Checkin the tfsbuild.proj file and launch the build.

 

Recommendations

  • Please make sure that CopyLocal option is set for the file reference.
  • Use File reference to add references for assemblies belonging to different team projects. Sometime these reference paths will be broken because the relative path on the build machine is different from the one mentioned in the csproj file of your project. In such cases you need to specify the correct location of the external assembly. You can easily do it by defining “AdditionalReferencePath” item pointing to the folder containing the assembly.
  • Please try to avoid mapping directly to “$(SolutionRoot)”. Still if your “Map” item group contains any working folder mapping that points to only “$(SolutionRoot)”, then you need to remove the following line from the BeforeGet target :-

<Exec Command="&quot;$(TfCommand)&quot; workfold /unmap /workspace:$(WorkSpaceName) &quot;$(SolutionRoot)&quot;" WorkingDirectory="$(SolutionRoot)"/>   

References

 

Other interesting posts on the related/same issue are [post] [post1] [post2] [post3]