In my project there is several modules. Source files for them are generated by heatdirectory.
<HeatDirectory DirectoryRefId="ServerAdminService" OutputFile="Source\ServerAdminServiceSource.wxs" Transforms="Filter.xsl" Directory="..\..\Server\ServerServiceManager\bin\Debug\" PreprocessorVariable="var.ServerAdminServicePath" ComponentGroupName="ServerAdminServiceGroup" ToolPath="$(WixToolPath)" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="true" GenerateGuidsNow="false">
</HeatDirectory>
<HeatDirectory DirectoryRefId="ServerAdminService" OutputFile="Source\ServerAdminClientSource.wxs" Transforms="Filter.xsl" Directory="..\Setup\C24.ServerAdmin.UI\bin\Debug\" PreprocessorVariable="var.ServerAdminClientPath" ComponentGroupName="ServerAdminClientGroup" ToolPath="$(WixToolPath)" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="true" GenerateGuidsNow="false">
</HeatDirectory>
It work fine. I need to install them in one directory. But they use several libraries, which presents in both modules, and after generating source files consist the component with duplicate ID. Actually i don't know what to do. Does somebody have an idea?
I had the exact same issue.
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 instead.