README: add some build instructions
[gtk-doc.git] / gtkdoc / check.py
blobb410910c04e09995326955ecf97f2c33ea85cf39
1 # -*- python; coding: utf-8 -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 2007 David Nečas
5 # 2007-2017 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 The check tool runs various tests on built documentation and outputs test
24 results. Can be run druring make check, by adding this to the documentations
25 Makefile.am: TESTS = $(GTKDOC_CHECK).
26 """
28 import os
29 import re
30 from glob import glob
32 from . import common
35 class FileFormatError(Exception):
36 pass
39 def grep(regexp, lines, what):
40 pattern = re.compile(regexp)
41 for line in lines:
42 for match in re.finditer(pattern, line):
43 return match.group(1)
44 raise FileFormatError(what)
47 def check_empty(filename):
48 with open(filename, "rb") as f:
49 count = sum(1 for line in f if line.strip())
50 return count
53 def read_file(filename):
54 with open(filename, 'r', encoding='utf-8') as f:
55 return f.read().splitlines()
58 def check_includes(filename):
59 # Check that each XML file in the xml directory is included in doc_main_file
60 lines = read_file(filename)
61 num_missing = 0
62 for include in glob('xml/*.xml'):
63 try:
64 next(line for line in lines if include in line)
65 except StopIteration:
66 num_missing += 1
67 print('%s:1:E: doesn\'t appear to include "%s"' % (filename, include))
69 return num_missing
72 def get_variable(env, lines, variable):
73 value = env.get(variable,
74 grep(r'^\s*' + variable + r'\s*=\s*(\S+)', lines, variable))
75 return value
78 def run_tests(workdir, doc_module, doc_main_file):
79 checks = 4
81 print('Running suite(s): gtk-doc-' + doc_module)
83 # Test #1
84 statusfilename = os.path.join(workdir, doc_module + '-undocumented.txt')
85 statusfile = read_file(statusfilename)
86 try:
87 undocumented = int(grep(r'^(\d+)\s+not\s+documented\.\s*$',
88 statusfile, 'number of undocumented symbols'))
89 incomplete = int(grep(r'^(\d+)\s+symbols?\s+incomplete\.\s*$',
90 statusfile, 'number of incomplete symbols'))
91 except FileFormatError as e:
92 print('Cannot find %s in %s' % (str(e), statusfilename))
93 return checks # consider all failed
95 total = undocumented + incomplete
96 if total:
97 print(doc_module + '-undocumented.txt:1:E: %d undocumented or incomplete symbols' % total)
99 # Test #2
100 undeclared = check_empty(os.path.join(workdir, doc_module + '-undeclared.txt'))
101 if undeclared:
102 print(doc_module + '-undeclared.txt:1:E: %d undeclared symbols\n' % undeclared)
104 # Test #3
105 unused = check_empty(os.path.join(workdir, doc_module + '-unused.txt'))
106 if unused:
107 print(doc_module + '-unused.txt:1:E: %d unused documentation entries\n' % unused)
109 # Test #4
110 missing_includes = check_includes(os.path.join(workdir, doc_main_file))
112 # Test Summary
113 failed = (total > 0) + (undeclared != 0) + (unused != 0) + (missing_includes != 0)
114 rate = 100.0 * (checks - failed) / checks
115 print("%.1f%%: Checks %d, Failures: %d" % (rate, checks, failed))
116 return failed
119 def run(options=None):
120 """Runs the tests.
122 Returns:
123 int: a system exit code.
126 # Get parameters from test env, if not there try to grab them from the makefile
127 # We like Makefile.am more but builddir does not necessarily contain one.
128 makefilename = 'Makefile.am'
129 if not os.path.exists(makefilename):
130 makefilename = 'Makefile'
131 makefile = []
132 try:
133 makefile = read_file(makefilename)
134 except (OSError, IOError):
135 pass
137 # For historic reasons tests are launched in srcdir
138 workdir = os.environ.get('BUILDDIR', None)
139 if not workdir:
140 workdir = '.'
142 try:
143 doc_module = get_variable(os.environ, makefile, 'DOC_MODULE')
144 doc_main_file = get_variable(os.environ, makefile, 'DOC_MAIN_SGML_FILE')
145 except FileFormatError as e:
146 print('Cannot find %s in %s' % (str(e), makefilename))
147 return 1
149 doc_main_file = doc_main_file.replace('$(DOC_MODULE)', doc_module)
151 return run_tests(workdir, doc_module, doc_main_file)