/// <summary> /// Creates a binder. /// </summary> public WixBinder() { this.extensions = new List <BinderExtension>(); this.inspectorExtensions = new List <InspectorExtension>(); this.fileManager = new BinderFileManager(); this.fileManager.TempFilesLocation = this.TempFilesLocation; }
/// <summary> /// Saves an output to a path on disk. /// </summary> /// <param name="path">Path to save output file to on disk.</param> /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the output.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> /// <param name="tempFilesLocation">Location for temporary files.</param> public void Save(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation) { FileMode fileMode = FileMode.Create; // Assure the location to output the xml exists Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); // Check if there was a cab on the output when it was created if (SaveCab(path, binderFileManager, wixVariableResolver, tempFilesLocation)) { fileMode = FileMode.Append; } // save the xml using (FileStream fs = new FileStream(path, fileMode)) { XmlWriter writer = null; try { writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8); writer.WriteStartDocument(); this.Persist(writer); writer.WriteEndDocument(); } finally { if (null != writer) { writer.Close(); } } } }
/// <summary> /// Instantiate a new CabinetWorkItem. /// </summary> /// <param name="fileRows">The collection of files in this cabinet.</param> /// <param name="cabinetFile">The cabinet file.</param> /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> /// <param name="compressionLevel">The compression level of the cabinet.</param> /// <param name="binderFileManager">The binder file manager.</param> public CabinetWorkItem(FileRowCollection fileRows, string cabinetFile, int maxThreshold, Cab.CompressionLevel compressionLevel, BinderFileManager binderFileManager) { this.cabinetFile = cabinetFile; this.compressionLevel = compressionLevel; this.fileRows = fileRows; this.binderFileManager = binderFileManager; this.maxThreshold = maxThreshold; }
/// <summary> /// Resolves the source path of a file. /// </summary> /// <param name="source">Original source value.</param> /// <param name="type">Optional type of source file being resolved.</param> /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> /// <returns>Should return a valid path for the stream to be imported.</returns> public virtual string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) { if (String.IsNullOrEmpty(source)) { throw new ArgumentNullException("source"); } if (BinderFileManager.CheckFileExists(source)) // if the file exists, we're good to go. { return(source); } else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist. { return(null); } else // not a rooted path so let's try applying all the different source resolution options. { const string bindPathOpenString = "!(bindpath."; string bindName = String.Empty; string path = source; string pathWithoutSourceDir = null; if (source.StartsWith(bindPathOpenString, StringComparison.Ordinal)) { int closeParen = source.IndexOf(')', bindPathOpenString.Length); if (-1 != closeParen) { bindName = source.Substring(bindPathOpenString.Length, closeParen - bindPathOpenString.Length); path = source.Substring(bindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace. path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted. } } else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) { pathWithoutSourceDir = path.Substring(10); } var bindPaths = this.Core.GetBindPaths(bindStage, bindName); foreach (string bindPath in bindPaths) { string filePath; if (!String.IsNullOrEmpty(pathWithoutSourceDir)) { filePath = Path.Combine(bindPath, pathWithoutSourceDir); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } } filePath = Path.Combine(bindPath, path); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } } } // Didn't find the file. return(null); }
public virtual ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable <BindFileWithPath> filesWithPath) { if (null == filesWithPath) { throw new ArgumentNullException("fileRows"); } // By default cabinet should be built and moved to the suggested location. ResolvedCabinet resolved = new ResolvedCabinet() { BuildOption = CabinetBuildOption.BuildAndMove, Path = cabinetPath }; // If a cabinet cache path was provided, change the location for the cabinet // to be built to and check if there is a cabinet that can be reused. if (!String.IsNullOrEmpty(this.Core.CabCachePath)) { string cabinetName = Path.GetFileName(cabinetPath); resolved.Path = Path.Combine(this.Core.CabCachePath, cabinetName); if (BinderFileManager.CheckFileExists(resolved.Path)) { // Assume that none of the following are true: // 1. any files are added or removed // 2. order of files changed or names changed // 3. modified time changed bool cabinetValid = true; // Need to force garbage collection of WixEnumerateCab to ensure the handle // associated with it is closed before it is reused. using (Cab.WixEnumerateCab wixEnumerateCab = new Cab.WixEnumerateCab()) { List <CabinetFileInfo> fileList = wixEnumerateCab.Enumerate(resolved.Path); if (filesWithPath.Count() != fileList.Count) { cabinetValid = false; } else { int i = 0; foreach (BindFileWithPath file in filesWithPath) { // First check that the file identifiers match because that is quick and easy. CabinetFileInfo cabFileInfo = fileList[i]; cabinetValid = (cabFileInfo.FileId == file.Id); if (cabinetValid) { // Still valid so ensure the file sizes are the same. FileInfo fileInfo = new FileInfo(file.Path); cabinetValid = (cabFileInfo.Size == fileInfo.Length); if (cabinetValid) { // Still valid so ensure the source time stamp hasn't changed. Thus we need // to convert the source file time stamp into a cabinet compatible data/time. ushort sourceCabDate; ushort sourceCabTime; WixToolset.Core.Native.CabInterop.DateTimeToCabDateAndTime(fileInfo.LastWriteTime, out sourceCabDate, out sourceCabTime); cabinetValid = (cabFileInfo.Date == sourceCabDate && cabFileInfo.Time == sourceCabTime); } } if (!cabinetValid) { break; } i++; } } } resolved.BuildOption = cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy; } } return(resolved); }
public virtual CabinetBuildOption ResolveCabinet(FileRowCollection fileRows, ref string cabinetPath) { if (fileRows == null) { throw new ArgumentNullException("fileRows"); } // no special behavior specified, use the default if (null == this.cabCachePath && !this.reuseCabinets) { return(CabinetBuildOption.BuildAndMove); } // if a cabinet cache path was provided, change the location for the cabinet // to be built to if (null != this.cabCachePath) { string cabinetName = Path.GetFileName(cabinetPath); cabinetPath = Path.Combine(this.cabCachePath, cabinetName); } // if we still think we're going to reuse the cabinet check to see if the cabinet exists first if (this.reuseCabinets) { bool cabinetValid = false; if (BinderFileManager.CheckFileExists(cabinetPath)) { // check to see if // 1. any files are added or removed // 2. order of files changed or names changed // 3. modified time changed cabinetValid = true; // Need to force garbage collection of WixEnumerateCab to ensure the handle // associated with it is closed before it is reused. using (Cab.WixEnumerateCab wixEnumerateCab = new Cab.WixEnumerateCab()) { ArrayList fileList = wixEnumerateCab.Enumerate(cabinetPath); if (fileRows.Count != fileList.Count) { cabinetValid = false; } else { int i = 0; foreach (FileRow fileRow in fileRows) { // First check that the file identifiers match because that is quick and easy. CabinetFileInfo cabFileInfo = fileList[i] as CabinetFileInfo; cabinetValid = (cabFileInfo.FileId == fileRow.File); if (cabinetValid) { // Still valid so ensure the source time stamp hasn't changed. Thus we need // to convert the source file time stamp into a cabinet compatible data/time. DateTime sourceFileTime = File.GetLastWriteTime(fileRow.Source); ushort sourceCabDate; ushort sourceCabTime; Cab.Interop.CabInterop.DateTimeToCabDateAndTime(sourceFileTime, out sourceCabDate, out sourceCabTime); cabinetValid = (cabFileInfo.Date == sourceCabDate && cabFileInfo.Time == sourceCabTime); } if (!cabinetValid) { break; } i++; } } } } return(cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy); } else // by default move the built cabinet { return(CabinetBuildOption.BuildAndMove); } }
/// <summary> /// Resolves the source path of a file. /// </summary> /// <param name="source">Original source value.</param> /// <param name="type">Optional type of source file being resolved.</param> /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> /// <returns>Should return a valid path for the stream to be imported.</returns> public virtual string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) { // the following new local variables are used for bind path and protect the changes to object field. StringCollection currentBindPaths = null; NameValueCollection currentNamedBindPaths = null; StringCollection currentSourcePaths = null; if (String.IsNullOrEmpty(source)) { throw new ArgumentNullException("source"); } // Call the original override function first. If it returns an answer then return that, // otherwise using the default resolving logic string filePath = this.ResolveFile(source, type, sourceLineNumbers); if (!String.IsNullOrEmpty(filePath)) { return(filePath); } // Assign the correct bind path to file manager currentSourcePaths = this.sourcePaths[bindStage]; currentNamedBindPaths = this.namedBindPaths[bindStage]; if (BindStage.Target != bindStage && BindStage.Updated != bindStage) { currentBindPaths = this.bindPaths[bindStage]; } else { currentBindPaths = this.sourcePaths[bindStage]; } // If the path is rooted, it better exist or we're not going to find it. if (Path.IsPathRooted(source)) { if (BinderFileManager.CheckFileExists(source)) { return(source); } } else // not a rooted path so let's try applying all the different source resolution options. { const string bindPathOpenString = "!(bindpath."; if (source.StartsWith(bindPathOpenString, StringComparison.Ordinal) && source.IndexOf(')') != -1) { int bindpathSignatureLength = bindPathOpenString.Length; string name = source.Substring(bindpathSignatureLength, source.IndexOf(')') - bindpathSignatureLength); string[] values = currentNamedBindPaths.GetValues(name); if (null != values) { foreach (string bindPath in values) { // Parse out '\\' chars that separate the "bindpath" variable and the next part of the path, // because Path.Combine() thinks that rooted second paths don't need the first path. string nameSection = string.Empty; int nameStart = bindpathSignatureLength + 1 + name.Length; // +1 for the closing bracket. nameSection = source.Substring(nameStart).TrimStart('\\'); filePath = Path.Combine(bindPath, nameSection); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } } } } else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) { foreach (string bindPath in currentBindPaths) { filePath = Path.Combine(bindPath, source.Substring(10)); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } } } else if (BinderFileManager.CheckFileExists(source)) { return(source); } foreach (string path in currentSourcePaths) { filePath = Path.Combine(path, source); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) { filePath = Path.Combine(path, source.Substring(10)); if (BinderFileManager.CheckFileExists(filePath)) { return(filePath); } } } } // Didn't find the file. throw new WixFileNotFoundException(sourceLineNumbers, source, type); }
public ContainerInfo(string id, string name, string type, string downloadUrl, BinderFileManager fileManager) { this.Id = id; this.Name = name; this.Type = type; this.DownloadUrl = downloadUrl; this.FileManager = fileManager; this.TempPath = Path.Combine(fileManager.TempFilesLocation, name); this.FileInfo = new FileInfo(this.TempPath); }
public ContainerInfo(Row row, BinderFileManager fileManager) : this((string)row[0], (string)row[1], (string)row[2], (string)row[3], fileManager) { this.SourceLineNumbers = row.SourceLineNumbers; }
/// <summary> /// Saves a library to a path on disk. /// </summary> /// <param name="path">Path to save library file to on disk.</param> /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the library.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> public void Save(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver) { FileMode fileMode = FileMode.Create; StringCollection fileIds = new StringCollection(); StringCollection files = new StringCollection(); int index = 0; // resolve paths to files and create the library cabinet file foreach (Section section in this.sections) { foreach (Table table in section.Tables) { foreach (Row row in table.Rows) { foreach (Field field in row.Fields) { ObjectField objectField = field as ObjectField; if (null != objectField) { if (null != binderFileManager && null != objectField.Data) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); // resolve wix variables string resolvedValue = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false); files.Add(binderFileManager.ResolveFile(resolvedValue, table.Name, row.SourceLineNumbers, BindStage.Normal)); // File was successfully resolved so track this cabient file id. objectField.CabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); } else // clear out a previous cabinet file id value { objectField.CabinetFileId = null; } } } } } } // do not save the library if errors were found while resolving object paths if (wixVariableResolver.EncounteredError) { return; } // create the cabinet file if (0 < fileIds.Count) { using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), fileIds.Count, 0, 0, CompressionLevel.Mszip)) { for (int i = 0; i < fileIds.Count; i++) { cab.AddFile(files[i], fileIds[i]); } cab.Complete(); } // append the library xml to the end of the newly created cabinet file fileMode = FileMode.Append; } // Assure the location to output the lib exists Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); // save the xml using (FileStream fs = new FileStream(path, fileMode)) { XmlWriter writer = null; try { writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8); writer.WriteStartDocument(); this.Persist(writer); writer.WriteEndDocument(); } finally { if (null != writer) { writer.Close(); } } } }
/// <summary> /// Resolves paths to files. /// </summary> /// <param name="sectionTables">TableCollection of tables to process</param> /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the output.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> /// <param name="tempFilesLocation">Location for temporary files.</param> /// <param name="cabinets">Hash of source cabinets.</param> /// <param name="fileIds">Collection of CabinetFileIds.</param> /// <param name="files">Collection of file paths from compressed files.</param> /// <param name="index">CabinetFileId generator.</param> private static void ResolveSectionFiles(TableCollection sectionTables, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation, Hashtable cabinets, StringCollection fileIds, StringCollection files, ref int index) { foreach (Table table in sectionTables) { foreach (Row row in table.Rows) { foreach (Field field in row.Fields) { ObjectField objectField = field as ObjectField; if (null != objectField && null != objectField.Data) { string file = null; string previousFile = null; bool isDefault = true; bool isPreviousDefault = true; // resolve localization and wix variables if there is a file manager that would use the value // if it was different, otherwise we just don't care so skip the whole variable resolution thing. if (null != wixVariableResolver && null != binderFileManager) { objectField.Data = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false, ref isDefault); if (null != objectField.PreviousData) { objectField.PreviousData = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, ref isPreviousDefault); } // do not save the output if errors were found while resolving object paths if (Messaging.Instance.EncounteredError) { return; } } // file is compressed in a cabinet (and not modified above) if (null != objectField.CabinetFileId && isDefault) { // index cabinets that have not been previously encountered if (!cabinets.ContainsKey(objectField.BaseUri)) { Uri baseUri = new Uri(objectField.BaseUri); string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath); string extractedDirectoryName = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension); // index the cabinet file's base URI (source location) and extracted directory cabinets.Add(objectField.BaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName)); } // set the path to the file once its extracted from the cabinet file = Path.Combine((string)cabinets[objectField.BaseUri], objectField.CabinetFileId); } else if (null != binderFileManager) { file = binderFileManager.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); } // add the file to the list of files to go in the cabinet if (null != file) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.CabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); files.Add(file); } // previous file is compressed in a cabinet (and not modified above) if (null != objectField.PreviousCabinetFileId && isPreviousDefault) { // index cabinets that have not been previously encountered if (!cabinets.ContainsKey(objectField.PreviousBaseUri)) { Uri baseUri = new Uri(objectField.PreviousBaseUri); string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath); string extractedDirectoryName = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension); // index the cabinet file's base URI (source location) and extracted directory cabinets.Add(objectField.PreviousBaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName)); } // set the path to the file once its extracted from the cabinet previousFile = Path.Combine((string)cabinets[objectField.PreviousBaseUri], objectField.PreviousCabinetFileId); } else if (null != objectField.PreviousData && null != binderFileManager) { previousFile = binderFileManager.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal); } // add the file to the list of files to go in the cabinet if (null != previousFile) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.PreviousCabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); files.Add(previousFile); } } } } } }
/// <summary> /// Saves an outputs cab to a path on disk. /// </summary> /// <param name="path">Path to save outputs cab to on disk.</param> /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the outputs cab.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> /// <param name="tempFilesLocation">Location for temporary files.</param> /// <returns>Returns true if a cabinet existed or was created, false otherwise.</returns> public bool SaveCab(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation) { bool hasCab = false; // Check if there was a cab on the wixout when it was created if (null != this.cabPath) { // There was already a cab on the wixout when it was loaded. Reuse that one. File.Copy(this.cabPath, path, true); if (null != this.tempFileCollection) { this.tempFileCollection.Delete(); } hasCab = true; } else { int index = 0; Hashtable cabinets = new Hashtable(); StringCollection fileIds = new StringCollection(); StringCollection files = new StringCollection(); if (null != tempFilesLocation) { // resolve paths to files and create the output cabinet file if (0 == this.sections.Count) { Output.ResolveSectionFiles(this.tables, binderFileManager, wixVariableResolver, tempFilesLocation, cabinets, fileIds, files, ref index); } else { foreach (Section section in this.sections) { Output.ResolveSectionFiles(section.Tables, binderFileManager, wixVariableResolver, tempFilesLocation, cabinets, fileIds, files, ref index); } } } // extract files that come from cabinet files if (0 < cabinets.Count) { // ensure the temporary directory exists Directory.CreateDirectory(tempFilesLocation); foreach (DictionaryEntry cabinet in cabinets) { Uri baseUri = new Uri((string)cabinet.Key); string localPath; if ("embeddedresource" == baseUri.Scheme) { int bytesRead; byte[] buffer = new byte[512]; string originalLocalPath = Path.GetFullPath(baseUri.LocalPath.Substring(1)); string resourceName = baseUri.Fragment.Substring(1); Assembly assembly = Assembly.LoadFile(originalLocalPath); localPath = String.Concat(cabinet.Value, ".cab"); using (FileStream fs = File.OpenWrite(localPath)) { using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) { while (0 < (bytesRead = resourceStream.Read(buffer, 0, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } } } else // normal file { localPath = baseUri.LocalPath; } // extract the cabinet's files into a temporary directory Directory.CreateDirectory((string)cabinet.Value); using (WixExtractCab extractCab = new WixExtractCab()) { extractCab.Extract(localPath, (string)cabinet.Value); } } } // create the cabinet file if (0 < fileIds.Count) { using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), fileIds.Count, 0, 0, CompressionLevel.Mszip)) { for (int i = 0; i < fileIds.Count; i++) { cab.AddFile(files[i], fileIds[i]); } cab.Complete(); } // append the output xml to the end of the newly created cabinet file hasCab = true; } } return(hasCab); }