Add CEF API hash and version info retrieval functions (issue #914).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1160 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2013-03-25 21:54:53 +00:00
parent fe30a139a7
commit e185bcb54d
8 changed files with 337 additions and 8 deletions

View File

@ -272,6 +272,7 @@
'tests/unittests/urlrequest_unittest.cc', 'tests/unittests/urlrequest_unittest.cc',
'tests/unittests/v8_unittest.cc', 'tests/unittests/v8_unittest.cc',
'tests/unittests/values_unittest.cc', 'tests/unittests/values_unittest.cc',
'tests/unittests/version_unittest.cc',
'tests/unittests/xml_reader_unittest.cc', 'tests/unittests/xml_reader_unittest.cc',
'tests/unittests/zip_reader_unittest.cc', 'tests/unittests/zip_reader_unittest.cc',
], ],

View File

@ -6,7 +6,26 @@
#include "include/cef_version.h" #include "include/cef_version.h"
#include "cef_logging.h" #include "cef_logging.h"
CEF_EXPORT int cef_build_revision() CEF_EXPORT int cef_build_revision() {
{
return CEF_REVISION; return CEF_REVISION;
} }
CEF_EXPORT int cef_version_info(int entry) {
switch (entry) {
case 0: return CEF_VERSION_MAJOR;
case 1: return CEF_REVISION;
case 2: return CHROME_VERSION_MAJOR;
case 3: return CHROME_VERSION_MINOR;
case 4: return CHROME_VERSION_BUILD;
case 5: return CHROME_VERSION_PATCH;
default: return 0;
}
}
CEF_EXPORT const char* cef_api_hash(int entry) {
switch (entry) {
case 0: return CEF_API_HASH_PLATFORM;
case 1: return CEF_API_HASH_UNIVERSAL;
default: return NULL;
}
}

View File

@ -0,0 +1,24 @@
// 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.
#include "include/cef_version.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(VersionTest, BuildRevision) {
EXPECT_EQ(CEF_REVISION, cef_build_revision());
}
TEST(VersionTest, VersionInfo) {
EXPECT_EQ(CEF_VERSION_MAJOR, cef_version_info(0));
EXPECT_EQ(CEF_REVISION, cef_version_info(1));
EXPECT_EQ(CHROME_VERSION_MAJOR, cef_version_info(2));
EXPECT_EQ(CHROME_VERSION_MINOR, cef_version_info(3));
EXPECT_EQ(CHROME_VERSION_BUILD, cef_version_info(4));
EXPECT_EQ(CHROME_VERSION_PATCH, cef_version_info(5));
}
TEST(VersionTest, ApiHash) {
EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(0));
EXPECT_STREQ(CEF_API_HASH_UNIVERSAL, cef_api_hash(1));
}

242
tools/cef_api_hash.py Normal file
View File

@ -0,0 +1,242 @@
# 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.excluded_files = [
"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("\n\s*?CEF_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("\n\s*?typedef\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("\n\s*?enum\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("\n\s*?typedef\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 = get_files(os.path.join(self.__headerdir, "*.h"))
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();

View File

@ -17,7 +17,8 @@ print "\nGenerating CEF version header file..."
gyper = [ 'python', 'tools/make_version_header.py', gyper = [ 'python', 'tools/make_version_header.py',
'--header', 'include/cef_version.h', '--header', 'include/cef_version.h',
'--cef_version', 'VERSION', '--cef_version', 'VERSION',
'--chrome_version', '../chrome/VERSION' ] '--chrome_version', '../chrome/VERSION',
'--cpp_header_dir', 'include' ]
RunAction(cef_dir, gyper) RunAction(cef_dir, gyper)
print "\nPatching build configuration and source files for CEF..." print "\nPatching build configuration and source files for CEF..."

View File

@ -1,2 +1,2 @@
@echo off @echo off
..\third_party\python_26\python.exe tools\make_version_header.py --header include\cef_version.h --cef_version VERSION --chrome_version ../chrome/VERSION ..\third_party\python_26\python.exe tools\make_version_header.py --header include\cef_version.h --cef_version VERSION --chrome_version ../chrome/VERSION --cpp_header_dir include

View File

@ -5,6 +5,7 @@
from date_util import * from date_util import *
from file_util import * from file_util import *
from optparse import OptionParser from optparse import OptionParser
from cef_api_hash import cef_api_hash
import svn_util as svn import svn_util as svn
import git_util as git import git_util as git
import sys import sys
@ -27,17 +28,19 @@ parser.add_option('--cef_version', dest='cef_version', metavar='FILE',
help='input CEF version config file [required]') help='input CEF version config file [required]')
parser.add_option('--chrome_version', dest='chrome_version', metavar='FILE', parser.add_option('--chrome_version', dest='chrome_version', metavar='FILE',
help='input Chrome version config file [required]') help='input Chrome version config file [required]')
parser.add_option('--cpp_header_dir', dest='cpp_header_dir', metavar='DIR',
help='input directory for C++ header files [required]')
parser.add_option('-q', '--quiet', parser.add_option('-q', '--quiet',
action='store_true', dest='quiet', default=False, action='store_true', dest='quiet', default=False,
help='do not output detailed status information') help='do not output detailed status information')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# the header option is required # the header option is required
if options.header is None or options.cef_version is None or options.chrome_version is None: if options.header is None or options.cef_version is None or options.chrome_version is None or options.cpp_header_dir is None:
parser.print_help(sys.stdout) parser.print_help(sys.stdout)
sys.exit() sys.exit()
def write_svn_header(header, chrome_version, cef_version): def write_svn_header(header, chrome_version, cef_version, cpp_header_dir):
""" Creates the header file for the current revision and Chrome version information """ Creates the header file for the current revision and Chrome version information
if the information has changed or if the file doesn't already exist. """ if the information has changed or if the file doesn't already exist. """
@ -62,6 +65,10 @@ def write_svn_header(header, chrome_version, cef_version):
except: except:
revision = git.get_svn_revision() revision = git.get_svn_revision()
# calculate api hashes
api_hash_calculator = cef_api_hash(cpp_header_dir, verbose = False)
api_hashes = api_hash_calculator.calculate()
newcontents = '// Copyright (c) '+year+' Marshall A. Greenblatt. All rights reserved.\n'+\ newcontents = '// Copyright (c) '+year+' Marshall A. Greenblatt. All rights reserved.\n'+\
'//\n'+\ '//\n'+\
'// Redistribution and use in source and binary forms, with or without\n'+\ '// Redistribution and use in source and binary forms, with or without\n'+\
@ -111,10 +118,43 @@ def write_svn_header(header, chrome_version, cef_version):
'extern "C" {\n'+\ 'extern "C" {\n'+\
'#endif\n\n'+\ '#endif\n\n'+\
'#include "internal/cef_export.h"\n\n'+\ '#include "internal/cef_export.h"\n\n'+\
'// The API hash is created by analyzing CEF header files for C API type\n'+\
'// definitions. The hash value will change when header files are modified\n'+\
'// in a way that may cause binary incompatibility with other builds. The\n'+\
'// universal hash value will change if any platform is affected whereas the\n'+\
'// platform hash values will change only if that particular platform is\n'+\
'// affected.\n'+\
'#define CEF_API_HASH_UNIVERSAL "' + api_hashes['universal'] + '"\n'+\
'#if defined(OS_WIN)\n'+\
'#define CEF_API_HASH_PLATFORM "' + api_hashes['windows'] + '"\n'+\
'#elif defined(OS_MACOSX)\n'+\
'#define CEF_API_HASH_PLATFORM "' + api_hashes['macosx'] + '"\n'+\
'#elif defined(OS_LINUX)\n'+\
'#define CEF_API_HASH_PLATFORM "' + api_hashes['linux'] + '"\n'+\
'#endif\n\n'+\
'///\n'+\ '///\n'+\
'// Returns the CEF build revision of the libcef library.\n'+\ '// Returns the CEF build revision for the libcef library.\n'+\
'///\n'+\ '///\n'+\
'CEF_EXPORT int cef_build_revision();\n\n'+\ 'CEF_EXPORT int cef_build_revision();\n\n'+\
'///\n'+\
'// Returns CEF version information for the libcef library. The |entry|\n'+\
'// parameter describes which version component will be returned:\n'+\
'// 0 - CEF_VERSION_MAJOR\n'+\
'// 1 - CEF_REVISION\n'+\
'// 2 - CHROME_VERSION_MAJOR\n'+\
'// 3 - CHROME_VERSION_MINOR\n'+\
'// 4 - CHROME_VERSION_BUILD\n'+\
'// 5 - CHROME_VERSION_PATCH\n'+\
'///\n'+\
'CEF_EXPORT int cef_version_info(int entry);\n\n'+\
'///\n'+\
'// Returns CEF API hashes for the libcef library. The returned string is owned\n'+\
'// by the library and should not be freed. The |entry| parameter describes which\n'+\
'// hash value will be returned:\n'+\
'// 0 - CEF_API_HASH_PLATFORM\n'+\
'// 1 - CEF_API_HASH_UNIVERSAL\n'+\
'///\n'+\
'CEF_EXPORT const char* cef_api_hash(int entry);\n\n'+\
'#ifdef __cplusplus\n'+\ '#ifdef __cplusplus\n'+\
'}\n'+\ '}\n'+\
'#endif\n\n'+\ '#endif\n\n'+\
@ -126,7 +166,7 @@ def write_svn_header(header, chrome_version, cef_version):
return False return False
written = write_svn_header(options.header, options.chrome_version, options.cef_version) written = write_svn_header(options.header, options.chrome_version, options.cef_version, options.cpp_header_dir)
if not options.quiet: if not options.quiet:
if written: if written:
sys.stdout.write('File '+options.header+' updated.\n') sys.stdout.write('File '+options.header+' updated.\n')

2
tools/make_version_header.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
python tools/make_version_header.py --header include/cef_version.h --cef_version VERSION --chrome_version ../chrome/VERSION --cpp_header_dir include