Main.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. #
  2. # Cython Top Level
  3. #
  4. from __future__ import absolute_import
  5. import os
  6. import re
  7. import sys
  8. import io
  9. if sys.version_info[:2] < (2, 6) or (3, 0) <= sys.version_info[:2] < (3, 3):
  10. sys.stderr.write("Sorry, Cython requires Python 2.6+ or 3.3+, found %d.%d\n" % tuple(sys.version_info[:2]))
  11. sys.exit(1)
  12. try:
  13. from __builtin__ import basestring
  14. except ImportError:
  15. basestring = str
  16. # Do not import Parsing here, import it when needed, because Parsing imports
  17. # Nodes, which globally needs debug command line options initialized to set a
  18. # conditional metaclass. These options are processed by CmdLine called from
  19. # main() in this file.
  20. # import Parsing
  21. from . import Errors
  22. from .StringEncoding import EncodedString
  23. from .Scanning import PyrexScanner, FileSourceDescriptor
  24. from .Errors import PyrexError, CompileError, error, warning
  25. from .Symtab import ModuleScope
  26. from .. import Utils
  27. from . import Options
  28. from . import Version # legacy import needed by old PyTables versions
  29. version = Version.version # legacy attribute - use "Cython.__version__" instead
  30. module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
  31. verbose = 0
  32. class CompilationData(object):
  33. # Bundles the information that is passed from transform to transform.
  34. # (For now, this is only)
  35. # While Context contains every pxd ever loaded, path information etc.,
  36. # this only contains the data related to a single compilation pass
  37. #
  38. # pyx ModuleNode Main code tree of this compilation.
  39. # pxds {string : ModuleNode} Trees for the pxds used in the pyx.
  40. # codewriter CCodeWriter Where to output final code.
  41. # options CompilationOptions
  42. # result CompilationResult
  43. pass
  44. class Context(object):
  45. # This class encapsulates the context needed for compiling
  46. # one or more Cython implementation files along with their
  47. # associated and imported declaration files. It includes
  48. # the root of the module import namespace and the list
  49. # of directories to search for include files.
  50. #
  51. # modules {string : ModuleScope}
  52. # include_directories [string]
  53. # future_directives [object]
  54. # language_level int currently 2 or 3 for Python 2/3
  55. cython_scope = None
  56. def __init__(self, include_directories, compiler_directives, cpp=False,
  57. language_level=2, options=None, create_testscope=True):
  58. # cython_scope is a hack, set to False by subclasses, in order to break
  59. # an infinite loop.
  60. # Better code organization would fix it.
  61. from . import Builtin, CythonScope
  62. self.modules = {"__builtin__" : Builtin.builtin_scope}
  63. self.cython_scope = CythonScope.create_cython_scope(self)
  64. self.modules["cython"] = self.cython_scope
  65. self.include_directories = include_directories
  66. self.future_directives = set()
  67. self.compiler_directives = compiler_directives
  68. self.cpp = cpp
  69. self.options = options
  70. self.pxds = {} # full name -> node tree
  71. self._interned = {} # (type(value), value, *key_args) -> interned_value
  72. standard_include_path = os.path.abspath(os.path.normpath(
  73. os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
  74. self.include_directories = include_directories + [standard_include_path]
  75. self.set_language_level(language_level)
  76. self.gdb_debug_outputwriter = None
  77. def set_language_level(self, level):
  78. self.language_level = level
  79. if level >= 3:
  80. from .Future import print_function, unicode_literals, absolute_import, division
  81. self.future_directives.update([print_function, unicode_literals, absolute_import, division])
  82. self.modules['builtins'] = self.modules['__builtin__']
  83. def intern_ustring(self, value, encoding=None):
  84. key = (EncodedString, value, encoding)
  85. try:
  86. return self._interned[key]
  87. except KeyError:
  88. pass
  89. value = EncodedString(value)
  90. if encoding:
  91. value.encoding = encoding
  92. self._interned[key] = value
  93. return value
  94. def intern_value(self, value, *key):
  95. key = (type(value), value) + key
  96. try:
  97. return self._interned[key]
  98. except KeyError:
  99. pass
  100. self._interned[key] = value
  101. return value
  102. # pipeline creation functions can now be found in Pipeline.py
  103. def process_pxd(self, source_desc, scope, module_name):
  104. from . import Pipeline
  105. if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx':
  106. source = CompilationSource(source_desc, module_name, os.getcwd())
  107. result_sink = create_default_resultobj(source, self.options)
  108. pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink)
  109. result = Pipeline.run_pipeline(pipeline, source)
  110. else:
  111. pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name)
  112. result = Pipeline.run_pipeline(pipeline, source_desc)
  113. return result
  114. def nonfatal_error(self, exc):
  115. return Errors.report_error(exc)
  116. def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1,
  117. absolute_fallback=True):
  118. # Finds and returns the module scope corresponding to
  119. # the given relative or absolute module name. If this
  120. # is the first time the module has been requested, finds
  121. # the corresponding .pxd file and process it.
  122. # If relative_to is not None, it must be a module scope,
  123. # and the module will first be searched for relative to
  124. # that module, provided its name is not a dotted name.
  125. debug_find_module = 0
  126. if debug_find_module:
  127. print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
  128. module_name, relative_to, pos, need_pxd))
  129. scope = None
  130. pxd_pathname = None
  131. if relative_to:
  132. if module_name:
  133. # from .module import ...
  134. qualified_name = relative_to.qualify_name(module_name)
  135. else:
  136. # from . import ...
  137. qualified_name = relative_to.qualified_name
  138. scope = relative_to
  139. relative_to = None
  140. else:
  141. qualified_name = module_name
  142. if not module_name_pattern.match(qualified_name):
  143. raise CompileError(pos or (module_name, 0, 0),
  144. "'%s' is not a valid module name" % module_name)
  145. if relative_to:
  146. if debug_find_module:
  147. print("...trying relative import")
  148. scope = relative_to.lookup_submodule(module_name)
  149. if not scope:
  150. pxd_pathname = self.find_pxd_file(qualified_name, pos)
  151. if pxd_pathname:
  152. scope = relative_to.find_submodule(module_name)
  153. if not scope:
  154. if debug_find_module:
  155. print("...trying absolute import")
  156. if absolute_fallback:
  157. qualified_name = module_name
  158. scope = self
  159. for name in qualified_name.split("."):
  160. scope = scope.find_submodule(name)
  161. if debug_find_module:
  162. print("...scope = %s" % scope)
  163. if not scope.pxd_file_loaded:
  164. if debug_find_module:
  165. print("...pxd not loaded")
  166. if not pxd_pathname:
  167. if debug_find_module:
  168. print("...looking for pxd file")
  169. # Only look in sys.path if we are explicitly looking
  170. # for a .pxd file.
  171. pxd_pathname = self.find_pxd_file(qualified_name, pos, sys_path=need_pxd)
  172. if debug_find_module:
  173. print("......found %s" % pxd_pathname)
  174. if not pxd_pathname and need_pxd:
  175. # Set pxd_file_loaded such that we don't need to
  176. # look for the non-existing pxd file next time.
  177. scope.pxd_file_loaded = True
  178. package_pathname = self.search_include_directories(qualified_name, ".py", pos)
  179. if package_pathname and package_pathname.endswith('__init__.py'):
  180. pass
  181. else:
  182. error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep))
  183. if pxd_pathname:
  184. scope.pxd_file_loaded = True
  185. try:
  186. if debug_find_module:
  187. print("Context.find_module: Parsing %s" % pxd_pathname)
  188. rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1]
  189. if not pxd_pathname.endswith(rel_path):
  190. rel_path = pxd_pathname # safety measure to prevent printing incorrect paths
  191. source_desc = FileSourceDescriptor(pxd_pathname, rel_path)
  192. err, result = self.process_pxd(source_desc, scope, qualified_name)
  193. if err:
  194. raise err
  195. (pxd_codenodes, pxd_scope) = result
  196. self.pxds[module_name] = (pxd_codenodes, pxd_scope)
  197. except CompileError:
  198. pass
  199. return scope
  200. def find_pxd_file(self, qualified_name, pos, sys_path=True):
  201. # Search include path (and sys.path if sys_path is True) for
  202. # the .pxd file corresponding to the given fully-qualified
  203. # module name.
  204. # Will find either a dotted filename or a file in a
  205. # package directory. If a source file position is given,
  206. # the directory containing the source file is searched first
  207. # for a dotted filename, and its containing package root
  208. # directory is searched first for a non-dotted filename.
  209. pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=sys_path)
  210. if pxd is None: # XXX Keep this until Includes/Deprecated is removed
  211. if (qualified_name.startswith('python') or
  212. qualified_name in ('stdlib', 'stdio', 'stl')):
  213. standard_include_path = os.path.abspath(os.path.normpath(
  214. os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
  215. deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
  216. self.include_directories.append(deprecated_include_path)
  217. try:
  218. pxd = self.search_include_directories(qualified_name, ".pxd", pos)
  219. finally:
  220. self.include_directories.pop()
  221. if pxd:
  222. name = qualified_name
  223. if name.startswith('python'):
  224. warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
  225. elif name in ('stdlib', 'stdio'):
  226. warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
  227. elif name in ('stl'):
  228. warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
  229. if pxd is None and Options.cimport_from_pyx:
  230. return self.find_pyx_file(qualified_name, pos)
  231. return pxd
  232. def find_pyx_file(self, qualified_name, pos):
  233. # Search include path for the .pyx file corresponding to the
  234. # given fully-qualified module name, as for find_pxd_file().
  235. return self.search_include_directories(qualified_name, ".pyx", pos)
  236. def find_include_file(self, filename, pos):
  237. # Search list of include directories for filename.
  238. # Reports an error and returns None if not found.
  239. path = self.search_include_directories(filename, "", pos,
  240. include=True)
  241. if not path:
  242. error(pos, "'%s' not found" % filename)
  243. return path
  244. def search_include_directories(self, qualified_name, suffix, pos,
  245. include=False, sys_path=False):
  246. return Utils.search_include_directories(
  247. tuple(self.include_directories), qualified_name, suffix, pos, include, sys_path)
  248. def find_root_package_dir(self, file_path):
  249. return Utils.find_root_package_dir(file_path)
  250. def check_package_dir(self, dir, package_names):
  251. return Utils.check_package_dir(dir, tuple(package_names))
  252. def c_file_out_of_date(self, source_path, output_path):
  253. if not os.path.exists(output_path):
  254. return 1
  255. c_time = Utils.modification_time(output_path)
  256. if Utils.file_newer_than(source_path, c_time):
  257. return 1
  258. pos = [source_path]
  259. pxd_path = Utils.replace_suffix(source_path, ".pxd")
  260. if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
  261. return 1
  262. for kind, name in self.read_dependency_file(source_path):
  263. if kind == "cimport":
  264. dep_path = self.find_pxd_file(name, pos)
  265. elif kind == "include":
  266. dep_path = self.search_include_directories(name, pos)
  267. else:
  268. continue
  269. if dep_path and Utils.file_newer_than(dep_path, c_time):
  270. return 1
  271. return 0
  272. def find_cimported_module_names(self, source_path):
  273. return [ name for kind, name in self.read_dependency_file(source_path)
  274. if kind == "cimport" ]
  275. def is_package_dir(self, dir_path):
  276. return Utils.is_package_dir(dir_path)
  277. def read_dependency_file(self, source_path):
  278. dep_path = Utils.replace_suffix(source_path, ".dep")
  279. if os.path.exists(dep_path):
  280. f = open(dep_path, "rU")
  281. chunks = [ line.strip().split(" ", 1)
  282. for line in f.readlines()
  283. if " " in line.strip() ]
  284. f.close()
  285. return chunks
  286. else:
  287. return ()
  288. def lookup_submodule(self, name):
  289. # Look up a top-level module. Returns None if not found.
  290. return self.modules.get(name, None)
  291. def find_submodule(self, name):
  292. # Find a top-level module, creating a new one if needed.
  293. scope = self.lookup_submodule(name)
  294. if not scope:
  295. scope = ModuleScope(name,
  296. parent_module = None, context = self)
  297. self.modules[name] = scope
  298. return scope
  299. def parse(self, source_desc, scope, pxd, full_module_name):
  300. if not isinstance(source_desc, FileSourceDescriptor):
  301. raise RuntimeError("Only file sources for code supported")
  302. source_filename = source_desc.filename
  303. scope.cpp = self.cpp
  304. # Parse the given source file and return a parse tree.
  305. num_errors = Errors.num_errors
  306. try:
  307. with Utils.open_source_file(source_filename) as f:
  308. from . import Parsing
  309. s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
  310. scope = scope, context = self)
  311. tree = Parsing.p_module(s, pxd, full_module_name)
  312. if self.options.formal_grammar:
  313. try:
  314. from ..Parser import ConcreteSyntaxTree
  315. except ImportError:
  316. raise RuntimeError(
  317. "Formal grammar can only be used with compiled Cython with an available pgen.")
  318. ConcreteSyntaxTree.p_module(source_filename)
  319. except UnicodeDecodeError as e:
  320. #import traceback
  321. #traceback.print_exc()
  322. raise self._report_decode_error(source_desc, e)
  323. if Errors.num_errors > num_errors:
  324. raise CompileError()
  325. return tree
  326. def _report_decode_error(self, source_desc, exc):
  327. msg = exc.args[-1]
  328. position = exc.args[2]
  329. encoding = exc.args[0]
  330. line = 1
  331. column = idx = 0
  332. with io.open(source_desc.filename, "r", encoding='iso8859-1', newline='') as f:
  333. for line, data in enumerate(f, 1):
  334. idx += len(data)
  335. if idx >= position:
  336. column = position - (idx - len(data)) + 1
  337. break
  338. return error((source_desc, line, column),
  339. "Decoding error, missing or incorrect coding=<encoding-name> "
  340. "at top of source (cannot decode with encoding %r: %s)" % (encoding, msg))
  341. def extract_module_name(self, path, options):
  342. # Find fully_qualified module name from the full pathname
  343. # of a source file.
  344. dir, filename = os.path.split(path)
  345. module_name, _ = os.path.splitext(filename)
  346. if "." in module_name:
  347. return module_name
  348. names = [module_name]
  349. while self.is_package_dir(dir):
  350. parent, package_name = os.path.split(dir)
  351. if parent == dir:
  352. break
  353. names.append(package_name)
  354. dir = parent
  355. names.reverse()
  356. return ".".join(names)
  357. def setup_errors(self, options, result):
  358. Errors.reset() # clear any remaining error state
  359. if options.use_listing_file:
  360. path = result.listing_file = Utils.replace_suffix(result.main_source_file, ".lis")
  361. else:
  362. path = None
  363. Errors.open_listing_file(path=path,
  364. echo_to_stderr=options.errors_to_stderr)
  365. def teardown_errors(self, err, options, result):
  366. source_desc = result.compilation_source.source_desc
  367. if not isinstance(source_desc, FileSourceDescriptor):
  368. raise RuntimeError("Only file sources for code supported")
  369. Errors.close_listing_file()
  370. result.num_errors = Errors.num_errors
  371. if result.num_errors > 0:
  372. err = True
  373. if err and result.c_file:
  374. try:
  375. Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
  376. except EnvironmentError:
  377. pass
  378. result.c_file = None
  379. def get_output_filename(source_filename, cwd, options):
  380. if options.cplus:
  381. c_suffix = ".cpp"
  382. else:
  383. c_suffix = ".c"
  384. suggested_file_name = Utils.replace_suffix(source_filename, c_suffix)
  385. if options.output_file:
  386. out_path = os.path.join(cwd, options.output_file)
  387. if os.path.isdir(out_path):
  388. return os.path.join(out_path, os.path.basename(suggested_file_name))
  389. else:
  390. return out_path
  391. else:
  392. return suggested_file_name
  393. def create_default_resultobj(compilation_source, options):
  394. result = CompilationResult()
  395. result.main_source_file = compilation_source.source_desc.filename
  396. result.compilation_source = compilation_source
  397. source_desc = compilation_source.source_desc
  398. result.c_file = get_output_filename(source_desc.filename,
  399. compilation_source.cwd, options)
  400. result.embedded_metadata = options.embedded_metadata
  401. return result
  402. def run_pipeline(source, options, full_module_name=None, context=None):
  403. from . import Pipeline
  404. source_ext = os.path.splitext(source)[1]
  405. options.configure_language_defaults(source_ext[1:]) # py/pyx
  406. if context is None:
  407. context = options.create_context()
  408. # Set up source object
  409. cwd = os.getcwd()
  410. abs_path = os.path.abspath(source)
  411. full_module_name = full_module_name or context.extract_module_name(source, options)
  412. if options.relative_path_in_code_position_comments:
  413. rel_path = full_module_name.replace('.', os.sep) + source_ext
  414. if not abs_path.endswith(rel_path):
  415. rel_path = source # safety measure to prevent printing incorrect paths
  416. else:
  417. rel_path = abs_path
  418. source_desc = FileSourceDescriptor(abs_path, rel_path)
  419. source = CompilationSource(source_desc, full_module_name, cwd)
  420. # Set up result object
  421. result = create_default_resultobj(source, options)
  422. if options.annotate is None:
  423. # By default, decide based on whether an html file already exists.
  424. html_filename = os.path.splitext(result.c_file)[0] + ".html"
  425. if os.path.exists(html_filename):
  426. with io.open(html_filename, "r", encoding="UTF-8") as html_file:
  427. if u'<!-- Generated by Cython' in html_file.read(100):
  428. options.annotate = True
  429. # Get pipeline
  430. if source_ext.lower() == '.py' or not source_ext:
  431. pipeline = Pipeline.create_py_pipeline(context, options, result)
  432. else:
  433. pipeline = Pipeline.create_pyx_pipeline(context, options, result)
  434. context.setup_errors(options, result)
  435. err, enddata = Pipeline.run_pipeline(pipeline, source)
  436. context.teardown_errors(err, options, result)
  437. return result
  438. # ------------------------------------------------------------------------
  439. #
  440. # Main Python entry points
  441. #
  442. # ------------------------------------------------------------------------
  443. class CompilationSource(object):
  444. """
  445. Contains the data necessary to start up a compilation pipeline for
  446. a single compilation unit.
  447. """
  448. def __init__(self, source_desc, full_module_name, cwd):
  449. self.source_desc = source_desc
  450. self.full_module_name = full_module_name
  451. self.cwd = cwd
  452. class CompilationOptions(object):
  453. """
  454. Options to the Cython compiler:
  455. show_version boolean Display version number
  456. use_listing_file boolean Generate a .lis file
  457. errors_to_stderr boolean Echo errors to stderr when using .lis
  458. include_path [string] Directories to search for include files
  459. output_file string Name of generated .c file
  460. generate_pxi boolean Generate .pxi file for public declarations
  461. capi_reexport_cincludes
  462. boolean Add cincluded headers to any auto-generated
  463. header files.
  464. timestamps boolean Only compile changed source files.
  465. verbose boolean Always print source names being compiled
  466. compiler_directives dict Overrides for pragma options (see Options.py)
  467. embedded_metadata dict Metadata to embed in the C file as json.
  468. evaluate_tree_assertions boolean Test support: evaluate parse tree assertions
  469. language_level integer The Python language level: 2 or 3
  470. formal_grammar boolean Parse the file with the formal grammar
  471. cplus boolean Compile as c++ code
  472. """
  473. def __init__(self, defaults=None, **kw):
  474. self.include_path = []
  475. if defaults:
  476. if isinstance(defaults, CompilationOptions):
  477. defaults = defaults.__dict__
  478. else:
  479. defaults = default_options
  480. options = dict(defaults)
  481. options.update(kw)
  482. # let's assume 'default_options' contains a value for most known compiler options
  483. # and validate against them
  484. unknown_options = set(options) - set(default_options)
  485. # ignore valid options that are not in the defaults
  486. unknown_options.difference_update(['include_path'])
  487. if unknown_options:
  488. message = "got unknown compilation option%s, please remove: %s" % (
  489. 's' if len(unknown_options) > 1 else '',
  490. ', '.join(unknown_options))
  491. raise ValueError(message)
  492. directives = dict(options['compiler_directives']) # copy mutable field
  493. # check for invalid directives
  494. unknown_directives = set(directives) - set(Options.get_directive_defaults())
  495. if unknown_directives:
  496. message = "got unknown compiler directive%s: %s" % (
  497. 's' if len(unknown_directives) > 1 else '',
  498. ', '.join(unknown_directives))
  499. raise ValueError(message)
  500. options['compiler_directives'] = directives
  501. if directives.get('np_pythran', False) and not options['cplus']:
  502. import warnings
  503. warnings.warn("C++ mode forced when in Pythran mode!")
  504. options['cplus'] = True
  505. if 'language_level' in directives and 'language_level' not in kw:
  506. options['language_level'] = int(directives['language_level'])
  507. if 'formal_grammar' in directives and 'formal_grammar' not in kw:
  508. options['formal_grammar'] = directives['formal_grammar']
  509. if options['cache'] is True:
  510. options['cache'] = os.path.expanduser("~/.cycache")
  511. self.__dict__.update(options)
  512. def configure_language_defaults(self, source_extension):
  513. if source_extension == 'py':
  514. if self.compiler_directives.get('binding') is None:
  515. self.compiler_directives['binding'] = True
  516. def create_context(self):
  517. return Context(self.include_path, self.compiler_directives,
  518. self.cplus, self.language_level, options=self)
  519. class CompilationResult(object):
  520. """
  521. Results from the Cython compiler:
  522. c_file string or None The generated C source file
  523. h_file string or None The generated C header file
  524. i_file string or None The generated .pxi file
  525. api_file string or None The generated C API .h file
  526. listing_file string or None File of error messages
  527. object_file string or None Result of compiling the C file
  528. extension_file string or None Result of linking the object file
  529. num_errors integer Number of compilation errors
  530. compilation_source CompilationSource
  531. """
  532. def __init__(self):
  533. self.c_file = None
  534. self.h_file = None
  535. self.i_file = None
  536. self.api_file = None
  537. self.listing_file = None
  538. self.object_file = None
  539. self.extension_file = None
  540. self.main_source_file = None
  541. class CompilationResultSet(dict):
  542. """
  543. Results from compiling multiple Pyrex source files. A mapping
  544. from source file paths to CompilationResult instances. Also
  545. has the following attributes:
  546. num_errors integer Total number of compilation errors
  547. """
  548. num_errors = 0
  549. def add(self, source, result):
  550. self[source] = result
  551. self.num_errors += result.num_errors
  552. def compile_single(source, options, full_module_name = None):
  553. """
  554. compile_single(source, options, full_module_name)
  555. Compile the given Pyrex implementation file and return a CompilationResult.
  556. Always compiles a single file; does not perform timestamp checking or
  557. recursion.
  558. """
  559. return run_pipeline(source, options, full_module_name)
  560. def compile_multiple(sources, options):
  561. """
  562. compile_multiple(sources, options)
  563. Compiles the given sequence of Pyrex implementation files and returns
  564. a CompilationResultSet. Performs timestamp checking and/or recursion
  565. if these are specified in the options.
  566. """
  567. # run_pipeline creates the context
  568. # context = options.create_context()
  569. sources = [os.path.abspath(source) for source in sources]
  570. processed = set()
  571. results = CompilationResultSet()
  572. timestamps = options.timestamps
  573. verbose = options.verbose
  574. context = None
  575. cwd = os.getcwd()
  576. for source in sources:
  577. if source not in processed:
  578. if context is None:
  579. context = options.create_context()
  580. output_filename = get_output_filename(source, cwd, options)
  581. out_of_date = context.c_file_out_of_date(source, output_filename)
  582. if (not timestamps) or out_of_date:
  583. if verbose:
  584. sys.stderr.write("Compiling %s\n" % source)
  585. result = run_pipeline(source, options, context=context)
  586. results.add(source, result)
  587. # Compiling multiple sources in one context doesn't quite
  588. # work properly yet.
  589. context = None
  590. processed.add(source)
  591. return results
  592. def compile(source, options = None, full_module_name = None, **kwds):
  593. """
  594. compile(source [, options], [, <option> = <value>]...)
  595. Compile one or more Pyrex implementation files, with optional timestamp
  596. checking and recursing on dependencies. The source argument may be a string
  597. or a sequence of strings. If it is a string and no recursion or timestamp
  598. checking is requested, a CompilationResult is returned, otherwise a
  599. CompilationResultSet is returned.
  600. """
  601. options = CompilationOptions(defaults = options, **kwds)
  602. if isinstance(source, basestring) and not options.timestamps:
  603. return compile_single(source, options, full_module_name)
  604. else:
  605. return compile_multiple(source, options)
  606. # ------------------------------------------------------------------------
  607. #
  608. # Main command-line entry point
  609. #
  610. # ------------------------------------------------------------------------
  611. def setuptools_main():
  612. return main(command_line = 1)
  613. def main(command_line = 0):
  614. args = sys.argv[1:]
  615. any_failures = 0
  616. if command_line:
  617. from .CmdLine import parse_command_line
  618. options, sources = parse_command_line(args)
  619. else:
  620. options = CompilationOptions(default_options)
  621. sources = args
  622. if options.show_version:
  623. sys.stderr.write("Cython version %s\n" % version)
  624. if options.working_path!="":
  625. os.chdir(options.working_path)
  626. try:
  627. result = compile(sources, options)
  628. if result.num_errors > 0:
  629. any_failures = 1
  630. except (EnvironmentError, PyrexError) as e:
  631. sys.stderr.write(str(e) + '\n')
  632. any_failures = 1
  633. if any_failures:
  634. sys.exit(1)
  635. # ------------------------------------------------------------------------
  636. #
  637. # Set the default options depending on the platform
  638. #
  639. # ------------------------------------------------------------------------
  640. default_options = dict(
  641. show_version = 0,
  642. use_listing_file = 0,
  643. errors_to_stderr = 1,
  644. cplus = 0,
  645. output_file = None,
  646. annotate = None,
  647. annotate_coverage_xml = None,
  648. generate_pxi = 0,
  649. capi_reexport_cincludes = 0,
  650. working_path = "",
  651. timestamps = None,
  652. verbose = 0,
  653. quiet = 0,
  654. compiler_directives = {},
  655. embedded_metadata = {},
  656. evaluate_tree_assertions = False,
  657. emit_linenums = False,
  658. relative_path_in_code_position_comments = True,
  659. c_line_in_traceback = True,
  660. language_level = 2,
  661. formal_grammar = False,
  662. gdb_debug = False,
  663. compile_time_env = None,
  664. common_utility_include_dir = None,
  665. output_dir=None,
  666. build_dir=None,
  667. cache=None,
  668. create_extension=None,
  669. np_pythran=False
  670. )