- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Tuesday, October 09, 2012

VS Web Publish: How to include files outside of the project to be published

I just received an email asking the following:

“I’m using Visual Studio 2012 and publishing web projects to the file system. How can I include files to be published which are not a part of my project"?”

I thought that I would answer here so that everyone could see the answer.

In Visual Studio 2012 (and in VS 2010 if you have downloaded the web publish updates via the Azure SDK) when you create a web publish profile we will create a .pubxml file under the Properties\PublishProfiles for C# or My Project\PublishProfiles for VB. If you open that file you will notice that it is actually an MSBuild file. When you publish your web project (either from the VS dialog or from the command prompt) the .pubxml file is combined with your project file to execute the publish. Since this is an MSBuild file you can directly edit the file to customize behavior of the publish process. In this case the question asker, lets call him Chris, has asked how to include some additional files into the publish payload.

This can be accomplished by the following.

  1. Create an MSBuild target which can gather the set of files to be added
  2. Extend the web publish process to include the files

Creating a target is pretty simple, and I’ll cover the contents of the target but first lets look at issue #2.

We need to add an MSBuild target which executes during the period in which files are being collected to be published. We have an MSBuild property which contains the set of targets which make up this phase, PipelineCollectFilesPhaseDependsOn, which is used as a dependency property as you might have guessed. In order to insert a target into that you can use something like the following in your .pubxml file.

<PropertyGroup>
  <PipelineCollectFilesPhaseDependsOn>
    CustomCollectFiles;
    $(PipelineCollectFilesPhaseDependsOn);
  </PipelineCollectFilesPhaseDependsOn>
</PropertyGroup>

Here I extend the PipelineCollectFilesPhaseDependsOn by pre-pending CustomCollectFiles, which is a target that I declared directly in the .pubxml file. This will make sure that my target gets executed at the correct time. Now when you publish your web app the CustomCollectFiles will be executed. Now let’s move on to what the contents of the target should contain. One way that you can do this is to add files to the FilesForPackagingFromProject item list. In my case I wanted to publish all the files in a folder named “additional files” which was one folder above the project directory. Take a look at the modifications that I made to my .pubxml file.

<PropertyGroup>
  <PipelineCollectFilesPhaseDependsOn>
    CustomCollectFiles;
    $(PipelineCollectFilesPhaseDependsOn);
  </PipelineCollectFilesPhaseDependsOn>
</PropertyGroup>

<Target Name="CustomCollectFiles">
  <Message Text="Inside of CustomCollectFiles" Importance="high"/>
  <ItemGroup>
    <_CustomFiles Include="$(MSBuildThisFileDirectory)..\..\..\additional files\**\*" />

    <FilesForPackagingFromProject  Include="%(_CustomFiles.Identity)">
      <DestinationRelativePath>additional files\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
    </FilesForPackagingFromProject>
  </ItemGroup>
</Target>

In this case the CustomCollectFiles target gathers the files into the _CustomFiles item. Notice how I declared the path to the folder. I used the MSBuildThisFileDirectory property. This property was introduced in MSBuild 4.0 and it points to the folder of the MSBuild file which contains it. So it will point to the folder which contains the .pubxml file (either Properties\PublishProfiles or My Project\PublishProfiles). After that, the files are included in the FilesForPackagingFromProject item list. The one special item to take notice of here is the declaration for the DestinationRelativePath metadata value. Since this file is not in the project we have no context on where the files should be placed when published. This is where the DestinationRelativePath metadata comes in. Here you specify the relative location to the publish root of the file. In my case I used additional files\%(RecursiveDir)%(Filename)%(Extension). Which will place all the files in the additional files folder under the same relative path to where they were picked up from.

You can see a sample project at https://github.com/sayedihashimi/publish-samples/tree/master/FileSysIncludeOtherFiles and if you are just looking for the .pubxml file https://github.com/sayedihashimi/publish-samples/blob/master/FileSysIncludeOtherFiles/FileSysIncludeOtherFiles/Properties/PublishProfiles/ToFilesys.pubxml.

Sayed Ibrahim Hashimi | @SayedIHashimi

Tuesday, October 09, 2012 6:12:19 AM (GMT Daylight Time, UTC+01:00)  #     |