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.
27 from __future__
import print_function
28 from six
import iteritems
, iterkeys
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
46 # Remember what mangling we did.
50 def log(options
, *msg
):
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 glib_dir
= common
.GetModuleDocDir('glib-2.0')
69 log(options
, "Prepending GLib directory", glib_dir
)
70 other_dirs
= [glib_dir
] + other_dirs
72 # Check all other dirs, but skip already scanned dirs ord subdirs of those
74 for dir in other_dirs
:
75 ScanDirectory(dir, options
)
78 RelativizeLocalMap(options
.html_dir
, options
)
80 RebaseReferences(options
.html_dir
, options
)
84 def ScanDirectory(scan_dir
, options
):
85 log(options
, "Scanning documentation directory %s", scan_dir
)
87 if scan_dir
== options
.html_dir
:
88 log(options
, "Excluding self")
91 if not os
.path
.isdir(scan_dir
):
92 logging
.info('Cannot open dir "%s"', scan_dir
)
98 for entry
in sorted(os
.listdir(scan_dir
)):
99 full_entry
= os
.path
.join(scan_dir
, entry
)
100 if os
.path
.isdir(full_entry
):
101 subdirs
.append(full_entry
)
104 if entry
.endswith('.devhelp2'):
105 log(options
, "Reading index from " + entry
)
106 o
= ReadDevhelp(scan_dir
, entry
)
107 # Prefer this location over possibly stale index.sgml
112 if onlinedir
and entry
== "index.sgml":
113 log(options
, "Reading index from index.sgml")
114 onlinedir
= ReadIndex(dir, entry
)
116 elif entry
== "index.sgml.gz" and not os
.path
.exists(os
.path
.join(scan_dir
, 'index.sgml')):
117 # debian/ubuntu started to compress this as index.sgml.gz :/
118 print(''' Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/77138 . For now run:
120 ''' % (scan_dir
, entry
))
121 elif entry
.endswith('.devhelp2.gz') and not os
.path
.exists(full_entry
[:-3]):
122 # debian/ubuntu started to compress this as *devhelp2.gz :/
123 print('''Please fix https://bugs.launchpad.net/ubuntu/+source/gtk-doc/+bug/1466210 . For now run:
125 ''' % (scan_dir
, entry
))
126 # we could consider supporting: gzip module
129 AddMap(scan_dir
, onlinedir
, options
)
131 # Now recursively scan the subdirectories.
132 for subdir
in subdirs
:
133 ScanDirectory(subdir
, options
)
136 def ReadDevhelp(dir, file):
139 for line
in common
.open_text(os
.path
.join(dir, file)):
140 # online must come before chapter/functions
141 if '<chapters' in line
or '<functions' in line
:
143 match
= re
.search(r
' online="([^"]*)"', line
)
145 # Remove trailing non-directory component.
146 onlinedir
= re
.sub(r
'(.*/).*', r
'\1', match
.group(1))
150 def ReadIndex(dir, file):
153 for line
in common
.open_text(os
.path
.join(dir, file)):
154 # ONLINE must come before any ANCHORs
155 if '<ANCHOR' in line
:
157 match
= re
.match(r
'''^<ONLINE\s+href\s*=\s*"([^"]+)"\s*>''', line
)
159 # Remove trailing non-directory component.
160 onlinedir
= re
.sub(r
'''(.*/).*''', r
'\1', match
.groups(1))
164 def AddMap(dir, onlinedir
, options
):
167 package
= os
.path
.split(dir)[1]
168 if options
.dest_dir
!= '' and dir.startswith(options
.dest_dir
):
169 dir = dir[len(options
.dest_dir
) - 1:]
172 log(options
, "On-line location of %s." % onlinedir
)
173 OnlineMap
[package
] = onlinedir
174 RevMap
[onlinedir
] = package
176 log(options
, "No On-line location for %s found" % package
)
178 log(options
, "Local location of $package: " + dir)
179 LocalMap
[package
] = dir
180 RevMap
[dir] = package
183 def RelativizeLocalMap(dirname
, options
):
187 dirname
= os
.path
.realpath(dirname
)
188 prefix
= os
.path
.split(dirname
)
189 for package
, dir in LocalMap
.items():
190 if dir.startswith(prefix
):
191 dir = os
.path
.join("..", dir[len(prefix
):])
192 LocalMap
[package
] = dir
193 log(options
, "Relativizing local location of $package to " + dir)
196 def RebaseReferences(dirname
, options
):
197 for ifile
in sorted(os
.listdir(dirname
)):
198 if ifile
.endswith('.html'):
199 RebaseFile(os
.path
.join(dirname
, ifile
), options
)
202 def RebaseFile(filename
, options
):
203 log(options
, "Fixing file: " + filename
)
204 regex
= re
.compile(r
'''(<a(?:\s+\w+=(?:"[^"]*"|'[^']*'))*\s+href=")([^"]*)(")''',
207 def repl_func(match
):
208 return match
.group(1) + RebaseLink(match
.group(2), options
) + match
.group(3)
210 contents
= common
.open_text(filename
).read()
211 processed
= re
.sub(regex
, repl_func
, contents
)
212 newfilename
= filename
+ '.new'
213 with common
.open_text(newfilename
, 'w') as h
:
216 os
.rename(newfilename
, filename
)
219 def RebaseLink(href
, options
):
220 match
= re
.match(r
'^(.*/)([^/]*)$', href
)
225 dir = origdir
= match
.group(1)
226 file = match
.group(2)
228 package
= RevMap
[dir]
230 match
= re
.match(r
'\.\./([^/]+)', href
)
231 if match
is not None:
232 package
= match
.groups(1)
233 elif options
.aggressive
:
234 match
= re
.search(r
'''([^/]+)/$''', href
)
235 package
= match
.groups(1)
238 if options
.online
and package
in OnlineMap
:
239 dir = OnlineMap
[package
]
240 elif package
in LocalMap
:
241 dir = LocalMap
[package
]
242 href
= os
.path
.join(dir, file)
244 log(options
, "Can't determine package for '%s'" % href
)
247 if origdir
in Mapped
:
248 Mapped
[origdir
][1] += 1
250 Mapped
[origdir
] = [dir, 1]
254 def PrintWhatWeHaveDone():
255 for origdir
in sorted(iterkeys(Mapped
)):
256 info
= Mapped
[origdir
]
257 print(origdir
, "->", info
[0], "(%s)" % info
[1])