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.
24 The rebase tool rewrites URI references in installed HTML documentation.
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
43 # Remember what mangling we did.
47 def log(options
, *msg
):
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')
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
)
75 RelativizeLocalMap(options
.html_dir
, options
)
77 RebaseReferences(options
.html_dir
, options
)
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")
88 if not os
.path
.isdir(scan_dir
):
89 logging
.info('Cannot open dir "%s"', scan_dir
)
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
)
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
109 if onlinedir
and entry
== "index.sgml":
110 log(options
, "Reading index from index.sgml")
111 onlinedir
= ReadIndex(scan_dir
, entry
)
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:
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:
122 ''' % (scan_dir
, entry
))
123 # we could consider supporting: gzip module
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):
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
:
140 match
= re
.search(r
' online="([^"]*)"', line
)
142 # Remove trailing non-directory component.
143 onlinedir
= re
.sub(r
'(.*/).*', r
'\1', match
.group(1))
147 def ReadIndex(dir, file):
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
:
154 match
= re
.match(r
'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line
)
156 # Remove trailing non-directory component.
157 onlinedir
= re
.sub(r
'''(.*/).*''', r
'\1', match
.group(1))
161 def AddMap(dir, onlinedir
, options
):
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:]
169 log(options
, "On-line location of %s." % onlinedir
)
170 OnlineMap
[package
] = onlinedir
171 RevMap
[onlinedir
] = package
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
):
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=")([^"]*)(")''',
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
:
213 os
.rename(newfilename
, filename
)
216 def RebaseLink(href
, options
):
217 match
= re
.match(r
'^(.*/)([^/]*)$', href
)
222 dir = origdir
= match
.group(1)
223 file = match
.group(2)
225 package
= RevMap
[dir]
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)
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)
241 log(options
, "Can't determine package for '%s'" % href
)
244 if origdir
in Mapped
:
245 Mapped
[origdir
][1] += 1
247 Mapped
[origdir
] = [dir, 1]
251 def PrintWhatWeHaveDone():
252 for origdir
in sorted(Mapped
.keys()):
253 info
= Mapped
[origdir
]
254 print(origdir
, "->", info
[0], "(%s)" % info
[1])