From c2b0355b0de23f6ec83b5fa5be038bcce208e26f Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Sat, 15 Sep 2018 14:51:03 +0200
Subject: Squashed 'waflib/' content from commit 982416b

git-subtree-dir: waflib
git-subtree-split: 982416b8a6c6728e200243e1be3ab60435c08830
---
 Tools/fc.py | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 189 insertions(+)
 create mode 100644 Tools/fc.py

(limited to 'Tools/fc.py')

diff --git a/Tools/fc.py b/Tools/fc.py
new file mode 100644
index 0000000..d9e8d8c
--- /dev/null
+++ b/Tools/fc.py
@@ -0,0 +1,189 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# DC 2008
+# Thomas Nagy 2016-2018 (ita)
+
+"""
+Fortran support
+"""
+
+from waflib import Utils, Task, Errors
+from waflib.Tools import ccroot, fc_config, fc_scan
+from waflib.TaskGen import extension
+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'])
+ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
+ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
+
+@extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
+def fc_hook(self, node):
+	"Binds the Fortran file extensions create :py:class:`waflib.Tools.fc.fc` instances"
+	return self.create_compiled_task('fc', node)
+
+@conf
+def modfile(conf, name):
+	"""
+	Turns a module name into the right module file name.
+	Defaults to all lower case.
+	"""
+	return {'lower'     :name.lower() + '.mod',
+		'lower.MOD' :name.lower() + '.MOD',
+		'UPPER.mod' :name.upper() + '.mod',
+		'UPPER'     :name.upper() + '.MOD'}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
+
+def get_fortran_tasks(tsk):
+	"""
+	Obtains all fortran tasks from the same build group. Those tasks must not have
+	the attribute 'nomod' or 'mod_fortran_done'
+
+	:return: a list of :py:class:`waflib.Tools.fc.fc` instances
+	"""
+	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
+	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
+	"""
+	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}'
+	vars = ["FORTRANMODPATHFLAG"]
+
+	def scan(self):
+		"""Fortran dependency scanner"""
+		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):
+		"""
+		Sets the mod file outputs and the dependencies on the mod files over all Fortran tasks
+		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
+				# 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)
+
+		# 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)
+
+		# 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)
+
+				# 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)
+				a.dep_nodes.sort(key=lambda x: x.abspath())
+
+		# 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):
+	"""Links Fortran programs"""
+	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}'
+	inst_to = '${BINDIR}'
+
+class fcshlib(fcprogram):
+	"""Links Fortran libraries"""
+	inst_to = '${LIBDIR}'
+
+class fcstlib(ccroot.stlink_task):
+	"""Links Fortran static libraries (uses ar by default)"""
+	pass # do not remove the pass statement
+
+class fcprogram_test(fcprogram):
+	"""Custom link task to obtain compiler outputs for Fortran configuration tests"""
+
+	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):
+		"""Stores the compiler std our/err onto the build context, to bld.out + bld.err"""
+		bld = self.generator.bld
+
+		kw['shell'] = isinstance(cmd, str)
+		kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
+		kw['cwd'] = self.get_cwd()
+		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:
+			return -1
+
+		if bld.out:
+			bld.to_log('out: %s\n' % bld.out)
+		if bld.err:
+			bld.to_log('err: %s\n' % bld.err)
+
-- 
cgit v1.2.1