Skip to content

Commit 7191270

Browse files
linesightclaude
andcommitted
Fix first-pass C++ build error for Python 3.14 (and all versions)
Cython must transpile the .pyx file before C++ projects can be compiled, because the C++ code includes cefpython_pyXX_fixed.h which is derived from the Cython-generated header. The previous two-pass re-run hack let the first C++ compile fail noisily, then re-ran the whole script. Now build.py detects a missing API header at startup and calls cython_setup.py --cython-only (new flag) to transpile the .pyx and produce cefpython_pyXX.h before fix_cefpython_api_header_file() and the C++ compilation run. The FIRST_RUN re-run machinery is removed entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9e5d3e4 commit 7191270

2 files changed

Lines changed: 48 additions & 44 deletions

File tree

tools/build.py

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@
9393
ENABLE_PROFILING = False
9494
ENABLE_LINE_TRACING = False
9595

96-
# First run
97-
FIRST_RUN = False
98-
99-
10096
def main():
10197
command_line_args()
10298
print("[build.py] Python version: {ver} {arch}"
@@ -107,21 +103,15 @@ def main():
107103
check_cython_version()
108104
check_directories()
109105
setup_environ()
110-
if os.path.exists(CEFPYTHON_API_HFILE):
111-
fix_cefpython_api_header_file()
112-
if WINDOWS:
113-
compile_cpp_projects_with_setuptools()
114-
elif MAC or LINUX:
115-
compile_cpp_projects_unix()
116-
else:
117-
print("[build.py] INFO: Looks like first run, as"
118-
" cefpython_py{pyver}.h is missing. Skip building"
119-
" C++ projects."
120-
.format(pyver=PYVERSION))
121-
global FIRST_RUN
122-
FIRST_RUN = True
123106
clear_cache()
124107
copy_and_fix_pyx_files()
108+
if not os.path.exists(CEFPYTHON_API_HFILE):
109+
cythonize_pyx_to_generate_header()
110+
fix_cefpython_api_header_file()
111+
if WINDOWS:
112+
compile_cpp_projects_with_setuptools()
113+
elif MAC or LINUX:
114+
compile_cpp_projects_unix()
125115
build_cefpython_module()
126116
fix_cefpython_api_header_file()
127117
install_and_run()
@@ -443,13 +433,6 @@ def compile_cpp_projects_unix():
443433
print("[build.py] Clean C++ projects (--clean flag passed)")
444434
clean_cpp_projects_unix()
445435

446-
# Need to allow continuing even when make fails, as it may
447-
# fail because the "public" function declaration is not yet
448-
# in cefpython API header file, but for it to be generated we need
449-
# to run cython compiling, so in this case you continue even when
450-
# make fails and then run the compile.py script again and this time
451-
# make should succeed.
452-
453436
# -- CLIENT_HANDLER
454437
print("[build.py] ~~ Build CLIENT_HANDLER project")
455438

@@ -672,6 +655,19 @@ def except_all_missing(content):
672655
return lineNumber
673656

674657

658+
def cythonize_pyx_to_generate_header():
659+
"""On first run, transpile the pyx file via cython_setup.py --cython-only
660+
to generate the Cython public API header before C++ compilation."""
661+
print("[build.py] First run: generating Cython API header via transpilation")
662+
os.chdir(BUILD_CEFPYTHON)
663+
command = ("\"{python}\" {tools_dir}/cython_setup.py --cython-only"
664+
.format(python=sys.executable, tools_dir=TOOLS_DIR))
665+
ret = subprocess.call(command, shell=True)
666+
if ret != 0:
667+
print("[build.py] ERROR: Failed to generate Cython API header")
668+
sys.exit(1)
669+
670+
675671
def build_cefpython_module():
676672
# if DEBUG_FLAG:
677673
# ret = subprocess.call("python-dbg setup.py build_ext --inplace"
@@ -719,26 +715,7 @@ def build_cefpython_module():
719715

720716
# Check if built succeeded after pyx files were removed
721717
if ret != 0:
722-
if FIRST_RUN and os.path.exists(CEFPYTHON_API_HFILE):
723-
print("[build.py] INFO: looks like this was first run and"
724-
" building the cefpython module is expected to fail"
725-
" in such case due to cefpython API header file not"
726-
" being generated yet. Will re-run the build.py script"
727-
" programmatically now.")
728-
args = list()
729-
args.append("\"{python}\"".format(python=sys.executable))
730-
args.append(os.path.join(TOOLS_DIR, os.path.basename(__file__)))
731-
assert os.path.basename(__file__) in sys.argv[0]
732-
args.extend(SYS_ARGV_ORIGINAL[1:])
733-
command = " ".join(args)
734-
print("[build.py] Running command: %s" % command)
735-
ret = subprocess.call(command, shell=True)
736-
# Always pass fixed value to sys.exit, read note at
737-
# the top of the script about os.system and sys.exit
738-
# issue.
739-
sys.exit(0 if ret == 0 else 1)
740-
else:
741-
print("[build.py] ERROR: failed to build the cefpython module")
718+
print("[build.py] ERROR: failed to build the cefpython module")
742719
sys.exit(1)
743720

744721
# Move the cefpython module

tools/cython_setup.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,45 @@ def get_libraries_new(self, ext):
9191
FAST_FLAG = False
9292
ENABLE_PROFILING = False
9393
ENABLE_LINE_TRACING = False
94+
CYTHON_ONLY = False
9495

9596
# Cython options. Stop on first error, otherwise hundreds
9697
# of errors appear in the console.
9798
Options.fast_fail = True
9899

99100

101+
def cython_transpile_only():
102+
"""Transpile pyx->cpp+h without C++ compilation to generate the public API header."""
103+
from Cython.Build import cythonize as _cythonize
104+
pyx = "cefpython_py{pyver}.pyx".format(pyver=PYVERSION)
105+
print("[cython_setup.py] Transpile-only: %s" % pyx)
106+
_cythonize(
107+
[Extension(
108+
name="cefpython_py{pyver}".format(pyver=PYVERSION),
109+
sources=[pyx],
110+
language="c++",
111+
)],
112+
include_path=get_include_dirs(),
113+
compiler_directives={"language_level": "3str"},
114+
)
115+
116+
100117
def main():
101118
print("[cython_setup.py] Python version: {ver} {arch}"
102119
.format(ver=platform.python_version(), arch=ARCH_STR))
103120
print("[cython_setup.py] Python executable: %s" % sys.executable)
104121
print("[cython_setup.py] Cython version: %s" % Cython.__version__)
105122

123+
global CYTHON_ONLY
124+
if "--cython-only" in sys.argv:
125+
CYTHON_ONLY = True
126+
sys.argv.remove("--cython-only")
127+
128+
if CYTHON_ONLY:
129+
compile_time_constants()
130+
cython_transpile_only()
131+
return
132+
106133
global FAST_FLAG
107134
if "--fast" in sys.argv:
108135
# Fast mode disables optimization flags

0 commit comments

Comments
 (0)