public EngineManager(Preprocessor preprocessor, ConfigOptions configOptions) { _configOptions = configOptions; Engine = new Engine(); Configurator = new Configurator(Engine, preprocessor); // Set no cache if requested if (_configOptions.NoCache) { Engine.Settings.UseCache = false; } // Set folders Engine.FileSystem.RootPath = _configOptions.RootPath; if (_configOptions.InputPaths != null && _configOptions.InputPaths.Count > 0) { // Clear existing default paths if new ones are set // and reverse the inputs so the last one is first to match the semantics of multiple occurrence single options Engine.FileSystem.InputPaths.Clear(); Engine.FileSystem.InputPaths.AddRange(_configOptions.InputPaths.Reverse()); } if (_configOptions.OutputPath != null) { Engine.FileSystem.OutputPath = _configOptions.OutputPath; } if (_configOptions.NoClean) { Engine.Settings.CleanOutputPath = false; } if (_configOptions.GlobalMetadata != null) { foreach (KeyValuePair<string, object> item in _configOptions.GlobalMetadata) { Engine.GlobalMetadata.Add(item); } } // Set NuGet settings Configurator.PackageInstaller.UpdatePackages = _configOptions.UpdatePackages; Configurator.PackageInstaller.UseLocalPackagesFolder = _configOptions.UseLocalPackages; Configurator.PackageInstaller.UseGlobalPackageSources = _configOptions.UseGlobalSources; if (_configOptions.PackagesPath != null) { Configurator.PackageInstaller.PackagesPath = _configOptions.PackagesPath; } // Metadata Configurator.GlobalMetadata = configOptions.GlobalMetadata; Configurator.InitialMetadata = configOptions.InitialMetadata; // Script output Configurator.OutputScript = _configOptions.OutputScript; // Application input Engine.ApplicationInput = _configOptions.Stdin; }
protected override ExitCode RunCommand(Preprocessor preprocessor) { _path = new DirectoryPath(Environment.CurrentDirectory).Combine(_path ?? "output"); using (PreviewServer.Start(_path, _port, _forceExtension)) { Trace.Information("Hit any key to exit"); Console.ReadKey(); Trace.Information("Shutting down"); } return ExitCode.Normal; }
public static EngineManager Get(Preprocessor preprocessor, ConfigOptions configOptions) { try { return new EngineManager(preprocessor, configOptions); } catch (Exception ex) { Trace.Critical("Error while instantiating engine: {0}", ex.Message); return null; } }
public void Parse(ArgumentSyntax syntax, Preprocessor preprocessor) { // Global options if(GlobalArguments) { syntax.DefineOption("v|verbose", ref _verbose, "Turns on verbose output showing additional trace message useful for debugging."); syntax.DefineOption("attach", ref _attach, "Pause execution at the start of the program until a debugger is attached."); } // Command options ParseOptions(syntax); // Directives if (SupportedDirectives != null) { foreach (IDirective directive in preprocessor.Directives .Where(x => SupportedDirectives.Contains(x.Name, StringComparer.OrdinalIgnoreCase))) { // Get the option name and help text string optionName = (string.IsNullOrEmpty(directive.ShortName) ? string.Empty : directive.ShortName + "|") + directive.Name; string optionHelp = $"{directive.Description}{(string.IsNullOrEmpty(directive.GetHelpText()) ? string.Empty : " See below for syntax details.")}"; // Single or multiple? if (directive.SupportsMultiple) { // Multiple IReadOnlyList<string> directiveValues = null; syntax.DefineOptionList(optionName, ref directiveValues, optionHelp); if (directiveValues != null) { foreach (string directiveValue in directiveValues) { preprocessor.AddValue(new DirectiveValue(directive.Name, directiveValue)); } } } else { // Single string directiveValue = null; syntax.DefineOption(optionName, ref directiveValue, optionHelp); if (directiveValue != null) { preprocessor.AddValue(new DirectiveValue(directive.Name, directiveValue)); } } } } // Command parameters ParseParameters(syntax); }
/// <summary> /// Initializes a new instance of the <see cref="Configurator"/> class. This overload /// allows passing in a <see cref="Preprocessor"/> that can be reused and pre-configured /// with directives not sourced from the script. /// </summary> /// <param name="engine">The engine to configure.</param> /// <param name="preprocessor">The preprocessor.</param> public Configurator(Engine engine, Preprocessor preprocessor) { _engine = engine; _preprocessor = preprocessor; _assemblyResolver = new AssemblyResolver(_scriptManager); AssemblyLoader = new AssemblyLoader(engine.FileSystem, engine.Assemblies, _assemblyResolver); PackageInstaller = new PackageInstaller(engine.FileSystem, AssemblyLoader); ClassCatalog = new ClassCatalog(); // Add this namespace and assembly engine.Namespaces.Add(typeof(ScriptBase).Namespace); engine.Assemblies.Add(typeof(ScriptBase).Assembly); }
protected override ExitCode RunCommand(Preprocessor preprocessor) { // Directives if (_directives) { Console.WriteLine("Available preprocessor directives:"); foreach (IDirective directive in preprocessor.Directives) { Console.WriteLine(); Console.WriteLine("#" + directive.Name + (string.IsNullOrEmpty(directive.ShortName) ? string.Empty : ", #" + directive.ShortName)); string helpText = directive.GetHelpText(); if (!string.IsNullOrEmpty(helpText)) { Console.WriteLine(helpText); } Console.WriteLine($"{directive.Description}"); } } return ExitCode.Normal; }
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 Preprocessor preprocessor = new Preprocessor(); Command command; try { bool hasParseArgsErrors; command = CommandParser.Parse(args, preprocessor, out hasParseArgsErrors); if (command == null) { return hasParseArgsErrors ? (int)ExitCode.CommandLineError : (int)ExitCode.Normal; } } catch (Exception ex) { Trace.Error("Error while parsing command line: {0}", ex.Message); if (Trace.Level == SourceLevels.Verbose) { Trace.Error("Stack trace:{0}{1}", Environment.NewLine, ex.StackTrace); } return (int)ExitCode.CommandLineError; } // Run the command return (int) command.Run(preprocessor); }
public ExitCode Run(Preprocessor preprocessor) { if (GlobalArguments) { // Set verbose tracing if (_verbose) { Trace.Level = System.Diagnostics.SourceLevels.Verbose; } // Attach if (_attach) { Trace.Information("Waiting for a debugger to attach (or press a key to continue)..."); while (!Debugger.IsAttached && !Console.KeyAvailable) { Thread.Sleep(100); } if (Console.KeyAvailable) { Console.ReadKey(true); Trace.Information("Key pressed, continuing execution"); } else { Trace.Information("Debugger attached, continuing execution"); } } } return RunCommand(preprocessor); }
protected abstract ExitCode RunCommand(Preprocessor preprocessor);
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; }
/// <summary> /// Parses the command line arguments. /// </summary> /// <param name="args">The arguments.</param> /// <param name="preprocessor">The preprocessor.</param> /// <param name="hasErrors">If set to <c>true</c>, the command line had errors.</param> /// <returns>The resulting command.</returns> public static Command Parse(string[] args, Preprocessor preprocessor, out bool hasErrors) { // Construct a mapping of command names to commands List<Tuple<string, Command>> commands = Commands .Select(x => { string commandName = x.GetType().Name.ToLowerInvariant(); commandName = commandName.EndsWith("command") ? commandName.Substring(0, commandName.Length - 7) : commandName; return Tuple.Create(commandName, x); }) .ToList(); // If the first argument is not a valid command, set it to the first command as a default // Make sure to allow the default help flags to handle help output if (args == null || args.Length == 0) { args = new[] {commands[0].Item1}; } else if (args[0] != "-?" && args[0] != "-h" && args[0] != "--help" && commands.All(x => x.Item1 != args[0])) { args = new[] { commands[0].Item1 }.Concat(args).ToArray(); } else if (args.Length == 1 && args[0] == "help") { // Special case for the help command without any additional arguments, output global help instead args = new[] {"--help"}; } // Parse the command line arguments Command command = null; ArgumentSyntax parsed = ArgumentSyntax.Parse(args, syntax => { // Add all commands foreach (Tuple<string, Command> cmd in commands) { syntax.DefineCommand(cmd.Item1, ref command, cmd.Item2, cmd.Item2.Description); cmd.Item2.Parse(syntax, preprocessor); } }); hasErrors = parsed.HasErrors; // Output help text for any directive that got used for this command if (parsed.IsHelpRequested() && command?.SupportedDirectives != null) { foreach (IDirective directive in preprocessor.Directives .Where(x => command.SupportedDirectives.Contains(x.Name, StringComparer.OrdinalIgnoreCase))) { string helpText = directive.GetHelpText(); if (!string.IsNullOrEmpty(helpText)) { Console.WriteLine($"--{directive.Name} usage:"); Console.WriteLine(); Console.WriteLine(directive.GetHelpText()); Console.WriteLine(); } } } if (parsed.IsHelpRequested() || hasErrors) { return null; } Trace.Information($"**{commands.First(x => x.Item2 == command).Item1.ToUpperInvariant()}**"); return command; }
protected override ExitCode RunCommand(Preprocessor preprocessor) { // Make sure we actually got a recipe value if (preprocessor.Values.All(x => x.Name != "recipe")) { Trace.Critical("A recipe must be specified"); return ExitCode.CommandLineError; } _configOptions.RootPath = Environment.CurrentDirectory; _path = _path ?? "input"; // Get the engine and configurator using (EngineManager engineManager = EngineManager.Get(preprocessor, _configOptions)) { if (engineManager == null) { return ExitCode.CommandLineError; } // Check to make sure the directory is empty (and provide option to clear it) IDirectory scaffoldDirectory = engineManager.Engine.FileSystem.GetRootDirectory(_path); if (scaffoldDirectory.Exists) { Console.WriteLine($"Scaffold directory {scaffoldDirectory.Path.FullPath} exists, are you sure you want to clear it [y|N]?"); char inputChar = Console.ReadKey(true).KeyChar; if (inputChar != 'y' && inputChar != 'Y') { Trace.Information($"Scaffold directory will not be cleared"); return ExitCode.Normal; } Trace.Information($"Scaffold directory will be cleared"); } else { Trace.Information($"Scaffold directory {scaffoldDirectory.Path.FullPath} does not exist and will be created"); } if (scaffoldDirectory.Exists) { scaffoldDirectory.Delete(true); } scaffoldDirectory.Create(); // We can ignore theme packages since we don't care about the theme for scaffolding engineManager.Configurator.IgnoreKnownThemePackages = true; // Configure everything (primarily to get the recipe) try { engineManager.Configurator.Configure(null); } catch (Exception ex) { Trace.Critical("Error while configuring engine: {0}", ex.Message); return ExitCode.ConfigurationError; } // Scaffold the recipe engineManager.Configurator.Recipe.Scaffold(scaffoldDirectory); } return ExitCode.Normal; }