Exemple #1
0
        private int Run(string[] args)
        {
            AssemblyInformationalVersionAttribute versionAttribute
                = Attribute.GetCustomAttribute(typeof(Program).Assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
            Console.WriteLine("Wyam version {0}", versionAttribute == null ? "unknown" : versionAttribute.InformationalVersion);

            // Parse the command line
            bool hasParseArgsErrors;
            if (!ParseArgs(args, out hasParseArgsErrors))
            {
                return hasParseArgsErrors ? (int)ExitCode.CommandLineError : (int)ExitCode.Normal;
            }

            // It's not a serious console app unless there's some ASCII art
            OutputLogo();

            // Fix the root folder and other files
            _rootFolder = _rootFolder == null ? Environment.CurrentDirectory : Path.Combine(Environment.CurrentDirectory, _rootFolder);
            _logFile = _logFile == null ? null : Path.Combine(_rootFolder, _logFile);
            _configFile = string.IsNullOrWhiteSpace(_configFile)
                ? Path.Combine(_rootFolder, "config.wyam") : Path.Combine(_rootFolder, _configFile);

            // Get the engine
            Engine engine = GetEngine();
            if (engine == null)
            {
                return (int)ExitCode.CommandLineError;
            }

            // Pause
            if (_pause)
            {
                engine.Trace.Information("Pause requested, hit any key to continue");
                Console.ReadKey();
            }

            // Configure and execute
            if (!Configure(engine))
            {
                return (int)ExitCode.ConfigurationError;
            }
            Console.WriteLine("Root folder: {0}", engine.RootFolder);
            Console.WriteLine("Input folder: {0}", engine.InputFolder);
            Console.WriteLine("Output folder: {0}", engine.OutputFolder);
            if (!Execute(engine))
            {
                return (int)ExitCode.ExecutionError;
            }

            bool messagePump = false;

            // Start the preview server
            IDisposable previewServer = null;
            if (_preview)
            {
                messagePump = true;
                try
                {
                    engine.Trace.Information("Preview server listening on port {0} and serving from {1}", _previewPort, engine.OutputFolder);
                    previewServer = Preview(engine);
                }
                catch (Exception ex)
                {
                    engine.Trace.Critical("Error while running preview server: {0}", ex.Message);
                }
            }

            // Start the watchers
            IDisposable inputFolderWatcher = null;
            IDisposable configFileWatcher = null;
            if (_watch)
            {
                messagePump = true;

                engine.Trace.Information("Watching folder {0}", engine.InputFolder);
                inputFolderWatcher = new ActionFileSystemWatcher(engine.OutputFolder, engine.InputFolder, true, "*.*", path =>
                {
                    _changedFiles.Enqueue(path);
                    _messageEvent.Set();
                });

                if (_configFile != null)
                {
                    engine.Trace.Information("Watching configuration file {0}", _configFile);
                    configFileWatcher = new ActionFileSystemWatcher(engine.OutputFolder, Path.GetDirectoryName(_configFile), false, Path.GetFileName(_configFile), path =>
                    {
                        if (path == _configFile)
                        {
                            _newEngine.Set();
                            _messageEvent.Set();
                        }
                    });
                }
            }

            // Start the message pump if an async process is running
            ExitCode exitCode = ExitCode.Normal;
            if (messagePump)
            {
                // Start the key listening thread
                engine.Trace.Information("Hit any key to exit");
                var thread = new Thread(() =>
                {
                    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
                        engine.Trace.Information("Configuration file {0} has changed, re-running", _configFile);
                        engine.Dispose();
                        engine = GetEngine();

                        // Configure and execute
                        if (!Configure(engine))
                        {
                            exitCode = ExitCode.ConfigurationError;
                            break;
                        }
                        Console.WriteLine("Root folder: {0}", engine.RootFolder);
                        Console.WriteLine("Input folder: {0}", engine.InputFolder);
                        Console.WriteLine("Output folder: {0}", engine.OutputFolder);
                        if (!Execute(engine))
                        {
                            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))
                            {
                                engine.Trace.Verbose("{0} has changed", changedFile);
                            }
                        }
                        if (changedFiles.Count > 0)
                        {
                            engine.Trace.Information("{0} files have changed, re-executing", changedFiles.Count);
                            if (!Execute(engine))
                            {
                                exitCode = ExitCode.ExecutionError;
                                break;
                            }
                        }
                    }

                    // Check one more time for exit
                    if (_exit)
                    {
                        break;
                    }
                    engine.Trace.Information("Hit any key to exit");
                    _messageEvent.Reset();
                }

                // Shutdown
                engine.Trace.Information("Shutting down");
                engine.Dispose();
                inputFolderWatcher?.Dispose();
                configFileWatcher?.Dispose();
                previewServer?.Dispose();
            }
            return (int)exitCode;
        }
Exemple #2
0
        private int Run(string[] args)
        {
            // Add a default trace listener
            Trace.AddListener(new SimpleColorConsoleTraceListener {
                TraceOutputOptions = System.Diagnostics.TraceOptions.None
            });

            // Output version info
            AssemblyInformationalVersionAttribute versionAttribute
                = Attribute.GetCustomAttribute(typeof(Program).Assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;

            Trace.Information("Wyam version {0}", versionAttribute == null ? "unknown" : versionAttribute.InformationalVersion);

            // It's not a serious console app unless there's some ASCII art
            OutputLogo();

            // Parse the command line
            try
            {
                bool hasParseArgsErrors;
                if (!_settings.ParseArgs(args, _preprocessor, out hasParseArgsErrors))
                {
                    return(hasParseArgsErrors ? (int)ExitCode.CommandLineError : (int)ExitCode.Normal);
                }

                // Was help for the preprocessor directives requested?
                if (_settings.HelpDirectives)
                {
                    Console.WriteLine("Available preprocessor directives:");
                    foreach (IDirective directive in _preprocessor.Directives)
                    {
                        Console.WriteLine();
                        Console.WriteLine($"{directive.Description}:");
                        Console.WriteLine(string.Join(", ", directive.DirectiveNames.Select(x => "#" + x)));
                        Console.WriteLine(directive.GetHelpText());
                    }
                    return((int)ExitCode.Normal);
                }
            }
            catch (Exception ex)
            {
                Trace.Error("Error while parsing command line: {0}", ex.Message);
                if (Trace.Level == System.Diagnostics.SourceLevels.Verbose)
                {
                    Trace.Error("Stack trace:{0}{1}", Environment.NewLine, ex.StackTrace);
                }

                return((int)ExitCode.CommandLineError);
            }

            // Fix the root folder and other files
            DirectoryPath currentDirectory = Environment.CurrentDirectory;

            _settings.RootPath       = _settings.RootPath == null ? currentDirectory : currentDirectory.Combine(_settings.RootPath);
            _settings.LogFilePath    = _settings.LogFilePath == null ? null : _settings.RootPath.CombineFile(_settings.LogFilePath);
            _settings.ConfigFilePath = _settings.RootPath.CombineFile(_settings.ConfigFilePath ?? "config.wyam");

            // Set up the log file
            if (_settings.LogFilePath != null)
            {
                Trace.AddListener(new SimpleFileTraceListener(_settings.LogFilePath.FullPath));
            }

            // Prepare engine metadata
            if (!_settings.VerifyConfig && _settings.GlobalMetadataArgs != null && _settings.GlobalMetadataArgs.Count > 0)
            {
                try
                {
                    _settings.GlobalMetadata = GlobalMetadataParser.Parse(_settings.GlobalMetadataArgs);
                }
                catch (MetadataParseException ex)
                {
                    Trace.Error("Error while parsing metadata: {0}", ex.Message);
                    if (Trace.Level == System.Diagnostics.SourceLevels.Verbose)
                    {
                        Trace.Error("Stack trace:{0}{1}", Environment.NewLine, ex.StackTrace);
                    }

                    return((int)ExitCode.CommandLineError);
                }
                // Not used anymore, release resources.
                _settings.GlobalMetadataArgs = null;
            }

            // Get the engine and configurator
            EngineManager engineManager = GetEngineManager();

            if (engineManager == null)
            {
                return((int)ExitCode.CommandLineError);
            }

            // Pause
            if (_settings.Pause)
            {
                Trace.Information("Pause requested, hit any key to continue");
                Console.ReadKey();
            }

            // Configure and execute
            if (!engineManager.Configure())
            {
                return((int)ExitCode.ConfigurationError);
            }

            if (_settings.VerifyConfig)
            {
                Trace.Information("No errors. Exiting.");
                return((int)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((int)ExitCode.ExecutionError);
            }

            bool messagePump = false;

            // Start the preview server
            IDisposable previewServer = null;

            if (_settings.Preview)
            {
                messagePump = true;
                try
                {
                    DirectoryPath previewPath = _settings.PreviewRoot == null
                        ? engineManager.Engine.FileSystem.GetOutputDirectory().Path
                        : engineManager.Engine.FileSystem.GetOutputDirectory(_settings.PreviewRoot).Path;

                    Trace.Information("Preview server listening on port {0} and serving from path {1}", _settings.PreviewPort, previewPath);
                    previewServer = Preview(previewPath);
                }
                catch (Exception ex)
                {
                    Trace.Critical("Error while running preview server: {0}", ex.Message);
                }
            }

            // Start the watchers
            IDisposable inputFolderWatcher = null;
            IDisposable configFileWatcher  = null;

            if (_settings.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 (_settings.ConfigFilePath != null)
                {
                    Trace.Information("Watching configuration file {0}", _settings.ConfigFilePath);
                    configFileWatcher = new ActionFileSystemWatcher(engineManager.Engine.FileSystem.GetOutputDirectory().Path,
                                                                    new[] { _settings.ConfigFilePath.Directory }, false, _settings.ConfigFilePath.FileName.FullPath, path =>
                    {
                        FilePath filePath = new FilePath(path);
                        if (_settings.ConfigFilePath.Equals(filePath))
                        {
                            _newEngine.Set();
                            _messageEvent.Set();
                        }
                    });
                }
            }

            // Start the message pump if an async process is running
            ExitCode exitCode = ExitCode.Normal;

            if (messagePump)
            {
                // Start the key listening thread
                Trace.Information("Hit any key to exit");
                var thread = new Thread(() =>
                {
                    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", _settings.ConfigFilePath);
                        engineManager.Dispose();
                        engineManager = GetEngineManager();

                        // 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((int)exitCode);
        }
Exemple #3
0
        private void Run(string[] args)
        {
            // Parse the command line
            if (!ParseArgs(args))
            {
                return;
            }

            // Fix the root folder and other files
            _rootFolder = _rootFolder == null ? Environment.CurrentDirectory : Path.Combine(Environment.CurrentDirectory, _rootFolder);
            _logFile = _logFile == null ? null : Path.Combine(_rootFolder, _logFile);
            _configFile = string.IsNullOrWhiteSpace(_configFile)
                ? Path.Combine(_rootFolder, "config.wyam") : Path.Combine(_rootFolder, _configFile);

            // Get the engine
            Engine engine = GetEngine();
            if (engine == null)
            {
                return;
            }

            // Pause
            if (_pause)
            {
                engine.Trace.Information("Pause requested, hit any key to continue.");
                Console.ReadKey();
            }

            // Configure and execute
            if (!Configure(engine))
            {
                return;
            }
            if (!Execute(engine))
            {
                return;
            }

            bool messagePump = false;

            // Start the preview server
            IDisposable previewServer = null;
            if (_preview)
            {
                messagePump = true;
                try
                {
                    engine.Trace.Information("Preview server listening on port {0} and serving from {1}...", _previewPort, engine.OutputFolder);
                    previewServer = Preview(engine);
                }
                catch (Exception ex)
                {
                    engine.Trace.Critical("Error while running preview server: {0}.", ex.Message);
                }
            }

            // Start the watchers
            IDisposable inputFolderWatcher = null;
            IDisposable configFileWatcher = null;
            if (_watch)
            {
                messagePump = true;

                engine.Trace.Information("Watching folder {0}...", engine.InputFolder);
                inputFolderWatcher = new ActionFileSystemWatcher(engine.InputFolder, true, "*.*", path =>
                {
                    _changedFiles.Enqueue(path);
                    _messageEvent.Set();
                });

                if (_configFile != null)
                {
                    engine.Trace.Information("Watching configuration file {0}...", _configFile);
                    configFileWatcher = new ActionFileSystemWatcher(Path.GetDirectoryName(_configFile), false, Path.GetFileName(_configFile), path =>
                    {
                        if (path == _configFile)
                        {
                            _newEngine.Set();
                            _messageEvent.Set();
                        }
                    });
                }
            }

            // Start the message pump if an async process is running
            if (messagePump)
            {
                // Start the key listening thread
                engine.Trace.Information("Hit any key to exit...");
                var thread = new Thread(() =>
                {
                    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
                        engine.Trace.Information("Configuration file {0} has changed, re-running...", _configFile);
                        engine.Dispose();
                        engine = GetEngine();

                        // Configure and execute
                        if (!Configure(engine))
                        {
                            break;
                        }
                        if (!Execute(engine))
                        {
                            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))
                            {
                                engine.Trace.Verbose("{0} has changed.", changedFile);
                            }
                        }
                        if (changedFiles.Count > 0)
                        {
                            engine.Trace.Information("{0} files have changed, re-executing...", changedFiles.Count);
                            if (!Execute(engine))
                            {
                                break;
                            }
                        }
                    }

                    // Check one more time for exit
                    if (_exit)
                    {
                        break;
                    }
                    engine.Trace.Information("Hit any key to exit...");
                    _messageEvent.Reset();
                }

                // Shutdown
                engine.Trace.Information("Shutting down...");
                engine.Dispose();
                if (inputFolderWatcher != null)
                {
                    inputFolderWatcher.Dispose();
                }
                if (configFileWatcher != null)
                {
                    configFileWatcher.Dispose();
                }
                if (previewServer != null)
                {
                    previewServer.Dispose();
                }
            }
        }
Exemple #4
0
        private void Run(string[] args)
        {
            AssemblyInformationalVersionAttribute versionAttribute
                = Attribute.GetCustomAttribute(typeof(Program).Assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;

            Console.WriteLine("Wyam version {0}", versionAttribute == null ? "unknown" : versionAttribute.InformationalVersion);

            // Parse the command line
            if (!ParseArgs(args))
            {
                return;
            }

            // It's not a serious console app unless there's some ASCII art
            OutputLogo();

            // Fix the root folder and other files
            _rootFolder = _rootFolder == null ? Environment.CurrentDirectory : Path.Combine(Environment.CurrentDirectory, _rootFolder);
            _logFile    = _logFile == null ? null : Path.Combine(_rootFolder, _logFile);
            _configFile = string.IsNullOrWhiteSpace(_configFile)
                ? Path.Combine(_rootFolder, "config.wyam") : Path.Combine(_rootFolder, _configFile);

            // Get the engine
            Engine engine = GetEngine();

            if (engine == null)
            {
                return;
            }

            // Pause
            if (_pause)
            {
                engine.Trace.Information("Pause requested, hit any key to continue");
                Console.ReadKey();
            }

            // Configure and execute
            if (!Configure(engine))
            {
                return;
            }
            Console.WriteLine("Root folder: {0}", engine.RootFolder);
            Console.WriteLine("Input folder: {0}", engine.InputFolder);
            Console.WriteLine("Output folder: {0}", engine.OutputFolder);
            if (!Execute(engine))
            {
                return;
            }

            bool messagePump = false;

            // Start the preview server
            IDisposable previewServer = null;

            if (_preview)
            {
                messagePump = true;
                try
                {
                    engine.Trace.Information("Preview server listening on port {0} and serving from {1}", _previewPort, engine.OutputFolder);
                    previewServer = Preview(engine);
                }
                catch (Exception ex)
                {
                    engine.Trace.Critical("Error while running preview server: {0}", ex.Message);
                }
            }

            // Start the watchers
            IDisposable inputFolderWatcher = null;
            IDisposable configFileWatcher  = null;

            if (_watch)
            {
                messagePump = true;

                engine.Trace.Information("Watching folder {0}", engine.InputFolder);
                inputFolderWatcher = new ActionFileSystemWatcher(engine.InputFolder, true, "*.*", path =>
                {
                    _changedFiles.Enqueue(path);
                    _messageEvent.Set();
                });

                if (_configFile != null)
                {
                    engine.Trace.Information("Watching configuration file {0}", _configFile);
                    configFileWatcher = new ActionFileSystemWatcher(Path.GetDirectoryName(_configFile), false, Path.GetFileName(_configFile), path =>
                    {
                        if (path == _configFile)
                        {
                            _newEngine.Set();
                            _messageEvent.Set();
                        }
                    });
                }
            }

            // Start the message pump if an async process is running
            if (messagePump)
            {
                // Start the key listening thread
                engine.Trace.Information("Hit any key to exit");
                var thread = new Thread(() =>
                {
                    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
                        engine.Trace.Information("Configuration file {0} has changed, re-running", _configFile);
                        engine.Dispose();
                        engine = GetEngine();

                        // Configure and execute
                        if (!Configure(engine))
                        {
                            break;
                        }
                        Console.WriteLine("Root folder: {0}", engine.RootFolder);
                        Console.WriteLine("Input folder: {0}", engine.InputFolder);
                        Console.WriteLine("Output folder: {0}", engine.OutputFolder);
                        if (!Execute(engine))
                        {
                            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))
                            {
                                engine.Trace.Verbose("{0} has changed", changedFile);
                            }
                        }
                        if (changedFiles.Count > 0)
                        {
                            engine.Trace.Information("{0} files have changed, re-executing", changedFiles.Count);
                            if (!Execute(engine))
                            {
                                break;
                            }
                        }
                    }

                    // Check one more time for exit
                    if (_exit)
                    {
                        break;
                    }
                    engine.Trace.Information("Hit any key to exit");
                    _messageEvent.Reset();
                }

                // Shutdown
                engine.Trace.Information("Shutting down");
                engine.Dispose();
                inputFolderWatcher?.Dispose();
                configFileWatcher?.Dispose();
                previewServer?.Dispose();
            }
        }
Exemple #5
0
        private int Run(string[] args)
        {
            AssemblyInformationalVersionAttribute versionAttribute
                = Attribute.GetCustomAttribute(typeof(Program).Assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
            Console.WriteLine("Wyam version {0}", versionAttribute == null ? "unknown" : versionAttribute.InformationalVersion);

            // Parse the command line
            bool hasParseArgsErrors;
            if (!ParseArgs(args, out hasParseArgsErrors))
            {
                return hasParseArgsErrors ? (int)ExitCode.CommandLineError : (int)ExitCode.Normal;
            }

            // It's not a serious console app unless there's some ASCII art
            OutputLogo();

            // Fix the root folder and other files
            DirectoryPath currentDirectory = Environment.CurrentDirectory;
            _rootPath = _rootPath == null ? currentDirectory : currentDirectory.Combine(_rootPath);
            _logFilePath = _logFilePath == null ? null : _rootPath.CombineFile(_logFilePath);
            _configFilePath = _rootPath.CombineFile(_configFilePath ?? "config.wyam");

            // Get the engine
            Engine engine = GetEngine();
            if (engine == null)
            {
                return (int)ExitCode.CommandLineError;
            }

            // Pause
            if (_pause)
            {
                Trace.Information("Pause requested, hit any key to continue");
                Console.ReadKey();
            }

            // Configure and execute
            if (!Configure(engine))
            {
                return (int)ExitCode.ConfigurationError;
            }

            if (_verifyConfig)
            {
                Trace.Information("No errors. Exiting.");
                return (int)ExitCode.Normal;
            }

            Console.WriteLine($"Root path:{Environment.NewLine}  {engine.FileSystem.RootPath}");
            Console.WriteLine($"Input path(s):{Environment.NewLine}  {string.Join(Environment.NewLine + "  ", engine.FileSystem.InputPaths)}");
            Console.WriteLine($"Output path:{Environment.NewLine}  {engine.FileSystem.OutputPath}");
            if (!Execute(engine))
            {
                return (int)ExitCode.ExecutionError;
            }

            bool messagePump = false;

            // Start the preview server
            IDisposable previewServer = null;
            if (_preview)
            {
                messagePump = true;
                try
                {
                    var rootPath = _previewRoot == null ? engine.FileSystem.GetOutputDirectory().Path.FullPath : _previewRoot.FullPath;
                    Trace.Information("Preview server listening on port {0} and serving from path {1}", _previewPort, rootPath);
                    previewServer = Preview(engine, rootPath);
                }
                catch (Exception ex)
                {
                    Trace.Critical("Error while running preview server: {0}", ex.Message);
                }
            }

            // Start the watchers
            IDisposable inputFolderWatcher = null;
            IDisposable configFileWatcher = null;
            if (_watch)
            {
                messagePump = true;

                Trace.Information("Watching paths(s) {0}", string.Join(", ", engine.FileSystem.InputPaths));
                inputFolderWatcher = new ActionFileSystemWatcher(engine.FileSystem.GetOutputDirectory().Path,
                    engine.FileSystem.GetInputDirectories().Select(x => x.Path), true, "*.*", path =>
                    {
                        _changedFiles.Enqueue(path);
                        _messageEvent.Set();
                    });

                if (_configFilePath != null)
                {
                    Trace.Information("Watching configuration file {0}", _configFilePath);
                    Engine closureEngine = engine;
                    configFileWatcher = new ActionFileSystemWatcher(engine.FileSystem.GetOutputDirectory().Path,
                        new[] { _configFilePath.GetDirectory() }, false, _configFilePath.GetFilename().FullPath, path =>
                        {
                            if (closureEngine.FileSystem.PathComparer.Equals((FilePath)path, _configFilePath))
                            {
                                _newEngine.Set();
                                _messageEvent.Set();
                            }
                        });
                }
            }

            // Start the message pump if an async process is running
            ExitCode exitCode = ExitCode.Normal;
            if (messagePump)
            {
                // Start the key listening thread
                Trace.Information("Hit any key to exit");
                var thread = new Thread(() =>
                {
                    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", _configFilePath);
                        engine.Dispose();
                        engine = GetEngine();

                        // Configure and execute
                        if (!Configure(engine))
                        {
                            exitCode = ExitCode.ConfigurationError;
                            break;
                        }
                        Console.WriteLine($"Root path:{Environment.NewLine}  {engine.FileSystem.RootPath}");
                        Console.WriteLine($"Input path(s):{Environment.NewLine}  {string.Join(Environment.NewLine + "  ", engine.FileSystem.InputPaths)}");
                        Console.WriteLine($"Root path:{Environment.NewLine}  {engine.FileSystem.OutputPath}");
                        if (!Execute(engine))
                        {
                            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 (!Execute(engine))
                            {
                                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");
                engine.Dispose();
                inputFolderWatcher?.Dispose();
                configFileWatcher?.Dispose();
                previewServer?.Dispose();
            }
            return (int)exitCode;
        }