///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using JetBrains.Build.AllAssemblies;
using JetBrains.Build.Omea.Infra;
using JetBrains.Build.Omea.Resolved.Infra;
using JetBrains.Build.Omea.Util;
using Microsoft.Build.Framework;
using Microsoft.Tools.WindowsInstallerXml.Serialize;
using Directory=Microsoft.Tools.WindowsInstallerXml.Serialize.Directory;
using File=Microsoft.Tools.WindowsInstallerXml.Serialize.File;
namespace JetBrains.Build.Omea.Resolved.Tasks
{
///
/// Creates a WiX source with the product biaries described in it.
///
public class WixProductReferencesResolved : TaskResolved
{
#region Data
///
/// Prefix for the generated WiX element ID.
///
public static readonly string DirectoryId = "D.ProductReferences";
///
/// Prefix for the generated WiX element ID.
///
public static readonly string FileComponentIdPrefix = "C.ProductReferences";
///
/// Prefix for the generated WiX element ID.
///
public static readonly string FileIdPrefix = "F.ProductReferences";
#endregion
#region Implementation
///
/// Writes a target file to the map, ensures that it's not duplicate.
///
/// The file name, relative to the install root.
/// Some textual comment on where the file is coming from.
/// Map.
private static void RegisterTargetFile(string name, string origin, Dictionary mapTargetFiles)
{
name = name.ToLowerInvariant();
string sOtherOrigin;
if(mapTargetFiles.TryGetValue(name, out sOtherOrigin))
throw new InvalidOperationException(string.Format("The target file “{0}”is installed twice, first as “{1}”, then as “{2}”.", name, sOtherOrigin, origin));
mapTargetFiles.Add(name, origin);
}
///
/// Processes the files that should be taken from the “References” folder and installed “AS IS”.
///
private int ProcessReferences(Directory wixDirectory, ComponentGroup wixComponentGroup, AllAssembliesXml allassembliesxml, Dictionary mapTargetFiles)
{
int nGeneratedComponents = 0;
// Replaces illegal chars with underscores
var regexMakeId = new Regex("[^a-zA-Z0-9_.]");
foreach(ItemGroupXml group in allassembliesxml.ItemGroup)
{
if(group.References == null)
continue;
foreach(ReferenceXml referencexml in group.References)
{
nGeneratedComponents++;
var fiReference = new FileInfo(Path.Combine(Bag.GetString(AttributeName.ProductReferencesDir), referencexml.Include));
if(!fiReference.Exists)
throw new InvalidOperationException(string.Format("The reference file “{0}” could not be found.", fiReference.FullName));
string sIdSuffix = regexMakeId.Replace(fiReference.Name, "_");
// Create the component for the assembly (one per assembly)
var wixComponent = new Component();
wixDirectory.AddChild(wixComponent);
wixComponent.Id = string.Format("{0}.{1}", FileComponentIdPrefix, sIdSuffix);
wixComponent.Guid = referencexml.MsiGuid;
wixComponent.DiskId = Bag.Get(AttributeName.DiskId);
wixComponent.Location = Component.LocationType.local;
// Register component in the group
var componentref = new ComponentRef();
wixComponentGroup.AddChild(componentref);
componentref.Id = wixComponent.Id;
// Add the reference file (and make it the key path)
var wixFileReference = new File();
wixComponent.AddChild(wixFileReference);
wixFileReference.Id = string.Format("{0}.{1}", FileIdPrefix, sIdSuffix);
wixFileReference.Name = fiReference.Name;
wixFileReference.KeyPath = YesNoType.yes;
wixFileReference.Checksum = YesNoType.yes;
wixFileReference.Vital = YesNoType.yes;
wixFileReference.ReadOnly = YesNoType.yes;
RegisterTargetFile(wixFileReference.Name, string.Format("The “{0}” reference.", referencexml.Include), mapTargetFiles);
}
}
return nGeneratedComponents;
}
#endregion
#region Overrides
///
/// Actions under the resolver.
///
protected override void ExecuteTaskResolved()
{
// Global structure of the WiX fragment file
var wix = new Wix();
var wixFragmentComponents = new Fragment(); // Fragment with the payload
wix.AddChild(wixFragmentComponents);
var wixDirectoryRef = new DirectoryRef(); // Mount into the directories tree, defined externally
wixFragmentComponents.AddChild(wixDirectoryRef);
wixDirectoryRef.Id = Bag.GetString(AttributeName.WixDirectoryId);
var wixDirectory = new Directory(); // A locally created nameless directory that does not add any nested folders but defines the sources location
wixDirectoryRef.AddChild(wixDirectory);
wixDirectory.Id = DirectoryId;
wixDirectory.FileSource = Bag.GetString(AttributeName.ProductReferencesDir);
var wixFragmentGroup = new Fragment(); // Fragment with the component-group that collects the components
wix.AddChild(wixFragmentGroup);
var wixComponentGroup = new ComponentGroup(); // ComponentGroup that collects the components
wixFragmentGroup.AddChild(wixComponentGroup);
wixComponentGroup.Id = Bag.GetString(AttributeName.WixComponentGroupId);
// Load the AllAssemblies file
AllAssembliesXml allassembliesxml = AllAssembliesXml.LoadFrom(Bag.Get(AttributeName.AllAssembliesXml).ItemSpec);
// Tracks the files on the target machine, to prevent the same file from being installed both as an assembly and as a reference
var mapTargetFiles = new Dictionary();
int nGeneratedComponents = ProcessReferences(wixDirectory, wixComponentGroup, allassembliesxml, mapTargetFiles);
// Save to the output file
using(var xw = new XmlTextWriter(new FileStream(Bag.GetString(AttributeName.OutputFile), FileMode.Create, FileAccess.Write, FileShare.Read), Encoding.UTF8))
{
xw.Formatting = Formatting.Indented;
wix.OutputXml(xw);
}
// Report (also to see the target in the build logs)
Log.LogMessage(MessageImportance.Normal, "Generated {0} product reference components.", nGeneratedComponents);
}
#endregion
}
}