protected override ExitCode RunCommand(Preprocessor preprocessor) { // Get the standard input stream _configOptions.Stdin = StandardInputReader.Read(); // Fix the root folder and other files DirectoryPath currentDirectory = Environment.CurrentDirectory; _configOptions.RootPath = _configOptions.RootPath == null ? currentDirectory : currentDirectory.Combine(_configOptions.RootPath); _logFilePath = _logFilePath == null ? null : _configOptions.RootPath.CombineFile(_logFilePath); _configOptions.ConfigFilePath = _configOptions.RootPath.CombineFile(_configOptions.ConfigFilePath ?? "config.wyam"); // Set up the log file if (_logFilePath != null) { // Delete an exiting log file if one exists if (File.Exists(_logFilePath.FullPath)) { try { File.Delete(_logFilePath.FullPath); } catch (Exception) { } } Trace.AddListener(new SimpleFileTraceListener(_logFilePath.FullPath)); } // Get the engine and configurator EngineManager engineManager = EngineManager.Get(preprocessor, _configOptions); if (engineManager == null) { return(ExitCode.CommandLineError); } // Configure and execute if (!engineManager.Configure()) { return(ExitCode.ConfigurationError); } if (_verifyConfig) { Trace.Information("No errors. Exiting."); return(ExitCode.Normal); } TraceEnviornment(engineManager); if (!engineManager.Execute()) { return(ExitCode.ExecutionError); } bool messagePump = false; // Start the preview server Server previewServer = null; if (_preview) { messagePump = true; DirectoryPath previewPath = _previewRoot == null ? engineManager.Engine.FileSystem.GetOutputDirectory().Path : engineManager.Engine.FileSystem.GetOutputDirectory(_previewRoot).Path; previewServer = PreviewServer.Start(previewPath, _previewPort, _previewForceExtension, _previewVirtualDirectory, _watch && !_noReload, _contentTypes); } // Start the watchers IDisposable inputFolderWatcher = null; IDisposable configFileWatcher = null; if (_watch) { messagePump = true; Trace.Information("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths)); inputFolderWatcher = new ActionFileSystemWatcher( engineManager.Engine.FileSystem.GetOutputDirectory().Path, engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path => { _changedFiles.Enqueue(path); _messageEvent.Set(); }); if (_configOptions.ConfigFilePath != null) { Trace.Information("Watching configuration file {0}", _configOptions.ConfigFilePath); configFileWatcher = new ActionFileSystemWatcher( engineManager.Engine.FileSystem.GetOutputDirectory().Path, new[] { _configOptions.ConfigFilePath.Directory }, false, _configOptions.ConfigFilePath.FileName.FullPath, path => { FilePath filePath = new FilePath(path); if (_configOptions.ConfigFilePath.Equals(filePath)) { _newEngine.Set(); _messageEvent.Set(); } }); } } // Start the message pump if an async process is running ExitCode exitCode = ExitCode.Normal; if (messagePump) { // Only wait for a key if console input has not been redirected, otherwise it's on the caller to exit if (!Console.IsInputRedirected) { // Start the key listening thread Thread thread = new Thread(() => { Trace.Information("Hit Ctrl-C to exit"); Console.TreatControlCAsInput = true; while (true) { // Would have prefered to use Console.CancelKeyPress, but that bubbles up to calling batch files // The (ConsoleKey)3 check is to support a bug in VS Code: https://github.com/Microsoft/vscode/issues/9347 ConsoleKeyInfo consoleKey = Console.ReadKey(true); if (consoleKey.Key == (ConsoleKey)3 || (consoleKey.Key == ConsoleKey.C && (consoleKey.Modifiers & ConsoleModifiers.Control) != 0)) { _exit.Set(); _messageEvent.Set(); break; } } }) { IsBackground = true }; thread.Start(); } // Wait for activity while (true) { _messageEvent.WaitOne(); // Blocks the current thread until a signal if (_exit) { break; } // See if we need a new engine if (_newEngine) { // Get a new engine Trace.Information("Configuration file {0} has changed, re-running", _configOptions.ConfigFilePath); engineManager.Dispose(); engineManager = EngineManager.Get(preprocessor, _configOptions); // Configure and execute if (!engineManager.Configure()) { exitCode = ExitCode.ConfigurationError; break; } TraceEnviornment(engineManager); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; } // Clear the changed files since we just re-ran string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { } _newEngine.Unset(); } else { // Execute if files have changed HashSet <string> changedFiles = new HashSet <string>(); string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { if (changedFiles.Add(changedFile)) { Trace.Verbose("{0} has changed", changedFile); } } if (changedFiles.Count > 0) { Trace.Information("{0} files have changed, re-executing", changedFiles.Count); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; } previewServer?.TriggerReload(); } } // Check one more time for exit if (_exit) { break; } Trace.Information("Hit Ctrl-C to exit"); _messageEvent.Reset(); } // Shutdown Trace.Information("Shutting down"); engineManager.Dispose(); inputFolderWatcher?.Dispose(); configFileWatcher?.Dispose(); previewServer?.Dispose(); } return(exitCode); }
public static void Preview(Engine engine, NukeBuild build) { var _messageEvent = new AutoResetEvent(false); var _changedFiles = new ConcurrentQueue <string>(); var _exit = new InterlockedBool(false); // Start the preview server DirectoryPath previewPath = (NukeBuild.RootDirectory / "output").ToString(); engine.Execute(); var previewServer = PreviewServer.Start(previewPath, 5080, true, null, true, new Dictionary <string, string>()); Trace.Information("Watching paths(s) {0}", string.Join(", ", engine.FileSystem.InputPaths)); var inputFolderWatcher = new ActionFileSystemWatcher( engine.FileSystem.GetOutputDirectory().Path, engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path => { _changedFiles.Enqueue(path); _messageEvent.Set(); }); // Start the message pump if an async process is running ExitCode exitCode = ExitCode.Normal; // Only wait for a key if console input has not been redirected, otherwise it's on the caller to exit if (!Console.IsInputRedirected) { // Start the key listening thread Thread thread = new Thread(() => { Trace.Information("Hit Ctrl-C to exit"); Console.TreatControlCAsInput = true; while (true) { // Would have prefered to use Console.CancelKeyPress, but that bubbles up to calling batch files // The (ConsoleKey)3 check is to support a bug in VS Code: https://github.com/Microsoft/vscode/issues/9347 ConsoleKeyInfo consoleKey = Console.ReadKey(true); if (consoleKey.Key == (ConsoleKey)3 || (consoleKey.Key == ConsoleKey.C && (consoleKey.Modifiers & ConsoleModifiers.Control) != 0)) { _exit.Set(); _messageEvent.Set(); break; } } }) { IsBackground = true }; thread.Start(); } // Wait for activity while (true) { _messageEvent.WaitOne(); // Blocks the current thread until a signal if (_exit) { break; } // Execute if files have changed HashSet <string> changedFiles = new HashSet <string>(); string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { if (changedFiles.Add(changedFile)) { Trace.Verbose("{0} has changed", changedFile); } } if (changedFiles.Count > 0) { Trace.Information("{0} files have changed, re-executing", changedFiles.Count); engine.Execute(); previewServer?.TriggerReloadAsync().GetAwaiter().GetResult(); } // Check one more time for exit if (_exit) { break; } Trace.Information("Hit Ctrl-C to exit"); _messageEvent.Reset(); } // Shutdown Trace.Information("Shutting down"); inputFolderWatcher?.Dispose(); previewServer?.Dispose(); }
protected override ExitCode RunCommand(Preprocessor preprocessor) { // Get the standard input stream _configOptions.Stdin = StandardInputReader.Read(); // Fix the root folder and other files DirectoryPath currentDirectory = Environment.CurrentDirectory; _configOptions.RootPath = _configOptions.RootPath == null ? currentDirectory : currentDirectory.Combine(_configOptions.RootPath); _logFilePath = _logFilePath == null ? null : _configOptions.RootPath.CombineFile(_logFilePath); _configOptions.ConfigFilePath = _configOptions.RootPath.CombineFile(_configOptions.ConfigFilePath ?? "config.wyam"); // Set up the log file if (_logFilePath != null) { Trace.AddListener(new SimpleFileTraceListener(_logFilePath.FullPath)); } // Get the engine and configurator EngineManager engineManager = EngineManager.Get(preprocessor, _configOptions); if (engineManager == null) { return ExitCode.CommandLineError; } // Configure and execute if (!engineManager.Configure()) { return ExitCode.ConfigurationError; } if (_verifyConfig) { Trace.Information("No errors. Exiting."); return ExitCode.Normal; } Trace.Information($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.RootPath}"); Trace.Information($"Input path(s):{Environment.NewLine} {string.Join(Environment.NewLine + " ", engineManager.Engine.FileSystem.InputPaths)}"); Trace.Information($"Output path:{Environment.NewLine} {engineManager.Engine.FileSystem.OutputPath}"); if (!engineManager.Execute()) { return ExitCode.ExecutionError; } bool messagePump = false; // Start the preview server IDisposable previewServer = null; if (_preview) { messagePump = true; DirectoryPath previewPath = _previewRoot == null ? engineManager.Engine.FileSystem.GetOutputDirectory().Path : engineManager.Engine.FileSystem.GetOutputDirectory(_previewRoot).Path; previewServer = PreviewServer.Start(previewPath, _previewPort, _previewForceExtension); } // Start the watchers IDisposable inputFolderWatcher = null; IDisposable configFileWatcher = null; if (_watch) { messagePump = true; Trace.Information("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths)); inputFolderWatcher = new ActionFileSystemWatcher(engineManager.Engine.FileSystem.GetOutputDirectory().Path, engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path => { _changedFiles.Enqueue(path); _messageEvent.Set(); }); if (_configOptions.ConfigFilePath != null) { Trace.Information("Watching configuration file {0}", _configOptions.ConfigFilePath); configFileWatcher = new ActionFileSystemWatcher(engineManager.Engine.FileSystem.GetOutputDirectory().Path, new[] { _configOptions.ConfigFilePath.Directory }, false, _configOptions.ConfigFilePath.FileName.FullPath, path => { FilePath filePath = new FilePath(path); if (_configOptions.ConfigFilePath.Equals(filePath)) { _newEngine.Set(); _messageEvent.Set(); } }); } } // Start the message pump if an async process is running ExitCode exitCode = ExitCode.Normal; if (messagePump) { // Only wait for a key if console input has not been redirected, otherwise it's on the caller to exit if (!Console.IsInputRedirected) { // Start the key listening thread var thread = new Thread(() => { Trace.Information("Hit any key to exit"); Console.ReadKey(); _exit.Set(); _messageEvent.Set(); }) { IsBackground = true }; thread.Start(); } // Wait for activity while (true) { _messageEvent.WaitOne(); // Blocks the current thread until a signal if (_exit) { break; } // See if we need a new engine if (_newEngine) { // Get a new engine Trace.Information("Configuration file {0} has changed, re-running", _configOptions.ConfigFilePath); engineManager.Dispose(); engineManager = EngineManager.Get(preprocessor, _configOptions); // Configure and execute if (!engineManager.Configure()) { exitCode = ExitCode.ConfigurationError; break; } Console.WriteLine($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.RootPath}"); Console.WriteLine($"Input path(s):{Environment.NewLine} {string.Join(Environment.NewLine + " ", engineManager.Engine.FileSystem.InputPaths)}"); Console.WriteLine($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.OutputPath}"); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; break; } // Clear the changed files since we just re-ran string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { } _newEngine.Unset(); } else { // Execute if files have changed HashSet<string> changedFiles = new HashSet<string>(); string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { if (changedFiles.Add(changedFile)) { Trace.Verbose("{0} has changed", changedFile); } } if (changedFiles.Count > 0) { Trace.Information("{0} files have changed, re-executing", changedFiles.Count); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; break; } } } // Check one more time for exit if (_exit) { break; } Trace.Information("Hit any key to exit"); _messageEvent.Reset(); } // Shutdown Trace.Information("Shutting down"); engineManager.Dispose(); inputFolderWatcher?.Dispose(); configFileWatcher?.Dispose(); previewServer?.Dispose(); } return exitCode; }
protected override ExitCode RunCommand(Preprocessor preprocessor) { // Get the standard input stream _configOptions.Stdin = StandardInputReader.Read(); // Fix the root folder and other files DirectoryPath currentDirectory = Environment.CurrentDirectory; _configOptions.RootPath = _configOptions.RootPath == null ? currentDirectory : currentDirectory.Combine(_configOptions.RootPath); _logFilePath = _logFilePath == null ? null : _configOptions.RootPath.CombineFile(_logFilePath); _configOptions.ConfigFilePath = _configOptions.RootPath.CombineFile(_configOptions.ConfigFilePath ?? "config.wyam"); // Set up the log file if (_logFilePath != null) { Trace.AddListener(new SimpleFileTraceListener(_logFilePath.FullPath)); } // Get the engine and configurator EngineManager engineManager = EngineManager.Get(preprocessor, _configOptions); if (engineManager == null) { return(ExitCode.CommandLineError); } // Configure and execute if (!engineManager.Configure()) { return(ExitCode.ConfigurationError); } if (_verifyConfig) { Trace.Information("No errors. Exiting."); return(ExitCode.Normal); } Trace.Information($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.RootPath}"); Trace.Information($"Input path(s):{Environment.NewLine} {string.Join(Environment.NewLine + " ", engineManager.Engine.FileSystem.InputPaths)}"); Trace.Information($"Output path:{Environment.NewLine} {engineManager.Engine.FileSystem.OutputPath}"); Trace.Information($"Settings:{Environment.NewLine} {string.Join(Environment.NewLine + " ", engineManager.Engine.Settings.Select(x => $"{x.Key}: {x.Value?.ToString() ?? "null"}"))}"); if (!engineManager.Execute()) { return(ExitCode.ExecutionError); } bool messagePump = false; // Start the preview server Server previewServer = null; if (_preview) { messagePump = true; DirectoryPath previewPath = _previewRoot == null ? engineManager.Engine.FileSystem.GetOutputDirectory().Path : engineManager.Engine.FileSystem.GetOutputDirectory(_previewRoot).Path; previewServer = PreviewServer.Start(previewPath, _previewPort, _previewForceExtension, _previewVirtualDirectory, _watch && !_noReload); } // Start the watchers IDisposable inputFolderWatcher = null; IDisposable configFileWatcher = null; if (_watch) { messagePump = true; Trace.Information("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths)); inputFolderWatcher = new ActionFileSystemWatcher( engineManager.Engine.FileSystem.GetOutputDirectory().Path, engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path => { _changedFiles.Enqueue(path); _messageEvent.Set(); }); if (_configOptions.ConfigFilePath != null) { Trace.Information("Watching configuration file {0}", _configOptions.ConfigFilePath); configFileWatcher = new ActionFileSystemWatcher( engineManager.Engine.FileSystem.GetOutputDirectory().Path, new[] { _configOptions.ConfigFilePath.Directory }, false, _configOptions.ConfigFilePath.FileName.FullPath, path => { FilePath filePath = new FilePath(path); if (_configOptions.ConfigFilePath.Equals(filePath)) { _newEngine.Set(); _messageEvent.Set(); } }); } } // Start the message pump if an async process is running ExitCode exitCode = ExitCode.Normal; if (messagePump) { // Only wait for a key if console input has not been redirected, otherwise it's on the caller to exit if (!Console.IsInputRedirected) { // Start the key listening thread Thread thread = new Thread(() => { Trace.Information("Hit any key to exit"); Console.ReadKey(); _exit.Set(); _messageEvent.Set(); }) { IsBackground = true }; thread.Start(); } // Wait for activity while (true) { _messageEvent.WaitOne(); // Blocks the current thread until a signal if (_exit) { break; } // See if we need a new engine if (_newEngine) { // Get a new engine Trace.Information("Configuration file {0} has changed, re-running", _configOptions.ConfigFilePath); engineManager.Dispose(); engineManager = EngineManager.Get(preprocessor, _configOptions); // Configure and execute if (!engineManager.Configure()) { exitCode = ExitCode.ConfigurationError; break; } Console.WriteLine($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.RootPath}"); Console.WriteLine($"Input path(s):{Environment.NewLine} {string.Join(Environment.NewLine + " ", engineManager.Engine.FileSystem.InputPaths)}"); Console.WriteLine($"Root path:{Environment.NewLine} {engineManager.Engine.FileSystem.OutputPath}"); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; } // Clear the changed files since we just re-ran string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { } _newEngine.Unset(); } else { // Execute if files have changed HashSet <string> changedFiles = new HashSet <string>(); string changedFile; while (_changedFiles.TryDequeue(out changedFile)) { if (changedFiles.Add(changedFile)) { Trace.Verbose("{0} has changed", changedFile); } } if (changedFiles.Count > 0) { Trace.Information("{0} files have changed, re-executing", changedFiles.Count); if (!engineManager.Execute()) { exitCode = ExitCode.ExecutionError; } previewServer?.TriggerReload(); } } // Check one more time for exit if (_exit) { break; } Trace.Information("Hit any key to exit"); _messageEvent.Reset(); } // Shutdown Trace.Information("Shutting down"); engineManager.Dispose(); inputFolderWatcher?.Dispose(); configFileWatcher?.Dispose(); previewServer?.Dispose(); } return(exitCode); }
protected override async Task <int> ExecuteEngineAsync( CommandContext commandContext, PreviewCommandSettings commandSettings, IEngineManager engineManager) { SetPipelines(commandContext, commandSettings, engineManager); ExitCode exitCode = ExitCode.Normal; ILogger logger = engineManager.Engine.Services.GetRequiredService <ILogger <Bootstrapper> >(); // Execute the engine for the first time using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource()) { exitCode = await engineManager.ExecuteAsync(cancellationTokenSource) ? ExitCode.Normal : ExitCode.ExecutionError; } // Start the preview server Dictionary <string, string> contentTypes = commandSettings.ContentTypes?.Length > 0 ? GetContentTypes(commandSettings.ContentTypes) : new Dictionary <string, string>(); ILoggerProvider loggerProvider = engineManager.Engine.Services.GetRequiredService <ILoggerProvider>(); IDirectory outputDirectory = engineManager.Engine.FileSystem.GetOutputDirectory(); Server previewServer = null; if (outputDirectory.Exists) { previewServer = await StartPreviewServerAsync( outputDirectory.Path, commandSettings.Port, commandSettings.ForceExt, commandSettings.VirtualDirectory, !commandSettings.NoReload, contentTypes, loggerProvider, logger); } // Start the watchers ActionFileSystemWatcher inputFolderWatcher = null; if (!commandSettings.NoWatch) { logger.LogInformation("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths)); inputFolderWatcher = new ActionFileSystemWatcher( outputDirectory.Path, engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path => { _changedFiles.Enqueue(path); _messageEvent.Set(); }); } // Start the message pump CommandUtilities.WaitForControlC( () => { _exit.Set(); _messageEvent.Set(); }, logger); // Wait for activity while (true) { _messageEvent.WaitOne(); // Blocks the current thread until a signal if (_exit) { break; } // Execute if files have changed HashSet <string> changedFiles = new HashSet <string>(); while (_changedFiles.TryDequeue(out string changedFile)) { if (changedFiles.Add(changedFile)) { logger.LogDebug($"{changedFile} has changed"); } } if (changedFiles.Count > 0) { logger.LogInformation($"{changedFiles.Count} files have changed, re-executing"); // Reset caches when an error occurs during the previous preview string existingResetCacheSetting = null; bool setResetCacheSetting = false; if (exitCode == ExitCode.ExecutionError) { existingResetCacheSetting = engineManager.Engine.Settings.GetString(Keys.ResetCache); setResetCacheSetting = true; ConfigurationSettings[Keys.ResetCache] = "true"; } // If there was an execution error due to reload, keep previewing but clear the cache using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource()) { exitCode = await engineManager.ExecuteAsync(cancellationTokenSource) ? ExitCode.Normal : ExitCode.ExecutionError; } // Reset the reset cache setting after removing it if (setResetCacheSetting) { if (existingResetCacheSetting == null) { ConfigurationSettings[Keys.ResetCache] = false; } { ConfigurationSettings[Keys.ResetCache] = existingResetCacheSetting; } } if (previewServer == null) { if (outputDirectory.Exists) { previewServer = await StartPreviewServerAsync( outputDirectory.Path, commandSettings.Port, commandSettings.ForceExt, commandSettings.VirtualDirectory, !commandSettings.NoReload, contentTypes, loggerProvider, logger); } } else { await previewServer.TriggerReloadAsync(); } } // Check one more time for exit if (_exit) { break; } logger.LogInformation("Hit Ctrl-C to exit"); _messageEvent.Reset(); } // Shutdown logger.LogInformation("Shutting down"); inputFolderWatcher?.Dispose(); previewServer?.Dispose(); return((int)exitCode); }