7b9c79faedeeeb67ebe47f6de2827f04017ae46e
[cython.git] / Cython / Build / BuildExecutable.py
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):
17     return sysconfig.get_config_var(name) or ''
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 if PYLIB:
24     PYLIB = '-l%s' % PYLIB[3:-2]
25
26 CC = get_config_var('CC')
27 CFLAGS = get_config_var('CFLAGS') + ' ' + os.environ.get('CFLAGS', '')
28 LINKCC = get_config_var('LINKCC')
29 LINKFORSHARED = get_config_var('LINKFORSHARED')
30 LIBS = get_config_var('LIBS')
31 SYSLIBS = get_config_var('SYSLIBS')
32
33 def _debug(msg, *args):
34     if DEBUG:
35         if args:
36             msg = msg % args
37         sys.stderr.write(msg + '\n')
38
39 def dump_config():
40     _debug('INCDIR: %s', INCDIR)
41     _debug('LIBDIR1: %s', LIBDIR1)
42     _debug('LIBDIR2: %s', LIBDIR2)
43     _debug('PYLIB: %s', PYLIB)
44     _debug('CC: %s', CC)
45     _debug('CFLAGS: %s', CFLAGS)
46     _debug('LINKCC: %s', LINKCC)
47     _debug('LINKFORSHARED: %s', LINKFORSHARED)
48     _debug('LIBS: %s', LIBS)
49     _debug('SYSLIBS: %s', SYSLIBS)
50
51 def runcmd(cmd, shell=True):
52     if shell:
53         cmd = ' '.join(cmd)
54         _debug(cmd)
55     else:
56         _debug(' '.join(cmd))
57
58     try:
59         import subprocess
60     except ImportError: # Python 2.3 ...
61         returncode = os.system(cmd)
62     else:
63         returncode = subprocess.call(cmd, shell=shell)
64     
65     if returncode:
66         sys.exit(returncode)
67
68 def clink(basename):
69     runcmd([LINKCC, '-o', basename, basename+'.o', '-L'+LIBDIR1, '-L'+LIBDIR2, PYLIB]
70            + LIBS.split() + SYSLIBS.split() + LINKFORSHARED.split())
71
72 def ccompile(basename):
73     runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAGS.split())
74
75 def cycompile(input_file, options=()):
76     from Cython.Compiler import Version, CmdLine, Main
77     options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embed', input_file])
78     _debug('Using Cython %s to compile %s', Version.version, input_file)
79     result = Main.compile(sources, options)
80     if result.num_errors > 0:
81         sys.exit(1)
82
83 def exec_file(basename, args=()):
84     runcmd([os.path.abspath(basename)] + list(args), shell=False)
85
86 def build(input_file, compiler_args=()):
87     """
88     Build an executable program from a Cython module.
89
90     Returns the name of the executable file.
91     """
92     basename = os.path.splitext(input_file)[0]
93     cycompile(input_file, compiler_args)
94     ccompile(basename)
95     clink(basename)
96     return basename
97
98 def build_and_run(args):
99     """
100     Build an executable program from a Cython module and runs it.
101
102     Arguments after the module name will be passed verbatimely to the
103     program.
104     """
105     cy_args = []
106     last_arg = None
107     for i, arg in enumerate(args):
108         if arg.startswith('-'):
109             cy_args.append(arg)
110         elif last_arg in ('-X', '--directive'):
111             cy_args.append(arg)
112         else:
113             input_file = arg
114             args = args[i+1:]
115             break
116         last_arg = arg
117     else:
118         raise ValueError('no input file provided')
119
120     program_name = build(input_file, cy_args)
121     exec_file(program_name, args)
122
123 if __name__ == '__main__':
124     build_and_run(sys.argv[1:])