3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Thomas Nagy, 2010-2015
10 from waflib import Task, Logs
11 from waflib.TaskGen import extension
13 cy_api_pat = re.compile(r'\s*?cdef\s*?(public|api)\w*')
14 re_cyt = re.compile(r"""
15 (?:from\s+(\w+)\s+)? # optionally match "from foo" and capture foo
16 c?import\s(\w+|[*]) # require "import bar" and capture bar
17 """, re.M | re.VERBOSE)
20 def add_cython_file(self, node):
22 Process a *.pyx* file given in the list of source files. No additional
26 bld(features='c cshlib pyext', source='main.c foo.pyx', target='app')
29 if 'cxx' in self.features:
30 self.env.append_unique('CYTHONFLAGS', '--cplus')
33 for x in getattr(self, 'cython_includes', []):
34 # TODO re-use these nodes in "scan" below
35 d = self.path.find_dir(x)
37 self.env.append_unique('CYTHONFLAGS', '-I%s' % d.abspath())
39 tsk = self.create_task('cython', node, node.change_ext(ext))
40 self.source += tsk.outputs
42 class cython(Task.Task):
43 run_str = '${CYTHON} ${CYTHONFLAGS} -o ${TGT[0].abspath()} ${SRC}'
48 Rebuild whenever the INCLUDES change. The variables such as CYTHONFLAGS will be appended
54 The creation of a .h file is known only after the build has begun, so it is not
55 possible to compute a build order just by looking at the task inputs/outputs.
58 def runnable_status(self):
60 Perform a double-check to add the headers created by cython
61 to the output nodes. The scanner is executed only when the cython task
62 must be executed (optimization).
64 ret = super(cython, self).runnable_status()
65 if ret == Task.ASK_LATER:
67 for x in self.generator.bld.raw_deps[self.uid()]:
68 if x.startswith('header:'):
69 self.outputs.append(self.inputs[0].parent.find_or_declare(x.replace('header:', '')))
70 return super(cython, self).runnable_status()
73 for x in self.outputs:
74 if x.name.endswith('.h'):
77 Logs.warn('Expected %r', x.abspath())
79 return Task.Task.post_run(self)
83 Return the dependent files (.pxd) by looking in the include folders.
84 Put the headers to generate in the custom list "bld.raw_deps".
85 To inspect the scanne results use::
87 $ waf clean build --zones=deps
93 for m in re_cyt.finditer(txt):
94 if m.group(1): # matches "from foo import bar"
95 mods.append(m.group(1))
97 mods.append(m.group(2))
99 Logs.debug('cython: mods %r', mods)
100 incs = getattr(self.generator, 'cython_includes', [])
101 incs = [self.generator.path.find_dir(x) for x in incs]
102 incs.append(node.parent)
108 k = y.find_resource(x + '.pxd')
115 # the cython file implicitly depends on a pxd file that might be present
116 implicit = node.parent.find_resource(node.name[:-3] + 'pxd')
118 found.append(implicit)
120 Logs.debug('cython: found %r', found)
122 # Now the .h created - store them in bld.raw_deps for later use
125 for l in txt.splitlines():
126 if cy_api_pat.match(l):
131 name = node.name.replace('.pyx', '')
133 missing.append('header:%s_api.h' % name)
135 missing.append('header:%s.h' % name)
137 return (found, missing)
140 ctx.add_option('--cython-flags', action='store', default='', help='space separated list of flags to pass to cython')
143 if not ctx.env.CC and not ctx.env.CXX:
144 ctx.fatal('Load a C/C++ compiler first')
145 if not ctx.env.PYTHON:
146 ctx.fatal('Load the python tool first!')
147 ctx.find_program('cython', var='CYTHON')
148 if ctx.options.cython_flags:
149 ctx.env.CYTHONFLAGS = ctx.options.cython_flags