mkman,mkpdf: also fix path arg handling
[gtk-doc.git] / gtkdoc / rebase.py
blob81ae6ddbb4952e32464a0dafa0aa308a70e9b371
1 # -*- python -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 1998 Damon Chaplin
5 # 2007 David Necas (Yeti)
6 # 2007-2016 Stefan Sauer
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 """
24 The rebase tool rewrites URI references in installed HTML documentation.
25 """
27 from __future__ import print_function
29 import logging
30 import os
31 import re
33 from . import common
35 # Maps.
36 # These two point to the last seen URI of given type for a package:
37 # OnlineMap: package => on-line URI
38 # LocalMap: package => local URI
39 # This maps all seen URIs of a package to fix broken links in the process:
40 # RevMap: URI => package
41 OnlineMap = {}
42 LocalMap = {}
43 RevMap = {}
44 # Remember what mangling we did.
45 Mapped = {}
48 def log(options, *msg):
49 if options.verbose:
50 print(*msg)
53 def run(options):
54 other_dirs = []
56 # We scan the directory containing GLib and any directories in GNOME2_PATH
57 # first, but these will be overriden by any later scans.
58 if "GNOME2_PATH" in os.environ:
59 for dir in os.environ["GNOME2_PATH"].split(':'):
60 dir = os.path.join(dir, "/share/gtk-doc/html")
61 if os.path.isdir(dir):
62 log(options, "Prepending GNOME2_PATH directory:", dir)
63 other_dirs = [dir] + other_dirs
65 dir = common.GetModuleDocDir('glib-2.0')
66 log(options, "Prepending GLib directory", dir)
67 other_dirs = [dir] + other_dirs
69 # Check all other dirs, but skip already scanned dirs ord subdirs of those
71 for dir in other_dirs:
72 ScanDirectory(dir, options)
74 if options.relative:
75 RelativizeLocalMap(options.html_dir, options)
77 RebaseReferences(options.html_dir, options)
78 PrintWhatWeHaveDone()
81 def ScanDirectory(scan_dir, options):
82 log(options, "Scanning documentation directory %s", scan_dir)
84 if scan_dir == options.html_dir:
85 log(options, "Excluding self")
86 return
88 if not os.path.isdir(scan_dir):
89 logging.info('Cannot open dir "%s"', scan_dir)
90 return
92 subdirs = []
93 onlinedir = None
94 have_index = False
95 for entry in os.listdir(scan_dir):
96 full_entry = os.path.join(scan_dir, entry)
97 if os.path.isdir(full_entry):
98 subdirs.append(full_entry)
99 continue
101 if entry.endswith('.devhelp2'):
102 log(options, "Reading index from " + entry)
103 o = ReadDevhelp(scan_dir, entry)
104 # Prefer this location over possibly stale index.sgml
105 if o is not None:
106 onlinedir = o
107 have_index = True
109 if onlinedir and entry == "index.sgml":
110 log(options, "Reading index from index.sgml")
111 onlinedir = ReadIndex(dir, entry)
112 have_index = True
113 elif entry == "index.sgml.gz" and not os.path.exists(os.path.join(scan_dir, 'index.sgml')):
114 # debian/ubuntu started to compress this as index.sgml.gz :/
115 print(''' Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
116 gunzip %s/%s
117 ''' % (scan_dir, entry))
118 elif entry.endswith('.devhelp2.gz') and not os.path.exists(full_entry[:-3]):
119 # debian/ubuntu started to compress this as *devhelp2.gz :/
120 print('''Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
121 gunzip %d/%s
122 ''' % (scan_dir, entry))
123 # we could consider supporting: gzip module
125 if have_index:
126 AddMap(scan_dir, onlinedir, options)
128 # Now recursively scan the subdirectories.
129 for subdir in subdirs:
130 ScanDirectory(subdir, options)
133 def ReadDevhelp(dir, file):
134 onlinedir = None
136 for line in open(os.path.join(dir, file)):
137 # online must come before chapter/functions
138 if '<chapters' in line or '<functions' in line:
139 break
140 match = re.search(r' online="([^"]*)"/', line)
141 if match:
142 # Remove trailing non-directory component.
143 onlinedir = re.sub(r'(.*/).*', r'\1', match.groups(1))
144 return onlinedir
147 def ReadIndex(dir, file):
148 onlinedir = None
150 for line in open(os.path.join(dir, file)):
151 # ONLINE must come before any ANCHORs
152 if '<ANCHOR' in line:
153 break
154 match = re.match(r'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line)
155 if match:
156 # Remove trailing non-directory component.
157 onlinedir = re.sub(r'''(.*/).*''', r'\1', match.groups(1))
158 return onlinedir
161 def AddMap(dir, onlinedir, options):
162 package = None
164 package = os.path.split(dir)[1]
165 if options.dest_dir != '' and dir.startswith(options.dest_dir):
166 dir = dir[len(options.dest_dir) - 1:]
168 if onlinedir:
169 log(options, "On-line location of %s." % onlinedir)
170 OnlineMap[package] = onlinedir
171 RevMap[onlinedir] = package
172 else:
173 log(options, "No On-line location for %s found" % package)
175 log(options, "Local location of $package: " + dir)
176 LocalMap[package] = dir
177 RevMap[dir] = package
180 def RelativizeLocalMap(dirname, options):
181 prefix = None
182 dir = None
184 dirname = os.path.realpath(dirname)
185 prefix = os.path.split(dirname)
186 for package, dir in LocalMap.items():
187 if dir.startswith(prefix):
188 dir = os.path.join("..", dir[len(prefix):])
189 LocalMap[package] = dir
190 log(options, "Relativizing local location of $package to " + dir)
193 def RebaseReferences(dirname, options):
194 for ifile in os.listdir(dirname):
195 if ifile.endswith('.html'):
196 RebaseFile(os.path.join(dirname, ifile), options)
199 def RebaseFile(filename, options):
200 log(options, "Fixing file: " + filename)
201 regex = re.compile(r'''(<a(?:\s+\w+=(?:"[^"]*"|'[^']*'))*\s+href=")([^"]*)(")''',
202 flags=re.MULTILINE)
204 def repl_func(match):
205 return match.group(1) + RebaseLink(match.group(2), options) + match.group(3)
207 contents = open(filename).read()
208 processed = re.sub(regex, repl_func, contents)
209 newfilename = filename + '.new'
210 open(newfilename, 'w').write(processed)
211 os.unlink(filename)
212 os.rename(newfilename, filename)
215 def RebaseLink(href, options):
216 match = re.match(r'^(.*/)([^/]*)$', href)
217 package = None
218 origdir = 'INVALID'
220 if match:
221 dir = origdir = match.group(1)
222 file = match.group(2)
223 if dir in RevMap:
224 package = RevMap[dir]
225 else:
226 match = re.match(r'\.\./([^/]+)', href)
227 if match is not None:
228 package = match.groups(1)
229 elif options.aggressive:
230 match = re.search(r'''([^/]+)/$''', href)
231 package = match.groups(1)
233 if package:
234 if options.online and package in OnlineMap:
235 dir = OnlineMap[package]
236 elif package in LocalMap:
237 dir = LocalMap[package]
238 href = os.path.join(dir, file)
239 else:
240 log(options, "Can't determine package for '%s'" % href)
242 if dir != origdir:
243 if origdir in Mapped:
244 Mapped[origdir][1] += 1
245 else:
246 Mapped[origdir] = [dir, 1]
247 return href
250 def PrintWhatWeHaveDone():
251 for origdir in sorted(Mapped.keys()):
252 info = Mapped[origdir]
253 print(origdir, "->", info[0], "(%s)" % info[1])