Example on how to resolve project to project reference (for single solution) in team build.

Consider a simple solution (OfficeSolution.sln) that has one main project (OfficeSolution.csproj). The main project has dependency (project to project reference) on two independent sub projects (WordApplication.csproj and ExcelApplication.csproj). Note that both the sub projects also belong to the same solution. The folder structure for the sources is shown in the following diagram.

 

├───OfficeSolution

│ │ OfficeSolution.sln

│ │ OfficeSolution.vssscc

│ │

│ ├───ExcelApplication

│ │ │ ExcelApplication.csproj

│ │ │ ExcelApplication.csproj.vspscc

│ │ │ ExcelClass.cs

│ │ └───Properties

│ │ AssemblyInfo.cs

│ │

│ ├───OfficeSolution

│ │ │ OfficeSolution.csproj

│ │ │ OfficeSolution.csproj.vspscc

│ │ │ OfficeDocument.cs

│ │ └───Properties

│ │ AssemblyInfo.cs

│ │

│ └───WordApplication

│ │ WordClass.cs

│ │ WordApplication.csproj

│ │ WordApplication.csproj.vspscc

│ └───Properties

│ AssemblyInfo.cs

 

figure 1

There are two team projects (MyProj1 and My Proj2). The Excel and Word projects belong to one team project (MyProj2) and main project (OfficeSolution) is checked in different team project (MyProj1). Now we want to build this solution using team build. Based on case 1.2, recommendation 1, the directory structure of files under build directory should be identical to figure 1.

$/MyProj1

├───OfficeSolution

│ │ OfficeSolution.sln

│ │ OfficeSolution.vssscc

│ ├───OfficeSolution

│ │ │ OfficeSolution.csproj

│ │ │ OfficeSolution.csproj.vspscc

│ │ │ OfficeDocument.cs

│ │ └───Properties

│ │ AssemblyInfo.cs

$/MyProj2

├───OfficeSolution

│ ├───ExcelApplication

│ │ │ ExcelApplication.csproj

│ │ │ ExcelApplication.csproj.vspscc

│ │ │ ExcelClass.cs

│ │ └───Properties

│ │ AssemblyInfo.cs

│ └───WordApplication

│ │ WordClass.cs

│ │ WordApplication.csproj

│ │ WordApplication.csproj.vspscc

│ └───Properties

│ AssemblyInfo.cs

figure 2

User needs to modify the build type to get sources from different team project. For example you can add the following targets in the tfsbuild.proj to get files from different team project. Please note that target "AfterGet" defined in this file will override the default target “AfterGet” of file Microsoft.TeamFoundation.Build.targets.

<PropertyGroup>

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

</PropertyGroup>

<Target Name="CustomGet">

<!-- Delete the default workspace used to sync sources from MyProj1 -->

<Exec

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

<!-- Create workspace to get files from MyProj2-->

<Exec

WorkingDirectory="$(SolutionRoot)"

Command="&quot;$(TfCommand)&quot; workspace /new

$(WorkspaceName) /s:$(TeamFoundationServerUrl)"/>

<!-- Set the folder mappings to get files from MyProj2-->

<Exec

Command="&quot;$(TfCommand)&quot; workfold /map /s:$(TeamFoundationServerUrl)

/workspace:$(WorkspaceName) $/MyProj2/OfficeSolution $(SolutionRoot)\OfficeSolution"/>

<!-- Get the files from team project MyProj2 -->

<Exec

WorkingDirectory="$(SolutionRoot)"

Command="&quot;$(TfCommand)&quot; get /r /force ."/>

</Target>

<!—Custom target to get sources from team project (MyProj2) -->

<Target Name="AfterGet"

DependsOnTargets="CustomGet;InitializeWorkspace"/>

 

<!—Assuming the second AT name is burton-tfs1 -->

<PropertyGroup>

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

    <TfsName2>https://burton-tfs1:8080/</TfsName2>

</PropertyGroup>

<Target Name="AfterGet">

    <!-- Delete the default workspace -->

    <Exec

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

    <!-- Create workspace to get files from different AT (burton-tfs1)-->

    <Exec

      WorkingDirectory="$(SolutionRoot)"

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

    <!-- Set the folder mappings to get files from burton-tfs1 -->

    <Exec

      Command="&quot;$(TfCommand)&quot; workfold /map /s:$(TfsName2)

/workspace:$(WorkspaceName) $/MyProj2/OfficeSolution $(SolutionRoot)\OfficeSolution"/>

    <!-- Get the files from team project belonging to different AT-->

    <Exec

      WorkingDirectory="$(SolutionRoot)\OfficeSolution"

      Command="&quot;$(TfCommand)&quot; get /r /force ."/>

    <!-- Delete the workspace used to sync files from different AT -->

    <Exec

      Command="&quot;$(TfCommand)&quot; workspace /delete $(WorkspaceName) /s:$(TfsName2)"/>

  

    <!-- Recreate the default workspace -->

    <CreateWorkspaceTask

      Condition=" '$(SkipInitializeWorkspace)'!='true' and '$(IsDesktopBuild)'!='true' "

      Url="$(TeamFoundationServerUrl)"

      MappingFile="WorkspaceMapping.xml"

      LocalPath="$(SolutionRoot)"

      Name="$(WorkspaceName)"

      TeamProject="$(TeamProject)" />

</Target>

 

I will be talking about file references in team build in next post.

Comments

  • Anonymous
    October 17, 2005
    Please note that example code of scenario 1, case 1.2 might result in logger failure. This happens because logger does the conversion of server path to local path of all the (sln and csproj) files. You have to make sure that all the sources present under the "sources" folder are mapped to workspace.

    An alternative example (hint):-

    <PropertyGroup>
    <WorkingDirectories>$(SolutionRoot)..Reusable Code</WorkingDirectories>
    <TfCommand>&quot;$(TeamBuildRefPath)..tf.exe&quot;</TfCommand>
    </PropertyGroup>

    <Target Name="AfterGet">
    <!-- Clean and create the working folder for the external project -->
    <RemoveDir Directories="$(WorkingDirectories)" ContinueOnError="true" />
    <MakeDir Directories="$(WorkingDirectories)" />

    <!-- Delete existing workspace created by TeamBuild -->
    <Exec Command="$(TfCommand) workspace /delete &quot;$(WorkSpaceName)&quot; /server:vsts /noprompt" WorkingDirectory="$(SolutionRoot)..Reusable Code" ContinueOnError="true" />

    <!-- Create a new one with the same name as the TeamBuild one -->
    <Exec Command="$(TfCommand) workspace /new &quot;$(WorkSpaceName)&quot; /server:vsts /noprompt" WorkingDirectory="$(SolutionRoot)..Reusable Code" />

    <!-- Map the external projects to this workspace -->
    <Exec Command="$(TfCommand) workfold /map /workspace:&quot;$(WorkSpaceName)&quot; /server:vsts &quot;$/Reusable Code/Framework&quot; &quot;$(SolutionRoot)..Reusable CodeFramework&quot;" WorkingDirectory="$(SolutionRoot)..Reusable Code" />

    <!-- Get the external projects into the correct locations -->
    <Exec Command="$(TfCommand) get &quot;$/Reusable Code/Framework&quot; /recursive /version:T /force" WorkingDirectory="$(SolutionRoot)..Reusable Code" />

    <!-- Label external projects -->
    <Exec Condition=" '$(SkipLabel)'!='true' " Command="$(TfCommand) label /server:vsts &quot;$(BuildNumber)&quot; &quot;$/Reusable Code/Framework&quot; /version:&quot;W$(WorkSpaceName)&quot; /recursive" WorkingDirectory="$(SolutionRoot)..Reusable Code" />

    <!-- Add mapping to default project back into the workspace -->
    <Exec Command="$(TfCommand) workfold /map /workspace:&quot;$(WorkSpaceName)&quot; /server:vsts &quot;$/CodeGeneration&quot; &quot;$(SolutionRoot)&quot;" WorkingDirectory="$(SolutionRoot)" />
    </target>

  • Anonymous
    September 23, 2007
    I have team build like in scenario_1 . It looks like in works right. But after I exec it always get "error CS0246: The type or namespace name 'ActionList' could not be found (are you missing a using directive or an assembly reference?)"(((. Could you advise something?

  • Anonymous
    January 27, 2010
    Thanks for your Suggestion. In TFS 2008 is another Option to do that. Just add other Team Projects' Workspaces to the build definition, so they get check out with. Cheers, Marco