[GTK][CMake] Add support for building with Clang
[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 common
23 import glob
24 import gtkdoc
25 import logging
26 import os.path
27 import sys
28 import webkitdom
29
30 def configure_logging(verbose):
31     level = logging.DEBUG if verbose else logging.INFO
32     logger = logging.getLogger('gtkdoc')
33     logger.setLevel(level)
34     handler = logging.StreamHandler()
35     handler.setLevel(level)
36     logger.addHandler(handler)
37     if level == logging.DEBUG:
38         handler.setFormatter(logging.Formatter('[%(asctime)s]  %(message)s'))
39     else:
40         handler.setFormatter(logging.Formatter('%(message)s'))
41
42 def get_gtkdoc_module_paths(gtk_version):
43     dependent_packages = {
44         'glib-2.0' : ['glib', 'gobject', 'gio'],
45         'libsoup-2.4' : ['libsoup-2.4'],
46         'gdk-pixbuf-2.0': ['gdk-pixbuf']
47     }
48     if gtk_version == 3:
49         dependent_packages.update({'gtk+-3.0' : ['gtk3', 'gdk3']})
50     else:
51         dependent_packages.update({'gtk+-2.0' : ['gtk', 'gdk']})
52
53     paths = []
54     html_dir = os.path.join('share', 'gtk-doc', 'html')
55     for package, modules in dependent_packages.iteritems():
56         prefix = common.prefix_of_pkg_config_file(package)
57         if prefix is None:
58             continue
59         for module in modules:
60             paths.append(os.path.join(prefix, html_dir, module))
61     # This technically is not needed for the GObject DOM bindings documentation itself,
62     # but adding it doesn't hurt and allows us to avoid a check here.
63     paths.append(common.build_path('Documentation', 'webkitdomgtk', 'html'))
64     return paths
65
66 def print_missing_api(generator):
67     missing_api = generator.api_missing_documentation()
68     if not missing_api:
69         return
70     print("\nThe following API are missing documentation:")
71     for api in missing_api:
72         print("\t{0}".format(api))
73
74 def files_to_ignore(source_dirs, headers_with_gtkdoc):
75     """
76     Find files to ignore during documentation generation. We assume that if an
77     implementation file exists for a header with gtkdoc (say webkitfoo.cpp for
78     webkitfoo.h) we shouldn't ignore that file. Currently this holds true for all
79     of the WebKit project.
80     """
81     implementation_files = list(headers_with_gtkdoc)
82     for header in headers_with_gtkdoc:
83         def add_file_if_exists(file):
84             if os.path.isfile(file):
85                 implementation_files.append(os.path.abspath(file))
86         header_name_without_extension = os.path.splitext(header)[0]
87         add_file_if_exists(header_name_without_extension + ".cpp")
88         add_file_if_exists(header_name_without_extension + ".c")
89
90     def file_should_be_ignored(file):
91         if os.path.splitext(file)[1] not in ['.h', '.c', '.cpp', '.cc']:
92             return False # These files are ignored anyway.
93         if not os.path.isfile(file):
94             return False
95         return os.path.abspath(file) not in implementation_files
96
97     all_files = sum([[os.path.join(dir, file) for file in os.listdir(dir)] for dir in source_dirs], [])
98     return filter(file_should_be_ignored, all_files)
99
100 def get_generator_for_config(config_file, virtual_root):
101     if not os.path.isfile(config_file):
102         return None
103
104     config = SafeConfigParser()
105     config.read(config_file)
106     module_name = config.sections()[0]
107     pkgconfig_file = config.get(module_name, 'pkgconfig_file')
108     gtk_version = common.gtk_version_of_pkg_config_file(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         'source_dirs': source_dirs,
125         'headers': headers,
126         'cflags': " ".join(config.get(module_name, 'cflags').split()),
127         'cross_reference_deps': get_gtkdoc_module_paths(gtk_version),
128         'ignored_files': files_to_ignore(source_dirs, headers),
129     })
130
131 def generate_doc(generator, skip_html):
132     print("\nGenerating {0} documentation...".format(generator.module_name))
133     generator.generate(not skip_html)
134     if generator.saw_warnings:
135         print_missing_api(generator)
136     return generator.saw_warnings
137
138 def rebase_doc(generator):
139     print("\nRebasing {0} documentation...".format(generator.module_name))
140     try:
141         generator.rebase_installed_docs()
142     except Exception:
143         print("Rebase did not happen, likely no documentation is present.")
144
145 def generate_documentation_for_config(config_file):
146     generator = get_generator_for_config(config_file, arguments.virtual_root)
147     if not generator:
148         print("{0} does not exist! Skipping that documentation.".format(os.path.basename(config_file)))
149         return
150
151     if not arguments.rebase:
152         generate_doc(generator, arguments.skip_html)
153     else:
154         rebase_doc(generator)
155     return generator.saw_warnings
156
157 def prepare_environment_for_gtkdoc_generation():
158     # We need to add the JavaScriptCore build directory to the PKG_CONFIG_PATH
159     # so that pkgconfig can properly resolve the libjavascriptcore dependency.
160     pkg_config_path = os.environ.get("PKG_CONFIG_PATH")
161     os.environ['PKG_CONFIG_PATH'] = common.build_path('Source', 'JavaScriptCore')
162     if pkg_config_path:
163         os.environ['PKG_CONFIG_PATH'] += ':' + pkg_config_path
164
165     # Newer versions of glib have deprecated g_type_init, so we need to disable
166     # that warning when running gtkdoc-scanobj by overriding the CFLAGS we use
167     # to compile it.
168     cflags = os.environ.get('CFLAGS', '')
169     cflags += ' -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'
170     os.environ['CFLAGS'] = cflags
171
172     # Paths from the GNUmakefile generated configuration files are relative to the build directory.
173     os.chdir(common.build_path())
174
175 if __name__ == "__main__":
176     parser = argparse.ArgumentParser(description='Generate gtkdoc for WebKit.')
177     parser.add_argument('-v', '--verbose', action='store_true',
178                         help='Whether or not to run in verbose mode.')
179     parser.add_argument('--rebase', action='store_true',
180                         help='When specified, run the tool in rebase mode.')
181     parser.add_argument('--skip-html', action='store_true',
182                         help='Whether or not to skip HTML generation, which can be slow.')
183     parser.add_argument('--virtual-root', type=str, default='',
184                         help='A temporary installation directory which is used as the root ' + \
185                              'where the actual installation prefix lives; this is mostly ' + \
186                              'useful for packagers, and should be set to what is given to ' + \
187                              'make install as DESTDIR.')
188
189     arguments = parser.parse_args()
190     configure_logging(arguments.verbose)
191
192     prepare_environment_for_gtkdoc_generation()
193
194     webkitdom.write_doc_files()
195     generate_documentation_for_config(common.build_path('gtkdoc-webkitdom.cfg'))
196
197     saw_webkit1_warnings = generate_documentation_for_config(common.build_path('gtkdoc-webkitgtk.cfg'))
198     saw_webkit2_warnings = generate_documentation_for_config(common.build_path('gtkdoc-webkit2gtk.cfg'))
199
200     sys.exit(saw_webkit1_warnings or saw_webkit2_warnings)