1#!/usr/bin/python 2 3# Copyright (c) 2014 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import collections 8import json 9import optparse 10import os 11import shutil 12import subprocess 13import sys 14import tempfile 15import urllib2 16 17 18SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 19DOC_DIR = os.path.dirname(SCRIPT_DIR) 20 21 22ChannelInfo = collections.namedtuple('ChannelInfo', ['branch', 'version']) 23 24 25def Trace(msg): 26 if Trace.verbose: 27 sys.stderr.write(str(msg) + '\n') 28 29Trace.verbose = False 30 31 32def GetChannelInfo(): 33 url = 'http://omahaproxy.appspot.com/json' 34 u = urllib2.urlopen(url) 35 try: 36 data = json.loads(u.read()) 37 finally: 38 u.close() 39 40 channel_info = {} 41 for os_row in data: 42 osname = os_row['os'] 43 if osname not in ('win', 'mac', 'linux'): 44 continue 45 for version_row in os_row['versions']: 46 channel = version_row['channel'] 47 # We don't display canary docs. 48 if channel == 'canary': 49 continue 50 51 version = version_row['version'].split('.')[0] # Major version 52 branch = version_row['true_branch'] 53 if branch is None: 54 branch = 'trunk' 55 56 if channel in channel_info: 57 existing_info = channel_info[channel] 58 if branch != existing_info.branch: 59 sys.stderr.write('Warning: found different branch numbers for ' 60 'channel %s: %s vs %s. Using %s.\n' % ( 61 channel, branch, existing_info.branch, existing_info.branch)) 62 else: 63 channel_info[channel] = ChannelInfo(branch, version) 64 65 return channel_info 66 67 68def RemoveFile(filename): 69 if os.path.exists(filename): 70 os.remove(filename) 71 72 73def RemoveDir(dirname): 74 if os.path.exists(dirname): 75 shutil.rmtree(dirname) 76 77 78def GetSVNRepositoryRoot(branch): 79 if branch == 'trunk': 80 return 'http://src.chromium.org/chrome/trunk/src' 81 return 'http://src.chromium.org/chrome/branches/%s/src' % branch 82 83 84def CheckoutPepperDocs(branch, doc_dirname): 85 Trace('Removing directory %s' % doc_dirname) 86 RemoveDir(doc_dirname) 87 88 svn_root_url = GetSVNRepositoryRoot(branch) 89 90 for subdir in ('api', 'generators', 'cpp', 'utility'): 91 url = svn_root_url + '/ppapi/%s' % subdir 92 cmd = ['svn', 'co', url, os.path.join(doc_dirname, subdir)] 93 Trace('Checking out docs into %s:\n %s' % (doc_dirname, ' '.join(cmd))) 94 subprocess.check_call(cmd) 95 96 # The IDL generator needs PLY (a python lexing library); check it out into 97 # generators. 98 url = svn_root_url + '/third_party/ply' 99 ply_dirname = os.path.join(doc_dirname, 'generators', 'ply') 100 cmd = ['svn', 'co', url, ply_dirname] 101 Trace('Checking out PLY into %s:\n %s' % (ply_dirname, ' '.join(cmd))) 102 subprocess.check_call(cmd) 103 104 105def FixPepperDocLinks(doc_dirname): 106 # TODO(binji): We can remove this step when the correct links are in the 107 # stable branch. 108 Trace('Looking for links to fix in Pepper headers...') 109 for root, dirs, filenames in os.walk(doc_dirname): 110 # Don't recurse into .svn 111 if '.svn' in dirs: 112 dirs.remove('.svn') 113 114 for filename in filenames: 115 header_filename = os.path.join(root, filename) 116 Trace(' Checking file %r...' % header_filename) 117 replacements = { 118 '<a href="/native-client/{{pepperversion}}/devguide/coding/audio">': 119 '<a href="/native-client/devguide/coding/audio.html">', 120 '<a href="/native-client/devguide/coding/audio">': 121 '<a href="/native-client/devguide/coding/audio.html">', 122 '<a href="/native-client/{{pepperversion}}/pepperc/globals_defs"': 123 '<a href="globals_defs.html"', 124 '<a href="../pepperc/ppb__image__data_8h.html">': 125 '<a href="../c/ppb__image__data_8h.html">'} 126 127 with open(header_filename) as f: 128 lines = [] 129 replaced = False 130 for line in f: 131 for find, replace in replacements.iteritems(): 132 pos = line.find(find) 133 if pos != -1: 134 Trace(' Found %r...' % find) 135 replaced = True 136 line = line[:pos] + replace + line[pos + len(find):] 137 lines.append(line) 138 139 if replaced: 140 Trace(' Writing new file.') 141 with open(header_filename, 'w') as f: 142 f.writelines(lines) 143 144 145def GenerateCHeaders(pepper_version, doc_dirname): 146 script = os.path.join(os.pardir, 'generators', 'generator.py') 147 cwd = os.path.join(doc_dirname, 'api') 148 out_dirname = os.path.join(os.pardir, 'c') 149 cmd = [sys.executable, script, '--cgen', '--release', 'M' + pepper_version, 150 '--wnone', '--dstroot', out_dirname] 151 Trace('Generating C Headers for version %s\n %s' % ( 152 pepper_version, ' '.join(cmd))) 153 subprocess.check_call(cmd, cwd=cwd) 154 155 156def GenerateDoxyfile(template_filename, out_dirname, doc_dirname, doxyfile): 157 Trace('Writing Doxyfile "%s" (from template %s)' % ( 158 doxyfile, template_filename)) 159 160 with open(template_filename) as f: 161 data = f.read() 162 163 with open(doxyfile, 'w') as f: 164 f.write(data % { 165 'out_dirname': out_dirname, 166 'doc_dirname': doc_dirname, 167 'script_dirname': SCRIPT_DIR}) 168 169 170def RunDoxygen(out_dirname, doxyfile): 171 Trace('Removing old output directory %s' % out_dirname) 172 RemoveDir(out_dirname) 173 174 Trace('Making new output directory %s' % out_dirname) 175 os.makedirs(out_dirname) 176 177 cmd = ['doxygen', doxyfile] 178 Trace('Running Doxygen:\n %s' % ' '.join(cmd)) 179 subprocess.check_call(cmd) 180 181 182def RunDoxyCleanup(out_dirname): 183 script = os.path.join(SCRIPT_DIR, 'doxy_cleanup.py') 184 cmd = [sys.executable, script, out_dirname] 185 if Trace.verbose: 186 cmd.append('-v') 187 Trace('Running doxy_cleanup:\n %s' % ' '.join(cmd)) 188 subprocess.check_call(cmd) 189 190 191def RunRstIndex(kind, channel, pepper_version, out_dirname, out_rst_filename): 192 assert kind in ('root', 'c', 'cpp') 193 script = os.path.join(SCRIPT_DIR, 'rst_index.py') 194 cmd = [sys.executable, script, 195 '--' + kind, 196 '--channel', channel, 197 '--version', pepper_version, 198 out_dirname, 199 '-o', out_rst_filename] 200 Trace('Running rst_index:\n %s' % ' '.join(cmd)) 201 subprocess.check_call(cmd) 202 203 204def GenerateDocs(root_dirname, channel, pepper_version, branch): 205 Trace('Generating docs for %s (branch %s)' % (channel, branch)) 206 pepper_dirname = 'pepper_%s' % channel 207 out_dirname = os.path.join(root_dirname, pepper_dirname) 208 209 try: 210 svn_dirname = tempfile.mkdtemp(prefix=pepper_dirname) 211 doxyfile_dirname = tempfile.mkdtemp(prefix='%s_doxyfiles' % pepper_dirname) 212 213 CheckoutPepperDocs(branch, svn_dirname) 214 FixPepperDocLinks(svn_dirname) 215 GenerateCHeaders(pepper_version, svn_dirname) 216 217 doxyfile_c = '' 218 doxyfile_cpp = '' 219 220 # Generate Root index 221 rst_index_root = os.path.join(DOC_DIR, pepper_dirname, 'index.rst') 222 RunRstIndex('root', channel, pepper_version, out_dirname, rst_index_root) 223 224 # Generate C docs 225 out_dirname_c = os.path.join(out_dirname, 'c') 226 doxyfile_c = os.path.join(doxyfile_dirname, 'Doxyfile.c.%s' % channel) 227 doxyfile_c_template = os.path.join(SCRIPT_DIR, 'Doxyfile.c.template') 228 rst_index_c = os.path.join(DOC_DIR, pepper_dirname, 'c', 'index.rst') 229 GenerateDoxyfile(doxyfile_c_template, out_dirname_c, svn_dirname, 230 doxyfile_c) 231 RunDoxygen(out_dirname_c, doxyfile_c) 232 RunDoxyCleanup(out_dirname_c) 233 RunRstIndex('c', channel, pepper_version, out_dirname_c, rst_index_c) 234 235 # Generate C++ docs 236 out_dirname_cpp = os.path.join(out_dirname, 'cpp') 237 doxyfile_cpp = os.path.join(doxyfile_dirname, 'Doxyfile.cpp.%s' % channel) 238 doxyfile_cpp_template = os.path.join(SCRIPT_DIR, 'Doxyfile.cpp.template') 239 rst_index_cpp = os.path.join(DOC_DIR, pepper_dirname, 'cpp', 'index.rst') 240 GenerateDoxyfile(doxyfile_cpp_template, out_dirname_cpp, svn_dirname, 241 doxyfile_cpp) 242 RunDoxygen(out_dirname_cpp, doxyfile_cpp) 243 RunDoxyCleanup(out_dirname_cpp) 244 RunRstIndex('cpp', channel, pepper_version, out_dirname_cpp, rst_index_cpp) 245 finally: 246 # Cleanup 247 RemoveDir(svn_dirname) 248 RemoveDir(doxyfile_dirname) 249 250 251def main(argv): 252 parser = optparse.OptionParser(usage='Usage: %prog [options] <out_directory>') 253 parser.add_option('-v', '--verbose', 254 help='Verbose output', action='store_true') 255 options, dirs = parser.parse_args(argv) 256 257 if options.verbose: 258 Trace.verbose = True 259 260 if len(dirs) != 1: 261 parser.error('Expected an output directory') 262 263 channel_info = GetChannelInfo() 264 for channel, info in channel_info.iteritems(): 265 GenerateDocs(dirs[0], channel, info.version, info.branch) 266 267 return 0 268 269 270if __name__ == '__main__': 271 try: 272 rtn = main(sys.argv[1:]) 273 except KeyboardInterrupt: 274 sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__)) 275 rtn = 1 276 sys.exit(rtn) 277