blob: efc74d2a9267aa24a5d5aba4abb5b2ef3ac22cc7 [file] [log] [blame]
#!/usr/bin/env python3
# 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
import argparse
import codecs
import glob
import gtkdoc
import logging
import os.path
import sys
if sys.version_info < (3, 2):
from ConfigParser import SafeConfigParser as ConfigParser
else:
from configparser import ConfigParser
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
if sys.version_info.major == 2:
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']
}
if arguments.gtk:
dependent_packages['gdk-pixbuf-2.0'] = ['gdk-pixbuf']
dependent_packages['gtk+-3.0'] = ['gtk3', 'gdk3']
paths = []
html_dir = os.path.join('share', 'gtk-doc', 'html')
for package, modules in dependent_packages.items():
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 = ConfigParser()
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())
def build_gtkdoc_for_wpe(arguments):
webextensions_generator = get_generator_for_config(common.build_path('gtkdoc-webextensions.cfg'), arguments.virtual_root)
if not webextensions_generator:
print("gtkdoc-webextensions.cfg does not exist! Skipping that documentation")
sys.exit(1)
saw_warnings = generate_documentation(webextensions_generator)
if saw_warnings:
sys.exit(saw_warnings)
wpe_generator = get_generator_for_config(common.build_path('gtkdoc-wpe.cfg'), arguments.virtual_root)
if not wpe_generator:
print("gtkdoc-wpe.cfg does not exist! Skipping that documentation")
sys.exit(1)
saw_warnings = generate_documentation(wpe_generator)
sys.exit(saw_warnings)
def build_gtkdoc_for_wkgtk(arguments):
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)
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.')
parser.add_argument('--gtk', action='store_true',
help='Build documentation for WebKitGTK')
parser.add_argument('--wpe', action='store_true',
help='Build documentation for WPE')
arguments = parser.parse_args()
configure_logging(arguments.verbose)
prepare_environment_for_gtkdoc_generation()
if arguments.wpe:
build_gtkdoc_for_wpe(arguments)
elif arguments.gtk:
build_gtkdoc_for_wkgtk(arguments)
else:
print('Please choose a WebKit port with --gtk or --wpe')
sys.exit(1)