Commit 4298c9f9 authored by Robert Bradshaw's avatar Robert Bradshaw

Add a mechanism to store metadata in the generated output file.

This will be useful for, e.g., implementing a fake cythonize that
can leverage the deductions made by the real cythonize, but could
have other uses as well.
parent 760da3f8
...@@ -621,6 +621,7 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa ...@@ -621,6 +621,7 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
to_exclude.update(map(os.path.abspath, extended_iglob(pattern))) to_exclude.update(map(os.path.abspath, extended_iglob(pattern)))
module_list = [] module_list = []
module_metadata = {}
for pattern in patterns: for pattern in patterns:
if isinstance(pattern, str): if isinstance(pattern, str):
filepattern = pattern filepattern = pattern
...@@ -677,7 +678,10 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa ...@@ -677,7 +678,10 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
source = encode_filename_in_py2(source) source = encode_filename_in_py2(source)
if source not in sources: if source not in sources:
sources.append(source) sources.append(source)
extra_sources = kwds['sources']
del kwds['sources'] del kwds['sources']
else:
extra_sources = None
if 'depends' in kwds: if 'depends' in kwds:
depends = resolve_depends(kwds['depends'], (kwds.get('include_dirs') or []) + [find_root_package_dir(file)]) depends = resolve_depends(kwds['depends'], (kwds.get('include_dirs') or []) + [find_root_package_dir(file)])
if template is not None: if template is not None:
...@@ -692,9 +696,12 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa ...@@ -692,9 +696,12 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
name=module_name, name=module_name,
sources=sources, sources=sources,
**kwds)) **kwds))
if extra_sources:
kwds['sources'] = extra_sources
module_metadata[module_name] = {'distutils': kwds}
m = module_list[-1] m = module_list[-1]
seen.add(name) seen.add(name)
return module_list return module_list, module_metadata
# This is the user-exposed entry point. # This is the user-exposed entry point.
...@@ -737,7 +744,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo ...@@ -737,7 +744,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
cpp_options = CompilationOptions(**options); cpp_options.cplus = True cpp_options = CompilationOptions(**options); cpp_options.cplus = True
ctx = c_options.create_context() ctx = c_options.create_context()
options = c_options options = c_options
module_list = create_extension_list( module_list, module_metadata = create_extension_list(
module_list, module_list,
exclude=exclude, exclude=exclude,
ctx=ctx, ctx=ctx,
...@@ -809,7 +816,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo ...@@ -809,7 +816,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
else: else:
fingerprint = None fingerprint = None
to_compile.append((priority, source, c_file, fingerprint, quiet, to_compile.append((priority, source, c_file, fingerprint, quiet,
options, not exclude_failures)) options, not exclude_failures, module_metadata.get(m.name)))
new_sources.append(c_file) new_sources.append(c_file)
if c_file not in modules_by_cfile: if c_file not in modules_by_cfile:
modules_by_cfile[c_file] = [m] modules_by_cfile[c_file] = [m]
...@@ -920,7 +927,7 @@ else: ...@@ -920,7 +927,7 @@ else:
# TODO: Share context? Issue: pyx processing leaks into pxd module # TODO: Share context? Issue: pyx processing leaks into pxd module
@record_results @record_results
def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True): def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True, embedded_metadata=None):
from ..Compiler.Main import compile, default_options from ..Compiler.Main import compile, default_options
from ..Compiler.Errors import CompileError, PyrexError from ..Compiler.Errors import CompileError, PyrexError
...@@ -954,6 +961,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f ...@@ -954,6 +961,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f
if options is None: if options is None:
options = CompilationOptions(default_options) options = CompilationOptions(default_options)
options.output_file = c_file options.output_file = c_file
options.embedded_metadata = embedded_metadata
any_failures = 0 any_failures = 0
try: try:
......
...@@ -400,6 +400,7 @@ def create_default_resultobj(compilation_source, options): ...@@ -400,6 +400,7 @@ def create_default_resultobj(compilation_source, options):
else: else:
c_suffix = ".c" c_suffix = ".c"
result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix) result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
result.embedded_metadata = options.embedded_metadata
return result return result
def run_pipeline(source, options, full_module_name=None, context=None): def run_pipeline(source, options, full_module_name=None, context=None):
...@@ -480,6 +481,7 @@ class CompilationOptions(object): ...@@ -480,6 +481,7 @@ class CompilationOptions(object):
timestamps boolean Only compile changed source files. timestamps boolean Only compile changed source files.
verbose boolean Always print source names being compiled verbose boolean Always print source names being compiled
compiler_directives dict Overrides for pragma options (see Options.py) compiler_directives dict Overrides for pragma options (see Options.py)
embedded_metadata dict Metadata to embed in the C file as json.
evaluate_tree_assertions boolean Test support: evaluate parse tree assertions evaluate_tree_assertions boolean Test support: evaluate parse tree assertions
language_level integer The Python language level: 2 or 3 language_level integer The Python language level: 2 or 3
formal_grammar boolean Parse the file with the formal grammar formal_grammar boolean Parse the file with the formal grammar
...@@ -690,6 +692,7 @@ default_options = dict( ...@@ -690,6 +692,7 @@ default_options = dict(
verbose = 0, verbose = 0,
quiet = 0, quiet = 0,
compiler_directives = {}, compiler_directives = {},
embedded_metadata = {},
evaluate_tree_assertions = False, evaluate_tree_assertions = False,
emit_linenums = False, emit_linenums = False,
relative_path_in_code_position_comments = True, relative_path_in_code_position_comments = True,
......
...@@ -9,6 +9,7 @@ cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=objec ...@@ -9,6 +9,7 @@ cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=objec
error=object, warning=object, py_object_type=object, UtilityCode=object, error=object, warning=object, py_object_type=object, UtilityCode=object,
EncodedString=object) EncodedString=object)
import json
import os import os
import operator import operator
from .PyrexTypes import CPtrType from .PyrexTypes import CPtrType
...@@ -308,7 +309,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -308,7 +309,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
globalstate.initialize_main_c_code() globalstate.initialize_main_c_code()
h_code = globalstate['h_code'] h_code = globalstate['h_code']
self.generate_module_preamble(env, modules, h_code) self.generate_module_preamble(env, modules, result.embedded_metadata, h_code)
globalstate.module_pos = self.pos globalstate.module_pos = self.pos
globalstate.directives = self.directives globalstate.directives = self.directives
...@@ -547,9 +548,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -547,9 +548,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_cvariable_declarations(module, modulecode, defined_here) self.generate_cvariable_declarations(module, modulecode, defined_here)
self.generate_cfunction_declarations(module, modulecode, defined_here) self.generate_cfunction_declarations(module, modulecode, defined_here)
def generate_module_preamble(self, env, cimported_modules, code): def generate_module_preamble(self, env, cimported_modules, metadata, code):
code.putln("/* Generated by Cython %s */" % Version.watermark) code.putln("/* Generated by Cython %s */" % Version.watermark)
code.putln("") code.putln("")
if metadata:
code.putln("/* Cython Metadata */")
code.putln("/*")
code.putln(json.dumps(metadata, indent=4))
code.putln("*/")
code.putln("")
code.putln("#define PY_SSIZE_T_CLEAN") code.putln("#define PY_SSIZE_T_CLEAN")
# sizeof(PyLongObject.ob_digit[0]) may have been determined dynamically # sizeof(PyLongObject.ob_digit[0]) may have been determined dynamically
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment