3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
8 # Thomas Nagy 2016 (ita)
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)
30 def modfile(conf, name):
32 Turns a module name into the right module file name.
33 Defaults to all lower case.
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):
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
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)]
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
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"]
62 """Fortran dependency scanner"""
63 tmp = fc_scan.fortran_parser(self.generator.includes_nodes)
65 tmp.start(self.inputs[0])
66 return (tmp.nodes, tmp.names)
68 def runnable_status(self):
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
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
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
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!)
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
100 ins = Utils.defaultdict(set)
101 outs = Utils.defaultdict(set)
103 # the .mod files to create
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
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
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)
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
141 delattr(tsk, 'cache_sig')
142 except AttributeError:
145 return super(fc, self).runnable_status()
147 class fcprogram(ccroot.link_task):
148 """Links Fortran programs"""
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:
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)
184 (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
189 bld.to_log('out: %s\n' % bld.out)
191 bld.to_log('err: %s\n' % bld.err)