2
Compile a Python script into an executable that embeds CPython.
3
Requires CPython to be built as a shared library ('libpythonX.Y').
7
python -m Cython.Build.BuildExecutable [ARGS] somefile.py
15
if sys.version_info < (3, 9):
16
from distutils import sysconfig as _sysconfig
22
assert name == 'include'
23
return _sysconfig.get_python_inc()
25
get_config_var = staticmethod(_sysconfig.get_config_var)
27
# sysconfig can be trusted from cpython >= 3.8.7
31
def get_config_var(name, default=''):
32
return sysconfig.get_config_var(name) or default
34
INCDIR = sysconfig.get_path('include')
35
LIBDIR1 = get_config_var('LIBDIR')
36
LIBDIR2 = get_config_var('LIBPL')
37
PYLIB = get_config_var('LIBRARY')
38
PYLIB_DYN = get_config_var('LDLIBRARY')
43
PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ
45
CC = get_config_var('CC', os.environ.get('CC', ''))
46
CFLAGS = get_config_var('CFLAGS') + ' ' + os.environ.get('CFLAGS', '')
47
LINKCC = get_config_var('LINKCC', os.environ.get('LINKCC', CC))
48
LINKFORSHARED = get_config_var('LINKFORSHARED')
49
LIBS = get_config_var('LIBS')
50
SYSLIBS = get_config_var('SYSLIBS')
51
EXE_EXT = sysconfig.get_config_var('EXE')
54
def _debug(msg, *args):
58
sys.stderr.write(msg + '\n')
62
_debug('INCDIR: %s', INCDIR)
63
_debug('LIBDIR1: %s', LIBDIR1)
64
_debug('LIBDIR2: %s', LIBDIR2)
65
_debug('PYLIB: %s', PYLIB)
66
_debug('PYLIB_DYN: %s', PYLIB_DYN)
68
_debug('CFLAGS: %s', CFLAGS)
69
_debug('LINKCC: %s', LINKCC)
70
_debug('LINKFORSHARED: %s', LINKFORSHARED)
71
_debug('LIBS: %s', LIBS)
72
_debug('SYSLIBS: %s', SYSLIBS)
73
_debug('EXE_EXT: %s', EXE_EXT)
79
for i, arg in enumerate(args):
80
if arg.startswith('-'):
82
elif last_arg in ('-X', '--directive'):
90
raise ValueError('no input file provided')
92
return input_file, cy_args, args
95
def runcmd(cmd, shell=True):
100
_debug(' '.join(cmd))
103
returncode = subprocess.call(cmd, shell=shell)
110
runcmd([LINKCC, '-o', basename + EXE_EXT, basename+'.o', '-L'+LIBDIR1, '-L'+LIBDIR2]
111
+ [PYLIB_DYN and ('-l'+PYLIB_DYN) or os.path.join(LIBDIR1, PYLIB)]
112
+ LIBS.split() + SYSLIBS.split() + LINKFORSHARED.split())
115
def ccompile(basename):
116
runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAGS.split())
119
def cycompile(input_file, options=()):
120
from ..Compiler import Version, CmdLine, Main
121
options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embed', input_file])
122
_debug('Using Cython %s to compile %s', Version.version, input_file)
123
result = Main.compile(sources, options)
124
if result.num_errors > 0:
128
def exec_file(program_name, args=()):
129
runcmd([os.path.abspath(program_name)] + list(args), shell=False)
132
def build(input_file, compiler_args=(), force=False):
134
Build an executable program from a Cython module.
136
Returns the name of the executable file.
138
basename = os.path.splitext(input_file)[0]
139
exe_file = basename + EXE_EXT
140
if not force and os.path.abspath(exe_file) == os.path.abspath(input_file):
141
raise ValueError("Input and output file names are the same, refusing to overwrite")
142
if (not force and os.path.exists(exe_file) and os.path.exists(input_file)
143
and os.path.getmtime(input_file) <= os.path.getmtime(exe_file)):
144
_debug("File is up to date, not regenerating %s", exe_file)
146
cycompile(input_file, compiler_args)
152
def build_and_run(args):
154
Build an executable program from a Cython module and run it.
156
Arguments after the module name will be passed verbatimly to the program.
158
program_name, args = _build(args)
159
exec_file(program_name, args)
163
input_file, cy_args, args = _parse_args(args)
164
program_name = build(input_file, cy_args)
165
return program_name, args
168
if __name__ == '__main__':