How to HeatDirectory 2 or more times against directories with the same files?

1k views Asked by At

I've been using the HeatDirectory task in our WiX installer project in the BeforeBuild target to harvest the files of a web application we deploy on the clients network. Been working great.

I now want to deploy a second set of files, which so happens to be some documentation, and it contains files that are of the same name that exist in the previous HeatDirectory output.

I get the following error:

LGHT0293: Multiple files with ID 'Web.Config' exist.

I understand why I am getting the error, I'm wondering how best to resolve it.

Option A:

Copy all the files into a directory and run heat on them in one massive pass.

I like this because it would be fairly easy to implement using stock MSBuild tasks. I dislike it because it would create one massive ComponentGroup and if I ever decided to make optional features (like not install something), I can't.

Option B:

Iterate over the output file of the HeatDirectory task and append a suffix on all the Component ID and File Id's. Example - web.config would become web.config_DocumenationFiles

I like this because it's clean; i.e. I can delete it later or add it to a project that's having the issue and not add it to projects that don't. I dislike it because I'm not sure what 'process' (or MSBuild task) is capable of doing this. I need a custom task I think.

Other Options: ?

Ideas?

2

There are 2 answers

0
Rick Glos On BEST ANSWER

I see someone has come along and upvoted this three years later. I thought I'd come back and state that I'm still using Option B, a custom MSBuild task to resolve the issue.

I made a comment on another post about the issue with more details and thought I'd move/copy my implementation over here in case it's helpful.

I had solved it by creating a custom build task to run after the HeatDirectory task to append a suffix to the Id attribute.

<AddSuffixToHeatDirectory File="ReportFiles.Generated.wxs" Suffix="_r" />

The AddSuffixToHeatDirectory task is as such

public class AddSuffixToHeatDirectory : Task
{
    public override bool Execute()
    {
        bool result = true;

        Log.LogMessage("Opening file '{0}'.", File);
        var document = XElement.Load(File);

        var defaultNamespace = GetDefaultNamespace(document);

        AddSuffixToAttribute(document, defaultNamespace, "Component", "Id");
        AddSuffixToAttribute(document, defaultNamespace, "File", "Id");
        AddSuffixToAttribute(document, defaultNamespace, "ComponentRef", "Id");
        AddSuffixToAttribute(document, defaultNamespace, "Directory", "Id");

        var files = (from x in document.Descendants(defaultNamespace.GetName("File")) select x).ToList();

        Log.LogMessage("Saving file '{0}'.", File);
        document.Save(File);

        return result;
    }

    private void AddSuffixToAttribute(XElement xml, XNamespace defaultNamespace, string elementName, string attributeName)
    {
        var items = (from x in xml.Descendants(defaultNamespace.GetName(elementName)) select x).ToList();
        foreach (var item in items)
        {
            var attribute = item.Attribute(attributeName);
            attribute.Value = string.Format("{0}{1}", attribute.Value, Suffix);
        }
    }

    private XNamespace GetDefaultNamespace(XElement root)
    {
        // I pieced together this query from hanselman's post.
        //  http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
        // 
        // Basically I'm just getting the namespace that doesn't have a localname.
        var result = root.Attributes()
                        .Where(a => a.IsNamespaceDeclaration)
                        .GroupBy(a => a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName, a => XNamespace.Get(a.Value))
                        .ToDictionary(g => g.Key, g => g.First());
        return result[string.Empty];
    }

    /// <summary>
    /// File to modify.
    /// </summary>
    [Required]
    public string File { get; set; }

    /// <summary>
    /// Suffix to append.
    /// </summary>
    [Required]
    public string Suffix { get; set; }
}

Hope that helps. I'm still using this method today and I've not done the legwork to look into a Transform or extending HeatDirectory.

1
Aaron Carlson On

The HeatDirectory task has Transforms attribute that you can use to transform the result file. You could create an xslt to add your suffix to the Component IDs.

Also, Heat is extensible. You may want to look into creating your own harvester that will append a suffix to the Component Ids for you.