|
|
@@ -0,0 +1,303 @@
|
|
|
+#!/usr/bin/python
|
|
|
+#
|
|
|
+# Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
|
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
+#
|
|
|
+
|
|
|
+# manpage_generator
|
|
|
+# Converts top level docs.json format command info to
|
|
|
+# nroff manpage format. Done in python for easy json parsing.
|
|
|
+#
|
|
|
+# Usage: argv[1] = path to docs.json; argv[2] = output path
|
|
|
+
|
|
|
+import sys
|
|
|
+import os
|
|
|
+import json
|
|
|
+import datetime
|
|
|
+
|
|
|
+SECTION_SEPARATOR = "\n.P \n"
|
|
|
+MANPAGE_EXTENSION = ".1"
|
|
|
+
|
|
|
+# For now this is a magic number
|
|
|
+# See https://www.debian.org/doc/manuals/maint-guide/dother.en.html#manpage
|
|
|
+SECTION_NUMBER = 1
|
|
|
+
|
|
|
+def generate_man_pages(doc_path, output_dir):
|
|
|
+
|
|
|
+ with open(doc_path) as doc_file:
|
|
|
+ doc_json = None
|
|
|
+ try:
|
|
|
+ doc_json = json.load(doc_file)
|
|
|
+ except:
|
|
|
+ raise Exception("Failed to load json file. Check formatting.")
|
|
|
+
|
|
|
+ tools = doc_json.get("tools", None)
|
|
|
+
|
|
|
+ if tools is None:
|
|
|
+ raise Exception("No tool sections in doc.json")
|
|
|
+
|
|
|
+ for tool_name in tools:
|
|
|
+ tool_data = tools[tool_name]
|
|
|
+
|
|
|
+ man_page_content = generate_man_page(tool_name, tool_data)
|
|
|
+ man_page_path = get_output_path(tool_name, output_dir)
|
|
|
+
|
|
|
+ write_man_page(man_page_path, man_page_content)
|
|
|
+
|
|
|
+def get_output_path(toolname, output_dir):
|
|
|
+ out_filename = toolname + MANPAGE_EXTENSION
|
|
|
+
|
|
|
+ return os.path.join(output_dir, out_filename)
|
|
|
+
|
|
|
+def write_man_page(path, content):
|
|
|
+ with open(path, 'w') as man_file:
|
|
|
+ man_file.write(content)
|
|
|
+
|
|
|
+ #Build Fails without a final newline
|
|
|
+ man_file.write('\n')
|
|
|
+
|
|
|
+def generate_man_page(tool_name, tool_data):
|
|
|
+
|
|
|
+ sections = [
|
|
|
+ generate_header_section(tool_name, tool_data),
|
|
|
+ generate_name_section(tool_name, tool_data),
|
|
|
+ generate_synopsis_section(tool_name, tool_data),
|
|
|
+ generate_description_section(tool_name, tool_data),
|
|
|
+ generate_options_section(tool_name, tool_data),
|
|
|
+ generate_author_section(tool_name, tool_data),
|
|
|
+ generate_copyright_section(tool_name, tool_data)
|
|
|
+ ]
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(sections)
|
|
|
+
|
|
|
+def generate_header_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+
|
|
|
+ header_format = ".TH {program_name} {section_number} {center_footer} {left_footer} {center_header}"
|
|
|
+
|
|
|
+ today = datetime.date.today()
|
|
|
+ today_string = today.strftime("%B %d, %Y")
|
|
|
+
|
|
|
+ format_args = {
|
|
|
+ "program_name" : tool_name,
|
|
|
+ "section_number" : SECTION_NUMBER,
|
|
|
+ "center_footer" : "", # Omitted
|
|
|
+ "left_footer" : "", # Omitted
|
|
|
+ "center_header" : "" # Omitted
|
|
|
+ }
|
|
|
+
|
|
|
+ roff_text_builder.append(header_format.format(**format_args))
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_name_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH NAME")
|
|
|
+
|
|
|
+ tool_short_description = tool_data.get("short_description", "")
|
|
|
+ name_format = ".B {program_name} - {short_description}"
|
|
|
+
|
|
|
+ name_format_args = {
|
|
|
+ "program_name": tool_name,
|
|
|
+ "short_description" : tool_short_description
|
|
|
+ }
|
|
|
+
|
|
|
+ roff_text_builder.append(name_format.format(**name_format_args))
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_synopsis_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH SYNOPSIS")
|
|
|
+
|
|
|
+ synopsis_format = '.B {program_name} {command_name} \n.RI {options} " "\n.I "{argument_list_name}"'
|
|
|
+
|
|
|
+ tool_commands = tool_data.get("commands", [])
|
|
|
+ for command_name in tool_commands:
|
|
|
+ command_data = tool_commands[command_name]
|
|
|
+
|
|
|
+ # Default options to empty list so the loop doesn't blow up
|
|
|
+ options = command_data.get("options", [])
|
|
|
+ argument_list = command_data.get("argumentlist", None)
|
|
|
+
|
|
|
+ # Construct Option Strings
|
|
|
+ option_string_list = []
|
|
|
+ argument_list_name = ""
|
|
|
+
|
|
|
+ for option_name in options:
|
|
|
+ option_data = options[option_name]
|
|
|
+
|
|
|
+ specifier_short = option_data.get("short", None)
|
|
|
+ specifier_long = option_data.get("long", None)
|
|
|
+ parameter = option_data.get("parameter", None)
|
|
|
+
|
|
|
+ option_string = _option_string_helper(specifier_short, specifier_long, parameter)
|
|
|
+
|
|
|
+ option_string_list.append(option_string)
|
|
|
+
|
|
|
+ # Populate Argument List Name
|
|
|
+ if argument_list:
|
|
|
+ argument_list_name = argument_list.get("name", "")
|
|
|
+
|
|
|
+ cmd_format_args = {
|
|
|
+ 'program_name' : tool_name,
|
|
|
+ 'command_name' : command_name,
|
|
|
+ 'options' : '" "'.join(option_string_list),
|
|
|
+ 'argument_list_name' : argument_list_name
|
|
|
+ }
|
|
|
+
|
|
|
+ cmd_string = synopsis_format.format(**cmd_format_args)
|
|
|
+
|
|
|
+ roff_text_builder.append(cmd_string)
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_description_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH DESCRIPTION")
|
|
|
+
|
|
|
+ # Tool Description
|
|
|
+ long_description = tool_data.get("long_description", "")
|
|
|
+ roff_text_builder.append(".PP {0}".format(long_description))
|
|
|
+
|
|
|
+ # Command Descriptions
|
|
|
+ cmd_description_format = ".B {program_name} {command_name}\n{command_description}"
|
|
|
+
|
|
|
+ tool_commands = tool_data.get("commands", [])
|
|
|
+ for command_name in tool_commands:
|
|
|
+ command_data = tool_commands[command_name]
|
|
|
+
|
|
|
+ command_description = command_data.get("description", "")
|
|
|
+
|
|
|
+ format_args = {
|
|
|
+ "program_name" : tool_name,
|
|
|
+ "command_name" : command_name,
|
|
|
+ "command_description" : command_description
|
|
|
+ }
|
|
|
+
|
|
|
+ cmd_string = cmd_description_format.format(**format_args)
|
|
|
+
|
|
|
+ roff_text_builder.append(cmd_string)
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_options_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH OPTIONS")
|
|
|
+
|
|
|
+ options_format = '.TP\n.B {option_specifiers}\n{option_description}'
|
|
|
+
|
|
|
+ tool_commands = tool_data.get("commands", [])
|
|
|
+ for command_name in tool_commands:
|
|
|
+ command_data = tool_commands[command_name]
|
|
|
+
|
|
|
+ # Default to empty list so the loop doesn't blow up
|
|
|
+ options = command_data.get("options", [])
|
|
|
+
|
|
|
+ for option_name in options:
|
|
|
+ option_data = options[option_name]
|
|
|
+
|
|
|
+ specifier_short = option_data.get("short", None)
|
|
|
+ specifier_long = option_data.get("long", None)
|
|
|
+ parameter = option_data.get("parameter", None)
|
|
|
+ description = option_data.get("description", "")
|
|
|
+
|
|
|
+ option_specifiers_string = _option_string_helper(specifier_short,
|
|
|
+ specifier_long,
|
|
|
+ parameter,
|
|
|
+ include_brackets = False,
|
|
|
+ delimiter=' ", " ')
|
|
|
+
|
|
|
+ format_args = {
|
|
|
+ "option_specifiers": option_specifiers_string,
|
|
|
+ "option_description" : description
|
|
|
+ }
|
|
|
+
|
|
|
+ roff_text_builder.append(options_format.format(**format_args))
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_author_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH AUTHOR")
|
|
|
+
|
|
|
+ author_format = '.B "{author_name}" " " \n.RI ( "{author_email}" )'
|
|
|
+
|
|
|
+ author_name = tool_data.get("author", "")
|
|
|
+ author_email = tool_data.get("author_email", "")
|
|
|
+
|
|
|
+ format_args = {
|
|
|
+ "author_name" : author_name,
|
|
|
+ "author_email" : author_email
|
|
|
+ }
|
|
|
+
|
|
|
+ roff_text_builder.append(author_format.format(**format_args))
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def generate_copyright_section(tool_name, tool_data):#
|
|
|
+ roff_text_builder = []
|
|
|
+ roff_text_builder.append(".SH COPYRIGHT")
|
|
|
+
|
|
|
+ copyright_data = tool_data.get("copyright")
|
|
|
+
|
|
|
+ roff_text_builder.append('.B "{0}"'.format(copyright_data))
|
|
|
+
|
|
|
+ return SECTION_SEPARATOR.join(roff_text_builder)
|
|
|
+
|
|
|
+def _option_string_helper(specifier_short, specifier_long, parameter, include_brackets = True, delimiter = " | "):
|
|
|
+ option_string = ""
|
|
|
+
|
|
|
+ if include_brackets:
|
|
|
+ option_string = " [ "
|
|
|
+
|
|
|
+ if specifier_short:
|
|
|
+ option_string += ' "{0}" '.format(specifier_short)
|
|
|
+
|
|
|
+ if specifier_short and specifier_long:
|
|
|
+ option_string += delimiter
|
|
|
+
|
|
|
+ if specifier_long:
|
|
|
+ option_string += ' "{0}" '.format(specifier_long)
|
|
|
+
|
|
|
+ if parameter:
|
|
|
+ option_string += ' " " '
|
|
|
+ option_string += ' "{0}" '.format(parameter)
|
|
|
+
|
|
|
+ if include_brackets:
|
|
|
+ option_string += " ] "
|
|
|
+
|
|
|
+ return option_string
|
|
|
+
|
|
|
+
|
|
|
+def print_usage():
|
|
|
+ print "Usage: argv[1] = path to docs.json; argv[2] = output path"
|
|
|
+ print "Example: manpage_generator.py ../docs.json ./dotnet-1.0/debian"
|
|
|
+
|
|
|
+def parse_args():
|
|
|
+ doc_path = sys.argv[1]
|
|
|
+ output_dir = sys.argv[2]
|
|
|
+
|
|
|
+ return (doc_path, output_dir)
|
|
|
+
|
|
|
+def validate_args(doc_path, output_dir):
|
|
|
+ if not os.path.isfile(doc_path):
|
|
|
+ raise Exception("Docs.json path is not valid.")
|
|
|
+
|
|
|
+ if not os.path.isdir(output_dir):
|
|
|
+ raise Exception("Output Directory Path is not valid.")
|
|
|
+
|
|
|
+def execute_command_line():
|
|
|
+ try:
|
|
|
+ doc_path, output_dir = parse_args()
|
|
|
+
|
|
|
+ validate_args(doc_path, output_dir)
|
|
|
+
|
|
|
+ generate_man_pages(doc_path, output_dir)
|
|
|
+
|
|
|
+ except Exception as exc:
|
|
|
+ print "Error: ", exc
|
|
|
+ print_usage()
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ execute_command_line()
|