# Copyright (c) 2013 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 __future__ import print_function from file_util import * import os import re import shutil import string import sys import textwrap import time import itertools import hashlib class cef_api_hash: """ CEF API hash calculator """ def __init__(self, headerdir, debugdir=None, verbose=False): if headerdir is None or len(headerdir) == 0: raise AssertionError("headerdir is not specified") self.__headerdir = headerdir self.__debugdir = debugdir self.__verbose = verbose self.__debug_enabled = not (self.__debugdir is None) and len(self.__debugdir) > 0 self.platforms = ["windows", "macosx", "linux"] self.platform_files = { # List of includes_win_capi from cef_paths2.gypi. "windows": [ "internal/cef_types_win.h", ], # List of includes_mac_capi from cef_paths2.gypi. "macosx": [ "internal/cef_types_mac.h", ], # List of includes_linux_capi from cef_paths2.gypi. "linux": [ "internal/cef_types_linux.h", ] } self.included_files = [] # List of include/ and include/internal/ files from cef_paths2.gypi. self.excluded_files = [ # includes_common "cef_api_hash.h", "cef_base.h", "cef_config.h", "cef_version.h", "internal/cef_export.h", "internal/cef_ptr.h", "internal/cef_string_wrappers.h", "internal/cef_types_wrappers.h", # includes_win "cef_sandbox_win.h", "internal/cef_win.h", # includes_mac "cef_application_mac.h", "cef_sandbox_mac.h", "internal/cef_mac.h", # includes_linux "internal/cef_linux.h", ] def calculate(self): filenames = [ filename for filename in self.__get_filenames() if not filename in self.excluded_files ] objects = [] for filename in filenames: if self.__verbose: print("Processing " + filename + "...") content = read_file(os.path.join(self.__headerdir, filename), True) platforms = list([ p for p in self.platforms if self.__is_platform_filename(filename, p) ]) # Parse cef_string.h happens in special case: grab only defined CEF_STRING_TYPE_xxx declaration content_objects = None if filename == "internal/cef_string.h": content_objects = self.__parse_string_type(content) else: content_objects = self.__parse_objects(content) for o in content_objects: o["text"] = self.__prepare_text(o["text"]) o["platforms"] = platforms o["filename"] = filename objects.append(o) # objects will be sorted including filename, to make stable universal hashes objects = sorted(objects, key=lambda o: o["name"] + "@" + o["filename"]) if self.__debug_enabled: namelen = max([len(o["name"]) for o in objects]) filenamelen = max([len(o["filename"]) for o in objects]) dumpsig = [] for o in objects: dumpsig.append( format(o["name"], str(namelen) + "s") + "|" + format( o["filename"], "" + str(filenamelen) + "s") + "|" + o["text"]) self.__write_debug_file("objects.txt", dumpsig) revisions = {} for platform in itertools.chain(["universal"], self.platforms): sig = self.__get_final_sig(objects, platform) if self.__debug_enabled: self.__write_debug_file(platform + ".sig", sig) revstr = hashlib.sha1(sig.encode('utf-8')).hexdigest() revisions[platform] = revstr return revisions def __parse_objects(self, content): """ Returns array of objects in content file. """ objects = [] content = re.sub("//.*\n", "", content) # function declarations for m in re.finditer( "\nCEF_EXPORT\s+?.*?\s+?(\w+)\s*?\(.*?\)\s*?;", content, flags=re.DOTALL): object = {"name": m.group(1), "text": m.group(0).strip()} objects.append(object) # structs for m in re.finditer( "\ntypedef\s+?struct\s+?(\w+)\s+?\{.*?\}\s+?(\w+)\s*?;", content, flags=re.DOTALL): object = {"name": m.group(2), "text": m.group(0).strip()} objects.append(object) # enums for m in re.finditer( "\nenum\s+?(\w+)\s+?\{.*?\}\s*?;", content, flags=re.DOTALL): object = {"name": m.group(1), "text": m.group(0).strip()} objects.append(object) # typedefs for m in re.finditer("\ntypedef\s+?.*?\s+(\w+);", content, flags=0): object = {"name": m.group(1), "text": m.group(0).strip()} objects.append(object) return objects def __parse_string_type(self, content): """ Grab defined CEF_STRING_TYPE_xxx """ objects = [] for m in re.finditer( "\n\s*?#\s*?define\s+?(CEF_STRING_TYPE_\w+)\s+?.*?\n", content, flags=0): object = { "name": m.group(1), "text": m.group(0), } objects.append(object) return objects def __prepare_text(self, text): text = text.strip() text = re.sub("\s+", " ", text) text = re.sub("\(\s+", "(", text) return text def __get_final_sig(self, objects, platform): sig = [] for o in objects: if platform == "universal" or platform in o["platforms"]: sig.append(o["text"]) return "\n".join(sig) def __get_filenames(self): """ Returns file names to be processed, relative to headerdir """ headers = [ os.path.join(self.__headerdir, filename) for filename in self.included_files ] headers = itertools.chain( headers, get_files(os.path.join(self.__headerdir, "capi", "*.h"))) headers = itertools.chain( headers, get_files(os.path.join(self.__headerdir, "internal", "*.h"))) for v in self.platform_files.values(): headers = itertools.chain(headers, [os.path.join(self.__headerdir, f) for f in v]) normalized = [ os.path.relpath(filename, self.__headerdir) for filename in headers ] normalized = [f.replace('\\', '/').lower() for f in normalized] return list(set(normalized)) def __is_platform_filename(self, filename, platform): if platform == "universal": return True if not platform in self.platform_files: return False listed = False for p in self.platforms: if filename in self.platform_files[p]: if p == platform: return True else: listed = True return not listed def __write_debug_file(self, filename, content): make_dir(self.__debugdir) outfile = os.path.join(self.__debugdir, filename) dir = os.path.dirname(outfile) make_dir(dir) if not isinstance(content, basestring): content = "\n".join(content) write_file(outfile, content) if __name__ == "__main__": from optparse import OptionParser import time disc = """ This utility calculates CEF API hash. """ parser = OptionParser(description=disc) parser.add_option( '--cpp-header-dir', dest='cppheaderdir', metavar='DIR', help='input directory for C++ header files [required]') parser.add_option( '--debug-dir', dest='debugdir', metavar='DIR', help='intermediate directory for easy debugging') parser.add_option( '-v', '--verbose', action='store_true', dest='verbose', default=False, help='output detailed status information') (options, args) = parser.parse_args() # the cppheader option is required if options.cppheaderdir is None: parser.print_help(sys.stdout) sys.exit() # calculate c_start_time = time.time() calc = cef_api_hash(options.cppheaderdir, options.debugdir, options.verbose) revisions = calc.calculate() c_completed_in = time.time() - c_start_time print("{") for k in sorted(revisions.keys()): print(format("\"" + k + "\"", ">12s") + ": \"" + revisions[k] + "\"") print("}") # print # print 'Completed in: ' + str(c_completed_in) # print # print "Press any key to continue..."; # sys.stdin.readline();