2 Compile a Python script into an executable that embeds CPython and run it.
3 Requires CPython to be built as a shared library ('libpythonX.Y').
7 python cythonrun somefile.py [ARGS]
14 from distutils
import sysconfig
16 def get_config_var(name
, default
=''):
17 return sysconfig
.get_config_var(name
) or default
19 INCDIR
= sysconfig
.get_python_inc()
20 LIBDIR1
= get_config_var('LIBDIR')
21 LIBDIR2
= get_config_var('LIBPL')
22 PYLIB
= get_config_var('LIBRARY')
23 PYLIB_DYN
= get_config_var('LDLIBRARY')
24 if PYLIB_DYN
== PYLIB
:
28 PYLIB_DYN
= os
.path
.splitext(PYLIB_DYN
[3:])[0] # 'lib(XYZ).so' -> XYZ
30 CC
= get_config_var('CC', os
.environ
.get('CC', ''))
31 CFLAGS
= get_config_var('CFLAGS') + ' ' + os
.environ
.get('CFLAGS', '')
32 LINKCC
= get_config_var('LINKCC', os
.environ
.get('LINKCC', CC
))
33 LINKFORSHARED
= get_config_var('LINKFORSHARED')
34 LIBS
= get_config_var('LIBS')
35 SYSLIBS
= get_config_var('SYSLIBS')
36 EXE_EXT
= sysconfig
.get_config_var('EXE')
38 def _debug(msg
, *args
):
42 sys
.stderr
.write(msg
+ '\n')
45 _debug('INCDIR: %s', INCDIR
)
46 _debug('LIBDIR1: %s', LIBDIR1
)
47 _debug('LIBDIR2: %s', LIBDIR2
)
48 _debug('PYLIB: %s', PYLIB
)
49 _debug('PYLIB_DYN: %s', PYLIB_DYN
)
51 _debug('CFLAGS: %s', CFLAGS
)
52 _debug('LINKCC: %s', LINKCC
)
53 _debug('LINKFORSHARED: %s', LINKFORSHARED
)
54 _debug('LIBS: %s', LIBS
)
55 _debug('SYSLIBS: %s', SYSLIBS
)
56 _debug('EXE_EXT: %s', EXE_EXT
)
58 def runcmd(cmd
, shell
=True):
67 except ImportError: # Python 2.3 ...
68 returncode
= os
.system(cmd
)
70 returncode
= subprocess
.call(cmd
, shell
=shell
)
76 runcmd([LINKCC
, '-o', basename
+ EXE_EXT
, basename
+'.o', '-L'+LIBDIR1
, '-L'+LIBDIR2
]
77 + [PYLIB_DYN
and ('-l'+PYLIB_DYN
) or os
.path
.join(LIBDIR1
, PYLIB
)]
78 + LIBS
.split() + SYSLIBS
.split() + LINKFORSHARED
.split())
80 def ccompile(basename
):
81 runcmd([CC
, '-c', '-o', basename
+'.o', basename
+'.c', '-I' + INCDIR
] + CFLAGS
.split())
83 def cycompile(input_file
, options
=()):
84 from Cython
.Compiler
import Version
, CmdLine
, Main
85 options
, sources
= CmdLine
.parse_command_line(list(options
or ()) + ['--embed', input_file
])
86 _debug('Using Cython %s to compile %s', Version
.version
, input_file
)
87 result
= Main
.compile(sources
, options
)
88 if result
.num_errors
> 0:
91 def exec_file(program_name
, args
=()):
92 runcmd([os
.path
.abspath(program_name
)] + list(args
), shell
=False)
94 def build(input_file
, compiler_args
=(), force
=False):
96 Build an executable program from a Cython module.
98 Returns the name of the executable file.
100 basename
= os
.path
.splitext(input_file
)[0]
101 exe_file
= basename
+ EXE_EXT
102 if not force
and os
.path
.abspath(exe_file
) == os
.path
.abspath(input_file
):
103 raise ValueError("Input and output file names are the same, refusing to overwrite")
104 if (not force
and os
.path
.exists(exe_file
) and os
.path
.exists(input_file
)
105 and os
.path
.getmtime(input_file
) <= os
.path
.getmtime(exe_file
)):
106 _debug("File is up to date, not regenerating %s", exe_file
)
108 cycompile(input_file
, compiler_args
)
113 def build_and_run(args
):
115 Build an executable program from a Cython module and runs it.
117 Arguments after the module name will be passed verbatimely to the
122 for i
, arg
in enumerate(args
):
123 if arg
.startswith('-'):
125 elif last_arg
in ('-X', '--directive'):
133 raise ValueError('no input file provided')
135 program_name
= build(input_file
, cy_args
)
136 exec_file(program_name
, args
)
138 if __name__
== '__main__':
139 build_and_run(sys
.argv
[1:])