/// <summary> /// Extract the input types for a Cmdlet. /// </summary> /// <param name="cmdletType"> /// The CLR type that implements the Cmdlet. /// </param> /// <returns> /// A list of values, which may be empty or null. /// </returns> public List <CommandValue> GetCmdletInputTypes(TypeInfo cmdletType) { if (cmdletType == null) { throw new ArgumentNullException(nameof(cmdletType)); } var inputTypeAttributes = cmdletType.GetCustomAttributes <CmdletInputTypeAttribute>().ToList(); if (inputTypeAttributes.Count == 0) { return(null); } var inputTypes = new List <CommandValue>(); foreach (var attribute in inputTypeAttributes) { inputTypes.Add(new CommandValue { DataType = { Name = attribute.IsCLRType ? MamlGenerator.PowerShellIfyTypeName(attribute.Type) : attribute.Name, Uri = attribute.Uri }, Description = MamlGenerator.ToParagraphs(attribute.Description) }); } return(inputTypes); }
/// <summary> /// Extract the examples for a Cmdlet parameter. /// </summary> /// <param name="cmdletType"> /// The CLR type that implements the Cmdlet. /// </param> /// <returns> /// A list of example, which may be empty. /// </returns> public List <CommandExample> GetCmdletExamples(TypeInfo cmdletType) { if (cmdletType == null) { throw new ArgumentNullException(nameof(cmdletType)); } var attributes = cmdletType.GetCustomAttributes <CmdletExampleAttribute>(); if (attributes == null) { return(null); } var examples = new List <CommandExample>(); foreach (var attribute in attributes) { examples.Add( new CommandExample { Title = attribute.Title, Description = MamlGenerator.ToParagraphs(attribute.Description), Code = attribute.Code, Remarks = MamlGenerator.ToParagraphs(attribute.Remarks) } ); } return(examples); }
/// <summary> /// Execute the task. /// </summary> /// <returns> /// <c>true</c>, if the task executed succesfully; otherwise, <c>false</c>. /// </returns> public override bool Execute() { FileInfo moduleAssemblyFile = new FileInfo( ModuleAssembly.GetMetadata("FullPath") ); Log.LogMessage(MessageImportance.Low, "Scanning assembly '{0}'...", moduleAssemblyFile.FullName ); DirectoryAssemblyLoadContext assemblyLoadContext = new DirectoryAssemblyLoadContext( fallbackDirectory: moduleAssemblyFile.Directory.FullName ); HelpItems help = new HelpItems(); MamlGenerator generator = new MamlGenerator(); Assembly moduleAssembly = assemblyLoadContext.LoadFromAssemblyPath(moduleAssemblyFile.FullName); foreach (Type cmdletType in Reflector.GetCmdletTypes(moduleAssembly)) { CmdletAttribute cmdletAttribute = cmdletType.GetTypeInfo().GetCustomAttribute <CmdletAttribute>(); Log.LogMessage(MessageImportance.Low, "Generating help for cmdlet '{0}-{1}' ('{2}').", cmdletAttribute.VerbName, cmdletAttribute.NounName, cmdletType.FullName ); help.Commands.Add( generator.Generate(cmdletType) ); } FileInfo helpFile = new FileInfo( HelpFile.GetMetadata("FullPath") ); if (helpFile.Exists) { helpFile.Delete(); } using (StreamWriter writer = helpFile.CreateText()) { help.WriteTo(writer); } Log.LogMessage(MessageImportance.Normal, "'{0}' -> '{1}'", moduleAssemblyFile.Name, helpFile.Name ); return(true); }
/// <summary> /// The main program entry-point. /// </summary> /// <param name="args"> /// Command-line arguments. /// </param> static void Main(string[] args) { if (args.Length < 2 || args.Length > 3 || args[0] != "gen-help") { Console.WriteLine("Usage:\n\tdotnet reptile gen-help <Module.dll> [Module.dll-Help.xml]"); return; } try { string modulePath = Path.GetFullPath(args[1]); // If the module's dependencies are not available from // the usual places (e.g. dotnet-reptile's base directory) // then load them from the module directory. DirectoryAssemblyLoadContext loadContext = new DirectoryAssemblyLoadContext( Path.GetDirectoryName(modulePath) ); Assembly moduleAssembly = loadContext.LoadFromAssemblyPath(modulePath); HelpItems help = new MamlGenerator().Generate(moduleAssembly); FileInfo helpFile = new FileInfo( fileName: args.Length == 3 ? Path.GetFullPath(args[2]) : modulePath + "-Help.xml" ); if (helpFile.Exists) { helpFile.Delete(); } using (StreamWriter writer = helpFile.CreateText()) { help.WriteTo(writer); } Console.WriteLine($"Generated '{helpFile.FullName}'."); } catch (ReflectionTypeLoadException typeLoadError) { Console.WriteLine(typeLoadError); Console.WriteLine( new String('=', 80) ); foreach (Exception loaderException in typeLoadError.LoaderExceptions) { Console.WriteLine(loaderException); } } catch (Exception unexpectedError) { Console.WriteLine(unexpectedError); } }
/// <summary> /// Extract the description for a Cmdlet parameter. /// </summary> /// <param name="parameterProperty"> /// The property that represents the parameter. /// </param> /// <returns> /// The description, or <c>null</c> if no description could be extracted by this extractor. /// /// An empty list means a description was extracted, but the description is empty (this is legal). /// </returns> public List <string> GetParameterDescription(PropertyInfo parameterProperty) { if (parameterProperty == null) { throw new ArgumentNullException(nameof(parameterProperty)); } var assemblyDoc = GetAssemblyDocumentation(parameterProperty); if (assemblyDoc == null) { return(null); } return(MamlGenerator.ToParagraphs(assemblyDoc.GetSummary(parameterProperty))); }
/// <summary> /// Extract the description for a Cmdlet parameter. /// </summary> /// <param name="parameterProperty"> /// The property that represents the parameter. /// </param> /// <returns> /// The description, or <c>null</c> if no description could be extracted by this extractor. /// /// An empty list means a description was extracted, but the description is empty (this is legal). /// </returns> public List <string> GetParameterDescription(PropertyInfo parameterProperty) { if (parameterProperty == null) { throw new ArgumentNullException(nameof(parameterProperty)); } var descriptionAttribute = parameterProperty.GetCustomAttributes <ParameterAttribute>().Last(); if (descriptionAttribute == null) { return(null); } // TODO: Handle resource-based messages (with locale). return(MamlGenerator.ToParagraphs(descriptionAttribute.HelpMessage?.Trim())); }
/// <summary> /// Extract the input types for a Cmdlet. /// </summary> /// <param name="cmdletType"> /// The CLR type that implements the Cmdlet. /// </param> /// <returns> /// A list of values, which may be empty or null. /// </returns> public List <CommandValue> GetCmdletInputTypes(TypeInfo cmdletType) { if (cmdletType == null) { throw new ArgumentNullException(nameof(cmdletType)); } var assemblyDoc = GetAssemblyDocumentation(cmdletType); if (assemblyDoc == null) { return(null); } var inputElements = assemblyDoc.GetInputs(cmdletType); var inputTypes = new List <CommandValue>(); foreach (var element in inputElements) { var see = element.Element("sees"); var paras = element.Elements("para")?.Select(e => MamlGenerator.ToParagraphs(e?.Value?.Trim())).ToList() ?? new List <List <string> >(); var description = new List <string>(); if (see == null && paras.Count == 0) { description.AddRange(MamlGenerator.ToParagraphs(element.Value?.Trim())); } else { foreach (var list in paras) { description.AddRange(list); } } if (see == null || !see.HasAttributes) { var first = description[0]; description.RemoveAt(0); element.Add( new CommandValue { DataType = { Name = first }, Description = description } ); continue; } var name = see.Attribute("cref")?.Value?.Trim() ?? string.Empty; if (name != null) { try { var type = Type.GetType(name); if (type != null) { name = MamlGenerator.PowerShellIfyTypeName(type); } } catch (ArgumentException) { // Ignore this exception } catch (TypeLoadException) { // Ignore this exception } } element.Add( new CommandValue { DataType = { Name = name ?? string.Empty, Uri = see.Attribute("uri")?.Value?.Trim(), Description = MamlGenerator.ToParagraphs(see.Value?.Trim()) }, Description = description } ); } return(inputTypes); }
/// <summary> /// Extract the return values for a Cmdlet. /// </summary> /// <param name="cmdletType"> /// The CLR type that implements the Cmdlet. /// </param> /// <returns> /// A list of values, which may be empty or null. /// </returns> public List <CommandValue> GetCmdletReturnValues(TypeInfo cmdletType) { if (cmdletType == null) { throw new ArgumentNullException(nameof(cmdletType)); } var assemblyDoc = GetAssemblyDocumentation(cmdletType); if (assemblyDoc == null) { return(null); } var returnElements = assemblyDoc.GetReturns(cmdletType); var returnValues = new List <CommandValue>(); foreach (var returnElement in returnElements) { var see = returnElement.Element("sees"); var paras = returnElement.Elements("para")?.Select(e => MamlGenerator.ToParagraphs(e?.Value?.Trim())).ToList() ?? new List <List <string> >(); var description = new List <string>(); if (see == null && paras.Count == 0) { description.AddRange(MamlGenerator.ToParagraphs(returnElement.Value?.Trim())); } else { foreach (var list in paras) { description.AddRange(list); } } if (see == null || !see.HasAttributes) { var first = description[0]; description.RemoveAt(0); returnValues.Add( new CommandValue { DataType = { Name = first }, Description = description } ); continue; } returnValues.Add( new CommandValue { DataType = { Name = see.Attribute("cref")?.Value?.Trim() ?? string.Empty, Uri = see.Attribute("uri")?.Value?.Trim(), Description = MamlGenerator.ToParagraphs(see.Value?.Trim()) }, Description = description } ); } return(returnValues); }
/// <summary> /// Extract the examples for a Cmdlet parameter. /// </summary> /// <param name="cmdletType"> /// The CLR type that implements the Cmdlet. /// </param> /// <returns> /// A list of example, which may be empty. /// </returns> public List <CommandExample> GetCmdletExamples(TypeInfo cmdletType) { if (cmdletType == null) { throw new ArgumentNullException(nameof(cmdletType)); } var assemblyDoc = GetAssemblyDocumentation(cmdletType); if (assemblyDoc == null) { return(null); } var rawExamples = assemblyDoc.GetExamples(cmdletType); var examples = new List <CommandExample>(); foreach (var example in rawExamples) { var para = example.Elements("para"); var descriptions = para.Where(e => e.Attribute("type")?.Value == "description").Select(e => MamlGenerator.ToParagraphs(e.Value.Trim())); var description = new List <string>(); foreach (var d in descriptions) { description.AddRange(d); } var remarks = para.Where(e => e.Attribute("type")?.Value != "description").Select(e => MamlGenerator.ToParagraphs(e.Value.Trim())); var remark = new List <string>(); foreach (var r in remarks) { remark.AddRange(r); } examples.Add( new CommandExample { Title = example.Element("title")?.Value.Trim() ?? "Example", Description = description, Code = string.Join("\n", example.Elements("code")?.InDocumentOrder().Select(e => e.Value)), Remarks = remark } ); } return(examples); }
public void GenerateXml_From_Cmdlet() { string expected = @" <?xml version=""1.0"" encoding=""utf-16""?> <command:command xmlns:maml=""http://schemas.microsoft.com/maml/2004/10"" xmlns:dev=""http://schemas.microsoft.com/maml/dev/2004/10"" xmlns:command=""http://schemas.microsoft.com/maml/dev/command/2004/10""> <command:details> <command:name>Get-Greeting</command:name> <maml:description> <maml:para>A simple Cmdlet that outputs a greeting to the pipeline</maml:para> </maml:description> <command:verb>Get</command:verb> <command:noun>Greeting</command:noun> </command:details> <maml:description> <maml:para>This Cmdlet works with greetings.</maml:para> <maml:para>It gets them.</maml:para> <maml:para>I can't see how to make it any clearer than that.</maml:para> </maml:description> <command:syntax> <command:syntaxItem> <maml:name>Get-Greeting</maml:name> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""false"" position=""named"" aliases=""none""> <maml:name>Name</maml:name> <maml:description> <maml:para>The name of the person to greet</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""false"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByPropertyName, FromRemainingArguments)"" position=""named"" aliases=""Honorific""> <maml:name>Title</maml:name> <maml:description> <maml:para>Title of the person to greet, sans period.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByValue)"" position=""named"" aliases=""none""> <maml:name>Greeting</maml:name> <maml:description> <maml:para>The last greeting to use.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>Hello</dev:defaultValue> </command:parameter> </command:syntaxItem> <command:syntaxItem> <maml:name>Get-Greeting</maml:name> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""false"" position=""named"" aliases=""none""> <maml:name>Name</maml:name> <maml:description> <maml:para>The name of the person to greet</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""false"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByPropertyName, FromRemainingArguments)"" position=""named"" aliases=""Honorific""> <maml:name>Title</maml:name> <maml:description> <maml:para>Title of the person to greet, sans period.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByValue)"" position=""named"" aliases=""none""> <maml:name>Greeting</maml:name> <maml:description> <maml:para>The last greeting to use.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>Hello</dev:defaultValue> </command:parameter> </command:syntaxItem> </command:syntax> <command:parameters> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""false"" position=""named"" aliases=""none""> <maml:name>Name</maml:name> <maml:description> <maml:para>The name of the person to greet</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""false"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByPropertyName, FromRemainingArguments)"" position=""named"" aliases=""Honorific""> <maml:name>Title</maml:name> <maml:description> <maml:para>Title of the person to greet, sans period.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required=""true"" variableLength=""true"" globbing=""false"" pipelineInput=""true (ByValue)"" position=""named"" aliases=""none""> <maml:name>Greeting</maml:name> <maml:description> <maml:para>The last greeting to use.</maml:para> </maml:description> <command:parameterValue required=""true"" variableLength=""false"">String</command:parameterValue> <dev:defaultValue>Hello</dev:defaultValue> </command:parameter> </command:parameters> <command:inputTypes /> <command:returnValues /> <command:examples /> </command:command> ".Trim(); Command cmdletHelp = new MamlGenerator().Generate( typeof(SampleModule.GetGreeting) ); XmlSerializerNamespaces namespacesWithPrefix = Constants.XmlNamespace.GetStandardPrefixes(); XmlSerializer serializer = new XmlSerializer(typeof(Command)); string actual; using (StringWriter writer = new StringWriter { NewLine = "\n" }) { serializer.Serialize(writer, cmdletHelp, namespaces: namespacesWithPrefix); writer.Flush(); actual = writer.ToString(); } using (var k = new StreamWriter(@"C:\BHK\out.txt")) { k.Write(actual); k.Flush(); } Assert.Equal(expected, actual); }