11#!/usr/bin/env python3
22#
3- # Copyright 2015-2021 the openage authors. See copying.md for legal info.
3+ # Copyright 2015-2025 the openage authors. See copying.md for legal info.
44
55"""
66Runs Cython on all modules that were listed via add_cython_module.
@@ -73,7 +73,7 @@ def remove_if_exists(filename):
7373 filename .unlink ()
7474
7575
76- def cythonize_wrapper (modules , ** kwargs ):
76+ def cythonize_wrapper (modules , force_optimized_lib = False , ** kwargs ):
7777 """ Calls cythonize, filtering useless warnings """
7878 bin_dir , bin_modules = kwargs ['build_dir' ], []
7979 src_dir , src_modules = Path .cwd (), []
@@ -88,13 +88,59 @@ def cythonize_wrapper(modules, **kwargs):
8888 with redirect_stdout (cython_filter ):
8989 if src_modules :
9090 cythonize (src_modules , ** kwargs )
91+ if sys .platform == 'win32' and force_optimized_lib :
92+ win_use_optimized_lib_python (src_modules , bin_dir )
9193
9294 if bin_modules :
9395 os .chdir (bin_dir )
9496 cythonize (bin_modules , ** kwargs )
97+ if sys .platform == 'win32' and force_optimized_lib :
98+ win_use_optimized_lib_python (bin_modules , bin_dir )
9599 os .chdir (src_dir )
96100
97101
102+ def win_use_optimized_lib_python (modules , path ):
103+ """
104+ Add an #ifdef statement in cythonized .cpp files to temporarily undefine _DEBUG before
105+ #include "Python.h"
106+
107+ This function modifies the generated C++ files from Cython to prevent linking to
108+ the debug version of the Python library on Windows. The debug version of the
109+ Python library cannot import Python libraries that contain extension modules.
110+ see: https://github.com/python/cpython/issues/127619 (To unserstand the problem)
111+ see: https://stackoverflow.com/a/59566420 (To understand the soloution)
112+ """
113+
114+ for module in modules :
115+ module = str (module )
116+ if path :
117+ module = path + "\\ " + module
118+ module = module .removesuffix (".py" ).removesuffix (".pyx" )
119+ module = module + ".cpp"
120+ with open (module , "r" , encoding = 'utf8' ) as file :
121+ text = file .read ()
122+ if not text .count ("OPENAGE: UNDEF_DEBUG_INSERTED" ):
123+ text = text .replace (
124+ '#include "Python.h"' ,
125+ (
126+ "\n \n // OPENAGE: UNDEF_DEBUG_INSERTED\n "
127+ "// Avoid linking to the debug version of the Python library on Windows\n \n "
128+ "#ifdef _DEBUG\n "
129+ "#define _DEBUG_WAS_DEFINED\n "
130+ "#undef _DEBUG\n #endif\n "
131+ "#include \" Python.h\" \n "
132+ "#ifdef _DEBUG_WAS_DEFINED\n "
133+ "#define _DEBUG\n "
134+ "#undef _DEBUG_WAS_DEFINED\n "
135+ "#endif\n \n "
136+ "// OPENAGE: UNDEF_DEBUG_INSERTED\n \n "
137+ ),
138+ 1
139+ )
140+ with open (module , "w" , encoding = 'utf8' ) as file :
141+ file .write (text )
142+
143+
98144def main ():
99145 """ CLI entry point """
100146 cli = argparse .ArgumentParser ()
@@ -125,6 +171,8 @@ def main():
125171 ))
126172 cli .add_argument ("--threads" , type = int , default = cpu_count (),
127173 help = "number of compilation threads to use" )
174+ cli .add_argument ("--force_optimized_lib" , action = "store_true" ,
175+ help = "edit compiled .cpp files to link to optimized version of python libary" )
128176 args = cli .parse_args ()
129177
130178 # cython emits warnings on using absolute paths to modules
@@ -159,12 +207,12 @@ def main():
159207 # writing funny lines at the head of each file.
160208 cythonize_args ['language' ] = 'c++'
161209
162- cythonize_wrapper (modules , ** cythonize_args )
210+ cythonize_wrapper (modules , args . force_optimized_lib , ** cythonize_args )
163211
164212 # build standalone executables that embed the py interpreter
165213 Options .embed = "main"
166214
167- cythonize_wrapper (embedded_modules , ** cythonize_args )
215+ cythonize_wrapper (embedded_modules , args . force_optimized_lib , ** cythonize_args )
168216
169217 # verify depends
170218 from Cython .Build .Dependencies import _dep_tree
0 commit comments