1#!/usr/bin/env python 2# Copyright 2015 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6 7"""Reads, parses, and (optionally) writes as HTML the contents of Markdown 8files passed as arguments. Intended for rendering network stack documentation 9stored as Markdown in the source tree to a human-readable format.""" 10 11 12import argparse 13import os.path 14import sys 15 16 17def nth_parent_directory(path, n): 18 for i in range(n): 19 path = os.path.dirname(path) 20 return path 21 22 23# Go up the directory tree from this script and add src/third_party to sys.path 24# so "import markdown" can find it in src/third_party/markdown. 25SCRIPT_PATH = os.path.abspath(__file__) 26SRC_PATH = nth_parent_directory(SCRIPT_PATH, 4) 27THIRD_PARTY_PATH = os.path.join(SRC_PATH, 'third_party') 28sys.path.insert(0, THIRD_PARTY_PATH) 29import markdown 30 31 32def ReadFile(filename): 33 with open(filename, 'r') as file: 34 return file.read() 35 36 37def WriteFile(filename, contents): 38 dir = os.path.dirname(filename) 39 if not os.path.isdir(dir): 40 os.mkdir(dir) 41 with open(filename, 'w') as file: 42 file.write(contents) 43 44 45TEMPLATE = """ 46<html> 47 <head> 48 <title>{title}</title> 49 </head> 50 <body> 51 {body} 52 </body> 53</html>""" 54 55 56def FormatPage(markdown_html, title): 57 # TODO(juliatuttle): Add a navigation list / table of contents of available 58 # Markdown files, perhaps? 59 return TEMPLATE.format(title=title, body=markdown_html) 60 61 62def ProcessDocs(input_filenames, input_pathname, output_pathname, 63 extensions=None): 64 """Processes a list of Markdown documentation files. 65 66 If input_pathname and output_pathname are specified, outputs HTML files 67 into the corresponding subdirectories of output_pathname. If one or both is 68 not specified, simply ensures the files exist and contain valid Markdown. 69 70 Args: 71 input_filenames: A list of filenames (absolute, or relative to $PWD) of 72 Markdown files to parse and possibly render. 73 input_pathname: The base directory of the input files. (Needed so they 74 can be placed in the same relative path in the output path.) 75 output_pathname: The output directory into which rendered Markdown files 76 go, using that relative path. 77 extensions: a list of Markdown.extensions to apply if any. 78 79 Returns: 80 nothing 81 82 Raises: 83 IOError: if any of the file operations fail (e.g. input_filenames 84 contains a non-existent file). 85 """ 86 87 outputting = (input_pathname is not None) and (output_pathname is not None) 88 89 if extensions: 90 markdown_parser = markdown.Markdown(extensions) 91 else: 92 markdown_parser = markdown.Markdown() 93 94 for input_filename in input_filenames: 95 markdown_text = ReadFile(input_filename) 96 markdown_html = markdown_parser.reset().convert(markdown_text) 97 if not outputting: 98 continue 99 100 full_html = FormatPage(markdown_html, title=input_filename) 101 rel_filename = os.path.relpath(input_filename, start=input_pathname) 102 output_filename = os.path.join(output_pathname, rel_filename) + '.html' 103 WriteFile(output_filename, full_html) 104 105 106def main(): 107 parser = argparse.ArgumentParser( 108 description='Parse and render Markdown documentation') 109 parser.add_argument('--input_path', default=None, 110 help="Input path for Markdown; required only if output_path set") 111 parser.add_argument('--output_path', default=None, 112 help="Output path for rendered HTML; if unspecified, won't output") 113 parser.add_argument('filenames', nargs=argparse.REMAINDER) 114 args = parser.parse_args() 115 116 extensions = ['markdown.extensions.def_list'] 117 ProcessDocs(args.filenames, args.input_path, args.output_path, extensions) 118 119 return 0 120 121 122if __name__ == '__main__': 123 sys.exit(main()) 124