waf/waflib/Tools/fc.py

204 lines
6.6 KiB
Python
Raw Permalink Normal View History

2011-09-10 11:13:51 +02:00
#! /usr/bin/env python
# encoding: utf-8
# DC 2008
2018-01-01 20:53:49 +01:00
# Thomas Nagy 2016-2018 (ita)
2011-09-10 11:13:51 +02:00
"""
2016-06-26 11:59:27 +02:00
Fortran support
2011-09-10 11:13:51 +02:00
"""
from waflib import Utils, Task, Errors
2011-09-10 11:13:51 +02:00
from waflib.Tools import ccroot, fc_config, fc_scan
2016-06-25 18:22:13 +02:00
from waflib.TaskGen import extension
2011-09-10 11:13:51 +02:00
from waflib.Configure import conf
ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
2011-09-10 11:13:51 +02:00
ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
2017-03-04 12:08:22 +01:00
@extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
2011-09-10 11:13:51 +02:00
def fc_hook(self, node):
2016-06-25 21:30:32 +02:00
"Binds the Fortran file extensions create :py:class:`waflib.Tools.fc.fc` instances"
2011-09-10 11:13:51 +02:00
return self.create_compiled_task('fc', node)
@conf
def modfile(conf, name):
"""
2016-06-25 21:30:32 +02:00
Turns a module name into the right module file name.
2011-09-10 11:13:51 +02:00
Defaults to all lower case.
"""
2018-12-21 19:53:12 +01:00
if name.find(':') >= 0:
# Depending on a submodule!
separator = conf.env.FC_SUBMOD_SEPARATOR or '@'
# Ancestors of the submodule will be prefixed to the
# submodule name, separated by a colon.
modpath = name.split(':')
# Only the ancestor (actual) module and the submodule name
# will be used for the filename.
modname = modpath[0] + separator + modpath[-1]
suffix = conf.env.FC_SUBMOD_SUFFIX or '.smod'
else:
modname = name
suffix = '.mod'
return {'lower' :modname.lower() + suffix.lower(),
'lower.MOD' :modname.lower() + suffix.upper(),
'UPPER.mod' :modname.upper() + suffix.lower(),
'UPPER' :modname.upper() + suffix.upper()}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
2011-09-10 11:13:51 +02:00
def get_fortran_tasks(tsk):
"""
2016-06-25 21:30:32 +02:00
Obtains all fortran tasks from the same build group. Those tasks must not have
2011-09-10 11:13:51 +02:00
the attribute 'nomod' or 'mod_fortran_done'
2016-06-25 21:30:32 +02:00
:return: a list of :py:class:`waflib.Tools.fc.fc` instances
2011-09-10 11:13:51 +02:00
"""
bld = tsk.generator.bld
tasks = bld.get_tasks_group(bld.get_group_idx(tsk.generator))
return [x for x in tasks if isinstance(x, fc) and not getattr(x, 'nomod', None) and not getattr(x, 'mod_fortran_done', None)]
class fc(Task.Task):
"""
Fortran tasks can only run when all fortran tasks in a current task group are ready to be executed
2016-06-25 21:30:32 +02:00
This may cause a deadlock if some fortran task is waiting for something that cannot happen (circular dependency)
Should this ever happen, set the 'nomod=True' on those tasks instances to break the loop
2011-09-10 11:13:51 +02:00
"""
color = 'GREEN'
run_str = '${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()} ${FCPPFLAGS}'
2011-09-10 11:13:51 +02:00
vars = ["FORTRANMODPATHFLAG"]
def scan(self):
2016-06-26 11:59:27 +02:00
"""Fortran dependency scanner"""
2011-09-10 11:13:51 +02:00
tmp = fc_scan.fortran_parser(self.generator.includes_nodes)
tmp.task = self
tmp.start(self.inputs[0])
return (tmp.nodes, tmp.names)
def runnable_status(self):
"""
2016-06-25 21:30:32 +02:00
Sets the mod file outputs and the dependencies on the mod files over all Fortran tasks
2011-09-10 11:13:51 +02:00
executed by the main thread so there are no concurrency issues
"""
if getattr(self, 'mod_fortran_done', None):
return super(fc, self).runnable_status()
# now, if we reach this part it is because this fortran task is the first in the list
bld = self.generator.bld
# obtain the fortran tasks
lst = get_fortran_tasks(self)
# disable this method for other tasks
for tsk in lst:
tsk.mod_fortran_done = True
# wait for all the .f tasks to be ready for execution
# and ensure that the scanners are called at least once
for tsk in lst:
ret = tsk.runnable_status()
if ret == Task.ASK_LATER:
# we have to wait for one of the other fortran tasks to be ready
# this may deadlock if there are dependencies between fortran tasks
2011-09-10 11:13:51 +02:00
# but this should not happen (we are setting them here!)
for x in lst:
x.mod_fortran_done = None
return Task.ASK_LATER
ins = Utils.defaultdict(set)
outs = Utils.defaultdict(set)
# the .mod files to create
for tsk in lst:
key = tsk.uid()
for x in bld.raw_deps[key]:
if x.startswith('MOD@'):
name = bld.modfile(x.replace('MOD@', ''))
node = bld.srcnode.find_or_declare(name)
tsk.set_outputs(node)
outs[node].add(tsk)
2011-09-10 11:13:51 +02:00
# the .mod files to use
for tsk in lst:
key = tsk.uid()
for x in bld.raw_deps[key]:
if x.startswith('USE@'):
name = bld.modfile(x.replace('USE@', ''))
node = bld.srcnode.find_resource(name)
if node and node not in tsk.outputs:
if not node in bld.node_deps[key]:
bld.node_deps[key].append(node)
ins[node].add(tsk)
2011-09-10 11:13:51 +02:00
# if the intersection matches, set the order
for k in ins.keys():
for a in ins[k]:
a.run_after.update(outs[k])
for x in outs[k]:
self.generator.bld.producer.revdeps[x].add(a)
2011-09-10 11:13:51 +02:00
# the scanner cannot output nodes, so we have to set them
# ourselves as task.dep_nodes (additional input nodes)
tmp = []
for t in outs[k]:
tmp.extend(t.outputs)
a.dep_nodes.extend(tmp)
2012-02-06 01:45:44 +01:00
a.dep_nodes.sort(key=lambda x: x.abspath())
2011-09-10 11:13:51 +02:00
# the task objects have changed: clear the signature cache
for tsk in lst:
try:
delattr(tsk, 'cache_sig')
except AttributeError:
pass
return super(fc, self).runnable_status()
class fcprogram(ccroot.link_task):
2016-06-26 11:59:27 +02:00
"""Links Fortran programs"""
2011-09-10 11:13:51 +02:00
color = 'YELLOW'
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}'
2011-09-10 11:13:51 +02:00
inst_to = '${BINDIR}'
class fcshlib(fcprogram):
2016-06-26 11:59:27 +02:00
"""Links Fortran libraries"""
2011-09-10 11:13:51 +02:00
inst_to = '${LIBDIR}'
2016-06-25 21:30:32 +02:00
class fcstlib(ccroot.stlink_task):
2016-06-26 11:59:27 +02:00
"""Links Fortran static libraries (uses ar by default)"""
2016-06-25 21:30:32 +02:00
pass # do not remove the pass statement
2011-09-10 11:13:51 +02:00
class fcprogram_test(fcprogram):
2016-06-25 21:30:32 +02:00
"""Custom link task to obtain compiler outputs for Fortran configuration tests"""
2011-09-10 11:13:51 +02:00
def runnable_status(self):
"""This task is always executed"""
ret = super(fcprogram_test, self).runnable_status()
if ret == Task.SKIP_ME:
ret = Task.RUN_ME
return ret
def exec_command(self, cmd, **kw):
2016-06-25 21:30:32 +02:00
"""Stores the compiler std our/err onto the build context, to bld.out + bld.err"""
2011-09-10 11:13:51 +02:00
bld = self.generator.bld
kw['shell'] = isinstance(cmd, str)
kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
2016-01-11 05:25:46 +01:00
kw['cwd'] = self.get_cwd()
2011-09-10 11:13:51 +02:00
bld.out = bld.err = ''
bld.to_log('command: %s\n' % cmd)
kw['output'] = 0
try:
(bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
except Errors.WafError:
2011-09-10 11:13:51 +02:00
return -1
if bld.out:
2016-06-26 11:59:27 +02:00
bld.to_log('out: %s\n' % bld.out)
2011-09-10 11:13:51 +02:00
if bld.err:
2016-06-26 11:59:27 +02:00
bld.to_log('err: %s\n' % bld.err)
2011-09-10 11:13:51 +02:00