Commit 8514f0f8 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'late_includes_auto'

parents 1c4f32b9 4592586d
...@@ -985,6 +985,7 @@ class GlobalState(object): ...@@ -985,6 +985,7 @@ class GlobalState(object):
'global_var', 'global_var',
'string_decls', 'string_decls',
'decls', 'decls',
'late_includes',
'all_the_rest', 'all_the_rest',
'pystring_table', 'pystring_table',
'cached_builtins', 'cached_builtins',
......
...@@ -90,7 +90,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -90,7 +90,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if x not in L1: if x not in L1:
L1.append(x) L1.append(x)
extend_if_not_in(self.scope.include_files, scope.include_files) extend_if_not_in(self.scope.include_files_early, scope.include_files_early)
extend_if_not_in(self.scope.include_files_late, scope.include_files_late)
extend_if_not_in(self.scope.included_files, scope.included_files) extend_if_not_in(self.scope.included_files, scope.included_files)
extend_if_not_in(self.scope.python_include_files, extend_if_not_in(self.scope.python_include_files,
scope.python_include_files) scope.python_include_files)
...@@ -362,6 +363,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -362,6 +363,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("") code.putln("")
code.putln("/* Implementation of '%s' */" % env.qualified_name) code.putln("/* Implementation of '%s' */" % env.qualified_name)
code = globalstate['late_includes']
code.putln("/* Late includes */")
self.generate_includes(env, modules, code, early=False)
code = globalstate['all_the_rest'] code = globalstate['all_the_rest']
self.generate_cached_builtins_decls(env, code) self.generate_cached_builtins_decls(env, code)
...@@ -653,7 +658,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -653,7 +658,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#define %s" % Naming.h_guard_prefix + self.api_name(env)) code.putln("#define %s" % Naming.h_guard_prefix + self.api_name(env))
code.putln("#define %s" % Naming.api_guard_prefix + self.api_name(env)) code.putln("#define %s" % Naming.api_guard_prefix + self.api_name(env))
self.generate_includes(env, cimported_modules, code) code.putln("/* Early includes */")
self.generate_includes(env, cimported_modules, code, late=False)
code.putln("") code.putln("")
code.putln("#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS)") code.putln("#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS)")
code.putln("#define CYTHON_WITHOUT_ASSERTIONS") code.putln("#define CYTHON_WITHOUT_ASSERTIONS")
...@@ -727,16 +733,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -727,16 +733,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(" #define DL_IMPORT(_T) _T") code.putln(" #define DL_IMPORT(_T) _T")
code.putln("#endif") code.putln("#endif")
def generate_includes(self, env, cimported_modules, code): def generate_includes(self, env, cimported_modules, code, early=True, late=True):
includes = [] includes = []
for filename in env.include_files: if early:
includes += env.include_files_early
if late:
includes += [include for include in env.include_files_late
if include not in env.include_files_early]
for filename in includes:
byte_decoded_filenname = str(filename) byte_decoded_filenname = str(filename)
if byte_decoded_filenname[0] == '<' and byte_decoded_filenname[-1] == '>': if byte_decoded_filenname[0] == '<' and byte_decoded_filenname[-1] == '>':
code.putln('#include %s' % byte_decoded_filenname) code.putln('#include %s' % byte_decoded_filenname)
else: else:
code.putln('#include "%s"' % byte_decoded_filenname) code.putln('#include "%s"' % byte_decoded_filenname)
code.putln_openmp("#include <omp.h>") if early:
code.putln_openmp("#include <omp.h>")
def generate_filename_table(self, code): def generate_filename_table(self, code):
from os.path import isabs, basename from os.path import isabs, basename
......
...@@ -461,17 +461,27 @@ class StatNode(Node): ...@@ -461,17 +461,27 @@ class StatNode(Node):
class CDefExternNode(StatNode): class CDefExternNode(StatNode):
# include_file string or None # include_file string or None
# body StatNode # body StatListNode
child_attrs = ["body"] child_attrs = ["body"]
def analyse_declarations(self, env): def analyse_declarations(self, env):
if self.include_file:
env.add_include_file(self.include_file)
old_cinclude_flag = env.in_cinclude old_cinclude_flag = env.in_cinclude
env.in_cinclude = 1 env.in_cinclude = 1
self.body.analyse_declarations(env) self.body.analyse_declarations(env)
env.in_cinclude = old_cinclude_flag env.in_cinclude = old_cinclude_flag
inc = self.include_file
if inc:
stats = self.body.stats
if inc[0] == '<' and inc[-1] == '>':
# System include => always early
env.add_include_file(inc)
elif stats and all(isinstance(node, CVarDefNode) for node in stats):
# Generate a late include if the body is not empty and
# all statements are variable or function declarations.
env.add_include_file(inc, late=True)
else:
env.add_include_file(inc)
def analyse_expressions(self, env): def analyse_expressions(self, env):
return self return self
......
...@@ -1067,7 +1067,8 @@ class ModuleScope(Scope): ...@@ -1067,7 +1067,8 @@ class ModuleScope(Scope):
# doc_cname string C name of module doc string # doc_cname string C name of module doc string
# utility_code_list [UtilityCode] Queuing utility codes for forwarding to Code.py # utility_code_list [UtilityCode] Queuing utility codes for forwarding to Code.py
# python_include_files [string] Standard Python headers to be included # python_include_files [string] Standard Python headers to be included
# include_files [string] Other C headers to be included # include_files_early [string] C headers to be included before Cython decls
# include_files_late [string] C headers to be included after Cython decls
# string_to_entry {string : Entry} Map string const to entry # string_to_entry {string : Entry} Map string const to entry
# identifier_to_entry {string : Entry} Map identifier string const to entry # identifier_to_entry {string : Entry} Map identifier string const to entry
# context Context # context Context
...@@ -1111,7 +1112,8 @@ class ModuleScope(Scope): ...@@ -1111,7 +1112,8 @@ class ModuleScope(Scope):
self.utility_code_list = [] self.utility_code_list = []
self.module_entries = {} self.module_entries = {}
self.python_include_files = ["Python.h"] self.python_include_files = ["Python.h"]
self.include_files = [] self.include_files_early = []
self.include_files_late = []
self.type_names = dict(outer_scope.type_names) self.type_names = dict(outer_scope.type_names)
self.pxd_file_loaded = 0 self.pxd_file_loaded = 0
self.cimported_modules = [] self.cimported_modules = []
...@@ -1247,15 +1249,24 @@ class ModuleScope(Scope): ...@@ -1247,15 +1249,24 @@ class ModuleScope(Scope):
module = module.lookup_submodule(submodule) module = module.lookup_submodule(submodule)
return module return module
def add_include_file(self, filename): def add_include_file(self, filename, late=False):
if filename not in self.python_include_files \ if filename in self.python_include_files:
and filename not in self.include_files: return
self.include_files.append(filename) # Possibly, the same include appears both as early and as late
# include. We'll deal with this at code generation time.
if late:
incs = self.include_files_late
else:
incs = self.include_files_early
if filename not in incs:
incs.append(filename)
def add_imported_module(self, scope): def add_imported_module(self, scope):
if scope not in self.cimported_modules: if scope not in self.cimported_modules:
for filename in scope.include_files: for filename in scope.include_files_early:
self.add_include_file(filename) self.add_include_file(filename, late=False)
for filename in scope.include_files_late:
self.add_include_file(filename, late=True)
self.cimported_modules.append(scope) self.cimported_modules.append(scope)
for m in scope.cimported_modules: for m in scope.cimported_modules:
self.add_imported_module(m) self.add_imported_module(m)
......
...@@ -129,6 +129,13 @@ A few more tricks and tips: ...@@ -129,6 +129,13 @@ A few more tricks and tips:
cdef extern from *: cdef extern from *:
... ...
* If a ``cdef extern from "inc.h"`` block is not empty and contains only
function or variable declarations (and no type declarations of any kind),
Cython will put the ``#include "inc.h"`` statement after all
declarations generated by Cython. This means that the included file
has access to the variables, functions, structures, ... which are
declared by Cython.
Implementing functions in C Implementing functions in C
--------------------------- ---------------------------
......
PYTHON setup.py build_ext --inplace
PYTHON -c "import a"
PYTHON -c "import b"
######## setup.py ########
from Cython.Build import cythonize
from distutils.core import setup
setup(
ext_modules = cythonize("*.pyx"),
)
######## a.pxd ########
cdef extern from "a_early.h":
ctypedef int my_int
cdef extern from "a_late.h":
my_int square_value_plus_one()
cdef my_int my_value "my_value"
cdef my_int square "square"(my_int x)
######## a.pyx ########
my_value = 10
cdef my_int square "square"(my_int x):
return x * x
assert square_value_plus_one() == 101
# Square must be explicitly used for its proto to be generated.
cdef my_int use_square(x):
return square(x)
######## a_early.h ########
typedef int my_int;
######## a_late.h ########
static my_int square_value_plus_one() {
return square(my_value) + 1;
}
######## b.pyx ########
cimport a
# Likewise, a.square must be explicitly used.
assert a.square(a.my_value) + 1 == 101
assert a.square_value_plus_one() == 101
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