8

I have a Pre-build event on a web project that minifies and concatenates javascript files using node. This creates a folder called BuiltScripts in the scripts folder that is a duplicate of the scripts folder except the files are minified. When I am doing a deploy I want to publish the scripts folder including the BuiltScripts folder within it. To achieve this I have added the BuiltScripts folder to the project. This is not an ideal solution as:

  1. I have to have the BuiltScripts folder checked out in order to build as the files in it are read only as the solution is under source control. This creates hassles when checking in as I have so many files checked out.
  2. When I add a new file to the project I have to make sure I remember to add it to the BuiltScripts folder or the built version of the file will not be deployed.
  3. My build will fail on the build server as the files in the BuiltScripts folder are read only there as well.
  4. Having two copies of a file with the same name is an issue when searching for files and doing text based searches.

I would like to have the build server build and minifiy the javascript files as a pre build step but I do not want the BuiltScripts folder added to the project. However when the build server packages the project at the end I want it to copy the BuiltScripts folder with the output of the build process. How can I achieve this?

Aran Mulholland
  • 23,555
  • 29
  • 141
  • 228
  • What are you using for deploy\publish - is this standard msbuild target or you made something custom ? – Alexey Shcherbak Oct 12 '12 at 08:37
  • @AlexeyShcherbak MSBuild, TFS, nothing custom – Aran Mulholland Oct 13 '12 at 11:22
  • I suspect Sayed Ibrahim Hashimi wrote this post with answer after your question :). http://sedodream.com/2012/10/09/VSWebPublishHowToIncludeFilesOutsideOfTheProjectToBePublished.aspx – Alexey Shcherbak Oct 15 '12 at 07:11
  • =) ok, this could be pure coincident. Anyway, looks like his answer fit best to your question. For this question I did some msbuild digging same direction as Sayed. Overal answer had to be very very similar to his. Now it's obsolete a bit ;) – Alexey Shcherbak Oct 15 '12 at 11:50
  • @AlexeyShcherbak why do you say it's obsolete? – Aran Mulholland Oct 21 '12 at 22:27
  • =) I just don't see any reason to post here answer very very similar to Sayed's blog post. I provided link to his very detailed post and I think your question answered there. This why I considering my own, "not yet posted" answer, unnecessary. – Alexey Shcherbak Oct 22 '12 at 05:55
  • @AlexeyShcherbak ok, misread you, thought you were saying his post was obsolete. – Aran Mulholland Oct 22 '12 at 05:59

3 Answers3

8

Instead of using the pre-build event of the project properties (which I think is what you mean), override the BeforeBuild target in the .csproj/.vbproj file.

<Project ...>
  ...
  <Target Name="BeforeBuild">
    <!-- Create the 'BuildScripts' directory. -->
    <!-- The $(IntermediateOutputPath) reference the 'obj' folder, which I like to -->
    <!-- use for these kinds of things. -->
    <MakeDir Directories="$(IntermediateOutputPath)BuiltScripts">
      <Output PropertyName="BuildScriptsPath" TaskParameter="DirectoriesCreated" />
    </MakeDir>
    <!-- Execute the javascript minifier. -->
    <Exec Command="..." />
    <!-- Create an item group for the minified scripts so we manipulate the target path. -->
    <CreateItem Include="$(BuildScriptsPath)\*.js">
      <Output ItemName="BuiltScripts" TaskParameter="Include" />
      <Output ItemName="FileWrites" TaskParameter="Include" />
    </CreateItem>
    <!-- Add the minified scripts to the Content item group, -->
    <!-- which the deployment MSBuild inspects for files to deploy. -->
    <CreateItem Include="@(BuiltScripts)"
      AdditionalMetadata="TargetPath=scripts\%(Filename)%(Extension)">
      <Output ItemName="ContentWithTargetPath" TaskParameter="Include" />
    </CreateItem>
  </Target>
  ...
</Project>

You may have to play with the shape of the Content item group that is the output of the CreateItem task if the files don't get deployed to the right directory. Note that I used an item transform to make the target path scripts\YourScript.js.

Also note the first CreateItem task stuffs the output into an item group called 'FileWrites.' I discovered that the Clean target inspects that item group to know what files to delete.

Place that XML into your project file after the <Import> elements and you should be good to go. No checking-into source control required and even your build server will be happy.

Matt
  • 1,871
  • 2
  • 11
  • 5
  • Would you add a step to delete the previous BuiltScripts folders content? – Aran Mulholland Oct 22 '12 at 06:12
  • I ran my sample code and it looks like the IncrementalClean target will delete the files automatically since they are included in the FileWrites item group, which is useful when you delete a .js file and you don't want it to be deployed anymore. – Matt Oct 22 '12 at 16:10
0

Can't you use a post-build event to run a script to do what you want? I use one to move my output files to a location completely unrelated to the project. Whether its plain batch, VBScript/JScript, or PowerShell you can do pretty much anything you want.

DaveE
  • 3,579
  • 28
  • 31
0

I had a similar requirement, though it only involved adding existing files to a deployment package rather than generating them as part of the build. I found the technique on this page useful.

Ken Keenan
  • 9,818
  • 5
  • 32
  • 49