Update French translation
[gtk-doc.git] / gtkdoc / rebase.py
blob4b0266ce4751d0d6fb8b83865775298f4cac1691
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 import logging
28 import os
29 import re
30 import subprocess
32 from . import common
34 # Maps.
35 # These two point to the last seen URI of given type for a package:
36 # OnlineMap: package => on-line URI
37 # LocalMap: package => local URI
38 # This maps all seen URIs of a package to fix broken links in the process:
39 # RevMap: URI => package
40 OnlineMap = {}
41 LocalMap = {}
42 RevMap = {}
43 # Remember what mangling we did.
44 Mapped = {}
47 def log(options, *msg):
48 if options.verbose:
49 print(*msg)
52 def run(options):
53 other_dirs = []
55 # We scan the directory containing GLib and any directories in GNOME2_PATH
56 # first, but these will be overriden by any later scans.
57 if "GNOME2_PATH" in os.environ:
58 for dir in os.environ["GNOME2_PATH"].split(':'):
59 dir = os.path.join(dir, "share/gtk-doc/html")
60 if os.path.isdir(dir):
61 log(options, "Prepending GNOME2_PATH directory:", dir)
62 other_dirs = [dir] + other_dirs
64 glib_dir = common.GetModuleDocDir('glib-2.0')
65 if glib_dir:
66 log(options, "Prepending GLib directory", glib_dir)
67 other_dirs = [glib_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 sorted(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(scan_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 %s/%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), mode='r', encoding='utf-8'):
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.group(1))
144 return onlinedir
147 def ReadIndex(dir, file):
148 onlinedir = None
150 for line in open(os.path.join(dir, file), mode='r', encoding='utf-8'):
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.group(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 sorted(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, mode='r', encoding='utf-8').read()
208 processed = re.sub(regex, repl_func, contents)
209 newfilename = filename + '.new'
210 with open(newfilename, mode='w', encoding='utf-8') as h:
211 h.write(processed)
212 os.unlink(filename)
213 os.rename(newfilename, filename)
216 def RebaseLink(href, options):
217 match = re.match(r'^(.*/)([^/]*)$', href)
218 package = None
219 origdir = 'INVALID'
221 if match:
222 dir = origdir = match.group(1)
223 file = match.group(2)
224 if dir in RevMap:
225 package = RevMap[dir]
226 else:
227 match = re.match(r'\.\./([^/]+)', href)
228 if match is not None:
229 package = match.group(1)
230 elif options.aggressive:
231 match = re.search(r'''([^/]+)/$''', href)
232 package = match.group(1)
234 if package:
235 if options.online and package in OnlineMap:
236 dir = OnlineMap[package]
237 elif package in LocalMap:
238 dir = LocalMap[package]
239 href = os.path.join(dir, file)
240 else:
241 log(options, "Can't determine package for '%s'" % href)
243 if dir != origdir:
244 if origdir in Mapped:
245 Mapped[origdir][1] += 1
246 else:
247 Mapped[origdir] = [dir, 1]
248 return href
251 def PrintWhatWeHaveDone():
252 for origdir in sorted(Mapped.keys()):
253 info = Mapped[origdir]
254 print(origdir, "->", info[0], "(%s)" % info[1])