[GTK] gtkdoc does not appear in DevHelp
[WebKit-https.git] / Tools / gtk / generate-gtkdoc
1 #!/usr/bin/env python
2 # Copyright (C) 2011 Igalia S.L.
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
18 from __future__ import print_function
19 from ConfigParser import SafeConfigParser
20
21 import argparse
22 import codecs
23 import common
24 import glob
25 import gtkdoc
26 import logging
27 import os.path
28 import sys
29 import webkitdom
30
31 sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
32 sys.stderr = codecs.getwriter("utf-8")(sys.stderr)
33
34 def configure_logging(verbose):
35     level = logging.DEBUG if verbose else logging.INFO
36     logger = logging.getLogger('gtkdoc')
37     logger.setLevel(level)
38     handler = logging.StreamHandler()
39     handler.setLevel(level)
40     logger.addHandler(handler)
41     if level == logging.DEBUG:
42         handler.setFormatter(logging.Formatter('[%(asctime)s]  %(message)s'))
43     else:
44         handler.setFormatter(logging.Formatter('%(message)s'))
45
46 def get_gtkdoc_module_paths(cross_reference_deps):
47     dependent_packages = {
48         'glib-2.0' : ['glib', 'gobject', 'gio'],
49         'libsoup-2.4' : ['libsoup-2.4'],
50         'gdk-pixbuf-2.0': ['gdk-pixbuf'],
51         'gtk+-3.0' : ['gtk3', 'gdk3']
52     }
53
54     paths = []
55     html_dir = os.path.join('share', 'gtk-doc', 'html')
56     for package, modules in dependent_packages.iteritems():
57         prefix = common.prefix_of_pkg_config_file(package)
58         if prefix is None:
59             continue
60         for module in modules:
61             paths.append(os.path.join(prefix, html_dir, module))
62
63     for local_dep in cross_reference_deps:
64         paths.append(common.build_path('Documentation', local_dep, 'html'))
65     return paths
66
67 def print_missing_api(generator):
68     missing_api = generator.api_missing_documentation()
69     if not missing_api:
70         return
71     print("\nThe following API are missing documentation:")
72     for api in missing_api:
73         print("\t{0}".format(api))
74
75 def files_to_ignore(source_dirs, headers_with_gtkdoc):
76     """
77     Find files to ignore during documentation generation. We assume that if an
78     implementation file exists for a header with gtkdoc (say webkitfoo.cpp for
79     webkitfoo.h) we shouldn't ignore that file. Currently this holds true for all
80     of the WebKit project.
81     """
82     implementation_files = list(headers_with_gtkdoc)
83     for header in headers_with_gtkdoc:
84         def add_file_if_exists(file):
85             if os.path.isfile(file):
86                 implementation_files.append(os.path.abspath(file))
87         header_name_without_extension = os.path.splitext(header)[0]
88         add_file_if_exists(header_name_without_extension + ".cpp")
89         add_file_if_exists(header_name_without_extension + ".c")
90
91     def file_should_be_ignored(file):
92         if os.path.splitext(file)[1] not in ['.h', '.c', '.cpp', '.cc']:
93             return False # These files are ignored anyway.
94         if not os.path.isfile(file):
95             return True
96         return os.path.abspath(file) not in implementation_files
97
98     all_files = sum([[os.path.join(dir, file) for file in os.listdir(dir)] for dir in source_dirs], [])
99     return filter(file_should_be_ignored, all_files)
100
101 def get_generator_for_config(config_file, virtual_root, cross_reference_deps = []):
102     if not os.path.isfile(config_file):
103         return None
104
105     config = SafeConfigParser()
106     config.read(config_file)
107     module_name = config.sections()[0]
108     pkgconfig_file = config.get(module_name, 'pkgconfig_file')
109
110     if not os.path.isfile(pkgconfig_file):
111         return None
112
113     source_dirs = config.get(module_name, 'source_dirs').replace(';', ' ').split()
114     headers = [os.path.abspath(f) for f in config.get(module_name, 'headers').replace(';', ' ').split()]
115     return gtkdoc.PkgConfigGTKDoc(pkgconfig_file, {
116         'decorator': 'WEBKIT_API|WEBKIT_DEPRECATED|WEBKIT_DEPRECATED_FOR\(.+\)',
117         'deprecation_guard': 'WEBKIT_DISABLE_DEPRECATED',
118         'library_path': common.library_build_path(),
119         'virtual_root': virtual_root,
120         'module_name': module_name,
121         'namespace': config.get(module_name, 'namespace'),
122         'doc_dir': config.get(module_name, 'doc_dir'),
123         'output_dir': common.build_path('Documentation', module_name),
124         'main_sgml_file': config.get(module_name, 'main_sgml_file'),
125         'source_dirs': source_dirs,
126         'headers': headers,
127         'cflags': " ".join(config.get(module_name, 'cflags').split()),
128         'cross_reference_deps': get_gtkdoc_module_paths(cross_reference_deps),
129         'ignored_files': files_to_ignore(source_dirs, headers),
130     })
131
132 def generate_doc(generator, skip_html):
133     print("\nGenerating {0} documentation...".format(generator.module_name))
134     generator.generate(not skip_html)
135     if generator.saw_warnings:
136         print_missing_api(generator)
137     return generator.saw_warnings
138
139 def rebase_doc(generator):
140     print("\nRebasing {0} documentation...".format(generator.module_name))
141     try:
142         generator.rebase_installed_docs()
143     except Exception:
144         print("Rebase did not happen, likely no documentation is present.")
145
146 def generate_documentation(generator):
147     if not arguments.rebase:
148         return generate_doc(generator, arguments.skip_html)
149
150     rebase_doc(generator)
151     return False
152
153 def prepare_environment_for_gtkdoc_generation():
154     # We need to add the JavaScriptCore build directory to the PKG_CONFIG_PATH
155     # so that pkgconfig can properly resolve the libjavascriptcore dependency.
156     pkg_config_path = os.environ.get("PKG_CONFIG_PATH")
157     os.environ['PKG_CONFIG_PATH'] = common.build_path('Source', 'JavaScriptCore')
158     if pkg_config_path:
159         os.environ['PKG_CONFIG_PATH'] += ':' + pkg_config_path
160
161     # Newer versions of glib have deprecated g_type_init, so we need to disable
162     # that warning when running gtkdoc-scanobj by overriding the CFLAGS we use
163     # to compile it.
164     cflags = os.environ.get('CFLAGS', '')
165     cflags += ' -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'
166
167     # In non-x86 architectures, when a pointer is cast to (void*) and
168     # back, the compiler thinks that the alignment is random. Since
169     # gtkdoc build is broken at any warning message, it is better to
170     # silence these false positives.
171     cflags += ' -Wno-cast-align'
172     os.environ['CFLAGS'] = cflags
173
174     # Paths from the GNUmakefile generated configuration files are relative to the build directory.
175     os.chdir(common.build_path())
176
177 if __name__ == "__main__":
178     parser = argparse.ArgumentParser(description='Generate gtkdoc for WebKit.')
179     parser.add_argument('-v', '--verbose', action='store_true',
180                         help='Whether or not to run in verbose mode.')
181     parser.add_argument('--rebase', action='store_true',
182                         help='When specified, run the tool in rebase mode.')
183     parser.add_argument('--skip-html', action='store_true',
184                         help='Whether or not to skip HTML generation, which can be slow.')
185     parser.add_argument('--virtual-root', type=str, default='',
186                         help='A temporary installation directory which is used as the root ' + \
187                              'where the actual installation prefix lives; this is mostly ' + \
188                              'useful for packagers, and should be set to what is given to ' + \
189                              'make install as DESTDIR.')
190
191     arguments = parser.parse_args()
192     configure_logging(arguments.verbose)
193
194     prepare_environment_for_gtkdoc_generation()
195
196     webkitdom_generator = get_generator_for_config(common.build_path('gtkdoc-webkitdom.cfg'), arguments.virtual_root)
197     if not webkitdom_generator:
198         print("gtkdoc-webkitdom.cfg does not exist! Skipping that documentation")
199         sys.exit(1)
200     webkitdom.write_doc_files(webkitdom_generator.module_name)
201     saw_warnings = generate_documentation(webkitdom_generator)
202     if saw_warnings:
203         sys.exit(saw_warnings)
204
205     webkit2_generator = get_generator_for_config(common.build_path('gtkdoc-webkit2gtk.cfg'), arguments.virtual_root, [webkitdom_generator.module_name])
206     if not webkit2_generator:
207         print("gtkdoc-webkit2gtk.cfg does not exist! Skipping that documentation")
208         sys.exit(1)
209     saw_warnings = generate_documentation(webkit2_generator)
210
211     sys.exit(saw_warnings)