python: format top comment as doc string.
[gtk-doc.git] / gtkdoc / rebase.py
blobe09bb8e9cad29838f329b86c4f6b0ec4876a83f3
1 #!@PYTHON@
2 # -*- python -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007 David Necas (Yeti)
7 # 2007-2016 Stefan Sauer
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 """
25 The rebase tool rewrites URI references in installed HTML documentation.
26 """
28 from __future__ import print_function
30 import os
31 import sys
32 import subprocess
33 import re
35 from . import config
37 # Maps.
38 # These two point to the last seen URI of given type for a package:
39 # OnlineMap: package => on-line URI
40 # LocalMap: package => local URI
41 # This maps all seen URIs of a package to fix broken links in the process:
42 # RevMap: URI => package
43 OnLineMap = {}
44 LocalMap = {}
45 RevMap = {}
46 # Remember what mangling we did.
47 Mapped = {}
50 def log(options, *msg):
51 if options.verbose:
52 print(*msg)
55 def Run(options):
56 other_dirs = []
58 # We scan the directory containing GLib and any directories in GNOME2_PATH
59 # first, but these will be overriden by any later scans.
60 if "GNOME2_PATH" in os.environ:
61 for dir in os.environ["GNOME2_PATH"].split(':'):
62 dir = os.path.join(dir, "/share/gtk-doc/html")
63 if os.path.isdir(dir):
64 log(options, "Prepending GNOME2_PATH directory:", dir)
65 other_dirs = [dir] + other_dirs
67 dir = subprocess.check_output([config.pkg_config, '--variable=prefix', 'glib-2.0'], universal_newlines=True)
68 dir = dir.strip()
69 dir = os.path.join(dir, "/share/gtk-doc/html")
70 log(options, "Prepending GLib directory", dir)
71 other_dirs = [dir] + other_dirs
73 # Check all other dirs, but skip already scanned dirs ord subdirs of those
75 for dir in other_dirs:
76 ScanDirectory(dir, options)
78 if options.relative:
79 RelativizeLocalMap(options.html_dir, options)
81 RebaseReferences(options.html_dir, options)
82 PrintWhatWeHaveDone()
85 def ScanDirectory(dir, options):
86 # This array holds any subdirectories found.
87 subdirs = []
88 onlinedir = None
90 log(options, "Scanning documentation directory " + dir)
92 if dir == options.html_dir:
93 log(options, "Excluding self")
94 return
96 if not os.path.isdir(dir):
97 print('Cannot open dir "%s"' % dir)
98 return
100 have_index = False
101 for entry in os.listdir(dir):
102 if os.path.isdir(entry):
103 subdirs.push_back(entry)
104 continue
106 if entry.endswith('.devhelp2'):
107 log(options, "Reading index from " + entry)
108 o = ReadDevhelp(dir, entry)
109 # Prefer this location over possibly stale index.sgml
110 if o is not None:
111 onlinedir = o
112 have_index = True
114 if onlinedir and entry == "index.sgml":
115 log(options, "Reading index from index.sgml")
116 onlinedir = ReadIndex(dir, entry)
117 have_index = True
118 elif entry == "index.sgml.gz" and not os.path.exists(os.path.join(dir, 'index.sgml')):
119 # debian/ubuntu started to compress this as index.sgml.gz :/
120 print(''' Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
121 gunzip %s/%s
122 ''' % (dir, entry))
123 elif entry.endswith('.devhelp2.gz') and not os.path.exists(os.path.join(dir, entry, 'devhelp2')):
124 # debian/ubuntu started to compress this as *devhelp2.gz :/
125 print('''Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
126 gunzip %d/%s
127 ''' % (dir, entry))
128 # we could consider supporting: gzip module
130 if have_index:
131 AddMap(dir, onlinedir)
133 # Now recursively scan the subdirectories.
134 for subdir in subdirs:
135 ScanDirectory(os.path.join(dir, subdir), options)
138 def ReadDevhelp(dir, file):
139 onlinedir = None
141 for line in open(os.path.join(dir, file)):
142 # online must come before chapter/functions
143 if '<chapters' in line or '<functions' in line:
144 break
145 match = re.search(r' online="([^"]*)"/')
146 if match:
147 # Remove trailing non-directory component.
148 onlinedir = re.sub(r'(.*/).*', r'\1', match.groups(1))
149 return onlinedir
152 def ReadIndex(dir, file):
153 onlinedir = None
155 for line in open(os.path.join(dir, file)):
156 # ONLINE must come before any ANCHORs
157 if '<ANCHOR' in line:
158 break
159 match = re.match(r'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line)
160 if match:
161 # Remove trailing non-directory component.
162 onlinedir = re.sub(r'''(.*/).*''', r'\1', match.groups(1))
163 return onlinedir
166 def AddMap(dir, onlinerdir, options):
167 package = None
169 package = os.path.split(dir)[1]
170 if options.dest_dir != '' and dir.startswith(options.dest_dir):
171 dir = dir[len(options.dest_dir) - 1:]
173 if onlinedir:
174 log(options, "On-line location of %s." % onlinedir)
175 OnlineMap[package] = onlinedir
176 RevMap[onlinedir] = package
177 else:
178 log(options, "No On-line location for %s found" % package)
180 log(options, "Local location of $package: " + dir)
181 LocalMap[package] = dir
182 RevMap[dir] = package
185 def RelativizeLocalMap(dirname, options):
186 prefix = None
187 dir = None
189 dirname = os.path.realpath(dirname)
190 prefix = os.path.split(dirname)
191 for package, dir in LocalMap.items():
192 if dir.startswith(prefix):
193 dir = os.path.join("..", dir[len(prefix):])
194 LocalMap[package] = dir
195 log(options, "Relativizing local location of $package to " + dir)
198 def RebaseReferences(dirname, options):
199 for ifile in os.listdir(dirname):
200 if ifile.endswith('.html'):
201 RebaseFile(os.path.join(dirname, ifile), options)
204 def RebaseFile(filename, options):
205 log(options, "Fixing file: " + filename)
206 regex = re.compile(r'''(<a(?:\s+\w+=(?:"[^"]*"|'[^']*'))*\s+href=")([^"]*)(")''',
207 flags=re.MULTILINE)
209 def repl_func(match):
210 return match.group(1) + RebaseLink(match.group(2), options) + match.group(3)
212 contents = open(filename).read()
213 processed = re.sub(regex, repl_func, contents)
214 newfilename = filename + '.new'
215 open(newfilename, 'w').write(processed)
216 os.unlink(filename)
217 os.rename(newfilename, filename)
220 def RebaseLink(href, options):
221 match = re.match(r'^(.*/)([^/]*)$', href)
222 package = None
223 origdir = 'INVALID'
225 if match:
226 dir = origdir = match.group(1)
227 file = match.group(2)
228 if dir in RevMap:
229 package = RevMap[dir]
230 else:
231 match = re.match(r'\.\./([^/]+)', href)
232 if match is not None:
233 package = match.groups(1)
234 elif options.aggressive:
235 match = re.search(r'''([^/]+)/$''', href)
236 package = match.groups(1)
238 if package:
239 if options.online and package in OnlineMap:
240 dir = OnlineMap[package]
241 elif package in LocalMap:
242 dir = LocalMap[package]
243 href = os.path.join(dir, file)
244 else:
245 log(options, "Can't determine package for '%s'" % href)
247 if dir != origdir:
248 if origdir in Mapped:
249 Mapped[origdir][1] += 1
250 else:
251 Mapped[origdir] = [dir, 1]
252 return href
255 def PrintWhatWeHaveDone():
256 for origdir in sorted(Mapped.keys()):
257 info = Mapped[origdir]
258 print(origdir, "->", info[0], "(%s)" % info[1])