public static void Main(string[] args) { const string StrictSwitch = "-strict"; bool treatWarningsAsErrors = false; var arguments = args.ToList(); if (arguments.Contains(StrictSwitch)) { treatWarningsAsErrors = true; arguments.Remove(StrictSwitch); } if (arguments.Count != 1) { Console.Error.WriteLine("Usage: XmlDoc2CmdletDoc.exe [{0}] assemblyPath", StrictSwitch); Environment.Exit(-1); } else { var options = new Options(treatWarningsAsErrors, arguments.First()); Console.WriteLine(options); var engine = new Engine(); var exitCode = engine.GenerateHelp(options); Console.WriteLine("GenerateHelp completed with exit code '{0}'", exitCode); Environment.Exit((int)exitCode); } }
public void SetUp() { // ARRANGE var assemblyPath = typeof(TestManualElementsCommand).Assembly.Location; var cmdletXmlHelpPath = Path.ChangeExtension(assemblyPath, ".dll-Help.xml"); if (File.Exists(cmdletXmlHelpPath)) { File.Delete(cmdletXmlHelpPath); } // ACT var options = new Options(false, assemblyPath); var engine = new Engine(); engine.GenerateHelp(options); // ASSERT Assert.That(File.Exists(cmdletXmlHelpPath)); using (var stream = File.OpenRead(cmdletXmlHelpPath)) { var document = XDocument.Load(stream); rootElement = document.Root; } testManualElementsCommandElement = rootElement.XPathSelectElement("command:command[command:details/command:name/text() = 'Test-ManualElements']", resolver); testMamlElementsCommandElement = rootElement.XPathSelectElement("command:command[command:details/command:name/text() = 'Test-MamlElements']", resolver); testReferencesCommandElement = rootElement.XPathSelectElement("command:command[command:details/command:name/text() = 'Test-References']", resolver); testInputTypesCommandElement = rootElement.XPathSelectElement("command:command[command:details/command:name/text() = 'Test-InputTypes']", resolver); }
/// <summary> /// Public entry point that triggers the creation of the cmdlet XML help file for a single assembly. /// </summary> /// <param name="options">Defines the locations of the input assembly, the input XML doc comments file for the /// assembly, and where the cmdlet XML help file should be written to.</param> /// <returns>A code indicating the result of the help generation.</returns> public EngineExitCode GenerateHelp(Options options) { try { var warnings = new List<Tuple<MemberInfo, string>>(); ReportWarning reportWarning = (target, warningText) => warnings.Add(Tuple.Create(target, warningText)); var assembly = LoadAssembly(options); var commentReader = LoadComments(options, reportWarning); var cmdletTypes = GetCommands(assembly); var document = new XDocument(new XDeclaration("1.0", "utf-8", null), GenerateHelpItemsElement(commentReader, cmdletTypes, reportWarning)); HandleWarnings(options, warnings, assembly); using (var stream = new FileStream(options.OutputHelpFilePath, FileMode.Create, FileAccess.Write, FileShare.None)) using (var writer = new StreamWriter(stream, Encoding.UTF8)) { document.Save(writer); } return EngineExitCode.Success; } catch (Exception exception) { Console.Error.WriteLine(exception); var typeLoadException = exception as ReflectionTypeLoadException; if (typeLoadException != null) { foreach (var loaderException in typeLoadException.LoaderExceptions) { Console.Error.WriteLine("Loader exception: {0}", loaderException); } } var engineException = exception as EngineException; return engineException == null ? EngineExitCode.UnhandledException : engineException.ExitCode; } }
/// <summary> /// Handles the list of warnings generated once the cmdlet help XML document has been generated. /// </summary> /// <param name="options">The options.</param> /// <param name="warnings">The warnings generated during the creation of the cmdlet help XML document. Each tuple /// consists of the type to which the warning pertains, and the text of the warning.</param> /// <param name="targetAssembly">The assembly of the PowerShell module being documented.</param> private static void HandleWarnings(Options options, IEnumerable<Tuple<MemberInfo, string>> warnings, Assembly targetAssembly) { var groups = warnings.Where(tuple => { // Exclude warnings about types outside of the assembly being documented. var type = tuple.Item1 as Type ?? tuple.Item1.DeclaringType; return type != null && type.Assembly == targetAssembly; }) .GroupBy(tuple => GetFullyQualifiedName(tuple.Item1), tuple => tuple.Item2) .OrderBy(group => group.Key) .ToList(); if (groups.Any()) { var writer = options.TreatWarningsAsErrors ? Console.Error : Console.Out; writer.WriteLine("Warnings:"); foreach (var group in groups) { writer.WriteLine(" {0}:", group.Key); foreach (var warningText in group.Distinct()) { writer.WriteLine(" {0}", warningText); } } if (options.TreatWarningsAsErrors) { throw new EngineException(EngineExitCode.WarningsAsErrors, "Failing due to the occurence of one or more warnings"); } } }
/// <summary> /// Obtains an XML Doc comment reader for the assembly in the specified <paramref name="options"/>. /// </summary> /// <param name="options">The options.</param> /// <param name="reportWarning">Function used to log warnings.</param> /// <returns>A comment reader for the assembly in the <paramref name="options"/>.</returns> private ICommentReader LoadComments(Options options, ReportWarning reportWarning) { var docCommentsPath = options.DocCommentsPath; if (!File.Exists(docCommentsPath)) { throw new EngineException(EngineExitCode.AssemblyCommentsNotFound, "Assembly comments file not found: " + docCommentsPath); } try { return new CachingCommentReader( new RewritingCommentReader( new LoggingCommentReader( new JoltCommentReader(docCommentsPath), reportWarning))); } catch (Exception exception) { throw new EngineException(EngineExitCode.DocCommentsLoadError, "Failed to load XML Doc comments from file: " + docCommentsPath, exception); } }
/// <summary> /// Loads the assembly indicated in the specified <paramref name="options"/>. /// </summary> /// <param name="options">The options.</param> /// <returns>The assembly indicated in the <paramref name="options"/>.</returns> private Assembly LoadAssembly(Options options) { var assemblyPath = options.AssemblyPath; if (!File.Exists(assemblyPath)) { throw new EngineException(EngineExitCode.AssemblyNotFound, "Assembly file not found: " + assemblyPath); } try { var assemblyDir = Path.GetDirectoryName(assemblyPath); AppDomain.CurrentDomain.AssemblyResolve += // TODO: Really ought to track this handler and cleanly remove it. (sender, args) => { var name = args.Name; var i = name.IndexOf(','); if (i != -1) { name = name.Substring(0, i); } name += ".dll"; var path = Path.Combine(assemblyDir, name); return Assembly.LoadFrom(path); }; return Assembly.LoadFile(assemblyPath); } catch (Exception exception) { throw new EngineException(EngineExitCode.AssemblyLoadError, "Failed to load assembly from file: " + assemblyPath, exception); } }