mkpdf: split into wrapper and module
[gtk-doc.git] / gtkdoc-rebase.in
blob1ce662fc252d4f5f0efd3fdbd101fb8c0ea5e713
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 # Script      : gtkdoc-rebase
26 # Description : Rebases URI references in installed HTML documentation.
27 #############################################################################
29 from __future__ import print_function
31 import os, sys, argparse, subprocess, re
33 # Maps.
34 # These two point to the last seen URI of given type for a package:
35 # OnlineMap: package => on-line URI
36 # LocalMap: package => local URI
37 # This maps all seen URIs of a package to fix broken links in the process:
38 # RevMap: URI => package
39 OnLineMap = {}
40 LocalMap = {}
41 RevMap = {}
42 # Remember what mangling we did.
43 Mapped = {}
46 parser = argparse.ArgumentParser()
48 parser.add_argument('--version', action='version', version='@VERSION@')
49 parser.add_argument('--html-dir', dest='html_dir', default='')
50 parser.add_argument('--other-dir', dest='other_dir', default=[], action='append')
51 parser.add_argument('--dest-dir', dest='dest_dir', default='')
52 parser.add_argument('--aggressive', action='store_true', default=False)
53 parser.add_argument('--verbose', action='store_true', default=False)
54 group = parser.add_mutually_exclusive_group()
55 group.add_argument('--online', action='store_true', default=False)
56 group.add_argument('--relative', action='store_true', default=False)
58 def log(options, *msg):
59     if options.verbose:
60         print(*msg)
62 def Run():
63     options = parser.parse_args()
64     other_dirs = []
66     if (options.html_dir == ''):
67         sys.exit("No HTML directory (--html-dir) given.")
69     # We scan the directory containing GLib and any directories in GNOME2_PATH
70     # first, but these will be overriden by any later scans.
71     if "GNOME2_PATH" in os.environ:
72         for dir in os.environ["GNOME2_PATH"].split(':'):
73             dir = os.path.join(dir, "/share/gtk-doc/html")
74             if os.path.isdir(dir):
75                 log(options, "Prepending GNOME2_PATH directory:", dir)
76                 other_dirs = [dir] + other_dirs
78     dir = subprocess.check_output(['@PKG_CONFIG@', '--variable=prefix', 'glib-2.0'], universal_newlines=True)
79     dir = dir.strip()
80     dir = os.path.join(dir, "/share/gtk-doc/html")
81     log(options, "Prepending GLib directory", dir)
82     other_dirs = [dir] + other_dirs
84     # Check all other dirs, but skip already scanned dirs ord subdirs of those
86     for dir in other_dirs:
87         ScanDirectory(dir, options);
89     if options.relative:
90         RelativizeLocalMap(options.html_dir, options);
92     RebaseReferences(options.html_dir, options)
93     PrintWhatWeHaveDone()
96 def ScanDirectory(dir, options):
97     # This array holds any subdirectories found.
98     subdirs = []
99     onlinedir = None
101     log(options, "Scanning documentation directory " + dir)
103     if dir == options.html_dir:
104         log(options, "Excluding self")
105         return
107     if not os.path.isdir(dir):
108         print('Cannot open dir "%s"' % dir)
109         return
111     have_index = False
112     for entry in os.listdir(dir):
113         if os.path.isdir(entry):
114             subdirs.push_back(entry)
115             continue
117         if entry.endswith('.devhelp2'):
118             log(options, "Reading index from " + entry)
119             o = ReadDevhelp(dir, entry);
120             # Prefer this location over possibly stale index.sgml
121             if o is not None:
122                 onlinedir = o
123             have_index = True
125         if onlinedir and entry == "index.sgml":
126             log(options, "Reading index from index.sgml")
127             onlinedir = ReadIndex(dir, entry);
128             have_index = True
129         elif entry == "index.sgml.gz" and not os.path.exists(os.path.join(dir, 'index.sgml')):
130             # debian/ubuntu started to compress this as index.sgml.gz :/
131             print(''' Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
132 gunzip %s/%s
133 ''' % (dir, entry))
134         elif entry.endswith('.devhelp2.gz') and not os.path.exists(os.path.join(dir, entry, 'devhelp2')):
135             # debian/ubuntu started to compress this as *devhelp2.gz :/
136             print('''Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
137 gunzip %d/%s
138 ''' % (dir, entry))
139         # we could consider supporting: gzip module
141     if have_index:
142         AddMap(dir, onlinedir);
144     # Now recursively scan the subdirectories.
145     for subdir in subdirs:
146         ScanDirectory(os.path.join(dir, subdir), options);
149 def ReadDevhelp(dir, file):
150     onlinedir = None
152     for line in open(os.path.join(dir, file)):
153         # online must come before chapter/functions
154         if '<chapters' in line or '<functions' in line:
155             break
156         match = re.search(r'  online="([^"]*)"/')
157         if match:
158             # Remove trailing non-directory component.
159             onlinedir = re.sub(r'(.*/).*', r'\1', match.groups(1))
160     return onlinedir
163 def ReadIndex(dir, file):
164     onlinedir = None
166     for line in open(os.path.join(dir, file)):
167         # ONLINE must come before any ANCHORs
168         if '<ANCHOR' in line:
169             break
170         match = re.match(r'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line)
171         if match:
172             # Remove trailing non-directory component.
173             onlinedir = re.sub(r'''(.*/).*''', r'\1', match.groups(1))
174     return onlinedir
177 def AddMap(dir, onlinerdir, options):
178     package = None
180     package = os.path.split(dir)[1]
181     if options.dest_dir != '' and dir.startswith(options.dest_dir):
182         dir = dir[len(options.dest_dir)-1:]
184     if onlinedir:
185         log(options, "On-line location of %s." % onlinedir)
186         OnlineMap[package] = onlinedir;
187         RevMap[onlinedir] = package;
188     else:
189         log(options, "No On-line location for %s found" % package)
191     log(options,  "Local location of $package: " + dir)
192     LocalMap[package] = dir;
193     RevMap[dir] = package;
196 def RelativizeLocalMap(dirname, options):
197     prefix = None
198     dir = None
200     dirname = os.path.realpath(dirname)
201     prefix = os.path.split(dirname)
202     for package, dir in LocalMap.items():
203         if dir.startswith(prefix):
204             dir = os.path.join("..", dir[len(prefix):])
205             LocalMap[package] = dir
206             log(options, "Relativizing local location of $package to " + dir)
208 def RebaseReferences(dirname, options):
209     for ifile in os.listdir(dirname):
210         if ifile.endswith('.html'):
211             RebaseFile(os.path.join(dirname, ifile), options)
214 def RebaseFile(filename, options):
215     log(options, "Fixing file: " + filename)
216     regex = re.compile(r'''(<a(?:\s+\w+=(?:"[^"]*"|'[^']*'))*\s+href=")([^"]*)(")''',
217                        flags=re.MULTILINE)
219     def repl_func(match):
220         return match.group(1) + RebaseLink(match.group(2), options) + match.group(3)
222     contents = open(filename).read()
223     processed = re.sub(regex, repl_func, contents)
224     newfilename = filename + '.new'
225     open(newfilename, 'w').write(processed)
226     os.unlink(filename)
227     os.rename(newfilename, filename)
230 def RebaseLink(href, options):
231     match = re.match(r'^(.*/)([^/]*)$', href)
232     package = None
233     origdir = 'INVALID'
235     if match:
236         dir = origdir = match.group(1)
237         file = match.group(2)
238         if dir in RevMap:
239             package = RevMap[dir]
240         else:
241             match = re.match(r'\.\./([^/]+)', href)
242             if match is not None:
243                 package = match.groups(1)
244             elif options.aggressive:
245                 match = re.search(r'''([^/]+)/$''', href)
246                 package = match.groups(1);
248         if package:
249             if options.online and package in OnlineMap:
250               dir = OnlineMap[package]
251             elif package in LocalMap:
252               dir = LocalMap[package]
253             href = os.path.join(dir, file)
254         else:
255           log(options, "Can't determine package for '%s'" % href);
257         if dir != origdir:
258             if origdir in Mapped:
259                 Mapped[origdir][1] += 1
260             else:
261                 Mapped[origdir] = [dir, 1]
262     return href
265 def PrintWhatWeHaveDone():
266     for origdir in sorted(Mapped.keys()):
267         info = Mapped[origdir]
268         print(origdir, "->", info[0], "(%s)" % info[1])
270 if __name__== '__main__':
271     Run()