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_config.py | 488 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 488 insertions(+)
 create mode 100644 Tools/fc_config.py

(limited to 'Tools/fc_config.py')

diff --git a/Tools/fc_config.py b/Tools/fc_config.py
new file mode 100644
index 0000000..222f3a5
--- /dev/null
+++ b/Tools/fc_config.py
@@ -0,0 +1,488 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# DC 2008
+# Thomas Nagy 2016-2018 (ita)
+
+"""
+Fortran configuration helpers
+"""
+
+import re, os, sys, shlex
+from waflib.Configure import conf
+from waflib.TaskGen import feature, before_method
+
+FC_FRAGMENT = '        program main\n        end     program main\n'
+FC_FRAGMENT2 = '        PROGRAM MAIN\n        END\n' # what's the actual difference between these?
+
+@conf
+def fc_flags(conf):
+	"""
+	Defines common fortran configuration flags and file extensions
+	"""
+	v = conf.env
+
+	v.FC_SRC_F    = []
+	v.FC_TGT_F    = ['-c', '-o']
+	v.FCINCPATH_ST  = '-I%s'
+	v.FCDEFINES_ST  = '-D%s'
+
+	if not v.LINK_FC:
+		v.LINK_FC = v.FC
+
+	v.FCLNK_SRC_F = []
+	v.FCLNK_TGT_F = ['-o']
+
+	v.FCFLAGS_fcshlib   = ['-fpic']
+	v.LINKFLAGS_fcshlib = ['-shared']
+	v.fcshlib_PATTERN   = 'lib%s.so'
+
+	v.fcstlib_PATTERN   = 'lib%s.a'
+
+	v.FCLIB_ST       = '-l%s'
+	v.FCLIBPATH_ST   = '-L%s'
+	v.FCSTLIB_ST     = '-l%s'
+	v.FCSTLIBPATH_ST = '-L%s'
+	v.FCSTLIB_MARKER = '-Wl,-Bstatic'
+	v.FCSHLIB_MARKER = '-Wl,-Bdynamic'
+
+	v.SONAME_ST      = '-Wl,-h,%s'
+
+@conf
+def fc_add_flags(conf):
+	"""
+	Adds FCFLAGS / LDFLAGS / LINKFLAGS from os.environ to conf.env
+	"""
+	conf.add_os_flags('FCPPFLAGS', dup=False)
+	conf.add_os_flags('FCFLAGS', dup=False)
+	conf.add_os_flags('LINKFLAGS', dup=False)
+	conf.add_os_flags('LDFLAGS', dup=False)
+
+@conf
+def check_fortran(self, *k, **kw):
+	"""
+	Compiles a Fortran program to ensure that the settings are correct
+	"""
+	self.check_cc(
+		fragment         = FC_FRAGMENT,
+		compile_filename = 'test.f',
+		features         = 'fc fcprogram',
+		msg              = 'Compiling a simple fortran app')
+
+@conf
+def check_fc(self, *k, **kw):
+	"""
+	Same as :py:func:`waflib.Tools.c_config.check` but defaults to the *Fortran* programming language
+	(this overrides the C defaults in :py:func:`waflib.Tools.c_config.validate_c`)
+	"""
+	kw['compiler'] = 'fc'
+	if not 'compile_mode' in kw:
+		kw['compile_mode'] = 'fc'
+	if not 'type' in kw:
+		kw['type'] = 'fcprogram'
+	if not 'compile_filename' in kw:
+		kw['compile_filename'] = 'test.f90'
+	if not 'code' in kw:
+		kw['code'] = FC_FRAGMENT
+	return self.check(*k, **kw)
+
+# ------------------------------------------------------------------------
+# --- These are the default platform modifiers, refactored here for
+#     convenience.  gfortran and g95 have much overlap.
+# ------------------------------------------------------------------------
+
+@conf
+def fortran_modifier_darwin(conf):
+	"""
+	Defines Fortran flags and extensions for OSX systems
+	"""
+	v = conf.env
+	v.FCFLAGS_fcshlib   = ['-fPIC']
+	v.LINKFLAGS_fcshlib = ['-dynamiclib']
+	v.fcshlib_PATTERN   = 'lib%s.dylib'
+	v.FRAMEWORKPATH_ST  = '-F%s'
+	v.FRAMEWORK_ST      = ['-framework']
+
+	v.LINKFLAGS_fcstlib = []
+
+	v.FCSHLIB_MARKER    = ''
+	v.FCSTLIB_MARKER    = ''
+	v.SONAME_ST         = ''
+
+@conf
+def fortran_modifier_win32(conf):
+	"""
+	Defines Fortran flags for Windows platforms
+	"""
+	v = conf.env
+	v.fcprogram_PATTERN = v.fcprogram_test_PATTERN  = '%s.exe'
+
+	v.fcshlib_PATTERN   = '%s.dll'
+	v.implib_PATTERN    = '%s.dll.a'
+	v.IMPLIB_ST         = '-Wl,--out-implib,%s'
+
+	v.FCFLAGS_fcshlib   = []
+
+	# Auto-import is enabled by default even without this option,
+	# but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages
+	# that the linker emits otherwise.
+	v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import'])
+
+@conf
+def fortran_modifier_cygwin(conf):
+	"""
+	Defines Fortran flags for use on cygwin
+	"""
+	fortran_modifier_win32(conf)
+	v = conf.env
+	v.fcshlib_PATTERN = 'cyg%s.dll'
+	v.append_value('LINKFLAGS_fcshlib', ['-Wl,--enable-auto-image-base'])
+	v.FCFLAGS_fcshlib = []
+
+# ------------------------------------------------------------------------
+
+@conf
+def check_fortran_dummy_main(self, *k, **kw):
+	"""
+	Determines if a main function is needed by compiling a code snippet with
+	the C compiler and linking it with the Fortran compiler (useful on unix-like systems)
+	"""
+	if not self.env.CC:
+		self.fatal('A c compiler is required for check_fortran_dummy_main')
+
+	lst = ['MAIN__', '__MAIN', '_MAIN', 'MAIN_', 'MAIN']
+	lst.extend([m.lower() for m in lst])
+	lst.append('')
+
+	self.start_msg('Detecting whether we need a dummy main')
+	for main in lst:
+		kw['fortran_main'] = main
+		try:
+			self.check_cc(
+				fragment = 'int %s() { return 0; }\n' % (main or 'test'),
+				features = 'c fcprogram',
+				mandatory = True
+			)
+			if not main:
+				self.env.FC_MAIN = -1
+				self.end_msg('no')
+			else:
+				self.env.FC_MAIN = main
+				self.end_msg('yes %s' % main)
+			break
+		except self.errors.ConfigurationError:
+			pass
+	else:
+		self.end_msg('not found')
+		self.fatal('could not detect whether fortran requires a dummy main, see the config.log')
+
+# ------------------------------------------------------------------------
+
+GCC_DRIVER_LINE = re.compile('^Driving:')
+POSIX_STATIC_EXT = re.compile('\S+\.a')
+POSIX_LIB_FLAGS = re.compile('-l\S+')
+
+@conf
+def is_link_verbose(self, txt):
+	"""Returns True if 'useful' link options can be found in txt"""
+	assert isinstance(txt, str)
+	for line in txt.splitlines():
+		if not GCC_DRIVER_LINE.search(line):
+			if POSIX_STATIC_EXT.search(line) or POSIX_LIB_FLAGS.search(line):
+				return True
+	return False
+
+@conf
+def check_fortran_verbose_flag(self, *k, **kw):
+	"""
+	Checks what kind of verbose (-v) flag works, then sets it to env.FC_VERBOSE_FLAG
+	"""
+	self.start_msg('fortran link verbose flag')
+	for x in ('-v', '--verbose', '-verbose', '-V'):
+		try:
+			self.check_cc(
+				features = 'fc fcprogram_test',
+				fragment = FC_FRAGMENT2,
+				compile_filename = 'test.f',
+				linkflags = [x],
+				mandatory=True)
+		except self.errors.ConfigurationError:
+			pass
+		else:
+			# output is on stderr or stdout (for xlf)
+			if self.is_link_verbose(self.test_bld.err) or self.is_link_verbose(self.test_bld.out):
+				self.end_msg(x)
+				break
+	else:
+		self.end_msg('failure')
+		self.fatal('Could not obtain the fortran link verbose flag (see config.log)')
+
+	self.env.FC_VERBOSE_FLAG = x
+	return x
+
+# ------------------------------------------------------------------------
+
+# linkflags which match those are ignored
+LINKFLAGS_IGNORED = [r'-lang*', r'-lcrt[a-zA-Z0-9\.]*\.o', r'-lc$', r'-lSystem', r'-libmil', r'-LIST:*', r'-LNO:*']
+if os.name == 'nt':
+	LINKFLAGS_IGNORED.extend([r'-lfrt*', r'-luser32', r'-lkernel32', r'-ladvapi32', r'-lmsvcrt', r'-lshell32', r'-lmingw', r'-lmoldname'])
+else:
+	LINKFLAGS_IGNORED.append(r'-lgcc*')
+RLINKFLAGS_IGNORED = [re.compile(f) for f in LINKFLAGS_IGNORED]
+
+def _match_ignore(line):
+	"""Returns True if the line should be ignored (Fortran verbose flag test)"""
+	for i in RLINKFLAGS_IGNORED:
+		if i.match(line):
+			return True
+	return False
+
+def parse_fortran_link(lines):
+	"""Given the output of verbose link of Fortran compiler, this returns a
+	list of flags necessary for linking using the standard linker."""
+	final_flags = []
+	for line in lines:
+		if not GCC_DRIVER_LINE.match(line):
+			_parse_flink_line(line, final_flags)
+	return final_flags
+
+SPACE_OPTS = re.compile('^-[LRuYz]$')
+NOSPACE_OPTS = re.compile('^-[RL]')
+
+def _parse_flink_token(lexer, token, tmp_flags):
+	# Here we go (convention for wildcard is shell, not regex !)
+	#   1 TODO: we first get some root .a libraries
+	#   2 TODO: take everything starting by -bI:*
+	#   3 Ignore the following flags: -lang* | -lcrt*.o | -lc |
+	#   -lgcc* | -lSystem | -libmil | -LANG:=* | -LIST:* | -LNO:*)
+	#   4 take into account -lkernel32
+	#   5 For options of the kind -[[LRuYz]], as they take one argument
+	#   after, the actual option is the next token
+	#   6 For -YP,*: take and replace by -Larg where arg is the old
+	#   argument
+	#   7 For -[lLR]*: take
+
+	# step 3
+	if _match_ignore(token):
+		pass
+	# step 4
+	elif token.startswith('-lkernel32') and sys.platform == 'cygwin':
+		tmp_flags.append(token)
+	# step 5
+	elif SPACE_OPTS.match(token):
+		t = lexer.get_token()
+		if t.startswith('P,'):
+			t = t[2:]
+		for opt in t.split(os.pathsep):
+			tmp_flags.append('-L%s' % opt)
+	# step 6
+	elif NOSPACE_OPTS.match(token):
+		tmp_flags.append(token)
+	# step 7
+	elif POSIX_LIB_FLAGS.match(token):
+		tmp_flags.append(token)
+	else:
+		# ignore anything not explicitly taken into account
+		pass
+
+	t = lexer.get_token()
+	return t
+
+def _parse_flink_line(line, final_flags):
+	"""private"""
+	lexer = shlex.shlex(line, posix = True)
+	lexer.whitespace_split = True
+
+	t = lexer.get_token()
+	tmp_flags = []
+	while t:
+		t = _parse_flink_token(lexer, t, tmp_flags)
+
+	final_flags.extend(tmp_flags)
+	return final_flags
+
+@conf
+def check_fortran_clib(self, autoadd=True, *k, **kw):
+	"""
+	Obtains the flags for linking with the C library
+	if this check works, add uselib='CLIB' to your task generators
+	"""
+	if not self.env.FC_VERBOSE_FLAG:
+		self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?')
+
+	self.start_msg('Getting fortran runtime link flags')
+	try:
+		self.check_cc(
+			fragment = FC_FRAGMENT2,
+			compile_filename = 'test.f',
+			features = 'fc fcprogram_test',
+			linkflags = [self.env.FC_VERBOSE_FLAG]
+		)
+	except Exception:
+		self.end_msg(False)
+		if kw.get('mandatory', True):
+			conf.fatal('Could not find the c library flags')
+	else:
+		out = self.test_bld.err
+		flags = parse_fortran_link(out.splitlines())
+		self.end_msg('ok (%s)' % ' '.join(flags))
+		self.env.LINKFLAGS_CLIB = flags
+		return flags
+	return []
+
+def getoutput(conf, cmd, stdin=False):
+	"""
+	Obtains Fortran command outputs
+	"""
+	from waflib import Errors
+	if conf.env.env:
+		env = conf.env.env
+	else:
+		env = dict(os.environ)
+		env['LANG'] = 'C'
+	input = stdin and '\n'.encode() or None
+	try:
+		out, err = conf.cmd_and_log(cmd, env=env, output=0, input=input)
+	except Errors.WafError as e:
+		# An WafError might indicate an error code during the command
+		# execution, in this case we still obtain the stderr and stdout,
+		# which we can use to find the version string.
+		if not (hasattr(e, 'stderr') and hasattr(e, 'stdout')):
+			raise e
+		else:
+			# Ignore the return code and return the original
+			# stdout and stderr.
+			out = e.stdout
+			err = e.stderr
+	except Exception:
+		conf.fatal('could not determine the compiler version %r' % cmd)
+	return (out, err)
+
+# ------------------------------------------------------------------------
+
+ROUTINES_CODE = """\
+      subroutine foobar()
+      return
+      end
+      subroutine foo_bar()
+      return
+      end
+"""
+
+MAIN_CODE = """
+void %(dummy_func_nounder)s(void);
+void %(dummy_func_under)s(void);
+int %(main_func_name)s() {
+  %(dummy_func_nounder)s();
+  %(dummy_func_under)s();
+  return 0;
+}
+"""
+
+@feature('link_main_routines_func')
+@before_method('process_source')
+def link_main_routines_tg_method(self):
+	"""
+	The configuration test declares a unique task generator,
+	so we create other task generators from there for fortran link tests
+	"""
+	def write_test_file(task):
+		task.outputs[0].write(task.generator.code)
+	bld = self.bld
+	bld(rule=write_test_file, target='main.c', code=MAIN_CODE % self.__dict__)
+	bld(rule=write_test_file, target='test.f', code=ROUTINES_CODE)
+	bld(features='fc fcstlib', source='test.f', target='test')
+	bld(features='c fcprogram', source='main.c', target='app', use='test')
+
+def mangling_schemes():
+	"""
+	Generate triplets for use with mangle_name
+	(used in check_fortran_mangling)
+	the order is tuned for gfortan
+	"""
+	for u in ('_', ''):
+		for du in ('', '_'):
+			for c in ("lower", "upper"):
+				yield (u, du, c)
+
+def mangle_name(u, du, c, name):
+	"""Mangle a name from a triplet (used in check_fortran_mangling)"""
+	return getattr(name, c)() + u + (name.find('_') != -1 and du or '')
+
+@conf
+def check_fortran_mangling(self, *k, **kw):
+	"""
+	Detect the mangling scheme, sets FORTRAN_MANGLING to the triplet found
+
+	This test will compile a fortran static library, then link a c app against it
+	"""
+	if not self.env.CC:
+		self.fatal('A c compiler is required for link_main_routines')
+	if not self.env.FC:
+		self.fatal('A fortran compiler is required for link_main_routines')
+	if not self.env.FC_MAIN:
+		self.fatal('Checking for mangling requires self.env.FC_MAIN (execute "check_fortran_dummy_main" first?)')
+
+	self.start_msg('Getting fortran mangling scheme')
+	for (u, du, c) in mangling_schemes():
+		try:
+			self.check_cc(
+				compile_filename   = [],
+				features           = 'link_main_routines_func',
+				msg                = 'nomsg',
+				errmsg             = 'nomsg',
+				dummy_func_nounder = mangle_name(u, du, c, 'foobar'),
+				dummy_func_under   = mangle_name(u, du, c, 'foo_bar'),
+				main_func_name     = self.env.FC_MAIN
+			)
+		except self.errors.ConfigurationError:
+			pass
+		else:
+			self.end_msg("ok ('%s', '%s', '%s-case')" % (u, du, c))
+			self.env.FORTRAN_MANGLING = (u, du, c)
+			break
+	else:
+		self.end_msg(False)
+		self.fatal('mangler not found')
+	return (u, du, c)
+
+@feature('pyext')
+@before_method('propagate_uselib_vars', 'apply_link')
+def set_lib_pat(self):
+	"""Sets the Fortran flags for linking with Python"""
+	self.env.fcshlib_PATTERN = self.env.pyext_PATTERN
+
+@conf
+def detect_openmp(self):
+	"""
+	Detects openmp flags and sets the OPENMP ``FCFLAGS``/``LINKFLAGS``
+	"""
+	for x in ('-fopenmp','-openmp','-mp','-xopenmp','-omp','-qsmp=omp'):
+		try:
+			self.check_fc(
+				msg          = 'Checking for OpenMP flag %s' % x,
+				fragment     = 'program main\n  call omp_get_num_threads()\nend program main',
+				fcflags      = x,
+				linkflags    = x,
+				uselib_store = 'OPENMP'
+			)
+		except self.errors.ConfigurationError:
+			pass
+		else:
+			break
+	else:
+		self.fatal('Could not find OpenMP')
+
+@conf
+def check_gfortran_o_space(self):
+	if self.env.FC_NAME != 'GFORTRAN' or int(self.env.FC_VERSION[0]) > 4:
+		# This is for old compilers and only for gfortran.
+		# No idea how other implementations handle this. Be safe and bail out.
+		return
+	self.env.stash()
+	self.env.FCLNK_TGT_F = ['-o', '']
+	try:
+		self.check_fc(msg='Checking if the -o link must be split from arguments', fragment=FC_FRAGMENT, features='fc fcshlib')
+	except self.errors.ConfigurationError:
+		self.env.revert()
+	else:
+		self.env.commit()
-- 
cgit v1.2.1