Convert documentation to AsciiDoc
[lttng-docs.git] / tools / check.py
CommitLineData
f0287ae1
PP
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
23from termcolor import colored
24import lxml.etree as etree
25import subprocess
26import argparse
27import os.path
28import sys
29import os
30
31
32def _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
40def _pinfo(msg):
41 print('{} {}'.format(colored('::', 'blue'), colored(msg, 'blue', attrs=['bold'])))
42
43
44def _get_script_dir():
45 return os.path.dirname(os.path.realpath(__file__))
46
47
48class _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
148def _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
160def _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
172if __name__ == '__main__':
173 sys.exit(_main())
This page took 0.028053 seconds and 4 git commands to generate.