Add check and check-VERSION targets
[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')
8d140568
PP
110 sections_anchors += self._root.findall('.//important')
111 sections_anchors += self._root.findall('.//tip')
112 sections_anchors += self._root.findall('.//caution')
113 sections_anchors += self._root.findall('.//warning')
114 sections_anchors += self._root.findall('.//note')
f0287ae1
PP
115 links = self._root.findall('.//link')
116 end_ids = set()
117
118 for sa in sections_anchors:
119 end_id = sa.get('id')
120
8d140568 121 if sa.tag in ('section', 'anchor') and end_id is None:
f0287ae1
PP
122 self._perror('Found a section/anchor with no ID', True)
123
124 end_ids.add(end_id)
125
126 link_ends = set()
127
128 for link in links:
129 end = link.get('linkend')
130
131 if end is None:
132 self._perror('Found a link with no end', True)
133
134 link_ends.add(end)
135
136 has_error = False
137
138 for end in link_ends:
139 if end not in end_ids:
140 self._perror('Link end "{}" does not name a section/anchor ID'.format(end))
141
142 def _check_images(self):
143 image_datas = self._root.findall('.//imagedata')
144
145 for image_data in image_datas:
146 fileref = image_data.get('fileref')
147 path = os.path.join(self._imgexportdir, fileref)
148
149 if not os.path.isfile(path):
150 self._perror('Cannot find image "{}"'.format(fileref))
151
152
153def _parse_args():
154 parser = argparse.ArgumentParser()
155 parser.add_argument('-v', '--verbose', action='store_true')
156 parser.add_argument('infile')
157 args = parser.parse_args()
158
159 if not os.path.isfile(args.infile):
160 _perror('"{}" is not an existing file'.format(args.infile))
161
162 return args
163
164
165def _main():
166 args = _parse_args()
167 checker = _Checker(args.infile, args.verbose)
168
169 if checker.has_error:
170 return 1
171
f9167c82 172 print(colored('{}: All good!'.format(args.infile), 'green', attrs=['bold']))
f0287ae1
PP
173
174 return 0
175
176
177if __name__ == '__main__':
178 sys.exit(_main())
This page took 0.029758 seconds and 4 git commands to generate.