third_party:waf: update to upstream 2.0.4 release
[samba.git] / third_party / waf / waflib / Tools / fc.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 # DC 2008
8 # Thomas Nagy 2016-2018 (ita)
9
10 """
11 Fortran support
12 """
13
14 from waflib import Utils, Task, Errors
15 from waflib.Tools import ccroot, fc_config, fc_scan
16 from waflib.TaskGen import extension
17 from waflib.Configure import conf
18
19 ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
20 ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
21 ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
22 ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
23
24 @extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
25 def fc_hook(self, node):
26         "Binds the Fortran file extensions create :py:class:`waflib.Tools.fc.fc` instances"
27         return self.create_compiled_task('fc', node)
28
29 @conf
30 def modfile(conf, name):
31         """
32         Turns a module name into the right module file name.
33         Defaults to all lower case.
34         """
35         return {'lower'     :name.lower() + '.mod',
36                 'lower.MOD' :name.lower() + '.MOD',
37                 'UPPER.mod' :name.upper() + '.mod',
38                 'UPPER'     :name.upper() + '.MOD'}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
39
40 def get_fortran_tasks(tsk):
41         """
42         Obtains all fortran tasks from the same build group. Those tasks must not have
43         the attribute 'nomod' or 'mod_fortran_done'
44
45         :return: a list of :py:class:`waflib.Tools.fc.fc` instances
46         """
47         bld = tsk.generator.bld
48         tasks = bld.get_tasks_group(bld.get_group_idx(tsk.generator))
49         return [x for x in tasks if isinstance(x, fc) and not getattr(x, 'nomod', None) and not getattr(x, 'mod_fortran_done', None)]
50
51 class fc(Task.Task):
52         """
53         Fortran tasks can only run when all fortran tasks in a current task group are ready to be executed
54         This may cause a deadlock if some fortran task is waiting for something that cannot happen (circular dependency)
55         Should this ever happen, set the 'nomod=True' on those tasks instances to break the loop
56         """
57         color = 'GREEN'
58         run_str = '${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()} ${FCPPFLAGS}'
59         vars = ["FORTRANMODPATHFLAG"]
60
61         def scan(self):
62                 """Fortran dependency scanner"""
63                 tmp = fc_scan.fortran_parser(self.generator.includes_nodes)
64                 tmp.task = self
65                 tmp.start(self.inputs[0])
66                 return (tmp.nodes, tmp.names)
67
68         def runnable_status(self):
69                 """
70                 Sets the mod file outputs and the dependencies on the mod files over all Fortran tasks
71                 executed by the main thread so there are no concurrency issues
72                 """
73                 if getattr(self, 'mod_fortran_done', None):
74                         return super(fc, self).runnable_status()
75
76                 # now, if we reach this part it is because this fortran task is the first in the list
77                 bld = self.generator.bld
78
79                 # obtain the fortran tasks
80                 lst = get_fortran_tasks(self)
81
82                 # disable this method for other tasks
83                 for tsk in lst:
84                         tsk.mod_fortran_done = True
85
86                 # wait for all the .f tasks to be ready for execution
87                 # and ensure that the scanners are called at least once
88                 for tsk in lst:
89                         ret = tsk.runnable_status()
90                         if ret == Task.ASK_LATER:
91                                 # we have to wait for one of the other fortran tasks to be ready
92                                 # this may deadlock if there are dependencies between fortran tasks
93                                 # but this should not happen (we are setting them here!)
94                                 for x in lst:
95                                         x.mod_fortran_done = None
96
97                                 return Task.ASK_LATER
98
99                 ins = Utils.defaultdict(set)
100                 outs = Utils.defaultdict(set)
101
102                 # the .mod files to create
103                 for tsk in lst:
104                         key = tsk.uid()
105                         for x in bld.raw_deps[key]:
106                                 if x.startswith('MOD@'):
107                                         name = bld.modfile(x.replace('MOD@', ''))
108                                         node = bld.srcnode.find_or_declare(name)
109                                         tsk.set_outputs(node)
110                                         outs[node].add(tsk)
111
112                 # the .mod files to use
113                 for tsk in lst:
114                         key = tsk.uid()
115                         for x in bld.raw_deps[key]:
116                                 if x.startswith('USE@'):
117                                         name = bld.modfile(x.replace('USE@', ''))
118                                         node = bld.srcnode.find_resource(name)
119                                         if node and node not in tsk.outputs:
120                                                 if not node in bld.node_deps[key]:
121                                                         bld.node_deps[key].append(node)
122                                                 ins[node].add(tsk)
123
124                 # if the intersection matches, set the order
125                 for k in ins.keys():
126                         for a in ins[k]:
127                                 a.run_after.update(outs[k])
128
129                                 # the scanner cannot output nodes, so we have to set them
130                                 # ourselves as task.dep_nodes (additional input nodes)
131                                 tmp = []
132                                 for t in outs[k]:
133                                         tmp.extend(t.outputs)
134                                 a.dep_nodes.extend(tmp)
135                                 a.dep_nodes.sort(key=lambda x: x.abspath())
136
137                 # the task objects have changed: clear the signature cache
138                 for tsk in lst:
139                         try:
140                                 delattr(tsk, 'cache_sig')
141                         except AttributeError:
142                                 pass
143
144                 return super(fc, self).runnable_status()
145
146 class fcprogram(ccroot.link_task):
147         """Links Fortran programs"""
148         color = 'YELLOW'
149         run_str = '${FC} ${LINKFLAGS} ${FCLNK_SRC_F}${SRC} ${FCLNK_TGT_F}${TGT[0].abspath()} ${RPATH_ST:RPATH} ${FCSTLIB_MARKER} ${FCSTLIBPATH_ST:STLIBPATH} ${FCSTLIB_ST:STLIB} ${FCSHLIB_MARKER} ${FCLIBPATH_ST:LIBPATH} ${FCLIB_ST:LIB} ${LDFLAGS}'
150         inst_to = '${BINDIR}'
151
152 class fcshlib(fcprogram):
153         """Links Fortran libraries"""
154         inst_to = '${LIBDIR}'
155
156 class fcstlib(ccroot.stlink_task):
157         """Links Fortran static libraries (uses ar by default)"""
158         pass # do not remove the pass statement
159
160 class fcprogram_test(fcprogram):
161         """Custom link task to obtain compiler outputs for Fortran configuration tests"""
162
163         def runnable_status(self):
164                 """This task is always executed"""
165                 ret = super(fcprogram_test, self).runnable_status()
166                 if ret == Task.SKIP_ME:
167                         ret = Task.RUN_ME
168                 return ret
169
170         def exec_command(self, cmd, **kw):
171                 """Stores the compiler std our/err onto the build context, to bld.out + bld.err"""
172                 bld = self.generator.bld
173
174                 kw['shell'] = isinstance(cmd, str)
175                 kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
176                 kw['cwd'] = self.get_cwd()
177                 bld.out = bld.err = ''
178
179                 bld.to_log('command: %s\n' % cmd)
180
181                 kw['output'] = 0
182                 try:
183                         (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
184                 except Errors.WafError:
185                         return -1
186
187                 if bld.out:
188                         bld.to_log('out: %s\n' % bld.out)
189                 if bld.err:
190                         bld.to_log('err: %s\n' % bld.err)
191