thirdparty:waf: New files for waf 1.9.10
[Samba.git] / third_party / waf / waflib / Tools / fc.py
blob5a98ce538edb5cfc79b1a8592d8e1a6d390cba75
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
5 #! /usr/bin/env python
6 # encoding: utf-8
7 # DC 2008
8 # Thomas Nagy 2016 (ita)
10 """
11 Fortran support
12 """
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
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'])
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)
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']
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'
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)]
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"]
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)
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()
76 # now, if we reach this part it is because this fortran task is the first in the list
77 bld = self.generator.bld
79 # obtain the fortran tasks
80 lst = get_fortran_tasks(self)
82 # disable this method for other tasks
83 for tsk in lst:
84 tsk.mod_fortran_done = True
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
97 # TODO sort the list of tasks in bld.producer.outstanding to put all fortran tasks at the end
98 return Task.ASK_LATER
100 ins = Utils.defaultdict(set)
101 outs = Utils.defaultdict(set)
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)
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)
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])
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())
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
145 return super(fc, self).runnable_status()
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}'
153 class fcshlib(fcprogram):
154 """Links Fortran libraries"""
155 inst_to = '${LIBDIR}'
157 class fcstlib(ccroot.stlink_task):
158 """Links Fortran static libraries (uses ar by default)"""
159 pass # do not remove the pass statement
161 class fcprogram_test(fcprogram):
162 """Custom link task to obtain compiler outputs for Fortran configuration tests"""
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
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
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 = ''
180 bld.to_log('command: %s\n' % cmd)
182 kw['output'] = 0
183 try:
184 (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
185 except Exception:
186 return -1
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)