/// <summary> /// Asynchronously execute a command line tool in this window, showing progress /// and finally calling the specified delegate on completion from the main / UI thread. /// </summary> /// <param name="toolPath">Tool to execute.</param> /// <param name="arguments">String to pass to the tools' command line.</param> /// <param name="completionDelegate">Called when the tool completes.</param> /// <param name="workingDirectory">Directory to execute the tool from.</param> /// <param name="ioHandler">Allows a caller to provide interactive input and also handle /// both output and error streams from a single delegate.</param> /// <param name="maxProgressLines">Specifies the number of lines output by the /// command line that results in a 100% value on a progress bar.</param> /// <returns>Reference to the new window.</returns> public void RunAsync( string toolPath, string arguments, CommandLine.CompletionHandler completionDelegate, string workingDirectory = null, Dictionary <string, string> envVars = null, CommandLine.IOHandler ioHandler = null, int maxProgressLines = 0) { CommandLineDialog.ProgressReporter reporter = new CommandLineDialog.ProgressReporter(logger); reporter.maxProgressLines = maxProgressLines; // Call the reporter from the UI thread from this window. UpdateEvent += reporter.Update; // Connect the user's delegate to the reporter's completion method. reporter.Complete += completionDelegate; // Connect the caller's IoHandler delegate to the reporter. reporter.DataHandler += ioHandler; // Disconnect the reporter when the command completes. CommandLine.CompletionHandler reporterUpdateDisable = (CommandLine.Result unusedResult) => { this.UpdateEvent -= reporter.Update; }; reporter.Complete += reporterUpdateDisable; logger.Log(String.Format( "Executing command: {0} {1}", toolPath, arguments), level: LogLevel.Verbose); CommandLine.RunAsync(toolPath, arguments, reporter.CommandLineToolCompletion, workingDirectory: workingDirectory, envVars: envVars, ioHandler: reporter.AggregateLine); }
/// <summary> /// Called from RunCommandLine() tool to report the output of the currently /// executing commmand. /// </summary> /// <param name="process">Executing process.</param> /// <param name="stdin">Standard input stream.</param> /// <param name="data">Data read from the standard output or error streams.</param> private void CommandLineIOHandler(Process process, StreamWriter stdin, CommandLine.StreamData data) { if (process.HasExited || data.data == null) { return; } // Count lines in stdout. if (data.handle == 0) { linesReported += CountLines(data.text); } // Enqueue data for the text view. var newLines = System.Text.Encoding.UTF8.GetString(data.data); textQueue.Enqueue(newLines); // Write to the logger. foreach (var line in CommandLine.SplitLines(newLines)) { logger.Log(line, level: LogLevel.Verbose); } }
/// <summary> /// Remove the given set of files and their folders. /// </summary> /// <param name = "filenames">Files to be removed/</param> /// <param name = "logger">Logger to log results.</param> /// <return>True if all files are removed. False if failed to remove any file or /// if any file is missing.</return> public static RemoveAssetsResult RemoveAssets(IEnumerable <string> filenames, Logger logger = null) { var result = new RemoveAssetsResult(); HashSet <string> folderToRemove = new HashSet <string>(); foreach (var filename in filenames) { if (File.Exists(filename)) { if (AssetDatabase.DeleteAsset(filename)) { result.Removed.Add(filename); } else { result.RemoveFailed.Add(filename); } // Add folder and parent folders to be removed later. var folder = Path.GetDirectoryName(filename); while (!String.IsNullOrEmpty(folder) && !Path.IsPathRooted(folder) && String.Compare(folder, ASSETS_FOLDER) != 0 && String.Compare(folder, PACKAGES_FOLDER) != 0) { folderToRemove.Add(folder); folder = Path.GetDirectoryName(folder); } } else { result.Missing.Add(filename); } } // Attempt to remove folders from bottom to top. // This is an unreliable way to remove folder as long as Directory class is used. // Directory.GetFiles() and Directory.GetDirectories() may return non-empty list // even everything under the folder is removed. This may due to Unity overriding // Files and Directory class. While Asset.DeleteAsset() can delete the folder, // regardless it is empty or not, the current approach still have some chance to // leave some empty folders. // TODO: Change the implementation to remove folder directly as long as every files // and folders are planned to be removed. List <string> sortedFolders = new List <string>(folderToRemove); // Sort folders in descending order so that sub-folder is removed first. sortedFolders.Sort((lhs, rhs) => { return(String.Compare(rhs, lhs)); }); List <string> folderRemoveFailed = new List <string>(); foreach (var folder in sortedFolders) { if (Directory.GetFiles(folder).Length == 0 && Directory.GetDirectories(folder).Length == 0) { if (!AssetDatabase.DeleteAsset(folder)) { folderRemoveFailed.Add(folder); result.RemoveFailed.Add(folder); } } } if (logger != null) { var components = new List <string>(); if (result.Removed.Count > 0) { components.Add(String.Format("Removed:\n{0}", String.Join("\n", result.Removed.ToArray()))); } if (result.RemoveFailed.Count > 0) { components.Add(String.Format("Failed to Remove:\n{0}", String.Join("\n", result.RemoveFailed.ToArray()))); } if (result.Missing.Count > 0) { components.Add(String.Format("Missing:\n{0}", String.Join("\n", result.Missing.ToArray()))); } if (folderRemoveFailed.Count > 0) { components.Add(String.Format("Failed to Remove Folders:\n{0}", String.Join("\n", folderRemoveFailed.ToArray()))); } if (components.Count > 0) { logger.Log(String.Join("\n", components.ToArray()), level: LogLevel.Verbose); } } return(result); }