3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
8 # Thomas Nagy 2016-2018 (ita)
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
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 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
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 fortran tasks
93 # but this should not happen (we are setting them here!)
95 x.mod_fortran_done = None
99 ins = Utils.defaultdict(set)
100 outs = Utils.defaultdict(set)
102 # the .mod files to create
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)
112 # the .mod files to use
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)
124 # if the intersection matches, set the order
127 a.run_after.update(outs[k])
129 # the scanner cannot output nodes, so we have to set them
130 # ourselves as task.dep_nodes (additional input nodes)
133 tmp.extend(t.outputs)
134 a.dep_nodes.extend(tmp)
135 a.dep_nodes.sort(key=lambda x: x.abspath())
137 # the task objects have changed: clear the signature cache
140 delattr(tsk, 'cache_sig')
141 except AttributeError:
144 return super(fc, self).runnable_status()
146 class fcprogram(ccroot.link_task):
147 """Links Fortran programs"""
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}'
152 class fcshlib(fcprogram):
153 """Links Fortran libraries"""
154 inst_to = '${LIBDIR}'
156 class fcstlib(ccroot.stlink_task):
157 """Links Fortran static libraries (uses ar by default)"""
158 pass # do not remove the pass statement
160 class fcprogram_test(fcprogram):
161 """Custom link task to obtain compiler outputs for Fortran configuration tests"""
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:
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
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 = ''
179 bld.to_log('command: %s\n' % cmd)
183 (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
184 except Errors.WafError:
188 bld.to_log('out: %s\n' % bld.out)
190 bld.to_log('err: %s\n' % bld.err)