# 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.')