thirdparty:waf: New files for waf 1.9.10
[vlendec/samba-autobuild/.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 (ita)
9
10 """
11 Fortran support
12 """
13
14 from waflib import Utils, Task
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 the current 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 the 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                                 # TODO sort the list of tasks in bld.producer.outstanding to put all fortran tasks at the end
98                                 return Task.ASK_LATER
99
100                 ins = Utils.defaultdict(set)
101                 outs = Utils.defaultdict(set)
102
103                 # the .mod files to create
104                 for tsk in lst:
105                         key = tsk.uid()
106                         for x in bld.raw_deps[key]:
107                                 if x.startswith('MOD@'):
108                                         name = bld.modfile(x.replace('MOD@', ''))
109                                         node = bld.srcnode.find_or_declare(name)
110                                         tsk.set_outputs(node)
111                                         outs[id(node)].add(tsk)
112
113                 # the .mod files to use
114                 for tsk in lst:
115                         key = tsk.uid()
116                         for x in bld.raw_deps[key]:
117                                 if x.startswith('USE@'):
118                                         name = bld.modfile(x.replace('USE@', ''))
119                                         node = bld.srcnode.find_resource(name)
120                                         if node and node not in tsk.outputs:
121                                                 if not node in bld.node_deps[key]:
122                                                         bld.node_deps[key].append(node)
123                                                 ins[id(node)].add(tsk)
124
125                 # if the intersection matches, set the order
126                 for k in ins.keys():
127                         for a in ins[k]:
128                                 a.run_after.update(outs[k])
129
130                                 # the scanner cannot output nodes, so we have to set them
131                                 # ourselves as task.dep_nodes (additional input nodes)
132                                 tmp = []
133                                 for t in outs[k]:
134                                         tmp.extend(t.outputs)
135                                 a.dep_nodes.extend(tmp)
136                                 a.dep_nodes.sort(key=lambda x: x.abspath())
137
138                 # the task objects have changed: clear the signature cache
139                 for tsk in lst:
140                         try:
141                                 delattr(tsk, 'cache_sig')
142                         except AttributeError:
143                                 pass
144
145                 return super(fc, self).runnable_status()
146
147 class fcprogram(ccroot.link_task):
148         """Links Fortran programs"""
149         color = 'YELLOW'
150         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}'
151         inst_to = '${BINDIR}'
152
153 class fcshlib(fcprogram):
154         """Links Fortran libraries"""
155         inst_to = '${LIBDIR}'
156
157 class fcstlib(ccroot.stlink_task):
158         """Links Fortran static libraries (uses ar by default)"""
159         pass # do not remove the pass statement
160
161 class fcprogram_test(fcprogram):
162         """Custom link task to obtain compiler outputs for Fortran configuration tests"""
163
164         def runnable_status(self):
165                 """This task is always executed"""
166                 ret = super(fcprogram_test, self).runnable_status()
167                 if ret == Task.SKIP_ME:
168                         ret = Task.RUN_ME
169                 return ret
170
171         def exec_command(self, cmd, **kw):
172                 """Stores the compiler std our/err onto the build context, to bld.out + bld.err"""
173                 bld = self.generator.bld
174
175                 kw['shell'] = isinstance(cmd, str)
176                 kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
177                 kw['cwd'] = self.get_cwd()
178                 bld.out = bld.err = ''
179
180                 bld.to_log('command: %s\n' % cmd)
181
182                 kw['output'] = 0
183                 try:
184                         (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
185                 except Exception:
186                         return -1
187
188                 if bld.out:
189                         bld.to_log('out: %s\n' % bld.out)
190                 if bld.err:
191                         bld.to_log('err: %s\n' % bld.err)