#!/usr/bin/env python # Copyright (C) 2011 Igalia S.L. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function from ConfigParser import SafeConfigParser import argparse import codecs import common import glob import gtkdoc import logging import os.path import sys sys.stdout = codecs.getwriter("utf-8")(sys.stdout) sys.stderr = codecs.getwriter("utf-8")(sys.stderr) def configure_logging(verbose): level = logging.DEBUG if verbose else logging.INFO logger = logging.getLogger('gtkdoc') logger.setLevel(level) handler = logging.StreamHandler() handler.setLevel(level) logger.addHandler(handler) if level == logging.DEBUG: handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s')) else: handler.setFormatter(logging.Formatter('%(message)s')) def get_gtkdoc_module_paths(cross_reference_deps): dependent_packages = { 'glib-2.0' : ['glib', 'gobject', 'gio'], 'libsoup-2.4' : ['libsoup-2.4'], 'gdk-pixbuf-2.0': ['gdk-pixbuf'], 'gtk+-3.0' : ['gtk3', 'gdk3'] } paths = [] html_dir = os.path.join('share', 'gtk-doc', 'html') for package, modules in dependent_packages.iteritems(): prefix = common.prefix_of_pkg_config_file(package) if prefix is None: continue for module in modules: paths.append(os.path.join(prefix, html_dir, module)) for local_dep in cross_reference_deps: paths.append(common.build_path('Documentation', local_dep, 'html')) return paths def print_missing_api(generator): missing_api = generator.api_missing_documentation() if not missing_api: return print("\nThe following API are missing documentation:") for api in missing_api: print("\t{0}".format(api)) def files_to_ignore(source_dirs, headers_with_gtkdoc): """ Find files to ignore during documentation generation. We assume that if an implementation file exists for a header with gtkdoc (say webkitfoo.cpp for webkitfoo.h) we shouldn't ignore that file. Currently this holds true for all of the WebKit project. """ implementation_files = list(headers_with_gtkdoc) for header in headers_with_gtkdoc: def add_file_if_exists(file): if os.path.isfile(file): implementation_files.append(os.path.abspath(file)) header_name_without_extension = os.path.splitext(header)[0] add_file_if_exists(header_name_without_extension + ".cpp") add_file_if_exists(header_name_without_extension + ".c") def file_should_be_ignored(file): if os.path.splitext(file)[1] not in ['.h', '.c', '.cpp', '.cc']: return False # These files are ignored anyway. if not os.path.isfile(file): return True return os.path.abspath(file) not in implementation_files all_files = sum([[os.path.join(dir, file) for file in os.listdir(dir)] for dir in source_dirs], []) return filter(file_should_be_ignored, all_files) def get_generator_for_config(config_file, virtual_root, cross_reference_deps = []): if not os.path.isfile(config_file): return None config = SafeConfigParser() config.read(config_file) module_name = config.sections()[0] pkgconfig_file = config.get(module_name, 'pkgconfig_file') if not os.path.isfile(pkgconfig_file): return None source_dirs = config.get(module_name, 'source_dirs').replace(';', ' ').split() headers = [os.path.abspath(f) for f in config.get(module_name, 'headers').replace(';', ' ').split()] return gtkdoc.PkgConfigGTKDoc(pkgconfig_file, { 'decorator': 'WEBKIT_API|WEBKIT_DEPRECATED|WEBKIT_DEPRECATED_FOR\(.+\)', 'deprecation_guard': 'WEBKIT_DISABLE_DEPRECATED', 'library_path': common.library_build_path(), 'virtual_root': virtual_root, 'module_name': module_name, 'namespace': config.get(module_name, 'namespace'), 'doc_dir': config.get(module_name, 'doc_dir'), 'output_dir': common.build_path('Documentation', module_name), 'main_sgml_file': config.get(module_name, 'main_sgml_file'), 'source_dirs': source_dirs, 'headers': headers, 'cflags': " ".join(config.get(module_name, 'cflags').split()), 'cross_reference_deps': get_gtkdoc_module_paths(cross_reference_deps), 'ignored_files': files_to_ignore(source_dirs, headers), }) def generate_doc(generator, skip_html): generator.generate(not skip_html) if generator.saw_warnings: print_missing_api(generator) return generator.saw_warnings def rebase_doc(generator): try: generator.rebase_installed_docs() except Exception: print("Rebase did not happen, likely no documentation is present.") def generate_documentation(generator): if not arguments.rebase: return generate_doc(generator, arguments.skip_html) rebase_doc(generator) return False def prepare_environment_for_gtkdoc_generation(): # We need to add the JavaScriptCore build directory to the PKG_CONFIG_PATH # so that pkgconfig can properly resolve the libjavascriptcore dependency. pkg_config_path = os.environ.get("PKG_CONFIG_PATH") os.environ['PKG_CONFIG_PATH'] = common.build_path('Source', 'JavaScriptCore') if pkg_config_path: os.environ['PKG_CONFIG_PATH'] += ':' + pkg_config_path # Newer versions of glib have deprecated g_type_init, so we need to disable # that warning when running gtkdoc-scanobj by overriding the CFLAGS we use # to compile it. cflags = os.environ.get('CFLAGS', '') cflags += ' -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32' # In non-x86 architectures, when a pointer is cast to (void*) and # back, the compiler thinks that the alignment is random. Since # gtkdoc build is broken at any warning message, it is better to # silence these false positives. cflags += ' -Wno-cast-align' os.environ['CFLAGS'] = cflags # Paths from the GNUmakefile generated configuration files are relative to the build directory. os.chdir(common.build_path()) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Generate gtkdoc for WebKit.') parser.add_argument('-v', '--verbose', action='store_true', help='Whether or not to run in verbose mode.') parser.add_argument('--rebase', action='store_true', help='When specified, run the tool in rebase mode.') parser.add_argument('--skip-html', action='store_true', help='Whether or not to skip HTML generation, which can be slow.') parser.add_argument('--virtual-root', type=str, default='', help='A temporary installation directory which is used as the root ' + \ 'where the actual installation prefix lives; this is mostly ' + \ 'useful for packagers, and should be set to what is given to ' + \ 'make install as DESTDIR.') arguments = parser.parse_args() configure_logging(arguments.verbose) prepare_environment_for_gtkdoc_generation() webkitdom_generator = get_generator_for_config(common.build_path('gtkdoc-webkitdom.cfg'), arguments.virtual_root) if not webkitdom_generator: print("gtkdoc-webkitdom.cfg does not exist! Skipping that documentation") sys.exit(1) saw_warnings = generate_documentation(webkitdom_generator) if saw_warnings: sys.exit(saw_warnings) webkit2_generator = get_generator_for_config(common.build_path('gtkdoc-webkit2gtk.cfg'), arguments.virtual_root, [webkitdom_generator.module_name]) if not webkit2_generator: print("gtkdoc-webkit2gtk.cfg does not exist! Skipping that documentation") sys.exit(1) saw_warnings = generate_documentation(webkit2_generator) sys.exit(saw_warnings)