How do I get an msbuild task to do config transforms on a collection of files?
Asked Answered
R

1

8

I am trying to transform all of the web.config files in a project I have, here's a my tree structure:

  • Transform.bat
  • Transforms
    • ConfigTransform.proj
    • Web.Transform.config
  • Website
    • web.config
    • Views
      • web.config

There's more web.config files, but the idea is that this will find all of them and apply the same config transform on them.

I've taken a few hints from a blog post I found but I get stuck in the last step, the actual transformation. Also there's a bit of a rough part in the middle that I don't really like (I don't quite understand what I'm doing and I'm obviously doing it wrong). Here's where I am so far:

<Project ToolsVersion="4.0" DefaultTargets="Transform" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="TransformXml" AssemblyFile="Tools\Microsoft.Web.Publishing.Tasks.dll"/>

    <PropertyGroup>
        <SitePath>..\..\Website</SitePath>
        <WebConfigTransformInputFile>$(SitePath)\Web.config</WebConfigTransformInputFile>
        <WebConfigTransformFile>Web.Transform.config</WebConfigTransformFile>
        <OutDir>..\N\N\</OutDir>

    </PropertyGroup>

    <ItemGroup>
        <_FilesToTransform Include="$(SitePath)\**\web.config"/>
    </ItemGroup>

    <Target Name="Transform">

    <MakeDir Directories="@(_FilesToTransform->'$(OutDir)%(RelativeDir)')" />

    <TransformXml Source="@(_FilesToTransform->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')"
                  Transform="$(WebConfigTransformFile)"
                  Destination="@(_FilesToTransform->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')" />
    </Target>
</Project>

My Transform.bat looks like this:

%systemroot%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe %CD%\Transforms\ConfigTransform.proj

So when I call the batch, the appropriate directories get created. However, as you can see I've had to be a little creative with the OutDir, making it ..\N\N. For some reason, if I don't do this the OutDir path will be exactly the same as the input directory. So I obviously need to change something in MakeDir but I'm not sure what.

The real problem comes when it starts to do the transforms. I've tried to keep the TransformXml Source parameter like this or like so:

@(_FilesToTransformNotAppConfig->'%(FullPath)')

The latter gives me an error "Could not open Source file: The given path's format is not supported." and the former gives me this output:

Build started 30-4-2012 14:02:48.
Project "D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj" on node 1 (default targets).
Transform:
  Creating directory "..\N\N\..\..\Website\Views\".
  Transforming Source File: ..\N\N\..\..\Website\Views\Web.config;..\N\N\..\..\Website\Web.config
D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj(32,2): error : Could not open Source file: Could not find a part of the path 'D:\Dev\transform\DoTransforms\Website\Views\Web.config;\Website\Web.config'.
  Transformation failed
Done Building Project "D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj" (default targets) -- FAILED.

Build FAILED.

To summarize my questions:

  1. How do I avoid the path issue for the OutDir? I've fiddled with multiple paths but I can't get it right.
  2. How do I get the TransformXml task to accept multiple files in the Source attribute?
Rosemarie answered 30/4, 2012 at 12:8 Comment(5)
Before going into where you are at can you explain what it is that you are trying to do?Nahama
Transforming all of the config files in my solution.Rosemarie
Clarified in the post (under the tree structure).Rosemarie
Where to you want the transformed files to be written to?Nahama
I don't care, anywhere that has the same folder structure as the original files.Rosemarie
N
11

I think you were pretty close. I have pasted a sample below which shows how to do this.

In my sample I discover the transform sitting next to the web.config file itself. For your scenario you can just use an MSBuild property pointing to a specific file.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="TransformAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>

  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <OutputFolder Condition=" '$(OutputFolder)'=='' ">C:\temp\transformed-files\</OutputFolder>
  </PropertyGroup>

  <!--
  This target shows how to transform web.config with a specific transform file associated to that specific web.config file.
  -->
  <Target Name="TransformAll">

    <!-- discover the files to transform -->
    <ItemGroup>
      <FilesToTransofm Include="$(MSBuildProjectDirectory)\**\web.config"/>
    </ItemGroup>

    <!-- Ensure all target directories exist -->
    <MakeDir Directories="@(FilesToTransofm->'$(OutputFolder)%(RecursiveDir)')"/>

    <!-- TransformXml only supports single values for source/transform/destination so use %(FilesToTransofm.Identity)
         to sned only 1 value to it -->
    <TransformXml Source="%(FilesToTransofm.Identity)"
                  Transform="@(FilesToTransofm->'%(RecursiveDir)web.$(Configuration).config')"
                  Destination="@(FilesToTransofm->'$(OutputFolder)%(RecursiveDir)web.config')" />    
  </Target>

</Project>

FYI you can download a full sample at https://github.com/sayedihashimi/sayed-samples/tree/master/TransformMultipleWebConfigs.

Nahama answered 2/5, 2012 at 6:22 Comment(4)
Wonderful, with a little tweaking to make it work the way I need it and by fixing an error with the MakeDir (you forgot the backslash) it's doing exactly what I want! gist.github.com/2601426Rosemarie
One more question: Can I do $(MSBuildProjectDirectory)**\web.config but exclude the first web.config found (the one in the root of the site)?Rosemarie
FYI I didn't forget the slash, it was defined on the property itself. In MSBuild typically the slash is defined at the end of the property so that you don't forget when you use the property. I would recommend you follow that unless you can't for some reason. About your other question you should add Exclude="$(MSBuildProjectDirectory)\web.config" to skip the root web.config.Nahama
Makes sense! I've seen a few different conventions. Thanks for all your help, excluding it worked indeed. A blog post with the end result of this is here: cultiv.nl/blog/2012/5/5/transform-umbraco-51-to-mvc-4Rosemarie

© 2022 - 2024 — McMap. All rights reserved.