third_party:waf: update to upstream 2.0.4 release
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / cython.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #! /usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2010-2015
8
9 import re
10 from waflib import Task, Logs
11 from waflib.TaskGen import extension
12
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)
18
19 @extension('.pyx')
20 def add_cython_file(self, node):
21         """
22         Process a *.pyx* file given in the list of source files. No additional
23         feature is required::
24
25                 def build(bld):
26                         bld(features='c cshlib pyext', source='main.c foo.pyx', target='app')
27         """
28         ext = '.c'
29         if 'cxx' in self.features:
30                 self.env.append_unique('CYTHONFLAGS', '--cplus')
31                 ext = '.cc'
32
33         for x in getattr(self, 'cython_includes', []):
34                 # TODO re-use these nodes in "scan" below
35                 d = self.path.find_dir(x)
36                 if d:
37                         self.env.append_unique('CYTHONFLAGS', '-I%s' % d.abspath())
38
39         tsk = self.create_task('cython', node, node.change_ext(ext))
40         self.source += tsk.outputs
41
42 class cython(Task.Task):
43         run_str = '${CYTHON} ${CYTHONFLAGS} -o ${TGT[0].abspath()} ${SRC}'
44         color   = 'GREEN'
45
46         vars    = ['INCLUDES']
47         """
48         Rebuild whenever the INCLUDES change. The variables such as CYTHONFLAGS will be appended
49         by the metaclass.
50         """
51
52         ext_out = ['.h']
53         """
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.
56         """
57
58         def runnable_status(self):
59                 """
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).
63                 """
64                 ret = super(cython, self).runnable_status()
65                 if ret == Task.ASK_LATER:
66                         return ret
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()
71
72         def post_run(self):
73                 for x in self.outputs:
74                         if x.name.endswith('.h'):
75                                 if not x.exists():
76                                         if Logs.verbose:
77                                                 Logs.warn('Expected %r', x.abspath())
78                                         x.write('')
79                 return Task.Task.post_run(self)
80
81         def scan(self):
82                 """
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::
86
87                         $ waf clean build --zones=deps
88                 """
89                 node = self.inputs[0]
90                 txt = node.read()
91
92                 mods = []
93                 for m in re_cyt.finditer(txt):
94                         if m.group(1):  # matches "from foo import bar"
95                                 mods.append(m.group(1))
96                         else:
97                                 mods.append(m.group(2))
98
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)
103
104                 found = []
105                 missing = []
106                 for x in mods:
107                         for y in incs:
108                                 k = y.find_resource(x + '.pxd')
109                                 if k:
110                                         found.append(k)
111                                         break
112                         else:
113                                 missing.append(x)
114
115                 # the cython file implicitly depends on a pxd file that might be present
116                 implicit = node.parent.find_resource(node.name[:-3] + 'pxd')
117                 if implicit:
118                         found.append(implicit)
119
120                 Logs.debug('cython: found %r', found)
121
122                 # Now the .h created - store them in bld.raw_deps for later use
123                 has_api = False
124                 has_public = False
125                 for l in txt.splitlines():
126                         if cy_api_pat.match(l):
127                                 if ' api ' in l:
128                                         has_api = True
129                                 if ' public ' in l:
130                                         has_public = True
131                 name = node.name.replace('.pyx', '')
132                 if has_api:
133                         missing.append('header:%s_api.h' % name)
134                 if has_public:
135                         missing.append('header:%s.h' % name)
136
137                 return (found, missing)
138
139 def options(ctx):
140         ctx.add_option('--cython-flags', action='store', default='', help='space separated list of flags to pass to cython')
141
142 def configure(ctx):
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
150