// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset.Harvesters
{
using System;
using System.IO;
using WixToolset.Data;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Harvesters.Data;
using WixToolset.Harvesters.Extensibility;
using Wix = WixToolset.Harvesters.Serialize;
///
/// Harvest WiX authoring for a directory from the file system.
///
public sealed class DirectoryHarvester : BaseHarvesterExtension
{
private readonly FileHarvester fileHarvester;
private const string ComponentPrefix = "cmp";
private const string DirectoryPrefix = "dir";
private const string FilePrefix = "fil";
///
/// Instantiate a new DirectoryHarvester.
///
public DirectoryHarvester()
{
this.fileHarvester = new FileHarvester();
this.SetUniqueIdentifiers = true;
}
///
/// Gets or sets what type of elements are to be generated.
///
/// The type of elements being generated.
public GenerateType GenerateType { get; set; }
///
/// Gets or sets the option to keep empty directories.
///
/// The option to keep empty directories.
public bool KeepEmptyDirectories { get; set; }
///
/// Gets or sets the rooted DirectoryRef Id if the user has supplied it.
///
/// The DirectoryRef Id to use as the root.
public string RootedDirectoryRef { get; set; }
///
/// Gets of sets the option to set unique identifiers.
///
/// The option to set unique identifiers.
public bool SetUniqueIdentifiers { get; set; }
///
/// Gets or sets the option to suppress including the root directory as an element.
///
/// The option to suppress including the root directory as an element.
public bool SuppressRootDirectory { get; set; }
///
/// Harvest a directory.
///
/// The path of the directory.
/// The harvested directory.
public override Wix.Fragment[] Harvest(string argument)
{
if (null == argument)
{
throw new ArgumentNullException("argument");
}
Wix.IParentElement harvestParent = this.HarvestDirectory(argument, true, this.GenerateType);
Wix.ISchemaElement harvestElement;
if (this.GenerateType == GenerateType.PayloadGroup)
{
Wix.PayloadGroup payloadGroup = (Wix.PayloadGroup)harvestParent;
payloadGroup.Id = this.RootedDirectoryRef;
harvestElement = payloadGroup;
}
else
{
Wix.Directory directory = (Wix.Directory)harvestParent;
var directoryRef = DirectoryHelper.CreateDirectoryReference(this.RootedDirectoryRef);
if (this.SuppressRootDirectory)
{
foreach (Wix.ISchemaElement element in directory.Children)
{
directoryRef.AddChild(element);
}
}
else
{
directoryRef.AddChild(directory);
}
harvestElement = directoryRef;
}
Wix.Fragment fragment = new Wix.Fragment();
fragment.AddChild(harvestElement);
return new Wix.Fragment[] { fragment };
}
///
/// Harvest a directory.
///
/// The path of the directory.
/// The option to harvest child directories and files.
/// The harvested directory.
public Wix.Directory HarvestDirectory(string path, bool harvestChildren)
{
if (null == path)
{
throw new ArgumentNullException("path");
}
return (Wix.Directory)this.HarvestDirectory(path, harvestChildren, GenerateType.Components);
}
///
/// Harvest a directory.
///
/// The path of the directory.
/// The option to harvest child directories and files.
/// The type to generate.
/// The harvested directory.
private Wix.IParentElement HarvestDirectory(string path, bool harvestChildren, GenerateType generateType)
{
if (File.Exists(path))
{
throw new WixException(ErrorMessages.ExpectedDirectoryGotFile("dir", path));
}
if (null == this.RootedDirectoryRef)
{
this.RootedDirectoryRef = "TARGETDIR";
}
// use absolute paths
path = Path.GetFullPath(path);
// Remove any trailing separator to ensure Path.GetFileName() will return the directory name.
path = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
Wix.IParentElement harvestParent;
if (generateType == GenerateType.PayloadGroup)
{
harvestParent = new Wix.PayloadGroup();
}
else
{
Wix.Directory directory = new Wix.Directory();
directory.Name = Path.GetFileName(path);
directory.FileSource = path;
if (this.SetUniqueIdentifiers)
{
if (this.SuppressRootDirectory)
{
directory.Id = this.Core.GenerateIdentifier(DirectoryPrefix, this.RootedDirectoryRef);
}
else
{
directory.Id = this.Core.GenerateIdentifier(DirectoryPrefix, this.RootedDirectoryRef, directory.Name);
}
}
harvestParent = directory;
}
if (harvestChildren)
{
try
{
int fileCount = this.HarvestDirectory(path, "", harvestParent, generateType);
if (generateType != GenerateType.PayloadGroup)
{
// its an error to not harvest anything with the option to keep empty directories off
if (0 == fileCount && !this.KeepEmptyDirectories)
{
throw new WixException(HarvesterErrors.EmptyDirectory(path));
}
}
}
catch (DirectoryNotFoundException)
{
throw new WixException(HarvesterErrors.DirectoryNotFound(path));
}
}
return harvestParent;
}
///
/// Harvest a directory.
///
/// The path of the directory.
/// The relative path that will be used when harvesting.
/// The directory for this path.
///
/// The number of files harvested.
private int HarvestDirectory(string path, string relativePath, Wix.IParentElement harvestParent, GenerateType generateType)
{
int fileCount = 0;
Wix.DirectoryBase directory = generateType != GenerateType.PayloadGroup ? (Wix.DirectoryBase)harvestParent : null;
// harvest the child directories
foreach (string childDirectoryPath in Directory.GetDirectories(path))
{
var childDirectoryName = Path.GetFileName(childDirectoryPath);
Wix.IParentElement newParent;
Wix.Directory childDirectory = null;
if (generateType == GenerateType.PayloadGroup)
{
newParent = harvestParent;
}
else
{
childDirectory = new Wix.Directory();
newParent = childDirectory;
childDirectory.Name = childDirectoryName;
childDirectory.FileSource = childDirectoryPath;
if (this.SetUniqueIdentifiers)
{
childDirectory.Id = this.Core.GenerateIdentifier(DirectoryPrefix, directory.Id, childDirectory.Name);
}
}
int childFileCount = this.HarvestDirectory(childDirectoryPath, String.Concat(relativePath, childDirectoryName, "\\"), newParent, generateType);
if (generateType != GenerateType.PayloadGroup)
{
// keep the directory if it contained any files (or empty directories are being kept)
if (0 < childFileCount || this.KeepEmptyDirectories)
{
directory.AddChild(childDirectory);
}
}
fileCount += childFileCount;
}
// harvest the files
string[] files = Directory.GetFiles(path);
if (0 < files.Length)
{
foreach (string filePath in Directory.GetFiles(path))
{
string fileName = Path.GetFileName(filePath);
string source = String.Concat("SourceDir\\", relativePath, fileName);
Wix.ISchemaElement newChild;
if (generateType == GenerateType.PayloadGroup)
{
Wix.Payload payload = new Wix.Payload();
newChild = payload;
payload.SourceFile = source;
if (!String.IsNullOrEmpty(relativePath))
{
payload.Name = String.Concat(relativePath, fileName);
}
}
else
{
Wix.Component component = new Wix.Component();
newChild = component;
Wix.File file = this.fileHarvester.HarvestFile(filePath);
file.Source = source;
if (this.SetUniqueIdentifiers)
{
file.Id = this.Core.GenerateIdentifier(FilePrefix, directory.Id, fileName);
component.Id = this.Core.GenerateIdentifier(ComponentPrefix, directory.Id, file.Id);
}
component.AddChild(file);
}
harvestParent.AddChild(newChild);
}
}
else if (generateType != GenerateType.PayloadGroup && 0 == fileCount && this.KeepEmptyDirectories)
{
Wix.Component component = new Wix.Component();
component.KeyPath = Wix.YesNoType.yes;
if (this.SetUniqueIdentifiers)
{
component.Id = this.Core.GenerateIdentifier(ComponentPrefix, directory.Id);
}
Wix.CreateFolder createFolder = new Wix.CreateFolder();
component.AddChild(createFolder);
directory.AddChild(component);
}
return fileCount + files.Length;
}
}
}