Emidee
Emidee

Reputation: 1275

How to create a XML file with MSBuild?

I'd like to create a XML file within a MSBuild task.

I have a list of files:

<CreateItem Include="$(TestsAssembliesOutputDir)\Emidee.*.Tests.dll">
  <Output ItemName="TestsAssemblies" TaskParameter="Include" />
</CreateItem>

I'd like to create a XML which would look like:

<?xml version="1.0" encoding="utf-8"?>
<xunit>
  <assemblies>
    <assembly filename="PATH OF FILE #1" shadow-copy="true" />
    <assembly filename="PATH OF FILE #2" shadow-copy="true" />
  </assemblies>
</xunit>

How can I achieve that?

Thanks in advance

Mike

Upvotes: 21

Views: 12357

Answers (4)

Alix
Alix

Reputation: 380

New version of Brian solution with the "initial < ? xml line", without the "Text" and without encoding quotes:

<Target Name="CreateXml">
  <ItemGroup>
    <TestAssembly Include="$(TestsAssembliesOutputDir)\Emidee.*.Tests.dll" />
    <Line Include='&lt;%3Fxml version="1.0" encoding="utf-8"%3F&gt;' />
    <Line Include='&lt;xunit&gt;' />
    <Line Include='&lt;assemblies&gt;' />
    <Line Include='&lt;assembly filename="%(TestAssembly.Identity)" shadow-copy="true" /&gt;' />
    <Line Include='&lt;/assemblies&gt;' />
    <Line Include='&lt;/xunit&gt;' />
  </ItemGroup>
  <WriteLinesToFile
     File="out.xml"
     Lines="@(Line)"
     Overwrite="true"
     />
</Target>

Upvotes: 1

MarkPflug
MarkPflug

Reputation: 29528

One option would be to use an inline task. This allows you to write arbitrary C# code to generate the Xml, which might be easier (or might be harder) than alternative solutions. Here is an example that I was using to hack together a NuGet nuspec file in my build.

...
<UsingTask
        TaskName="CreateNugetPackage"
        TaskFactory="CodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"
    >
        <ParameterGroup>
            <ProjectName ParameterType="System.String" Required="true" />
            <Version ParameterType="System.String" Required="true" />
            <BaseFolder ParameterType="System.String" Required="true" />

            <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <OutputFile ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
        </ParameterGroup>

        <Task>
            <Reference Include="System.Xml" />
            <Using Namespace="System" />
            <Using Namespace="System.Xml" />
            <Code Type="Fragment" Language="cs">
                <![CDATA[
                var root = Path.GetFullPath(BaseFolder);    

                using (var w = XmlWriter.Create(OutputFile.ItemSpec)) {
                    var ns = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd";
                    w.WriteStartElement("package", ns);

                    w.WriteStartElement("metadata");

                    w.WriteStartElement("id");
                    w.WriteValue(ProjectName);
                    w.WriteEndElement();
                    w.WriteStartElement("version");
                    w.WriteValue(Version);
                    w.WriteEndElement();
                    w.WriteStartElement("description");
                    w.WriteValue("Test");
                    w.WriteEndElement();

                    w.WriteStartElement("authors");
                    w.WriteValue("Test");
                    w.WriteEndElement();

                    w.WriteEndElement();


                    w.WriteStartElement("files");

                    foreach(var file in Files) {
                        w.WriteStartElement("file");
                        var inFile = file.ItemSpec;
                        inFile = inFile.Replace(root, "");
                        w.WriteAttributeString("src", inFile);
                        w.WriteAttributeString("target", inFile);
                        w.WriteEndElement();
                    }

                    w.WriteEndElement();

                    w.WriteEndElement();
                }

    ]]>
            </Code>
        </Task>
    </UsingTask>


    <Target Name="CreateNuspec" BeforeTargets="BuildNugetPackage">

       <CreateNugetPackage
            Files="@(Artifacts)"
            OutputFile="$(NugetPackageSpec)"
            ProjectName="$(ProjectName)"
            BaseFolder="$(ArtifactRoot)"
            Version="$(Version)"
            />
    </Target>
...

Some relevant documentation:

UsingTaskElement

MSBuild inline tasks

Upvotes: 2

Sergio Rykov
Sergio Rykov

Reputation: 4286

MSBuild Extension Pack has XmlFile target.

Upvotes: -1

Brian Kretzler
Brian Kretzler

Reputation: 9938

Quick and dirty...

<Target Name="CreateXml">
  <ItemGroup>
    <TestAssembly Include="$(TestsAssembliesOutputDir)\Emidee.*.Tests.dll" />
    <Line Include="line01"><Text>&lt;xunit&gt;</Text></Line>
    <Line Include="line02"><Text>&lt;assemblies&gt;</Text></Line>
    <Line Include="line03"><Text>&lt;assembly filename=&quot;%(TestAssembly.Identity)&quot; shadow-copy=&quot;true&quot; /&gt;</Text></Line>
    <Line Include="line04"><Text>&lt;/assemblies&gt;</Text></Line>
    <Line Include="line05"><Text>&lt;/xunit&gt;</Text></Line>
    <LineText Include="%(Line.Text)" />
  </ItemGroup>
  <WriteLinesToFile
     File="out.xml"
     Lines="@(LineText)"
     Overwrite="true"
     />
</Target>

Left as an exercise for you

  • The initial < ? xml line
  • Indentation (hint use CDATA inside <`Text>)

You could also use the following in WriteLinesToFile and omit the synthesized @(LineText)

    Lines="@(Line->'%(Text)')"

Upvotes: 32

Related Questions