Convert documentation to AsciiDoc
[lttng-docs.git] / tools / check.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 # THE SOFTWARE.
22
23 from termcolor import colored
24 import lxml.etree as etree
25 import subprocess
26 import argparse
27 import os.path
28 import sys
29 import os
30
31
32 def _perror(msg, exit=True):
33 print('{} {}'.format(colored('Error:', 'red'), colored(msg, 'red', attrs=['bold'])),
34 file=sys.stderr)
35
36 if exit:
37 sys.exit(1)
38
39
40 def _pinfo(msg):
41 print('{} {}'.format(colored('::', 'blue'), colored(msg, 'blue', attrs=['bold'])))
42
43
44 def _get_script_dir():
45 return os.path.dirname(os.path.realpath(__file__))
46
47
48 class _Checker:
49 def __init__(self, infile, verbose):
50 self._infile = infile
51 self._verbose = verbose
52 self._has_error = False
53 self._set_paths()
54 self._pverbose('asciidoc -> DocBook')
55 self._build()
56 self._set_root()
57 self._check()
58
59 @property
60 def has_error(self):
61 return self._has_error
62
63 def _pverbose(self, msg):
64 if self._verbose:
65 _pinfo(msg)
66
67 def _perror(self, msg, fatal=False):
68 self._has_error = True
69 _perror(msg, fatal)
70
71 def _set_paths(self):
72 self._indir = os.path.dirname(self._infile)
73 self._imgexportdir = os.path.join(self._indir, 'images', 'export')
74 self._builddir = os.path.join(_get_script_dir(), 'check', os.path.basename(self._infile))
75 self._outfile = os.path.join(self._builddir, 'out.xml')
76
77 def _build(self):
78 conf = os.path.join(_get_script_dir(), 'asciidoc.check.conf')
79 os.makedirs(self._builddir, mode=0o755, exist_ok=True)
80 cmd = [
81 'asciidoc',
82 '-f', conf,
83 '-b', 'docbook',
84 '-o', self._outfile,
85 ]
86
87 if self._verbose:
88 cmd.append('-v')
89
90 cmd.append(self._infile)
91 res = subprocess.run(cmd)
92
93 if res.returncode != 0:
94 self._perror('asciidoc did not finish successfully', True)
95
96 def _set_root(self):
97 tree = etree.ElementTree(file=self._outfile)
98 self._root = tree.getroot()
99
100 def _check(self):
101 self._pverbose('Checking links')
102 self._check_links()
103 self._pverbose('Checking images')
104 self._check_images()
105
106 def _check_links(self):
107 sections_anchors = self._root.findall('.//section')
108 sections_anchors += self._root.findall('.//anchor')
109 sections_anchors += self._root.findall('.//glossary')
110 links = self._root.findall('.//link')
111 end_ids = set()
112
113 for sa in sections_anchors:
114 end_id = sa.get('id')
115
116 if end_id is None:
117 self._perror('Found a section/anchor with no ID', True)
118
119 end_ids.add(end_id)
120
121 link_ends = set()
122
123 for link in links:
124 end = link.get('linkend')
125
126 if end is None:
127 self._perror('Found a link with no end', True)
128
129 link_ends.add(end)
130
131 has_error = False
132
133 for end in link_ends:
134 if end not in end_ids:
135 self._perror('Link end "{}" does not name a section/anchor ID'.format(end))
136
137 def _check_images(self):
138 image_datas = self._root.findall('.//imagedata')
139
140 for image_data in image_datas:
141 fileref = image_data.get('fileref')
142 path = os.path.join(self._imgexportdir, fileref)
143
144 if not os.path.isfile(path):
145 self._perror('Cannot find image "{}"'.format(fileref))
146
147
148 def _parse_args():
149 parser = argparse.ArgumentParser()
150 parser.add_argument('-v', '--verbose', action='store_true')
151 parser.add_argument('infile')
152 args = parser.parse_args()
153
154 if not os.path.isfile(args.infile):
155 _perror('"{}" is not an existing file'.format(args.infile))
156
157 return args
158
159
160 def _main():
161 args = _parse_args()
162 checker = _Checker(args.infile, args.verbose)
163
164 if checker.has_error:
165 return 1
166
167 print(colored('All good!', 'green', attrs=['bold']))
168
169 return 0
170
171
172 if __name__ == '__main__':
173 sys.exit(_main())
This page took 0.041764 seconds and 4 git commands to generate.