mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2024-12-15 10:58:33 +01:00
a76f40eb83
This splits out the API hashes from the cef_version.h file which is generated at build time. Changes to the cef_api_hash.h file are committed to the repo and represent potentially breaking API changes. This commit history will be used to calculate the version number.
269 lines
7.6 KiB
Python
269 lines
7.6 KiB
Python
# 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 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 = {
|
|
"windows": [
|
|
"internal/cef_types_win.h",
|
|
],
|
|
"macosx": [
|
|
"internal/cef_types_mac.h",
|
|
],
|
|
"linux": [
|
|
"internal/cef_types_linux.h",
|
|
]
|
|
}
|
|
|
|
self.included_files = []
|
|
|
|
self.excluded_files = [
|
|
"cef_api_hash.h",
|
|
"cef_version.h",
|
|
"internal/cef_tuple.h",
|
|
"internal/cef_types_wrappers.h",
|
|
"internal/cef_string_wrappers.h",
|
|
"internal/cef_win.h",
|
|
"internal/cef_mac.h",
|
|
"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)
|
|
rev = hashlib.sha1(sig).digest()
|
|
revstr = ''.join(format(ord(i), '0>2x') for i in rev)
|
|
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();
|