mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
- Generated files are now created when running cef_create_projects or the new version_manager.py tool. These files are still created in the cef/ source tree (same location as before) but Git ignores them due to the generated .gitignore file. - API hashes are committed to Git as a new cef_api_versions.json file. This file is used for both code generation and CEF version calculation (replacing the previous usage of cef_api_hash.h for this purpose). It will be updated by the CEF admin before merging breaking API changes upstream. - As an added benefit to the above, contributor PRs will no longer contain generated code that is susceptible to frequent merge conflicts. - From a code generation perspective, the main difference is that we now use versioned structs (e.g. cef_browser_0_t instead of cef_browser_t) on the libcef (dll/framework) side. Most of the make_*.py tool changes are related to supporting this. - From the client perspective, you can now define CEF_API_VERSION in the project configuration (or get CEF_EXPERIMENTAL by default). This define will change the API exposed in CEF’s include/ and include/capi header files. All client-side targets including libcef_dll_wrapper will need be recompiled when changing this define. - Examples of the new API-related define usage are provided in cef_api_version_test.h, api_version_test_impl.cc and api_version_unittest.cc. To test: - Run `ceftests --gtest_filter=ApiVersionTest.*` - Add `cef_api_version=13300` to GN_DEFINES. Re-run configure, build and ceftests steps. - Repeat with 13301, 13302, 13303 (all supported test versions).
356 lines
12 KiB
Python
356 lines
12 KiB
Python
# Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
|
|
# reserved. Use of this source code is governed by a BSD-style license that
|
|
# can be found in the LICENSE file.
|
|
|
|
from __future__ import absolute_import
|
|
from cef_parser import obj_header
|
|
from cef_version import VersionFormatter
|
|
from clang_util import clang_format
|
|
from file_util import *
|
|
import hashlib
|
|
from make_capi_header import write_capi_header
|
|
from make_capi_versions_header import write_capi_versions_header
|
|
from make_cpptoc_header import write_cpptoc_header
|
|
from make_cpptoc_impl import write_cpptoc_impl
|
|
from make_ctocpp_header import write_ctocpp_header
|
|
from make_ctocpp_impl import write_ctocpp_impl
|
|
from make_gypi_file import write_gypi_file
|
|
from make_libcef_dll_dylib_impl import write_libcef_dll_dylib_impl
|
|
from make_wrapper_types_header import write_wrapper_types_header
|
|
from optparse import OptionParser
|
|
import sys
|
|
|
|
FILE_HEADER = """#
|
|
# This file was generated by the CEF translator tool and should not edited
|
|
# by hand.
|
|
#
|
|
# $hash=$$HASH$$$
|
|
#
|
|
|
|
"""
|
|
|
|
|
|
def _write_version():
|
|
return FILE_HEADER + VersionFormatter().get_version_string()
|
|
|
|
|
|
def _write_gitignore(gitignore, gitignore_file, root_dir):
|
|
contents = FILE_HEADER
|
|
|
|
in_file = gitignore_file + '.in'
|
|
if os.path.isfile(in_file):
|
|
contents += read_file(in_file)
|
|
|
|
# Include ourselves in generated files.
|
|
gitignore.append(gitignore_file)
|
|
|
|
root_dir_len = len(root_dir)
|
|
contents += '\n'.join(
|
|
[p[root_dir_len:].replace('\\', '/') for p in sorted(gitignore)])
|
|
|
|
return contents
|
|
|
|
|
|
def _update_file(file, newcontents, customized, force, clean, backup,
|
|
gitignore):
|
|
""" Replaces the contents of |file| with |newcontents| if necessary. """
|
|
if clean:
|
|
if not customized:
|
|
return 1 if remove_file(file, quiet=False) else 0
|
|
print('File %s has customizations and will not be removed' % file)
|
|
return 0
|
|
|
|
if not customized and not gitignore is None:
|
|
gitignore.append(file)
|
|
|
|
oldcontents = ''
|
|
oldhash = ''
|
|
|
|
if newcontents[-1:] != "\n":
|
|
# Add newline at end of file.
|
|
newcontents += "\n"
|
|
|
|
# clang-format is slow so we don't want to apply it if the pre-formatted
|
|
# content hasn't changed. To check for changes we embed a hash of the pre-
|
|
# formatted content in the resulting file.
|
|
hash_start = "$hash="
|
|
hash_end = "$"
|
|
hash_token = "$$HASH$$"
|
|
|
|
if not force and path_exists(file):
|
|
oldcontents = read_file(file)
|
|
|
|
# Extract the existing hash.
|
|
start = oldcontents.find(hash_start)
|
|
if start > 0:
|
|
end = oldcontents.find(hash_end, start + len(hash_start))
|
|
if end > 0:
|
|
oldhash = oldcontents[start + len(hash_start):end]
|
|
|
|
# Compute the new hash.
|
|
newhash = hashlib.sha1(newcontents.encode('utf-8')).hexdigest()
|
|
|
|
if oldhash == newhash:
|
|
# Pre-formatted contents have not changed.
|
|
return 0
|
|
|
|
newcontents = newcontents.replace(hash_token, newhash, 1)
|
|
|
|
# Apply clang-format for C/C++ files. This is slow, so we only do it for
|
|
# customized files.
|
|
if customized and os.path.splitext(file)[1][1:] in ('c', 'cc', 'cpp', 'h'):
|
|
result = clang_format(file, newcontents)
|
|
if result != None:
|
|
newcontents = result
|
|
else:
|
|
raise Exception("Call to clang-format failed for %s" % file)
|
|
|
|
if backup and oldcontents != '':
|
|
backup_file(file)
|
|
|
|
filedir = os.path.split(file)[0]
|
|
if not os.path.isdir(filedir):
|
|
make_dir(filedir)
|
|
|
|
print('Writing file %s' % file)
|
|
write_file(file, newcontents)
|
|
return 1
|
|
|
|
|
|
def translate(cef_dir,
|
|
force=False,
|
|
clean=False,
|
|
backup=False,
|
|
verbose=False,
|
|
selected_classes=None):
|
|
# determine the paths
|
|
root_dir = os.path.abspath(cef_dir)
|
|
cpp_header_dir = os.path.join(root_dir, 'include')
|
|
cpp_header_test_dir = os.path.join(cpp_header_dir, 'test')
|
|
cpp_header_views_dir = os.path.join(cpp_header_dir, 'views')
|
|
capi_header_dir = os.path.join(cpp_header_dir, 'capi')
|
|
libcef_dll_dir = os.path.join(root_dir, 'libcef_dll')
|
|
cpptoc_global_impl = os.path.join(libcef_dll_dir, 'libcef_dll.cc')
|
|
ctocpp_global_impl = os.path.join(libcef_dll_dir, 'wrapper',
|
|
'libcef_dll_wrapper.cc')
|
|
wrapper_types_header = os.path.join(libcef_dll_dir, 'wrapper_types.h')
|
|
cpptoc_dir = os.path.join(libcef_dll_dir, 'cpptoc')
|
|
ctocpp_dir = os.path.join(libcef_dll_dir, 'ctocpp')
|
|
gypi_file = os.path.join(root_dir, 'cef_paths.gypi')
|
|
libcef_dll_dylib_impl = os.path.join(libcef_dll_dir, 'wrapper',
|
|
'libcef_dll_dylib.cc')
|
|
version_file = os.path.join(root_dir, 'VERSION.stamp')
|
|
gitignore_file = os.path.join(root_dir, '.gitignore')
|
|
|
|
# make sure the header directory exists
|
|
if not path_exists(cpp_header_dir):
|
|
sys.stderr.write('ERROR: Directory ' + cpp_header_dir +
|
|
' does not exist.\n')
|
|
sys.exit(1)
|
|
|
|
# create the header object
|
|
if verbose:
|
|
print('Parsing C++ headers from ' + cpp_header_dir + '...')
|
|
header = obj_header()
|
|
|
|
# add include files to be processed
|
|
header.set_root_directory(cpp_header_dir)
|
|
excluded_files = [
|
|
'cef_api_hash.h', 'cef_application_mac.h', 'cef_version_info.h'
|
|
]
|
|
header.add_directory(cpp_header_dir, excluded_files)
|
|
header.add_directory(cpp_header_test_dir)
|
|
header.add_directory(cpp_header_views_dir)
|
|
|
|
# Track the number of files that were written.
|
|
writect = 0
|
|
|
|
# Track files that are not customized.
|
|
gitignore = []
|
|
|
|
debug_string = ''
|
|
|
|
try:
|
|
|
|
# output the C API header
|
|
if verbose:
|
|
print('In C API header directory ' + capi_header_dir + '...')
|
|
filenames = sorted(header.get_file_names())
|
|
for filename in filenames:
|
|
if verbose:
|
|
print('Generating ' + filename + ' C API headers...')
|
|
debug_string = 'CAPI header for ' + filename
|
|
writect += _update_file(*write_capi_header(header, capi_header_dir,
|
|
filename), False, force, clean,
|
|
backup, gitignore)
|
|
debug_string = 'CAPI versions header for ' + filename
|
|
writect += _update_file(*write_capi_versions_header(
|
|
header, capi_header_dir, filename), False, force, clean, backup,
|
|
gitignore)
|
|
|
|
# output the wrapper types header
|
|
if verbose:
|
|
print('Generating wrapper types header...')
|
|
debug_string = 'wrapper types header'
|
|
writect += _update_file(*write_wrapper_types_header(
|
|
header, wrapper_types_header), False, force, clean, backup, gitignore)
|
|
|
|
# build the list of classes to parse
|
|
allclasses = header.get_class_names()
|
|
if not selected_classes is None:
|
|
for cls in selected_classes:
|
|
if not cls in allclasses:
|
|
sys.stderr.write('ERROR: Unknown class: %s\n' % cls)
|
|
sys.exit(1)
|
|
classes = selected_classes
|
|
else:
|
|
classes = allclasses
|
|
|
|
classes = sorted(classes)
|
|
|
|
# output CppToC global file
|
|
if verbose:
|
|
print('Generating CppToC global implementation...')
|
|
debug_string = 'CppToC global implementation'
|
|
writect += _update_file(*write_cpptoc_impl(
|
|
header, None, cpptoc_global_impl), force, clean, backup, gitignore)
|
|
|
|
# output CToCpp global file
|
|
if verbose:
|
|
print('Generating CToCpp global implementation...')
|
|
debug_string = 'CToCpp global implementation'
|
|
writect += _update_file(*write_ctocpp_impl(
|
|
header, None, ctocpp_global_impl), force, clean, backup, gitignore)
|
|
|
|
# output CppToC class files
|
|
if verbose:
|
|
print('In CppToC directory ' + cpptoc_dir + '...')
|
|
for cls in classes:
|
|
if verbose:
|
|
print('Generating ' + cls + 'CppToC class header...')
|
|
debug_string = 'CppToC class header for ' + cls
|
|
writect += _update_file(*write_cpptoc_header(header, cls, cpptoc_dir),
|
|
False, force, clean, backup, gitignore)
|
|
|
|
if verbose:
|
|
print('Generating ' + cls + 'CppToC class implementation...')
|
|
debug_string = 'CppToC class implementation for ' + cls
|
|
writect += _update_file(*write_cpptoc_impl(header, cls, cpptoc_dir),
|
|
force, clean, backup, gitignore)
|
|
|
|
# output CppToC class files
|
|
if verbose:
|
|
print('In CToCpp directory ' + ctocpp_dir + '...')
|
|
for cls in classes:
|
|
if verbose:
|
|
print('Generating ' + cls + 'CToCpp class header...')
|
|
debug_string = 'CToCpp class header for ' + cls
|
|
writect += _update_file(*write_ctocpp_header(header, cls, ctocpp_dir),
|
|
False, force, clean, backup, gitignore)
|
|
if verbose:
|
|
print('Generating ' + cls + 'CToCpp class implementation...')
|
|
debug_string = 'CToCpp class implementation for ' + cls
|
|
writect += _update_file(*write_ctocpp_impl(header, cls, ctocpp_dir),
|
|
force, clean, backup, gitignore)
|
|
|
|
# output the gypi file
|
|
if verbose:
|
|
print('Generating ' + gypi_file + ' file...')
|
|
debug_string = gypi_file
|
|
writect += _update_file(*write_gypi_file(header, gypi_file), False, force,
|
|
clean, backup, gitignore)
|
|
|
|
# output the libcef dll dylib file
|
|
if verbose:
|
|
print('Generating ' + libcef_dll_dylib_impl + ' file...')
|
|
debug_string = libcef_dll_dylib_impl
|
|
writect += _update_file(*write_libcef_dll_dylib_impl(
|
|
header, libcef_dll_dylib_impl), False, force, clean, backup, gitignore)
|
|
|
|
# output the VERSION.stamp file that triggers cef_version.h regen at build time
|
|
if verbose:
|
|
print('Generating ' + version_file + ' file...')
|
|
debug_string = version_file
|
|
writect += _update_file(version_file,
|
|
_write_version(), False, force, clean, backup,
|
|
gitignore)
|
|
|
|
# output the top-level .gitignore file that lists uncustomized files
|
|
if verbose:
|
|
print('Generating ' + gitignore_file + ' file...')
|
|
debug_string = gitignore_file
|
|
writect += _update_file(gitignore_file,
|
|
_write_gitignore(gitignore, gitignore_file,
|
|
root_dir), False, force, clean,
|
|
backup, None)
|
|
|
|
except (AssertionError, Exception) as e:
|
|
sys.stderr.write('ERROR: while processing %s\n' % debug_string)
|
|
raise
|
|
|
|
if verbose or writect > 0:
|
|
print('Done translating - %s %d files.' % ('Removed'
|
|
if clean else 'Wrote', writect))
|
|
|
|
return writect
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from optparse import OptionParser
|
|
|
|
# parse command-line options
|
|
disc = """
|
|
This utility generates files for the CEF C++ to C API translation layer.
|
|
"""
|
|
|
|
parser = OptionParser(description=disc)
|
|
parser.add_option(
|
|
'--root-dir',
|
|
dest='rootdir',
|
|
metavar='DIR',
|
|
help='CEF root directory [required]')
|
|
parser.add_option(
|
|
'--backup',
|
|
action='store_true',
|
|
dest='backup',
|
|
default=False,
|
|
help='create a backup of modified files')
|
|
parser.add_option(
|
|
'--force',
|
|
action='store_true',
|
|
dest='force',
|
|
default=False,
|
|
help='force rewrite of the file')
|
|
parser.add_option(
|
|
'--clean',
|
|
action='store_true',
|
|
dest='clean',
|
|
default=False,
|
|
help='clean all files without custom modifications')
|
|
parser.add_option(
|
|
'-c',
|
|
'--classes',
|
|
dest='classes',
|
|
action='append',
|
|
help='only translate the specified classes')
|
|
parser.add_option(
|
|
'-v',
|
|
'--verbose',
|
|
action='store_true',
|
|
dest='verbose',
|
|
default=False,
|
|
help='output detailed status information')
|
|
(options, args) = parser.parse_args()
|
|
|
|
# the rootdir option is required
|
|
if options.rootdir is None:
|
|
parser.print_help(sys.stdout)
|
|
sys.exit()
|
|
|
|
if translate(options.rootdir, options.force, options.clean, options.backup,
|
|
options.verbose, options.classes) == 0:
|
|
if not options.verbose:
|
|
print('Nothing to do.')
|
|
elif not options.clean:
|
|
print('WARNING: You must run version_manager.py to update API hashes.')
|