Beispiel #1
0
        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;
        }
Beispiel #2
0
 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;
 }
Beispiel #3
0
 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;
     }
 }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        /// <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);
        }
Beispiel #6
0
        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;
        }
Beispiel #7
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
            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);
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
 protected abstract ExitCode RunCommand(Preprocessor preprocessor);
Beispiel #10
0
        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;
        }
Beispiel #11
0
        /// <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;
        }
Beispiel #12
0
        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;
        }