BuildExecutable.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. Basic usage:
  5. python cythonrun somefile.py [ARGS]
  6. """
  7. from __future__ import absolute_import
  8. DEBUG = True
  9. import sys
  10. import os
  11. from distutils import sysconfig
  12. def get_config_var(name, default=''):
  13. return sysconfig.get_config_var(name) or default
  14. INCDIR = sysconfig.get_python_inc()
  15. LIBDIR1 = get_config_var('LIBDIR')
  16. LIBDIR2 = get_config_var('LIBPL')
  17. PYLIB = get_config_var('LIBRARY')
  18. PYLIB_DYN = get_config_var('LDLIBRARY')
  19. if PYLIB_DYN == PYLIB:
  20. # no shared library
  21. PYLIB_DYN = ''
  22. else:
  23. PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ
  24. CC = get_config_var('CC', os.environ.get('CC', ''))
  25. CFLAGS = get_config_var('CFLAGS') + ' ' + os.environ.get('CFLAGS', '')
  26. LINKCC = get_config_var('LINKCC', os.environ.get('LINKCC', CC))
  27. LINKFORSHARED = get_config_var('LINKFORSHARED')
  28. LIBS = get_config_var('LIBS')
  29. SYSLIBS = get_config_var('SYSLIBS')
  30. EXE_EXT = sysconfig.get_config_var('EXE')
  31. def _debug(msg, *args):
  32. if DEBUG:
  33. if args:
  34. msg = msg % args
  35. sys.stderr.write(msg + '\n')
  36. def dump_config():
  37. _debug('INCDIR: %s', INCDIR)
  38. _debug('LIBDIR1: %s', LIBDIR1)
  39. _debug('LIBDIR2: %s', LIBDIR2)
  40. _debug('PYLIB: %s', PYLIB)
  41. _debug('PYLIB_DYN: %s', PYLIB_DYN)
  42. _debug('CC: %s', CC)
  43. _debug('CFLAGS: %s', CFLAGS)
  44. _debug('LINKCC: %s', LINKCC)
  45. _debug('LINKFORSHARED: %s', LINKFORSHARED)
  46. _debug('LIBS: %s', LIBS)
  47. _debug('SYSLIBS: %s', SYSLIBS)
  48. _debug('EXE_EXT: %s', EXE_EXT)
  49. def runcmd(cmd, shell=True):
  50. if shell:
  51. cmd = ' '.join(cmd)
  52. _debug(cmd)
  53. else:
  54. _debug(' '.join(cmd))
  55. try:
  56. import subprocess
  57. except ImportError: # Python 2.3 ...
  58. returncode = os.system(cmd)
  59. else:
  60. returncode = subprocess.call(cmd, shell=shell)
  61. if returncode:
  62. sys.exit(returncode)
  63. def clink(basename):
  64. runcmd([LINKCC, '-o', basename + EXE_EXT, basename+'.o', '-L'+LIBDIR1, '-L'+LIBDIR2]
  65. + [PYLIB_DYN and ('-l'+PYLIB_DYN) or os.path.join(LIBDIR1, PYLIB)]
  66. + LIBS.split() + SYSLIBS.split() + LINKFORSHARED.split())
  67. def ccompile(basename):
  68. runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAGS.split())
  69. def cycompile(input_file, options=()):
  70. from ..Compiler import Version, CmdLine, Main
  71. options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embed', input_file])
  72. _debug('Using Cython %s to compile %s', Version.version, input_file)
  73. result = Main.compile(sources, options)
  74. if result.num_errors > 0:
  75. sys.exit(1)
  76. def exec_file(program_name, args=()):
  77. runcmd([os.path.abspath(program_name)] + list(args), shell=False)
  78. def build(input_file, compiler_args=(), force=False):
  79. """
  80. Build an executable program from a Cython module.
  81. Returns the name of the executable file.
  82. """
  83. basename = os.path.splitext(input_file)[0]
  84. exe_file = basename + EXE_EXT
  85. if not force and os.path.abspath(exe_file) == os.path.abspath(input_file):
  86. raise ValueError("Input and output file names are the same, refusing to overwrite")
  87. if (not force and os.path.exists(exe_file) and os.path.exists(input_file)
  88. and os.path.getmtime(input_file) <= os.path.getmtime(exe_file)):
  89. _debug("File is up to date, not regenerating %s", exe_file)
  90. return exe_file
  91. cycompile(input_file, compiler_args)
  92. ccompile(basename)
  93. clink(basename)
  94. return exe_file
  95. def build_and_run(args):
  96. """
  97. Build an executable program from a Cython module and runs it.
  98. Arguments after the module name will be passed verbatimely to the
  99. program.
  100. """
  101. cy_args = []
  102. last_arg = None
  103. for i, arg in enumerate(args):
  104. if arg.startswith('-'):
  105. cy_args.append(arg)
  106. elif last_arg in ('-X', '--directive'):
  107. cy_args.append(arg)
  108. else:
  109. input_file = arg
  110. args = args[i+1:]
  111. break
  112. last_arg = arg
  113. else:
  114. raise ValueError('no input file provided')
  115. program_name = build(input_file, cy_args)
  116. exec_file(program_name, args)
  117. if __name__ == '__main__':
  118. build_and_run(sys.argv[1:])