| #!/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 glob |
| import gtkdoc |
| import logging |
| import os.path |
| import sys |
| |
| top_level_directory = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..')) |
| sys.path.insert(0, os.path.join(top_level_directory, 'Tools', 'glib')) |
| import common |
| |
| 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(filename): |
| for dir in source_dirs: |
| file = os.path.join(dir, filename) |
| if os.path.isfile(file): |
| implementation_files.append(os.path.abspath(file)) |
| header_basename_without_extension = os.path.splitext(os.path.basename(header))[0] |
| add_file_if_exists(header_basename_without_extension + ".cpp") |
| add_file_if_exists(header_basename_without_extension + "Gtk.cpp") |
| add_file_if_exists(header_basename_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': config.get(module_name, 'decorator'), |
| 'deprecation_guard': config.get(module_name, 'deprecation_guard'), |
| '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() |
| |
| jsc_generator = get_generator_for_config(common.build_path('gtkdoc-jsc-glib.cfg'), arguments.virtual_root) |
| if not jsc_generator: |
| print("gtkdoc-jsc-glib.cfg does not exist! Skipping that documentation") |
| sys.exit(1) |
| saw_warnings = generate_documentation(jsc_generator) |
| if saw_warnings: |
| sys.exit(saw_warnings) |
| |
| webkitdom_generator = get_generator_for_config(common.build_path('gtkdoc-webkitdom.cfg'), arguments.virtual_root, [jsc_generator.module_name]) |
| 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, jsc_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) |