0
votes

After spending a couple of days, I am here to seek help.

Here is the situation - Due to increasing the size of the git repository, our builds are taking too much time. Most of the time spent is in pulling a large set of files before making the builds. Before making the build our Jenkins workflow will clear the workspace to make sure no remnants of the previous build are present. Turned out these large content files aren't changing often. So, I have separated them into a different directory with a similar folder structure of my existing source code files. These content files are kept in a different repository and pulled only when needed. So here is my folder structure:

    **ContentFiles**
       \ConsoleApp1\Content\TestData
                               New Bitmap Image.bmp
                               abc.xyz
                               cosoleDep1.txt
       \Model\ConsoleApp2\Files
                          readme.txt
                          readme2.txt
       \Model\Tools\ConsoleApp3\Packages
                                New Bitmap Image.bmp
                                New Text Document.txt
    **Sources**
       \ConsoleApp1
                    ConsoleApp1.csproj
       \Model\ConsoleApp2
                    ConsoleApp2.csproj
       \Model\Tools\ConsoleApp3
                    ConsoleApp3.csproj
       ConsoleApp1.sln
       Directory.Build.props

Now for making the build some of these files to be included in the source project file (.csproj) and others in the respective output folder for packaging and do unit testing. The option I chose is to use the Directory.Build.props file to build the linkage as needed. So, added the Directory.Build.props at the root level of sources with the following script.

<Project  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
        <ContentFilesDir>$(MSBuildThisFileDirectory)\ContentFiles\</DependencyDir>
    </PropertyGroup>

    <ItemGroup>
        <ContentFiles Include="$(ContentFilesDir)\**\$(MSBuildProjectName)\**\*.*"/>
    </ItemGroup>

    <ItemGroup>
       <Content Include="@(ContentFiles)"  >
            <Link>%(RecursiveDir)\%(Filename)%(Extension)</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <Visible>True</Visible>
        </Content>

    </ItemGroup>
</Project>

Due to that file, each project links the content files in the respective folder matching with the project name in other locations. Content files are copied to the output folder properly. Though some files shouldn't be copied to the output folder, for now, ignore that case.

Now the problem: the folder structure is not what I am expecting and I don't know how I can fix it. If you see, the top-level folder name is also included in the link.

ConsoleApp1
       \ConsoleApp1  <----
                   \Content
                    New Bitmap Image.bmp
                   \TestData
                         abc.xyz
                         cosoleDep1.txt
ConsoleApp2
       \Model\ConsoleApp2  <----  
                   \Files
                          readme.txt
                          readme2.txt
ConsoleApp3
       \Model\Tools\ConsoleApp3 <----
                   \Packages
                             New Bitmap Image.bmp
                             New Text Document.txt

What I am trying to get is the following (without projectName as a parent for the content files):

ConsoleApp1
            \Content
                  New Bitmap Image.bmp
                  \TestData
                      abc.xyz
                      cosoleDep1.txt
Model\ConsoleApp2
              \Files
                 readme.txt
                 readme2.txt
Model\Tools\ConsoleApp3
              \Packages
                    New Bitmap Image.bmp
                    New Text Document.txt

the lines that are interested here are the filter <ContentFiles Include="$(ContentFilesDir)\**\$(MSBuildProjectName)\**\*.*"/> and <Link>%(RecursiveDir)\%(Filename)%(Extension)</Link>. I believe %(RecursiveDir) is retrieving the item from the list, and the name starts from the last backslash which includes the project name. So the list that it is processing must be in the right format. How can I modify the filter so remove that level when the projects are in a multilevel folder structure?

Must-have requirements: The content files has to show in the project as linked files in respective projects. If I use any tasks, then it will only be effected during the build task and that might work, but doesn't show up in the project files

Much appreciated for your help.

1

1 Answers

1
votes

After learning bit more how this works, I came up with the following. Hoping that this helps for others. Suggestions are welcome!!

<Project  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <SourceDirName>Sources</SourceDirName>
        <DependencyDirName>Dependencies</DependencyDirName>
        <RootDir>$(MSBuildThisFileDirectory)</RootDir>
        <DependencyDir>$([MSBuild]::NormalizePath($(MSBuildThisFileDirectory)..\Repo1\$(DependencyDirName)))</DependencyDir>
        <SourceDir>$([MSBuild]::NormalizePath($(MSBuildThisFileDirectory)..\Repo2\$(SourceDirName)))</SourceDir>
        <MSBuildProjectDirName>$([System.IO.Directory]::GetParent($(MSBuildProjectFullPath)).Name)</MSBuildProjectDirName>
    </PropertyGroup>

    <ItemGroup>
        <DepFiles Include="$(DependencyDir)\**\$(MSBuildProjectDirName)\**\*.*"></DepFiles>
    </ItemGroup>

    <Target Name="Test" BeforeTargets="CopyFilesToOutputDirectory">
        <Message Importance="Low" Text="MSBuildThisFileDirectory $(MSBuildThisFileDirectory)"/>
        <Message Importance="Low" Text="SourceDirName            $(SourceDirName)"/>
        <Message Importance="Low" Text="DependencyDirName        $(DependencyDirName)"/>
        <Message Importance="Low" Text="RootDir                  $(RootDir)"/>
        <Message Importance="Low" Text="DependencyDir            $(DependencyDir)"/>
        <Message Importance="Low" Text="SourceDir                $(SourceDir)"/>
        <Message Importance="Low" Text="MSBuildProjectDirName    $(MSBuildProjectDirName)"/>

        <Message Importance="Low" Text="@(DepFiles->Count())"/>
        <Message Importance="Low" Text="@(DepFiles)->$([System.String]::Copy('%(FullPath)'))"/>
        <Message Importance="Low" Text="@(DepFiles)->$([System.String]::Copy('%(FullPath)').Replace($(DependencyDir), $(SourceDir)))"/>
        <Message Importance="Low" Text="@(DepFiles)->$([System.String]::Copy('%(FullPath)').Replace($(DependencyDir), $(SourceDir)).Replace($(MSBuildProjectDirectory),''))"/>
    </Target>

    <ItemGroup>
            <Content Include="@(DepFiles)" Condition="'@(DepFiles->Count())' > 0">
                <Link>$([System.String]::Copy('%(FullPath)').Replace($(DependencyDir), $(SourceDir)).Replace($(MSBuildProjectDirectory),''))</Link>
                <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <Visible>True</Visible>
        </Content>

    </ItemGroup>
</Project>