Support API versioning in platform-specific headers (see #3836)

- Exclude platform-specific includes (anything in < >) from the
  clang preprocessor by using `!defined(GENERATING_CEF_API_HASH)`
  in CEF header files.
- Define "target platforms" by passing platform- and architecture-
  specific ifdefs to the clang preprocessor. Grep for `defined(OS_`
  to identify headers that require target platform processing, and
  then process for each target as the platform-specific API hash
  contribution.
- Delete the univeral hash which is no longer a useful concept.
This commit is contained in:
Nik Pavlov
2025-02-25 11:43:44 -05:00
committed by Marshall Greenblatt
parent 8ddb1bffbb
commit fdd36e8461
22 changed files with 922 additions and 550 deletions

View File

@ -1,46 +1,40 @@
{ {
"hashes": { "hashes": {
"13300": { "13300": {
"comment": "Added February 17, 2025.", "comment": "Added February 21, 2025.",
"linux": "87d31692cf1ff1f0f2445dd026dcc0aeb02c5786", "linux": "2508f3f0b0e5dfa191036fa6c04f8dcfa18c94b9",
"mac": "3891d2358697f15e046afc65fefd7019d888817a", "mac": "80c0b59ba9dd783aa71fae0aa5f7dad64620e8c9",
"universal": "c1a7d47be6fc130795366a1627573aa8eba1106f", "windows": "45d39c3669ba75467e3e609f626c31506c0eae22"
"windows": "0efb201c9e981c4513aeea04f9f2f8041ee9ce1f"
}, },
"13301": { "13301": {
"comment": "Added February 17, 2025.", "comment": "Added February 21, 2025.",
"linux": "747f624e5059839e6a7d385de7997d45bbd42184", "linux": "aa073dd1c586812503ca293c718358460d8c2dd6",
"mac": "d3ec7d79adbde067590ab1bfff1f30dd8b06546a", "mac": "fda40a5df44628cac50a589ff979c0746011591e",
"universal": "892e654249acc7360f8aca4ee77da896753b87b8", "windows": "7109702038d51512d35dd2ed77231f9100e38214"
"windows": "cf9fbb7aa6778fedce78b8557044f219f2b5ba0b"
}, },
"13302": { "13302": {
"comment": "Added February 17, 2025.", "comment": "Added February 21, 2025.",
"linux": "ae4ab1c52ad951d37e6397db9f4e0ab0ce5044cf", "linux": "d5597ebfa30081953425e897209a8387b9584205",
"mac": "cfe09351db37aa275613c27145fe622d9c28d4cc", "mac": "4aa24470ba3a4bd9c06bc0e4a201b896394a86b5",
"universal": "acab8df1244923f1f80b18214e73c276a553ded6", "windows": "18799961f4461a9cbae2aed89ac04b73ab7c37f3"
"windows": "c4b5a65478bc1a679f4a47ba5ff6899e5c3d8761"
}, },
"13303": { "13303": {
"comment": "Added February 17, 2025.", "comment": "Added February 21, 2025.",
"linux": "2683698b779f11bba19f4051e3760fe327a1c8b2", "linux": "f3a696ee30ce1e00490a58df017393c126c89709",
"mac": "8085a0f62f15946343c63788300e5c2b422b3301", "mac": "f2cdce2b9a4b635c28b5b92c42c35625a937380c",
"universal": "128210b71e3f621cb6d2c4b05c6d0408a4547f2f", "windows": "20016fd6a9b87ef4c539cd1f42bf1ca09b6903ca"
"windows": "5a22d42c3e6294eb4ee2424053eebdeae3027899"
}, },
"13304": { "13304": {
"comment": "Added February 17, 2025.", "comment": "Added February 21, 2025.",
"linux": "c709233cd071e0d715eb10ceec4c5aea805df997", "linux": "f1ababb4ff51ecbf77c481cee3721ef0eca9c8ca",
"mac": "32af7e8f8828b04cf2908de9857940faa54deb8a", "mac": "98964c37b8917d83da4b173e22905503d38ad08f",
"universal": "131b720865dd5c54c72041c0fbfb7c9bbfee8e0b", "windows": "19c014af0082aa901398e006381b6980e4f806e9"
"windows": "979b48138012378192dac7ee972fa731e9c0dae9"
}, },
"13400": { "13400": {
"comment": "Added February 19, 2025.", "comment": "Added February 21, 2025.",
"linux": "342696aa818bc9a512cc965ed4c19ba705d2c839", "linux": "ea2106b5bc012c25d735521e0c7fb719d433ea4a",
"mac": "7a20d0f7dc388406111461d6b1addd329b4be567", "mac": "ba5ab71db4f9447f19eb7b1943024981c88064dd",
"universal": "65de718ba0e0693dfc3b26e7e6a4814bbeeb10be", "windows": "6ab74b90e88b7397aab9911baac5484f12466eef"
"windows": "afe73f64f156154ff5b099c36186cbc03136172d"
} }
}, },
"last": "13400", "last": "13400",

View File

@ -77,19 +77,15 @@
#endif #endif
#endif #endif
#define _CEF_AH_PASTE(a, b, c) a##_##b##_##c #define _CEF_AH_PASTE(a, b) a##_##b
#define _CEF_AH_EVAL(a, b, c) _CEF_AH_PASTE(a, b, c) #define _CEF_AH_EVAL(a, b) _CEF_AH_PASTE(a, b)
#define _CEF_AH_DECLARE(version, suffix) \ #define _CEF_AH_DECLARE(version) _CEF_AH_EVAL(CEF_API_HASH, version)
_CEF_AH_EVAL(CEF_API_HASH, version, suffix)
// API hashes for the selected CEF_API_VERSION. API hashes are created for // API hashes for the selected CEF_API_VERSION. API hashes are created for
// each version by analyzing CEF header files for C API type definitions. The // each version by analyzing CEF header files for C API type definitions. The
// hash value will change when header files are modified in a way that may // hash value will change when header files are modified in a way that may
// cause binary incompatibility with other builds. The universal hash value // cause binary incompatibility with other builds.
// will change if any platform is affected whereas the platform hash values #define CEF_API_HASH_PLATFORM _CEF_AH_DECLARE(CEF_API_VERSION)
// will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL _CEF_AH_DECLARE(CEF_API_VERSION, UNIVERSAL)
#define CEF_API_HASH_PLATFORM _CEF_AH_DECLARE(CEF_API_VERSION, PLATFORM)
#if defined(BUILDING_CEF_SHARED) #if defined(BUILDING_CEF_SHARED)
@ -132,7 +128,7 @@ extern "C" {
/// parameter describes which hash value will be returned: /// parameter describes which hash value will be returned:
/// ///
/// 0 - CEF_API_HASH_PLATFORM /// 0 - CEF_API_HASH_PLATFORM
/// 1 - CEF_API_HASH_UNIVERSAL /// 1 - CEF_API_HASH_UNIVERSAL (deprecated, same as CEF_API_HASH_PLATFORM)
/// 2 - CEF_COMMIT_HASH (from cef_version.h) /// 2 - CEF_COMMIT_HASH (from cef_version.h)
/// ///
CEF_EXPORT const char* cef_api_hash(int version, int entry); CEF_EXPORT const char* cef_api_hash(int version, int entry);

View File

@ -31,8 +31,11 @@
#define CEF_INCLUDE_CEF_BASE_H_ #define CEF_INCLUDE_CEF_BASE_H_
#pragma once #pragma once
#include "include/base/cef_atomic_ref_count.h" #if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#include "include/base/cef_atomic_ref_count.h"
#include "include/base/cef_macros.h" #include "include/base/cef_macros.h"
// Bring in common C++ type definitions used by CEF consumers. // Bring in common C++ type definitions used by CEF consumers.

View File

@ -31,7 +31,10 @@
#define CEF_INCLUDE_CEF_SANDBOX_MAC_H_ #define CEF_INCLUDE_CEF_SANDBOX_MAC_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#include "include/internal/cef_export.h" #include "include/internal/cef_export.h"
#if defined(OS_MAC) #if defined(OS_MAC)

View File

@ -31,7 +31,9 @@
#define CEF_INCLUDE_CEF_SANDBOX_WIN_H_ #define CEF_INCLUDE_CEF_SANDBOX_WIN_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(OS_WIN) #if defined(OS_WIN)

View File

@ -31,7 +31,9 @@
#define CEF_INCLUDE_INTERNAL_CEF_APP_WIN_H_ #define CEF_INCLUDE_INTERNAL_CEF_APP_WIN_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(OS_WIN) #if defined(OS_WIN)

View File

@ -32,7 +32,9 @@
#define CEF_INCLUDE_INTERNAL_CEF_EXPORT_H_ #define CEF_INCLUDE_INTERNAL_CEF_EXPORT_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(COMPILER_MSVC) #if defined(COMPILER_MSVC)

View File

@ -31,12 +31,14 @@
#define CEF_INCLUDE_INTERNAL_CEF_THREAD_INTERNAL_H_ #define CEF_INCLUDE_INTERNAL_CEF_THREAD_INTERNAL_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#if defined(OS_WIN) #if defined(OS_WIN)
#include <windows.h> #include <windows.h>
#elif defined(OS_POSIX) #elif defined(OS_POSIX)
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#endif
#include "include/internal/cef_export.h" #include "include/internal/cef_export.h"

View File

@ -35,8 +35,11 @@
extern "C" { extern "C" {
#endif #endif
#include <stdint.h> #if !defined(GENERATING_CEF_API_HASH)
#include <time.h> #include <time.h>
#endif
#include <stdint.h>
#include "include/internal/cef_export.h" #include "include/internal/cef_export.h"

View File

@ -31,15 +31,12 @@
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_ #define CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(OS_LINUX) #if defined(OS_LINUX)
#if defined(CEF_X11)
typedef union _XEvent XEvent;
typedef struct _XDisplay XDisplay;
#endif
#include "include/internal/cef_export.h" #include "include/internal/cef_export.h"
#include "include/internal/cef_string.h" #include "include/internal/cef_string.h"
#include "include/internal/cef_types_color.h" #include "include/internal/cef_types_color.h"
@ -47,17 +44,6 @@ typedef struct _XDisplay XDisplay;
#include "include/internal/cef_types_osr.h" #include "include/internal/cef_types_osr.h"
#include "include/internal/cef_types_runtime.h" #include "include/internal/cef_types_runtime.h"
// Handle types.
#if defined(CEF_X11)
#define cef_cursor_handle_t unsigned long
#define cef_event_handle_t XEvent*
#else
#define cef_cursor_handle_t void*
#define cef_event_handle_t void*
#endif
#define cef_window_handle_t unsigned long
#define kNullCursorHandle 0 #define kNullCursorHandle 0
#define kNullEventHandle NULL #define kNullEventHandle NULL
#define kNullWindowHandle 0 #define kNullWindowHandle 0
@ -66,6 +52,20 @@ typedef struct _XDisplay XDisplay;
extern "C" { extern "C" {
#endif #endif
#if defined(CEF_X11)
typedef union _XEvent XEvent;
typedef struct _XDisplay XDisplay;
// Handle types.
typedef unsigned long cef_cursor_handle_t;
typedef XEvent* cef_event_handle_t;
#else
typedef void* cef_cursor_handle_t;
typedef void* cef_event_handle_t;
#endif
typedef unsigned long cef_window_handle_t;
/// ///
/// Return the singleton X11 display shared with Chromium. The display is not /// Return the singleton X11 display shared with Chromium. The display is not
/// thread-safe and must only be accessed on the browser process UI thread. /// thread-safe and must only be accessed on the browser process UI thread.

View File

@ -31,7 +31,9 @@
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_ #define CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(OS_MAC) #if defined(OS_MAC)
#include "include/internal/cef_string.h" #include "include/internal/cef_string.h"
@ -40,16 +42,6 @@
#include "include/internal/cef_types_osr.h" #include "include/internal/cef_types_osr.h"
#include "include/internal/cef_types_runtime.h" #include "include/internal/cef_types_runtime.h"
// Handle types.
// Actually NSCursor*
#define cef_cursor_handle_t void*
// Acutally NSEvent*
#define cef_event_handle_t void*
// Actually NSView*
#define cef_window_handle_t void*
// Actually IOSurface*
#define cef_shared_texture_handle_t void*
#define kNullCursorHandle NULL #define kNullCursorHandle NULL
#define kNullEventHandle NULL #define kNullEventHandle NULL
#define kNullWindowHandle NULL #define kNullWindowHandle NULL
@ -78,6 +70,16 @@
extern "C" { extern "C" {
#endif #endif
// Handle types.
// Actually NSCursor*
typedef void* cef_cursor_handle_t;
// Actually NSEvent*
typedef void* cef_event_handle_t;
// Actually NSView*
typedef void* cef_window_handle_t;
// Actually IOSurface*
typedef void* cef_shared_texture_handle_t;
/// ///
/// Structure representing CefExecuteProcess arguments. /// Structure representing CefExecuteProcess arguments.
/// ///

View File

@ -31,10 +31,15 @@
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_ #define CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
#pragma once #pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h" #include "include/base/cef_build.h"
#endif
#if defined(OS_WIN) #if defined(OS_WIN)
#if !defined(GENERATING_CEF_API_HASH)
#include <windows.h> #include <windows.h>
#endif
#include "include/internal/cef_string.h" #include "include/internal/cef_string.h"
#include "include/internal/cef_types_color.h" #include "include/internal/cef_types_color.h"
@ -42,12 +47,6 @@
#include "include/internal/cef_types_osr.h" #include "include/internal/cef_types_osr.h"
#include "include/internal/cef_types_runtime.h" #include "include/internal/cef_types_runtime.h"
// Handle types.
#define cef_cursor_handle_t HCURSOR
#define cef_event_handle_t MSG*
#define cef_window_handle_t HWND
#define cef_shared_texture_handle_t HANDLE
#define kNullCursorHandle NULL #define kNullCursorHandle NULL
#define kNullEventHandle NULL #define kNullEventHandle NULL
#define kNullWindowHandle NULL #define kNullWindowHandle NULL
@ -56,6 +55,12 @@
extern "C" { extern "C" {
#endif #endif
// Handle types.
typedef HCURSOR cef_cursor_handle_t;
typedef MSG* cef_event_handle_t;
typedef HWND cef_window_handle_t;
typedef HANDLE cef_shared_texture_handle_t;
/// ///
/// Structure representing CefExecuteProcess arguments. /// Structure representing CefExecuteProcess arguments.
/// ///

View File

@ -48,23 +48,23 @@ CEF_EXPORT int cef_version_info(int entry) {
#include "cef/libcef_dll/cef_api_versions.inc" #include "cef/libcef_dll/cef_api_versions.inc"
CEF_EXPORT const char* cef_api_hash(int version, int entry) { CEF_EXPORT const char* cef_api_hash(int version, int entry) {
static const ApiVersionHash* hash = nullptr; static const ApiVersionHash* current_version_hash = nullptr;
// Initialize on the first successful lookup. // Initialize on the first successful lookup.
if (!hash) { if (!current_version_hash) {
for (size_t i = 0; i < kApiVersionHashesSize; ++i) { for (const auto& version_hash : kApiVersionHashes) {
if (version == kApiVersionHashes[i].version) { if (version == version_hash.version) {
hash = &kApiVersionHashes[i]; current_version_hash = &version_hash;
break; break;
} }
} }
if (hash) { if (current_version_hash) {
g_version = version; g_version = version;
} }
} }
if (!hash) { if (!current_version_hash) {
LOG(ERROR) << "Request for unsupported CEF API version " << version; LOG(ERROR) << "Request for unsupported CEF API version " << version;
return nullptr; return nullptr;
} }
@ -76,9 +76,8 @@ CEF_EXPORT const char* cef_api_hash(int version, int entry) {
switch (entry) { switch (entry) {
case 0: case 0:
return hash->platform;
case 1: case 1:
return hash->universal; return current_version_hash->hash;
case 2: case 2:
return CEF_COMMIT_HASH; return CEF_COMMIT_HASH;
default: default:
@ -97,9 +96,8 @@ CEF_EXPORT int cef_id_for_pack_resource_name(const char* name) {
// Initialize on the first call. // Initialize on the first call.
if (string_to_id_map->empty()) { if (string_to_id_map->empty()) {
for (size_t i = 0; i < kIdNamesPackResourcesSize; ++i) { for (const auto& pack_resource : kIdNamesPackResources) {
(*string_to_id_map)[kIdNamesPackResources[i].name] = (*string_to_id_map)[pack_resource.name] = pack_resource.id;
kIdNamesPackResources[i].id;
} }
} }
@ -119,9 +117,8 @@ CEF_EXPORT int cef_id_for_pack_string_name(const char* name) {
// Initialize on the first call. // Initialize on the first call.
if (string_to_id_map->empty()) { if (string_to_id_map->empty()) {
for (size_t i = 0; i < kIdNamesPackStringsSize; ++i) { for (const auto& pack_string : kIdNamesPackStrings) {
(*string_to_id_map)[kIdNamesPackStrings[i].name] = (*string_to_id_map)[pack_string.name] = pack_string.id;
kIdNamesPackStrings[i].id;
} }
} }
@ -141,9 +138,8 @@ CEF_EXPORT int cef_id_for_command_id_name(const char* name) {
// Initialize on the first call. // Initialize on the first call.
if (string_to_id_map->empty()) { if (string_to_id_map->empty()) {
for (size_t i = 0; i < kIdNamesCommandIdsSize; ++i) { for (const auto& command : kIdNamesCommandIds) {
(*string_to_id_map)[kIdNamesCommandIds[i].name] = (*string_to_id_map)[command.name] = command.id;
kIdNamesCommandIds[i].id;
} }
} }

View File

@ -19,6 +19,6 @@ TEST(VersionTest, VersionInfo) {
TEST(VersionTest, ApiHash) { TEST(VersionTest, ApiHash) {
EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(CEF_API_VERSION, 0)); EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(CEF_API_VERSION, 0));
EXPECT_STREQ(CEF_API_HASH_UNIVERSAL, cef_api_hash(CEF_API_VERSION, 1)); EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(CEF_API_VERSION, 1));
EXPECT_STREQ(CEF_COMMIT_HASH, cef_api_hash(CEF_API_VERSION, 2)); EXPECT_STREQ(CEF_COMMIT_HASH, cef_api_hash(CEF_API_VERSION, 2));
} }

View File

@ -4,343 +4,380 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Dict, List
from clang_util import clang_eval from clang_util import clang_eval
from file_util import * from file_util import *
import hashlib import hashlib
import itertools
import os import os
import re import re
import string
import sys import sys
import time
from version_util import EXP_VERSION from version_util import EXP_VERSION
# Determines string type for python 2 and python 3. API_TOKEN = '#if CEF_API'
if sys.version_info[0] == 3: OS_TOKEN = '#if defined(OS_'
string_type = str INCLUDE_START_MARKER = 'int begin_includes_tag;\n'
else: INCLUDE_DIRS = [
string_type = basestring '.', # Includes relative to the 'src/cef' directory.
'..', # Includes relative to the 'src' directory.
]
PLATFORM_DEFINES = {
'windows': ['OS_WIN', 'ARCH_CPU_X86_64'],
'mac': ['OS_MAC', 'OS_POSIX', 'ARCH_CPU_ARM64'],
'linux': ['OS_LINUX', 'OS_POSIX', 'CEF_X11', 'ARCH_CPU_X86_64'],
}
SYSTEM_INCLUDES_PATTERN = re.compile(r'(#include <[^>]+>)')
FUNCTION_DECLARATION_PATTERN = re.compile(
r'\nCEF_EXPORT\s+?.*?\s+?(\w+)\s*?\(.*?\)\s*?;', re.DOTALL)
STRUCT_PATTERN = re.compile(
r'\ntypedef\s+?struct\s+?(\w+)\s+?\{.*?\}\s+?(\w+)\s*?;', re.DOTALL)
ENUM_PATTERN = re.compile(r'\ntypedef\s+?enum\s+?\{.*?\}\s+?(\w+)\s*?;',
re.DOTALL)
TYPEDEF_PATTERN = re.compile(r'\ntypedef\s+?.*?\s+(\w+);')
WHITESPACE_PATTERN = re.compile(r'\s+')
PARENTHESIS_PATTERN = re.compile(r'\(\s+')
SINGLE_LINE_COMMENT_PATTERN = re.compile(r'//.*\n')
CEF_STRING_TYPE_PATTERN = re.compile(
r'\n\s*?#\s*?define\s+?(CEF_STRING_TYPE_\w+)\s+?.*?\n')
DEFINE_PATTERN = re.compile(r'#define\s+?.*?')
def _run_clang_eval(filename, content, api_version, added_defines, verbose): def _prepare_content(content: str) -> str:
# Add a tag so we know where the header-specific output begins. """
tag = 'int begin_includes_tag;\n' Add a marker to the content to indicate where the header-specific output
begins and replace system includes with a placeholder
"""
find = '#ifdef __cplusplus\nextern "C" {' find = '#ifdef __cplusplus\nextern "C" {'
pos = content.find(find) pos = content.find(find)
assert pos > 0, filename assert pos > 0, f'Cannot find "{find}" in {content}'
content = content[0:pos] + tag + content[pos:] content = content[0:pos] + INCLUDE_START_MARKER + content[pos:]
defines = [ content = DEFINE_PATTERN.sub('//DEFINE_PLACEHOLDER//', content)
# Makes sure CEF_EXPORT is defined. return SYSTEM_INCLUDES_PATTERN.sub('//INCLUDE_PLACEHOLDER//', content)
'USING_CEF_SHARED',
# Avoid include of generated headers.
'GENERATING_CEF_API_HASH',
]
if filename.find('test/') >= 0: def _process_result(result: str) -> str:
# Avoids errors parsing test includes. """
defines.append('UNIT_TEST') Remove the non header-specific output and undo substitutions from cef_export.h
"""
# Not the experimental version. pos = result.find(INCLUDE_START_MARKER)
api_version = int(api_version) if pos == -1:
if api_version != EXP_VERSION: return result
# Specify the exact version. result = result[pos + len(INCLUDE_START_MARKER):]
defines.append('CEF_API_VERSION=%d' % api_version)
if not added_defines is None:
defines.extend(added_defines)
includes = [
# Includes relative to the 'src/cef' directory.
'.',
# Includes relative to the 'src' directory.
'..',
]
result = clang_eval(
filename,
content,
defines=defines,
includes=includes,
as_cpp=False,
verbose=verbose)
if result is None:
return None
pos = result.find(tag)
assert pos > 0, filename
result = result[pos + len(tag):]
replacements = [ replacements = [
# Undo substitutions from cef_export.h
['__declspec(dllimport)', 'CEF_EXPORT'], ['__declspec(dllimport)', 'CEF_EXPORT'],
['__attribute__((visibility("default")))', 'CEF_EXPORT'], ['__attribute__((visibility("default")))', 'CEF_EXPORT'],
['__stdcall', ''], ['__stdcall', ''],
] ]
for find, replace in replacements: for find, replace in replacements:
result = result.replace(find, replace) result = result.replace(find, replace)
# Must always start with newline as required by _parse_objects()
return result return '\n' + result
class cef_api_hash: def _get_defines(filename: str, added_defines: list) -> List[str]:
defines = [
# Makes sure CEF_EXPORT is defined.
'USING_CEF_SHARED',
# Avoid include of generated headers.
'GENERATING_CEF_API_HASH',
]
# Avoids errors parsing test includes.
if filename.find('test/') >= 0:
defines.append('UNIT_TEST')
defines.extend(added_defines)
return defines
def parse_versioned_content(filename: str,
content: str,
api_version: str,
added_defines: list,
debug_dir,
verbose) -> list:
"""
Parse the header file content using clang with the specified API version
Used for files that are version-specific but not platform-specific
"""
content = _prepare_content(content)
defines = _get_defines(filename, added_defines)
if api_version != EXP_VERSION:
# Specify the exact version.
defines.append(f'CEF_API_VERSION={api_version}')
result = clang_eval(filename, content, defines, INCLUDE_DIRS, verbose)
assert result, f'clang failed to eval {filename} with {api_version=}'
result = _process_result(result)
if debug_dir:
_write_debug_file(debug_dir, 'clang-' + filename.replace('/', '-'), result)
return _parse_objects(filename, result, 'universal')
def parse_platformed_content(filename: str,
content: str,
debug_dir,
verbose,
api_version,
added_defines: list) -> list:
"""
Parse the header file content using clang for every supported platform
with the specified API version. Used for files that are both version-specific
and platform-specific.
"""
content = _prepare_content(content)
defines = _get_defines(filename, added_defines)
if api_version and api_version != EXP_VERSION:
# Specify the exact version.
defines.append(f'CEF_API_VERSION={api_version}')
content_objects = []
for platform, platform_defines in PLATFORM_DEFINES.items():
result = clang_eval(filename, content, defines + platform_defines,
INCLUDE_DIRS, verbose)
assert result, f'clang failed to eval {filename} on {platform=}'
result = _process_result(result)
if debug_dir:
_write_debug_file(
debug_dir, f'clang-{platform}-' + filename.replace('/', '-'), result)
objects = _parse_objects(filename, result, platform)
content_objects.extend(objects)
return content_objects
def _write_debug_file(debug_dir, filename, content) -> None:
make_dir(debug_dir)
outfile = os.path.join(debug_dir, filename)
dir = os.path.dirname(outfile)
make_dir(dir)
if not isinstance(content, str):
content = '\n'.join(content)
write_file(outfile, content)
def _parse_objects(filename: str, content: str, platform: str) -> list:
objects = []
content = SINGLE_LINE_COMMENT_PATTERN.sub('', content)
for m in FUNCTION_DECLARATION_PATTERN.finditer(content):
objects.append({
'filename': filename,
'name': m.group(1),
'platform': platform,
'text': _prepare_text(m.group(0))
})
for m in STRUCT_PATTERN.finditer(content):
# Remove 'CEF_CALLBACK' to normalize cross-platform clang output
objects.append({
'filename': filename,
'name': m.group(2),
'platform': platform,
'text': _prepare_text(m.group(0).replace('CEF_CALLBACK', ''))
})
for m in ENUM_PATTERN.finditer(content):
objects.append({
'filename': filename,
'name': m.group(1),
'platform': platform,
'text': _prepare_text(m.group(0))
})
for m in TYPEDEF_PATTERN.finditer(content):
if m.group(1) == 'char16_t':
# Skip char16_t typedefs to avoid platform-specific differences.
continue
objects.append({
'filename': filename,
'name': m.group(1),
'platform': platform,
'text': _prepare_text(m.group(0))
})
return objects
def _prepare_text(text: str) -> str:
""" Normalize text for hashing. """
text = WHITESPACE_PATTERN.sub(' ', text.strip())
return PARENTHESIS_PATTERN.sub('(', text)
def _parse_string_type(filename: str, content: str) -> list:
""" Grab defined CEF_STRING_TYPE_xxx """
objects = []
for m in CEF_STRING_TYPE_PATTERN.finditer(content):
objects.append({
'filename': filename,
'name': m.group(1),
'text': _prepare_text(m.group(0)),
'platform': 'universal'
})
return objects
def _get_final_sig(objects, platform) -> str:
return '\n'.join(o['text'] for o in objects
if o['platform'] == 'universal' or o['platform'] == platform)
def _get_filenames(header_dir) -> List[str]:
""" Return file names to be processed, relative to header_dir """
cef_dir = os.path.abspath(os.path.join(header_dir, os.pardir))
# Read the variables list from the autogenerated cef_paths.gypi file.
cef_paths = eval_file(os.path.join(cef_dir, 'cef_paths.gypi'))
cef_paths = cef_paths['variables']
# Read the variables list from the manually edited cef_paths2.gypi file.
cef_paths2 = eval_file(os.path.join(cef_dir, 'cef_paths2.gypi'))
cef_paths2 = cef_paths2['variables']
# List of all C API include/ files.
paths = cef_paths2['includes_capi'] + cef_paths2['includes_common_capi'] + \
cef_paths2['includes_linux_capi'] + cef_paths2['includes_mac_capi'] + \
cef_paths2['includes_win_capi'] + cef_paths['autogen_capi_includes']
return [
os.path.relpath(os.path.join(cef_dir, filename), header_dir).replace(
'\\', '/').lower() for filename in paths
]
def _save_objects_to_file(debug_dir: str, objects: list) -> None:
name_len = max([len(o['name']) for o in objects])
filename_len = max([len(o['filename']) for o in objects])
platform_len = max([len(o['platform']) for o in objects])
dump_sig = [
f"{o['name']:<{name_len}}|{o['filename']:<{filename_len}}|{o['platform']:<{platform_len}}|{o['text']}"
for o in objects
]
_write_debug_file(debug_dir, 'objects.txt', dump_sig)
class CefApiHasher:
""" CEF API hash calculator """ """ CEF API hash calculator """
def __init__(self, headerdir, verbose=False): def __init__(self, header_dir, debug_dir, verbose=False):
if headerdir is None or len(headerdir) == 0: if header_dir is None or len(header_dir) == 0:
raise AssertionError("headerdir is not specified") raise AssertionError('header_dir is not specified')
self.__headerdir = headerdir self.filenames = _get_filenames(header_dir)
self.__verbose = verbose self.debug_dir = debug_dir
self.verbose = verbose
self.platforms = ["windows", "mac", "linux"] self.versioned_contents = {}
self.platformed_contents = {}
cef_dir = os.path.abspath(os.path.join(self.__headerdir, os.pardir)) self.file_content_objs = {}
# Read the variables list from the autogenerated cef_paths.gypi file.
cef_paths = eval_file(os.path.join(cef_dir, 'cef_paths.gypi'))
cef_paths = cef_paths['variables']
# Read the variables list from the manually edited cef_paths2.gypi file.
cef_paths2 = eval_file(os.path.join(cef_dir, 'cef_paths2.gypi'))
cef_paths2 = cef_paths2['variables']
# Excluded files (paths relative to the include/ directory).
excluded_files = []
# List of platform-specific C API include/ files.
self.platform_files = {
"windows":
self.__get_filenames(cef_dir, cef_paths2['includes_win_capi'],
excluded_files),
"mac":
self.__get_filenames(cef_dir, cef_paths2['includes_mac_capi'],
excluded_files),
"linux":
self.__get_filenames(cef_dir, cef_paths2['includes_linux_capi'],
excluded_files)
}
# List of all C API include/ files.
paths = cef_paths2['includes_capi'] + cef_paths2['includes_common_capi'] + \
cef_paths2['includes_linux_capi'] + cef_paths2['includes_mac_capi'] + \
cef_paths2['includes_win_capi'] + cef_paths['autogen_capi_includes']
self.filenames = self.__get_filenames(cef_dir, paths, excluded_files)
self.filecontents = {}
self.filecontentobjs = {}
# Cache values that will not change between calls to calculate(). # Cache values that will not change between calls to calculate().
for filename in self.filenames: for filename in self.filenames:
if self.__verbose: self.print(f'Processing {filename} ...')
print("Processing " + filename + "...")
assert not filename in self.filecontents, filename if filename in self.versioned_contents \
assert not filename in self.filecontentobjs, filename or filename in self.platformed_contents \
or filename in self.file_content_objs:
self.print(f'{filename} already processed, skipping...')
continue
content = read_file(os.path.join(self.__headerdir, filename), True) content = read_file(os.path.join(header_dir, filename), normalize=True)
content_objects = None content_objects = None
# Parse cef_string.h happens in special case: grab only defined CEF_STRING_TYPE_xxx declaration is_platform_specific = OS_TOKEN in content
if filename == "internal/cef_string.h": is_version_specific = API_TOKEN in content
content_objects = self.__parse_string_type(content)
elif content.find('#if CEF_API') >= 0: # Special case for parsing cef_string.h:
# Needs to be passed to clang with version-specific defines. # Only extract declarations of the form #define CEF_STRING_TYPE_xxx
self.filecontents[filename] = content if filename == 'internal/cef_string.h':
content_objects = _parse_string_type(filename, content)
elif is_platform_specific and not is_version_specific:
# Parse once and cache for all platforms
content_objects = parse_platformed_content(
filename,
content,
debug_dir,
verbose,
api_version=None,
added_defines=[])
elif is_platform_specific and is_version_specific:
# Needs to be passed to clang with version and platform defines.
self.platformed_contents[filename] = content
elif is_version_specific:
# Needs to be passed to clang with version defines.
self.versioned_contents[filename] = content
else: else:
content_objects = self.__parse_objects(content) content_objects = _parse_objects(filename, content, 'universal')
if not content_objects is None: if content_objects is not None:
self.__prepare_objects(filename, content_objects) self.file_content_objs[filename] = content_objects
self.filecontentobjs[filename] = content_objects
def calculate(self, api_version, debug_dir=None, added_defines=None): def calculate(self, api_version: str, added_defines: list) -> Dict[str, str]:
debug_enabled = not (debug_dir is None) and len(debug_dir) > 0 """ Calculate the API hash per platform for the specified API version """
debug_dir = os.path.join(self.debug_dir,
api_version) if self.debug_dir else None
objects = [] objects = []
for filename in self.filenames: for filename in self.filenames:
if self.__verbose: self.print(f'Processing {filename}...')
print("Processing " + filename + "...")
content = self.filecontents.get(filename, None) if filename in self.versioned_contents:
if not content is None: content = self.versioned_contents.get(filename, None)
assert content.find('#if CEF_API') >= 0, filename content_objects = parse_versioned_content(filename, content,
content = _run_clang_eval(filename, content, api_version, added_defines, api_version, added_defines,
self.__verbose) debug_dir, self.verbose)
if content is None: elif filename in self.platformed_contents:
sys.stderr.write( content = self.platformed_contents.get(filename, None)
'ERROR: Failed to compute API hash for %s\n' % filename) content_objects = parse_platformed_content(filename, content, debug_dir,
return False self.verbose, api_version,
if debug_enabled: added_defines)
self.__write_debug_file(
debug_dir, 'clang-' + filename.replace('/', '-'), content)
# content must always start with newline as required by __parse_objects()
content_objects = self.__parse_objects('\n' + content)
self.__prepare_objects(filename, content_objects)
else: else:
content_objects = self.filecontentobjs.get(filename, None) content_objects = self.file_content_objs.get(filename, None)
assert not content_objects is None, filename assert content_objects, f'content_objects is None for {filename}'
objects.extend(content_objects) objects.extend(content_objects)
# objects will be sorted including filename, to make stable universal hashes # objects will be sorted including filename to make stable hashes
objects = sorted(objects, key=lambda o: o["name"] + "@" + o["filename"]) objects = sorted(objects, key=lambda o: f"{o['name']}@{o['filename']}")
if debug_enabled: if debug_dir:
namelen = max([len(o["name"]) for o in objects]) _save_objects_to_file(debug_dir, 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(debug_dir, "objects.txt", dumpsig)
revisions = {} hashes = {}
for platform in PLATFORM_DEFINES.keys():
sig = _get_final_sig(objects, platform)
if debug_dir:
_write_debug_file(debug_dir, f'{platform}.sig', sig)
hashes[platform] = hashlib.sha1(sig.encode('utf-8')).hexdigest()
for platform in itertools.chain(["universal"], self.platforms): return hashes
sig = self.__get_final_sig(objects, platform)
if debug_enabled:
self.__write_debug_file(debug_dir, platform + ".sig", sig)
revstr = hashlib.sha1(sig.encode('utf-8')).hexdigest()
revisions[platform] = revstr
return revisions def print(self, msg):
if self.verbose:
def __parse_objects(self, content): print(msg)
""" Returns array of objects in content file. """
objects = []
content = re.sub(r"//.*\n", "", content)
# function declarations
for m in re.finditer(
r"\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(
r"\ntypedef\s+?struct\s+?(\w+)\s+?\{.*?\}\s+?(\w+)\s*?;",
content,
flags=re.DOTALL):
text = m.group(0).strip()
# remove 'CEF_CALLBACK' to normalize cross-platform clang output
text = text.replace('CEF_CALLBACK', '')
object = {"name": m.group(2), "text": text}
objects.append(object)
# enums
for m in re.finditer(
r"\ntypedef\s+?enum\s+?\{.*?\}\s+?(\w+)\s*?;", content,
flags=re.DOTALL):
object = {"name": m.group(1), "text": m.group(0).strip()}
objects.append(object)
# typedefs
for m in re.finditer(r"\ntypedef\s+?.*?\s+(\w+);", content, flags=0):
object = {"name": m.group(1), "text": m.group(0).strip()}
objects.append(object)
return objects
def __prepare_objects(self, filename, objects):
platforms = list(
[p for p in self.platforms if self.__is_platform_filename(filename, p)])
for o in objects:
o["text"] = self.__prepare_text(o["text"])
o["platforms"] = platforms
o["filename"] = filename
def __parse_string_type(self, content):
""" Grab defined CEF_STRING_TYPE_xxx """
objects = []
for m in re.finditer(
r"\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(r"\s+", " ", text)
text = re.sub(r"\(\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, cef_dir, paths, excluded_files):
""" Returns file names to be processed, relative to headerdir """
filenames = [
os.path.relpath(os.path.join(cef_dir, filename),
self.__headerdir).replace('\\', '/').lower()
for filename in paths
]
if len(excluded_files) == 0:
return filenames
return [
filename for filename in filenames if not filename in excluded_files
]
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, debug_dir, filename, content):
make_dir(debug_dir)
outfile = os.path.join(debug_dir, filename)
dir = os.path.dirname(outfile)
make_dir(dir)
if not isinstance(content, string_type):
content = "\n".join(content)
write_file(outfile, content)
if __name__ == "__main__": if __name__ == '__main__':
from optparse import OptionParser from optparse import OptionParser
import time
disc = """ parser = OptionParser(description='This utility calculates CEF API hash')
This utility calculates CEF API hash.
"""
parser = OptionParser(description=disc)
parser.add_option( parser.add_option(
'--cpp-header-dir', '--cpp-header-dir',
dest='cppheaderdir', dest='cpp_header_dir',
metavar='DIR', metavar='DIR',
help='input directory for C++ header files [required]') help='input directory for C++ header files [required]')
parser.add_option( parser.add_option(
'--debug-dir', '--debug-dir',
dest='debugdir', dest='debug_dir',
metavar='DIR', metavar='DIR',
help='intermediate directory for easy debugging') help='intermediate directory for easy debugging')
parser.add_option( parser.add_option(
@ -352,28 +389,16 @@ if __name__ == "__main__":
help='output detailed status information') help='output detailed status information')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# the cppheader option is required # the cpp_header_dir option is required
if options.cppheaderdir is None: if options.cpp_header_dir is None:
parser.print_help(sys.stdout) parser.print_help(sys.stdout)
sys.exit() sys.exit()
# calculate calc = CefApiHasher(options.cpp_header_dir, options.debug_dir,
c_start_time = time.time() options.verbose)
revisions = calc.calculate(EXP_VERSION, [])
calc = cef_api_hash(options.cppheaderdir, options.debugdir, options.verbose) print('{')
revisions = calc.calculate(api_version=EXP_VERSION) for k in sorted(revisions.keys()):
print(format('"' + k + '"', '>12s') + ': "' + revisions[k] + '"')
c_completed_in = time.time() - c_start_time print('}')
if bool(revisions):
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();

375
tools/cef_api_hash_test.py Normal file
View File

@ -0,0 +1,375 @@
# Copyright (c) 2025 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.
# Execute the following command to run unit tests:
# python3 -m unittest discover -s tools -p *_test.py
from cef_api_hash import *
from collections import Counter
import unittest
# Test constants:
FILENAME = 'test.h'
NONE_DEBUG_DIR = None
VERBOSE = False
ADDED_DEFINES = []
CEF_TYPES_WIN = """#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
#pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h"
#endif
#if defined(OS_WIN)
#if !defined(GENERATING_CEF_API_HASH)
#include <windows.h>
#endif
#include "include/cef_api_hash.h"
#include "include/internal/cef_types_runtime.h"
#define kNullCursorHandle NULL
#define kNullEventHandle NULL
#define kNullWindowHandle NULL
#ifdef __cplusplus
extern "C" {
#endif
// Handle types.
typedef HWND cef_window_handle_t;
typedef struct _cef_window_info_t {
size_t size;
DWORD ex_style;
HMENU menu;
cef_window_handle_t parent_window;
cef_runtime_style_t runtime_style;
#if CEF_API_ADDED(13304)
int api_version_test;
#endif
} cef_window_info_t;
#ifdef __cplusplus
}
#endif
#endif // OS_WIN
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
"""
CEF_TYPES_MAC = """#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
#pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h"
#endif
#include <this_header_does_not_exist.h>
#if defined(OS_MAC)
#include "include/internal/cef_string.h"
#include "include/internal/cef_types_geometry.h"
#include "include/internal/cef_types_runtime.h"
#define kNullCursorHandle NULL
#ifdef __OBJC__
#if __has_feature(objc_arc)
#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((__bridge NSCursor*)handle)
#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((__bridge NSView*)handle)
#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((__bridge void*)cursor)
#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((__bridge void*)view)
#else // __has_feature(objc_arc)
#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((NSCursor*)handle)
#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((NSView*)handle)
#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((void*)cursor)
#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((void*)view)
#endif // __has_feature(objc_arc)
#endif // __OBJC__
#ifdef __cplusplus
extern "C" {
#endif
// Actually NSView*
typedef void* cef_window_handle_t;
///
/// Class representing window information.
///
typedef struct _cef_window_info_t {
size_t size;
cef_string_t window_name;
cef_rect_t bounds;
cef_window_handle_t view;
cef_runtime_style_t runtime_style;
} cef_window_info_t;
#ifdef __cplusplus
}
#endif
#endif // OS_MAC
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
"""
CEF_TYPES_LINUX = """#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
#pragma once
#if !defined(GENERATING_CEF_API_HASH)
#include "include/base/cef_build.h"
#endif
#if defined(OS_LINUX)
#include "include/internal/cef_export.h"
#include "include/internal/cef_string.h"
#include "include/internal/cef_types_color.h"
#include "include/internal/cef_types_geometry.h"
#include "include/internal/cef_types_osr.h"
#include "include/internal/cef_types_runtime.h"
#define kNullCursorHandle 0
#define kNullEventHandle NULL
#define kNullWindowHandle 0
#ifdef __cplusplus
extern "C" {
#endif
#if defined(CEF_X11)
typedef union _XEvent XEvent;
typedef struct _XDisplay XDisplay;
// Handle types.
typedef XEvent* cef_event_handle_t;
#else
typedef void* cef_event_handle_t;
#endif
typedef unsigned long cef_window_handle_t;
///
/// Return the singleton X11 display shared with Chromium. The display is not
/// thread-safe and must only be accessed on the browser process UI thread.
///
#if defined(CEF_X11)
CEF_EXPORT XDisplay* cef_get_xdisplay(void);
#endif
typedef struct _cef_window_info_t {
size_t size;
cef_string_t window_name;
cef_rect_t bounds;
cef_window_handle_t parent_window;
cef_window_handle_t window;
cef_runtime_style_t runtime_style;
} cef_window_info_t;
#ifdef __cplusplus
}
#endif
#endif // OS_LINUX
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
"""
CEF_TYPES = """#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
#define CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
#pragma once
#include "include/cef_api_hash.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CEF_CPAIT_OPTIMIZATION_GUIDE,
#if CEF_API_ADDED(13304)
CEF_CPAIT_COLLABORATION_MESSAGING,
#endif
CEF_CPAIT_NUM_VALUES,
} cef_chrome_page_action_icon_type_t;
#ifdef __cplusplus
}
#endif
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
"""
class TestRunClangEval(unittest.TestCase):
def test_parse_platformed_content_version_13304(self):
expected_objects = [{
'filename': FILENAME,
'name': 'cef_window_handle_t',
'platform': 'windows',
'text': 'typedef HWND cef_window_handle_t;'
}, {
'filename':
FILENAME,
'name':
'cef_window_info_t',
'platform':
'windows',
'text':
'typedef struct _cef_window_info_t { size_t size; DWORD ex_style; HMENU menu; cef_window_handle_t parent_window; cef_runtime_style_t runtime_style; int api_version_test; } cef_window_info_t;'
}]
objects = parse_platformed_content(FILENAME, CEF_TYPES_WIN, NONE_DEBUG_DIR,
VERBOSE, '13304', ADDED_DEFINES)
self.__assert_equal_objects(objects, expected_objects)
def test_parse_platformed_content_version_13300(self):
expected_objects = [{
'filename': FILENAME,
'name': 'cef_window_handle_t',
'platform': 'windows',
'text': 'typedef HWND cef_window_handle_t;'
}, {
'filename':
FILENAME,
'name':
'cef_window_info_t',
'platform':
'windows',
'text':
'typedef struct _cef_window_info_t { size_t size; DWORD ex_style; HMENU menu; cef_window_handle_t parent_window; cef_runtime_style_t runtime_style; } cef_window_info_t;'
}]
objects = parse_platformed_content(FILENAME, CEF_TYPES_WIN, NONE_DEBUG_DIR,
VERBOSE, '13300', ADDED_DEFINES)
self.__assert_equal_objects(objects, expected_objects)
def test_parse_platformed_content_mac(self):
expected_objects = [{
'filename': FILENAME,
'name': 'cef_window_handle_t',
'platform': 'mac',
'text': 'typedef void* cef_window_handle_t;'
}, {
'filename':
FILENAME,
'name':
'cef_window_info_t',
'platform':
'mac',
'text':
'typedef struct _cef_window_info_t { size_t size; cef_string_t window_name; cef_rect_t bounds; cef_window_handle_t view; cef_runtime_style_t runtime_style; } cef_window_info_t;'
}]
objects = parse_platformed_content(
FILENAME,
CEF_TYPES_MAC,
NONE_DEBUG_DIR,
VERBOSE,
api_version=None,
added_defines=[])
self.__assert_equal_objects(objects, expected_objects)
def test_parse_platformed_content_linux(self):
expected_objects = [{
'filename': FILENAME,
'name': 'XEvent',
'platform': 'linux',
'text': 'typedef union _XEvent XEvent;'
}, {
'filename': FILENAME,
'name': 'XDisplay',
'platform': 'linux',
'text': 'typedef struct _XDisplay XDisplay;'
}, {
'filename': FILENAME,
'name': 'cef_event_handle_t',
'platform': 'linux',
'text': 'typedef XEvent* cef_event_handle_t;'
}, {
'filename': FILENAME,
'name': 'cef_window_handle_t',
'platform': 'linux',
'text': 'typedef unsigned long cef_window_handle_t;'
}, {
'filename': FILENAME,
'name': 'cef_get_xdisplay',
'platform': 'linux',
'text': 'CEF_EXPORT XDisplay* cef_get_xdisplay(void);'
}, {
'filename':
FILENAME,
'name':
'cef_window_info_t',
'platform':
'linux',
'text':
'typedef struct _cef_window_info_t { size_t size; cef_string_t window_name; cef_rect_t bounds; cef_window_handle_t parent_window; cef_window_handle_t window; cef_runtime_style_t runtime_style; } cef_window_info_t;'
}]
objects = parse_platformed_content(
FILENAME,
CEF_TYPES_LINUX,
NONE_DEBUG_DIR,
VERBOSE,
api_version=None,
added_defines=[])
self.__assert_equal_objects(objects, expected_objects)
def test_parse_versioned_content_version_13304(self):
expected_objects = [{
'filename':
FILENAME,
'name':
'cef_chrome_page_action_icon_type_t',
'platform':
'universal',
'text':
'typedef enum { CEF_CPAIT_OPTIMIZATION_GUIDE, CEF_CPAIT_COLLABORATION_MESSAGING, CEF_CPAIT_NUM_VALUES, } cef_chrome_page_action_icon_type_t;'
}]
objects = parse_versioned_content(FILENAME, CEF_TYPES, '13304',
ADDED_DEFINES, NONE_DEBUG_DIR, VERBOSE)
self.assertEqual(objects, expected_objects)
def test_parse_versioned_content_version_13303(self):
expected_objects = [{
'filename':
FILENAME,
'name':
'cef_chrome_page_action_icon_type_t',
'platform':
'universal',
'text':
'typedef enum { CEF_CPAIT_OPTIMIZATION_GUIDE, CEF_CPAIT_NUM_VALUES, } cef_chrome_page_action_icon_type_t;'
}]
objects = parse_versioned_content(FILENAME, CEF_TYPES, '13303',
ADDED_DEFINES, NONE_DEBUG_DIR, VERBOSE)
self.assertEqual(objects, expected_objects)
def __assert_equal_objects(self, actual, expected):
# Compare the objects as sets since the order is not guaranteed
expected = Counter(tuple(sorted(d.items())) for d in expected)
actual = Counter(tuple(sorted(d.items())) for d in actual)
self.assertEqual(expected, actual)
if __name__ == '__main__':
unittest.main()

View File

@ -161,7 +161,7 @@ class VersionFormatter:
# - "X" is the Chromium major version (e.g. 74). # - "X" is the Chromium major version (e.g. 74).
# - "Y" is an incremental number that starts at 0 when a release branch is # - "Y" is an incremental number that starts at 0 when a release branch is
# created and changes only when the CEF C/C++ API changes (similar to how # created and changes only when the CEF C/C++ API changes (similar to how
# the CEF_API_HASH_UNIVERSAL value behaves in cef_api_hash.h) (release # the CEF_API_HASH_PLATFORM value behaves in cef_api_hash.h) (release
# branch only). # branch only).
# - "Z" is an incremental number that starts at 0 when a release branch is # - "Z" is an incremental number that starts at 0 when a release branch is
# created and changes on each commit, with reset to 0 when "Y" changes # created and changes on each commit, with reset to 0 when "Y" changes

View File

@ -27,7 +27,7 @@ else:
def clang_format(file_name, file_contents): def clang_format(file_name, file_contents):
# -assume-filename is necessary to find the .clang-format file and determine # -assume-filename is necessary to find the .clang-format file and determine
# the language when specifying contents via stdin. # the language when specifying contents via stdin.
result = exec_cmd("%s -assume-filename=%s" % (clang_format_exe, file_name), \ result = exec_cmd("%s -assume-filename=%s" % (clang_format_exe, file_name),
cef_dir, file_contents.encode('utf-8')) cef_dir, file_contents.encode('utf-8'))
if result['err'] != '': if result['err'] != '':
sys.stderr.write("clang-format error: %s\n" % result['err']) sys.stderr.write("clang-format error: %s\n" % result['err'])
@ -48,40 +48,28 @@ def clang_format_inplace(file_name):
return True return True
def clang_eval(file_name, def clang_eval(file_name, file_contents, defines, includes, verbose):
file_contents, lang = 'c'
defines=[],
includes=[],
as_cpp=True,
verbose=False):
lang = 'c++' if as_cpp else 'c'
if file_name.lower().endswith('.h'): if file_name.lower().endswith('.h'):
lang += '-header' lang += '-header'
# The -P option removes unnecessary line markers and whitespace. # The -P option removes unnecessary line markers and whitespace.
format = '/EP' if sys.platform == 'win32' else '-E -P' format = '/EP' if sys.platform == 'win32' else '-E -P'
sdkroot = '' cmd = "%s -x %s %s %s %s -" % (clang_exe, lang, format,
if sys.platform == 'darwin': ' '.join(['-D' + v for v in defines]),
result = exec_cmd('xcrun --show-sdk-path', '.') ' '.join(['-I' + v for v in includes]))
if result['ret'] == 0:
sdkroot = " -isysroot %s" % result['out'].strip()
cmd = "%s -x %s %s %s %s %s -" % (clang_exe, lang, format,
' '.join(['-D' + v for v in defines]),
' '.join(['-I' + v
for v in includes]), sdkroot)
if verbose: if verbose:
print('--- Running "%s" in "%s"' % (cmd, cef_dir)) print(f'--- Running "{cmd}" in "{cef_dir}"')
result = exec_cmd(cmd, cef_dir, file_contents.encode('utf-8')) result = exec_cmd(cmd, cef_dir, file_contents.encode('utf-8'))
if result['err'] != '': if result['err'] != '' or result['ret'] != 0:
err = result['err'].replace('<stdin>', file_name) error = result['err'].replace('<stdin>', file_name)
sys.stderr.write("clang error: %s\n" % err) return_code = result['ret']
sys.stderr.write(f'clang {return_code=} {error=}\n')
return None return None
if result['out'] != '':
output = result['out'] output = result['out']
if sys.platform == 'win32': if output and sys.platform == 'win32':
# Convert to Unix line endings. # Convert to Unix line endings.
output = output.replace("\r", "") output = output.replace("\r", "")
return output return output
return None

View File

@ -13,29 +13,25 @@ def exec_cmd(cmd, path, input_string=None):
err = '' err = ''
ret = -1 ret = -1
parts = cmd.split() parts = cmd.split()
try:
if input_string is None: if input_string is None:
process = Popen( process = Popen(
parts, parts,
cwd=path, cwd=path,
stdout=PIPE, stdout=PIPE,
stderr=PIPE, stderr=PIPE,
shell=(sys.platform == 'win32')) shell=(sys.platform == 'win32'))
out, err = process.communicate() out, err = process.communicate()
ret = process.returncode ret = process.returncode
else: else:
process = Popen( process = Popen(
parts, parts,
cwd=path, cwd=path,
stdin=PIPE, stdin=PIPE,
stdout=PIPE, stdout=PIPE,
stderr=PIPE, stderr=PIPE,
shell=(sys.platform == 'win32')) shell=(sys.platform == 'win32'))
out, err = process.communicate(input=input_string) out, err = process.communicate(input=input_string)
ret = process.returncode ret = process.returncode
except IOError as e:
(errno, strerror) = e.args
raise
except:
raise
return {'out': out.decode('utf-8'), 'err': err.decode('utf-8'), 'ret': ret} return {'out': out.decode('utf-8'), 'err': err.decode('utf-8'), 'ret': ret}

View File

@ -11,8 +11,8 @@ import sys
def make_api_versions_header(json): def make_api_versions_header(json):
result = get_copyright(full=True, translator=False) + \ result = get_copyright(
"""// full=True, translator=False) + """//
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// //
// This file was generated by the make_api_versions_header.py tool. Versions // This file was generated by the make_api_versions_header.py tool. Versions
@ -27,42 +27,40 @@ def make_api_versions_header(json):
""" """
for version, hashes in json['hashes'].items(): for version, hashes in json['hashes'].items():
version_part = """ version_part = f"""
// $COMMENT$ // $COMMENT$
#define CEF_API_VERSION_$VER$ $VER$ #define CEF_API_VERSION_{version} {version}
#define CEF_API_HASH_$VER$_UNIVERSAL "$UNIVERSAL$"
#if defined(OS_WIN) #if defined(OS_WIN)
#define CEF_API_HASH_$VER$_PLATFORM "$WINDOWS$" #define CEF_API_HASH_{version} "$WINDOWS$"
#elif defined(OS_MAC) #elif defined(OS_MAC)
#define CEF_API_HASH_$VER$_PLATFORM "$MAC$" #define CEF_API_HASH_{version} "$MAC$"
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#define CEF_API_HASH_$VER$_PLATFORM "$LINUX$" #define CEF_API_HASH_{version} "$LINUX$"
#endif #endif
""".replace('$VER$', version) """
# Substitute hash values for placeholders. # Substitute hash values for placeholders.
for key, value in hashes.items(): for key, value in hashes.items():
version_part = version_part.replace('$%s$' % key.upper(), value) version_part = version_part.replace(f"${key.upper()}$", value)
result += version_part result += version_part
result += \ result += f"""
"""
// Oldest supported CEF version. // Oldest supported CEF version.
#define CEF_API_VERSION_MIN CEF_API_VERSION_$MIN$ #define CEF_API_VERSION_MIN CEF_API_VERSION_{json['min']}
// Newest supported CEF version. // Newest supported CEF version.
#define CEF_API_VERSION_LAST CEF_API_VERSION_$LAST$ #define CEF_API_VERSION_LAST CEF_API_VERSION_{json['last']}
#endif // CEF_INCLUDE_CEF_API_VERSIONS_H_ #endif // CEF_INCLUDE_CEF_API_VERSIONS_H_
""".replace('$LAST$', json['last']).replace('$MIN$', json['min']) """
return result return result
def make_api_versions_inc(json): def make_api_versions_inc(json):
result = get_copyright(full=False, translator=False) + \ result = get_copyright(
"""// full=False, translator=False) + """//
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// //
// This file was generated by the make_api_versions_header.py tool. // This file was generated by the make_api_versions_header.py tool.
@ -72,56 +70,53 @@ namespace {
struct ApiVersionHash { struct ApiVersionHash {
int version; int version;
const char* const universal; const char* const hash;
const char* const platform;
}; };
const ApiVersionHash kApiVersionHashes[] = {""" const ApiVersionHash kApiVersionHashes[] = {"""
for version, hashes in json['hashes'].items(): for version in json['hashes'].keys():
result += """ result += f"\n{{{version}, CEF_API_HASH_{version}}},"
{$VER$, CEF_API_HASH_$VER$_UNIVERSAL, CEF_API_HASH_$VER$_PLATFORM},""".replace(
'$VER$', version)
result += \ result += """
"""
}; };
const size_t kApiVersionHashesSize = std::size(kApiVersionHashes);
} // namespace } // namespace
""" """
return result return result
def write_api_versions(out_header_file, out_inc_file, json): def write_api_versions(out_header_file, out_inc_file, json) -> bool:
"""
Return True if the files were written, False if no changes were made.
"""
out_file = os.path.abspath(out_header_file) out_file = os.path.abspath(out_header_file)
result = make_api_versions_header(json) result = make_api_versions_header(json)
if not bool(result): if not result:
sys.stderr.write('Failed to create %s\n' % out_file) sys.stderr.write(f'Failed to create {out_file}\n')
sys.exit(1) sys.exit(1)
retval1 = write_file_if_changed(out_file, result) header_write_result = write_file_if_changed(out_file, result)
out_file = os.path.abspath(out_inc_file) out_file = os.path.abspath(out_inc_file)
result = make_api_versions_inc(json) result = make_api_versions_inc(json)
if not bool(result): if not result:
sys.stderr.write('Failed to create %s\n' % out_file) sys.stderr.write(f'Failed to create {out_file}\n')
sys.exit(1) sys.exit(1)
retval2 = write_file_if_changed(out_file, result) inc_write_result = write_file_if_changed(out_file, result)
return retval1 or retval2 return header_write_result or inc_write_result
def main(argv): def main(argv):
if len(argv) < 5: if len(argv) < 5:
print( print(
"Usage:\n %s <output_header_file> <output_inc_file> <api_versions_file> <api_untracked_file>" f"Usage:\n {argv[0]} <output_header_file> <output_inc_file> <api_versions_file> <api_untracked_file>"
% argv[0]) )
sys.exit(-1) sys.exit(-1)
json, initialized = \ json = read_version_files(argv[3], argv[4], initialize=True, combine=True)[0]
read_version_files(argv[3], argv[4], True, combine=True)
if not write_api_versions(argv[1], argv[2], json): if not write_api_versions(argv[1], argv[2], json):
print('Nothing done') print('Nothing done')

View File

@ -4,7 +4,8 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from cef_api_hash import cef_api_hash from typing import Dict
from cef_api_hash import CefApiHasher
from cef_version import VersionFormatter from cef_version import VersionFormatter
from date_util import get_date from date_util import get_date
from file_util import read_file, read_json_file, write_file, write_json_file from file_util import read_file, read_json_file, write_file, write_json_file
@ -41,49 +42,37 @@ def get_next_api_revision(api_versions_file, major_version):
return 0 return 0
_CALC = None def compute_api_hashes(api_version: str,
hasher: CefApiHasher,
next_allowed: bool) -> Dict[str, str]:
def compute_api_hashes(cpp_header_dir, api_version, next_allowed, debug_dir, """ Computes API hashes for the specified |api_version|. """
verbose):
""" Computes API hashes for the specified |api_version|.
"""
if not debug_dir is None:
debug_dir = os.path.join(debug_dir, api_version)
if not next_allowed: if not next_allowed:
# Next usage is banned with explicit API versions. # Next usage is banned with explicit API versions.
assert not api_version in UNTRACKED_VERSIONS, api_version assert api_version not in UNTRACKED_VERSIONS, api_version
added_defines = [ added_defines = [
# Using CEF_API_VERSION_NEXT is an error. # Using CEF_API_VERSION_NEXT is an error.
'CEF_API_VERSION_NEXT="Please_specify_an_exact_CEF_version"', 'CEF_API_VERSION_NEXT="Please_specify_an_exact_CEF_version"',
] ]
else: else:
added_defines = None added_defines = []
global _CALC hashes = hasher.calculate(api_version, added_defines)
if _CALC is None: if hashes:
_CALC = cef_api_hash(cpp_header_dir, verbose=verbose)
hashes = _CALC.calculate(api_version, debug_dir, added_defines)
if bool(hashes):
if api_version in UNTRACKED_VERSIONS: if api_version in UNTRACKED_VERSIONS:
label = version_label(api_version) label = version_label(api_version)
label = label[0:1].upper() + label[1:] label = label[0:1].upper() + label[1:]
hashes['comment'] = '%s last updated %s.' % (label, get_date()) hashes['comment'] = f'{label} last updated {get_date()}.'
else: else:
hashes['comment'] = 'Added %s.' % get_date() hashes['comment'] = f'Added {get_date()}.'
return hashes return hashes
def same_api_hashes(hashes1, hashes2): def same_api_hashes(hashes1, hashes2):
for key in ('universal', 'linux', 'mac', 'windows'): return all(hashes1[key] == hashes2[key]
if hashes1[key] != hashes2[key]: for key in ['linux', 'mac', 'windows'])
return False
return True
def compute_next_api_verson(api_versions_file): def compute_next_api_version(api_versions_file):
""" Computes the next available API version number. """ Computes the next available API version number.
""" """
major_version = int(VersionFormatter().get_chrome_major_version()) major_version = int(VersionFormatter().get_chrome_major_version())
@ -207,8 +196,11 @@ def find_replace_next_usage(cpp_header_dir, next_version):
return 0 return 0
def exec_apply(cpp_header_dir, api_versions_file, api_untracked_file, def exec_apply(api_versions_file,
next_version, debug_dir, apply_next, verbose): api_untracked_file,
next_version,
apply_next,
hasher: CefApiHasher) -> int:
""" Updates untracked API hashes if necessary. """ Updates untracked API hashes if necessary.
Saves the hash for the next API version if |apply_next| is true. Saves the hash for the next API version if |apply_next| is true.
""" """
@ -222,9 +214,8 @@ def exec_apply(cpp_header_dir, api_versions_file, api_untracked_file,
untracked_changed = False untracked_changed = False
for version in UNTRACKED_VERSIONS: for version in UNTRACKED_VERSIONS:
label = version_label(version) label = version_label(version)
hashes = compute_api_hashes(cpp_header_dir, version, True, debug_dir, hashes = compute_api_hashes(version, hasher, next_allowed=True)
verbose) if not hashes:
if not bool(hashes):
sys.stderr.write('ERROR: Failed to process %s\n' % label) sys.stderr.write('ERROR: Failed to process %s\n' % label)
return 1 return 1
@ -240,9 +231,8 @@ def exec_apply(cpp_header_dir, api_versions_file, api_untracked_file,
if apply_next: if apply_next:
next_label = version_label(next_version) next_label = version_label(next_version)
hashes = compute_api_hashes(cpp_header_dir, next_version, False, debug_dir, hashes = compute_api_hashes(next_version, hasher, next_allowed=False)
verbose) if not hashes:
if not bool(hashes):
sys.stderr.write('ERROR: Failed to process %s\n' % next_label) sys.stderr.write('ERROR: Failed to process %s\n' % next_label)
return 1 return 1
@ -278,29 +268,33 @@ def exec_apply(cpp_header_dir, api_versions_file, api_untracked_file,
return 0 return 0
def exec_check(cpp_header_dir, api_versions_file, api_untracked_file, debug_dir, def exec_check(api_versions_file,
fast_check, force_update, skip_untracked, verbose): api_untracked_file,
fast_check,
force_update,
skip_untracked,
hasher: CefApiHasher) -> int:
""" Checks existing API version hashes. """ Checks existing API version hashes.
Resaves all API hashes if |force_update| is true. Otherwise, hash Resaves all API hashes if |force_update| is true. Otherwise, hash
changes are considered an error. changes are considered an error.
""" """
assert not (fast_check and force_update) assert not (fast_check and force_update)
json_versions, json_untracked, initialized = \ json_versions, json_untracked, initialized = read_version_files(
read_version_files(api_versions_file, api_untracked_file, False) api_versions_file, api_untracked_file, initialize=False)
assert not initialized assert not initialized
versions = [] versions = []
len_versioned_existing = len_versioned_checked = len_versioned_failed = 0 len_versioned_existing = len_versioned_checked = len_versioned_failed = 0
len_untracked_existing = len_untracked_checked = len_untracked_failed = 0 len_untracked_existing = len_untracked_checked = len_untracked_failed = 0
if not json_versions is None: if json_versions is not None:
keys = json_versions['hashes'].keys() keys = json_versions['hashes'].keys()
len_versioned_existing = len(keys) len_versioned_existing = len(keys)
if len_versioned_existing > 0: if len_versioned_existing > 0:
if fast_check: if fast_check:
# Only checking a subset of versions. # Only checking a subset of versions.
for key in ('last', 'min'): for key in ['last', 'min']:
if key in json_versions: if key in json_versions:
version = json_versions[key] version = json_versions[key]
assert version in json_versions['hashes'], version assert version in json_versions['hashes'], version
@ -310,14 +304,14 @@ def exec_check(cpp_header_dir, api_versions_file, api_untracked_file, debug_dir,
versions.extend(keys) versions.extend(keys)
len_versioned_checked = len_versioned_existing len_versioned_checked = len_versioned_existing
if not json_untracked is None: if json_untracked is not None:
keys = json_untracked['hashes'].keys() keys = json_untracked['hashes'].keys()
len_untracked_existing = len(keys) len_untracked_existing = len(keys)
if len_untracked_existing > 0 and not skip_untracked: if len_untracked_existing > 0 and not skip_untracked:
versions.extend(keys) versions.extend(keys)
len_untracked_checked = len_untracked_existing len_untracked_checked = len_untracked_existing
if len(versions) == 0: if not versions:
print('No hashes to check.') print('No hashes to check.')
return 0 return 0
@ -331,8 +325,7 @@ def exec_check(cpp_header_dir, api_versions_file, api_untracked_file, debug_dir,
else: else:
stored_hashes = json_versions['hashes'][version] stored_hashes = json_versions['hashes'][version]
label = version_label(version) label = version_label(version)
computed_hashes = compute_api_hashes(cpp_header_dir, version, True, computed_hashes = compute_api_hashes(version, hasher, next_allowed=True)
debug_dir, verbose)
if not bool(computed_hashes): if not bool(computed_hashes):
sys.stderr.write('ERROR: Failed to process %s\n' % label) sys.stderr.write('ERROR: Failed to process %s\n' % label)
return 1 return 1
@ -417,7 +410,7 @@ https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md
parser = CustomParser(description=desc, epilog=epilog) parser = CustomParser(description=desc, epilog=epilog)
parser.add_option( parser.add_option(
'--debug-dir', '--debug-dir',
dest='debugdir', dest='debug_dir',
metavar='DIR', metavar='DIR',
help='intermediate directory for easy debugging') help='intermediate directory for easy debugging')
parser.add_option( parser.add_option(
@ -477,7 +470,7 @@ https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md
parser.add_option( parser.add_option(
'--force-update', '--force-update',
action='store_true', action='store_true',
dest='forceupdate', dest='force_update',
default=False, default=False,
help='force update all API hashes (use with -c)') help='force update all API hashes (use with -c)')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
@ -501,7 +494,7 @@ https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md
parser.print_help(sys.stdout) parser.print_help(sys.stdout)
sys.exit(1) sys.exit(1)
next_version = compute_next_api_verson(api_versions_file) next_version = compute_next_api_version(api_versions_file)
if next_version is None: if next_version is None:
sys.exit(1) sys.exit(1)
@ -527,17 +520,13 @@ https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md
changed = translate(cef_dir, verbose=options.verbose) > 0 changed = translate(cef_dir, verbose=options.verbose) > 0
skip_untracked = False skip_untracked = False
hasher = CefApiHasher(cpp_header_dir, options.debug_dir, options.verbose)
if options.update or will_apply_next or changed or not os.path.isfile( if options.update or will_apply_next or changed or not os.path.isfile(
api_untracked_file): api_untracked_file):
skip_untracked = True skip_untracked = True
if exec_apply( if exec_apply(api_versions_file, api_untracked_file, next_version,
cpp_header_dir, options.apply, hasher) > 0:
api_versions_file,
api_untracked_file,
next_version,
options.debugdir,
apply_next=options.apply,
verbose=options.verbose) > 0:
# Apply failed. # Apply failed.
sys.exit(1) sys.exit(1)
elif not options.check: elif not options.check:
@ -545,12 +534,6 @@ https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md
sys.exit(0) sys.exit(0)
sys.exit( sys.exit(
exec_check( exec_check(api_versions_file, api_untracked_file, options.fastcheck and
cpp_header_dir, not options.force_update, options.check and
api_versions_file, options.force_update, skip_untracked, hasher))
api_untracked_file,
options.debugdir,
options.fastcheck and not options.forceupdate,
options.check and options.forceupdate,
skip_untracked,
verbose=options.verbose))

View File

@ -92,7 +92,7 @@ def read_version_files(api_versions_file,
} }
initialized = True initialized = True
else: else:
json_version = None json_versions = None
else: else:
assert 'hashes' in json_versions, api_versions_file assert 'hashes' in json_versions, api_versions_file
assert 'last' in json_versions, api_versions_file assert 'last' in json_versions, api_versions_file