OLD | NEW |
(Empty) | |
| 1 """ |
| 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'). |
| 4 |
| 5 Basic usage: |
| 6 |
| 7 python cythonrun somefile.py [ARGS] |
| 8 """ |
| 9 |
| 10 DEBUG = True |
| 11 |
| 12 import sys |
| 13 import os |
| 14 from distutils import sysconfig |
| 15 |
| 16 def get_config_var(name, default=''): |
| 17 return sysconfig.get_config_var(name) or default |
| 18 |
| 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: |
| 25 # no shared library |
| 26 PYLIB_DYN = '' |
| 27 else: |
| 28 PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ |
| 29 |
| 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') |
| 37 |
| 38 def _debug(msg, *args): |
| 39 if DEBUG: |
| 40 if args: |
| 41 msg = msg % args |
| 42 sys.stderr.write(msg + '\n') |
| 43 |
| 44 def dump_config(): |
| 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) |
| 50 _debug('CC: %s', CC) |
| 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) |
| 57 |
| 58 def runcmd(cmd, shell=True): |
| 59 if shell: |
| 60 cmd = ' '.join(cmd) |
| 61 _debug(cmd) |
| 62 else: |
| 63 _debug(' '.join(cmd)) |
| 64 |
| 65 try: |
| 66 import subprocess |
| 67 except ImportError: # Python 2.3 ... |
| 68 returncode = os.system(cmd) |
| 69 else: |
| 70 returncode = subprocess.call(cmd, shell=shell) |
| 71 |
| 72 if returncode: |
| 73 sys.exit(returncode) |
| 74 |
| 75 def clink(basename): |
| 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()) |
| 79 |
| 80 def ccompile(basename): |
| 81 runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAG
S.split()) |
| 82 |
| 83 def cycompile(input_file, options=()): |
| 84 from Cython.Compiler import Version, CmdLine, Main |
| 85 options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embe
d', 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: |
| 89 sys.exit(1) |
| 90 |
| 91 def exec_file(program_name, args=()): |
| 92 runcmd([os.path.abspath(program_name)] + list(args), shell=False) |
| 93 |
| 94 def build(input_file, compiler_args=(), force=False): |
| 95 """ |
| 96 Build an executable program from a Cython module. |
| 97 |
| 98 Returns the name of the executable file. |
| 99 """ |
| 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) |
| 107 return exe_file |
| 108 cycompile(input_file, compiler_args) |
| 109 ccompile(basename) |
| 110 clink(basename) |
| 111 return exe_file |
| 112 |
| 113 def build_and_run(args): |
| 114 """ |
| 115 Build an executable program from a Cython module and runs it. |
| 116 |
| 117 Arguments after the module name will be passed verbatimely to the |
| 118 program. |
| 119 """ |
| 120 cy_args = [] |
| 121 last_arg = None |
| 122 for i, arg in enumerate(args): |
| 123 if arg.startswith('-'): |
| 124 cy_args.append(arg) |
| 125 elif last_arg in ('-X', '--directive'): |
| 126 cy_args.append(arg) |
| 127 else: |
| 128 input_file = arg |
| 129 args = args[i+1:] |
| 130 break |
| 131 last_arg = arg |
| 132 else: |
| 133 raise ValueError('no input file provided') |
| 134 |
| 135 program_name = build(input_file, cy_args) |
| 136 exec_file(program_name, args) |
| 137 |
| 138 if __name__ == '__main__': |
| 139 build_and_run(sys.argv[1:]) |
OLD | NEW |