2 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Script to regenerate API docs using doxygen.
20 if sys
.version_info
< (2, 7, 0):
21 sys
.stderr
.write("python 2.7 or later is required run this script\n")
25 SCRIPT_DIR
= os
.path
.dirname(os
.path
.abspath(__file__
))
26 DOC_DIR
= os
.path
.dirname(SCRIPT_DIR
)
29 ChannelInfo
= collections
.namedtuple('ChannelInfo', ['branch', 'version'])
34 sys
.stderr
.write(str(msg
) + '\n')
40 url
= 'http://omahaproxy.appspot.com/json'
41 u
= urllib2
.urlopen(url
)
43 data
= json
.loads(u
.read())
50 if osname
not in ('win', 'mac', 'linux'):
52 for version_row
in os_row
['versions']:
53 channel
= version_row
['channel']
54 # We don't display canary docs.
55 if channel
== 'canary':
58 version
= version_row
['version'].split('.')[0] # Major version
59 branch
= version_row
['true_branch']
63 if channel
in channel_info
:
64 existing_info
= channel_info
[channel
]
65 if branch
!= existing_info
.branch
:
66 sys
.stderr
.write('Warning: found different branch numbers for '
67 'channel %s: %s vs %s. Using %s.\n' % (
68 channel
, branch
, existing_info
.branch
, existing_info
.branch
))
70 channel_info
[channel
] = ChannelInfo(branch
, version
)
75 def RemoveFile(filename
):
76 if os
.path
.exists(filename
):
80 def RemoveDir(dirname
):
81 if os
.path
.exists(dirname
):
82 shutil
.rmtree(dirname
)
86 cmd
= ['git', 'for-each-ref', '--format=%(refname)',
87 'refs/remotes/branch-heads']
88 output
= subprocess
.check_output(cmd
).splitlines()
92 def CheckoutDirectories(dest_dirname
, refname
, root_path
, patterns
=None):
93 treeish
= '%s:%s' % (refname
, root_path
)
94 cmd
= ['git', 'ls-tree', '--full-tree', '-r', treeish
]
98 Trace('Running \"%s\":' % ' '.join(cmd
))
99 output
= subprocess
.check_output(cmd
)
100 for line
in output
.splitlines():
101 info
, rel_filename
= line
.split('\t')
102 sha
= info
.split(' ')[2]
104 Trace(' %s %s' % (sha
, rel_filename
))
106 cmd
= ['git', 'show', sha
]
107 blob
= subprocess
.check_output(cmd
)
108 filename
= os
.path
.join(dest_dirname
, rel_filename
)
109 dirname
= os
.path
.dirname(filename
)
110 if not os
.path
.exists(dirname
):
113 Trace(' writing to %s' % filename
)
114 with
open(filename
, 'w') as f
:
118 def CheckoutPepperDocs(branch
, doc_dirname
):
119 Trace('Removing directory %s' % doc_dirname
)
120 RemoveDir(doc_dirname
)
122 if branch
== 'master':
123 refname
= 'refs/remotes/origin/master'
125 refname
= 'refs/remotes/branch-heads/%s' % branch
127 Trace('Checking out docs into %s' % doc_dirname
)
128 subdirs
= ['api', 'generators', 'cpp', 'utility']
129 CheckoutDirectories(doc_dirname
, refname
, 'ppapi', subdirs
)
131 # The IDL generator needs PLY (a python lexing library); check it out into
133 ply_dirname
= os
.path
.join(doc_dirname
, 'generators', 'ply')
134 Trace('Checking out PLY into %s' % ply_dirname
)
135 CheckoutDirectories(ply_dirname
, refname
, 'third_party/ply')
138 def FixPepperDocLinks(doc_dirname
):
139 # TODO(binji): We can remove this step when the correct links are in the
141 Trace('Looking for links to fix in Pepper headers...')
142 for root
, dirs
, filenames
in os
.walk(doc_dirname
):
143 # Don't recurse into .svn
147 for filename
in filenames
:
148 header_filename
= os
.path
.join(root
, filename
)
149 Trace(' Checking file %r...' % header_filename
)
151 '<a href="/native-client/{{pepperversion}}/devguide/coding/audio">':
152 '<a href="/native-client/devguide/coding/audio.html">',
153 '<a href="/native-client/devguide/coding/audio">':
154 '<a href="/native-client/devguide/coding/audio.html">',
155 '<a href="/native-client/{{pepperversion}}/pepperc/globals_defs"':
156 '<a href="globals_defs.html"',
157 '<a href="../pepperc/ppb__image__data_8h.html">':
158 '<a href="../c/ppb__image__data_8h.html">'}
160 with
open(header_filename
) as f
:
164 for find
, replace
in replacements
.iteritems():
165 pos
= line
.find(find
)
167 Trace(' Found %r...' % find
)
169 line
= line
[:pos
] + replace
+ line
[pos
+ len(find
):]
173 Trace(' Writing new file.')
174 with
open(header_filename
, 'w') as f
:
178 def GenerateCHeaders(pepper_version
, doc_dirname
):
179 script
= os
.path
.join(os
.pardir
, 'generators', 'generator.py')
180 cwd
= os
.path
.join(doc_dirname
, 'api')
181 out_dirname
= os
.path
.join(os
.pardir
, 'c')
182 cmd
= [sys
.executable
, script
, '--cgen', '--release', 'M' + pepper_version
,
183 '--wnone', '--dstroot', out_dirname
]
184 Trace('Generating C Headers for version %s\n %s' % (
185 pepper_version
, ' '.join(cmd
)))
186 subprocess
.check_call(cmd
, cwd
=cwd
)
189 def GenerateDoxyfile(template_filename
, out_dirname
, doc_dirname
, doxyfile
):
190 Trace('Writing Doxyfile "%s" (from template %s)' % (
191 doxyfile
, template_filename
))
193 with
open(template_filename
) as f
:
196 with
open(doxyfile
, 'w') as f
:
198 'out_dirname': out_dirname
,
199 'doc_dirname': doc_dirname
,
200 'script_dirname': SCRIPT_DIR
})
203 def RunDoxygen(out_dirname
, doxyfile
):
204 Trace('Removing old output directory %s' % out_dirname
)
205 RemoveDir(out_dirname
)
207 Trace('Making new output directory %s' % out_dirname
)
208 os
.makedirs(out_dirname
)
210 doxygen
= os
.environ
.get('DOXYGEN', 'doxygen')
211 cmd
= [doxygen
, doxyfile
]
212 Trace('Running Doxygen:\n %s' % ' '.join(cmd
))
213 subprocess
.check_call(cmd
)
216 def RunDoxyCleanup(out_dirname
):
217 script
= os
.path
.join(SCRIPT_DIR
, 'doxy_cleanup.py')
218 cmd
= [sys
.executable
, script
, out_dirname
]
221 Trace('Running doxy_cleanup:\n %s' % ' '.join(cmd
))
222 subprocess
.check_call(cmd
)
225 def RunRstIndex(kind
, channel
, pepper_version
, out_dirname
, out_rst_filename
):
226 assert kind
in ('root', 'c', 'cpp')
227 script
= os
.path
.join(SCRIPT_DIR
, 'rst_index.py')
228 cmd
= [sys
.executable
, script
,
230 '--channel', channel
,
231 '--version', pepper_version
,
234 Trace('Running rst_index:\n %s' % ' '.join(cmd
))
235 subprocess
.check_call(cmd
)
238 def GetRstName(kind
, channel
):
239 if channel
== 'stable':
240 filename
= '%s-api.rst' % kind
242 filename
= '%s-api-%s.rst' % (kind
, channel
)
243 return os
.path
.join(DOC_DIR
, filename
)
246 def GenerateDocs(root_dirname
, channel
, pepper_version
, branch
):
247 Trace('Generating docs for %s (branch %s)' % (channel
, branch
))
248 pepper_dirname
= 'pepper_%s' % channel
249 out_dirname
= os
.path
.join(root_dirname
, pepper_dirname
)
252 svn_dirname
= tempfile
.mkdtemp(prefix
=pepper_dirname
)
253 doxyfile_dirname
= tempfile
.mkdtemp(prefix
='%s_doxyfiles' % pepper_dirname
)
255 CheckoutPepperDocs(branch
, svn_dirname
)
256 FixPepperDocLinks(svn_dirname
)
257 GenerateCHeaders(pepper_version
, svn_dirname
)
262 # Generate Root index
263 rst_index_root
= os
.path
.join(DOC_DIR
, pepper_dirname
, 'index.rst')
264 RunRstIndex('root', channel
, pepper_version
, out_dirname
, rst_index_root
)
267 out_dirname_c
= os
.path
.join(out_dirname
, 'c')
268 doxyfile_c
= os
.path
.join(doxyfile_dirname
, 'Doxyfile.c.%s' % channel
)
269 doxyfile_c_template
= os
.path
.join(SCRIPT_DIR
, 'Doxyfile.c.template')
270 rst_index_c
= GetRstName('c', channel
)
271 GenerateDoxyfile(doxyfile_c_template
, out_dirname_c
, svn_dirname
,
273 RunDoxygen(out_dirname_c
, doxyfile_c
)
274 RunDoxyCleanup(out_dirname_c
)
275 RunRstIndex('c', channel
, pepper_version
, out_dirname_c
, rst_index_c
)
278 out_dirname_cpp
= os
.path
.join(out_dirname
, 'cpp')
279 doxyfile_cpp
= os
.path
.join(doxyfile_dirname
, 'Doxyfile.cpp.%s' % channel
)
280 doxyfile_cpp_template
= os
.path
.join(SCRIPT_DIR
, 'Doxyfile.cpp.template')
281 rst_index_cpp
= GetRstName('cpp', channel
)
282 GenerateDoxyfile(doxyfile_cpp_template
, out_dirname_cpp
, svn_dirname
,
284 RunDoxygen(out_dirname_cpp
, doxyfile_cpp
)
285 RunDoxyCleanup(out_dirname_cpp
)
286 RunRstIndex('cpp', channel
, pepper_version
, out_dirname_cpp
, rst_index_cpp
)
289 RemoveDir(svn_dirname
)
290 RemoveDir(doxyfile_dirname
)
294 parser
= argparse
.ArgumentParser(description
=__doc__
)
295 parser
.add_argument('-v', '--verbose',
296 help='Verbose output', action
='store_true')
297 parser
.add_argument('out_directory')
298 options
= parser
.parse_args(argv
)
303 channel_info
= GetChannelInfo()
304 for channel
, info
in channel_info
.iteritems():
305 GenerateDocs(options
.out_directory
, channel
, info
.version
, info
.branch
)
310 if __name__
== '__main__':
312 rtn
= main(sys
.argv
[1:])
313 except KeyboardInterrupt:
314 sys
.stderr
.write('%s: interrupted\n' % os
.path
.basename(__file__
))