waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

fc.py (6763B)


      1 #! /usr/bin/env python
      2 # encoding: utf-8
      3 # DC 2008
      4 # Thomas Nagy 2016-2018 (ita)
      5 
      6 """
      7 Fortran support
      8 """
      9 
     10 from waflib import Utils, Task, Errors
     11 from waflib.Tools import ccroot, fc_config, fc_scan
     12 from waflib.TaskGen import extension
     13 from waflib.Configure import conf
     14 
     15 ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
     16 ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
     17 ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
     18 ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
     19 
     20 @extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
     21 def fc_hook(self, node):
     22 	"Binds the Fortran file extensions create :py:class:`waflib.Tools.fc.fc` instances"
     23 	return self.create_compiled_task('fc', node)
     24 
     25 @conf
     26 def modfile(conf, name):
     27 	"""
     28 	Turns a module name into the right module file name.
     29 	Defaults to all lower case.
     30 	"""
     31 	if name.find(':') >= 0:
     32 		# Depending on a submodule!
     33 		separator = conf.env.FC_SUBMOD_SEPARATOR or '@'
     34 		# Ancestors of the submodule will be prefixed to the
     35 		# submodule name, separated by a colon.
     36 		modpath = name.split(':')
     37 		# Only the ancestor (actual) module and the submodule name
     38 		# will be used for the filename.
     39 		modname = modpath[0] + separator + modpath[-1]
     40 		suffix = conf.env.FC_SUBMOD_SUFFIX or '.smod'
     41 	else:
     42 		modname = name
     43 		suffix = '.mod'
     44 
     45 	return {'lower'     :modname.lower() + suffix.lower(),
     46 		'lower.MOD' :modname.lower() + suffix.upper(),
     47 		'UPPER.mod' :modname.upper() + suffix.lower(),
     48 		'UPPER'     :modname.upper() + suffix.upper()}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
     49 
     50 def get_fortran_tasks(tsk):
     51 	"""
     52 	Obtains all fortran tasks from the same build group. Those tasks must not have
     53 	the attribute 'nomod' or 'mod_fortran_done'
     54 
     55 	:return: a list of :py:class:`waflib.Tools.fc.fc` instances
     56 	"""
     57 	bld = tsk.generator.bld
     58 	tasks = bld.get_tasks_group(bld.get_group_idx(tsk.generator))
     59 	return [x for x in tasks if isinstance(x, fc) and not getattr(x, 'nomod', None) and not getattr(x, 'mod_fortran_done', None)]
     60 
     61 class fc(Task.Task):
     62 	"""
     63 	Fortran tasks can only run when all fortran tasks in a current task group are ready to be executed
     64 	This may cause a deadlock if some fortran task is waiting for something that cannot happen (circular dependency)
     65 	Should this ever happen, set the 'nomod=True' on those tasks instances to break the loop
     66 	"""
     67 	color = 'GREEN'
     68 	run_str = '${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()} ${FCPPFLAGS}'
     69 	vars = ["FORTRANMODPATHFLAG"]
     70 
     71 	def scan(self):
     72 		"""Fortran dependency scanner"""
     73 		tmp = fc_scan.fortran_parser(self.generator.includes_nodes)
     74 		tmp.task = self
     75 		tmp.start(self.inputs[0])
     76 		return (tmp.nodes, tmp.names)
     77 
     78 	def runnable_status(self):
     79 		"""
     80 		Sets the mod file outputs and the dependencies on the mod files over all Fortran tasks
     81 		executed by the main thread so there are no concurrency issues
     82 		"""
     83 		if getattr(self, 'mod_fortran_done', None):
     84 			return super(fc, self).runnable_status()
     85 
     86 		# now, if we reach this part it is because this fortran task is the first in the list
     87 		bld = self.generator.bld
     88 
     89 		# obtain the fortran tasks
     90 		lst = get_fortran_tasks(self)
     91 
     92 		# disable this method for other tasks
     93 		for tsk in lst:
     94 			tsk.mod_fortran_done = True
     95 
     96 		# wait for all the .f tasks to be ready for execution
     97 		# and ensure that the scanners are called at least once
     98 		for tsk in lst:
     99 			ret = tsk.runnable_status()
    100 			if ret == Task.ASK_LATER:
    101 				# we have to wait for one of the other fortran tasks to be ready
    102 				# this may deadlock if there are dependencies between fortran tasks
    103 				# but this should not happen (we are setting them here!)
    104 				for x in lst:
    105 					x.mod_fortran_done = None
    106 
    107 				return Task.ASK_LATER
    108 
    109 		ins = Utils.defaultdict(set)
    110 		outs = Utils.defaultdict(set)
    111 
    112 		# the .mod files to create
    113 		for tsk in lst:
    114 			key = tsk.uid()
    115 			for x in bld.raw_deps[key]:
    116 				if x.startswith('MOD@'):
    117 					name = bld.modfile(x.replace('MOD@', ''))
    118 					node = bld.srcnode.find_or_declare(name)
    119 					tsk.set_outputs(node)
    120 					outs[node].add(tsk)
    121 
    122 		# the .mod files to use
    123 		for tsk in lst:
    124 			key = tsk.uid()
    125 			for x in bld.raw_deps[key]:
    126 				if x.startswith('USE@'):
    127 					name = bld.modfile(x.replace('USE@', ''))
    128 					node = bld.srcnode.find_resource(name)
    129 					if node and node not in tsk.outputs:
    130 						if not node in bld.node_deps[key]:
    131 							bld.node_deps[key].append(node)
    132 						ins[node].add(tsk)
    133 
    134 		# if the intersection matches, set the order
    135 		for k in ins.keys():
    136 			for a in ins[k]:
    137 				a.run_after.update(outs[k])
    138 				for x in outs[k]:
    139 					self.generator.bld.producer.revdeps[x].add(a)
    140 
    141 				# the scanner cannot output nodes, so we have to set them
    142 				# ourselves as task.dep_nodes (additional input nodes)
    143 				tmp = []
    144 				for t in outs[k]:
    145 					tmp.extend(t.outputs)
    146 				a.dep_nodes.extend(tmp)
    147 				a.dep_nodes.sort(key=lambda x: x.abspath())
    148 
    149 		# the task objects have changed: clear the signature cache
    150 		for tsk in lst:
    151 			try:
    152 				delattr(tsk, 'cache_sig')
    153 			except AttributeError:
    154 				pass
    155 
    156 		return super(fc, self).runnable_status()
    157 
    158 class fcprogram(ccroot.link_task):
    159 	"""Links Fortran programs"""
    160 	color = 'YELLOW'
    161 	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}'
    162 	inst_to = '${BINDIR}'
    163 
    164 class fcshlib(fcprogram):
    165 	"""Links Fortran libraries"""
    166 	inst_to = '${LIBDIR}'
    167 
    168 class fcstlib(ccroot.stlink_task):
    169 	"""Links Fortran static libraries (uses ar by default)"""
    170 	pass # do not remove the pass statement
    171 
    172 class fcprogram_test(fcprogram):
    173 	"""Custom link task to obtain compiler outputs for Fortran configuration tests"""
    174 
    175 	def runnable_status(self):
    176 		"""This task is always executed"""
    177 		ret = super(fcprogram_test, self).runnable_status()
    178 		if ret == Task.SKIP_ME:
    179 			ret = Task.RUN_ME
    180 		return ret
    181 
    182 	def exec_command(self, cmd, **kw):
    183 		"""Stores the compiler std our/err onto the build context, to bld.out + bld.err"""
    184 		bld = self.generator.bld
    185 
    186 		kw['shell'] = isinstance(cmd, str)
    187 		kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
    188 		kw['cwd'] = self.get_cwd()
    189 		bld.out = bld.err = ''
    190 
    191 		bld.to_log('command: %s\n' % cmd)
    192 
    193 		kw['output'] = 0
    194 		try:
    195 			(bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
    196 		except Errors.WafError:
    197 			return -1
    198 
    199 		if bld.out:
    200 			bld.to_log('out: %s\n' % bld.out)
    201 		if bld.err:
    202 			bld.to_log('err: %s\n' % bld.err)
    203