3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
8 # Thomas Nagy 2016-2018 (ita)
11 Fortran configuration helpers
14 import re, os, sys, shlex
15 from waflib.Configure import conf
16 from waflib.TaskGen import feature, before_method
18 FC_FRAGMENT = ' program main\n end program main\n'
19 FC_FRAGMENT2 = ' PROGRAM MAIN\n END\n' # what's the actual difference between these?
24 Defines common fortran configuration flags and file extensions
29 v.FC_TGT_F = ['-c', '-o']
30 v.FCINCPATH_ST = '-I%s'
31 v.FCDEFINES_ST = '-D%s'
37 v.FCLNK_TGT_F = ['-o']
39 v.FCFLAGS_fcshlib = ['-fpic']
40 v.LINKFLAGS_fcshlib = ['-shared']
41 v.fcshlib_PATTERN = 'lib%s.so'
43 v.fcstlib_PATTERN = 'lib%s.a'
46 v.FCLIBPATH_ST = '-L%s'
48 v.FCSTLIBPATH_ST = '-L%s'
49 v.FCSTLIB_MARKER = '-Wl,-Bstatic'
50 v.FCSHLIB_MARKER = '-Wl,-Bdynamic'
52 v.SONAME_ST = '-Wl,-h,%s'
55 def fc_add_flags(conf):
57 Adds FCFLAGS / LDFLAGS / LINKFLAGS from os.environ to conf.env
59 conf.add_os_flags('FCPPFLAGS', dup=False)
60 conf.add_os_flags('FCFLAGS', dup=False)
61 conf.add_os_flags('LINKFLAGS', dup=False)
62 conf.add_os_flags('LDFLAGS', dup=False)
65 def check_fortran(self, *k, **kw):
67 Compiles a Fortran program to ensure that the settings are correct
70 fragment = FC_FRAGMENT,
71 compile_filename = 'test.f',
72 features = 'fc fcprogram',
73 msg = 'Compiling a simple fortran app')
76 def check_fc(self, *k, **kw):
78 Same as :py:func:`waflib.Tools.c_config.check` but defaults to the *Fortran* programming language
79 (this overrides the C defaults in :py:func:`waflib.Tools.c_config.validate_c`)
82 if not 'compile_mode' in kw:
83 kw['compile_mode'] = 'fc'
85 kw['type'] = 'fcprogram'
86 if not 'compile_filename' in kw:
87 kw['compile_filename'] = 'test.f90'
89 kw['code'] = FC_FRAGMENT
90 return self.check(*k, **kw)
92 # ------------------------------------------------------------------------
93 # --- These are the default platform modifiers, refactored here for
94 # convenience. gfortran and g95 have much overlap.
95 # ------------------------------------------------------------------------
98 def fortran_modifier_darwin(conf):
100 Defines Fortran flags and extensions for OSX systems
103 v.FCFLAGS_fcshlib = ['-fPIC']
104 v.LINKFLAGS_fcshlib = ['-dynamiclib']
105 v.fcshlib_PATTERN = 'lib%s.dylib'
106 v.FRAMEWORKPATH_ST = '-F%s'
107 v.FRAMEWORK_ST = ['-framework']
109 v.LINKFLAGS_fcstlib = []
111 v.FCSHLIB_MARKER = ''
112 v.FCSTLIB_MARKER = ''
116 def fortran_modifier_win32(conf):
118 Defines Fortran flags for Windows platforms
121 v.fcprogram_PATTERN = v.fcprogram_test_PATTERN = '%s.exe'
123 v.fcshlib_PATTERN = '%s.dll'
124 v.implib_PATTERN = '%s.dll.a'
125 v.IMPLIB_ST = '-Wl,--out-implib,%s'
127 v.FCFLAGS_fcshlib = []
129 # Auto-import is enabled by default even without this option,
130 # but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages
131 # that the linker emits otherwise.
132 v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import'])
135 def fortran_modifier_cygwin(conf):
137 Defines Fortran flags for use on cygwin
139 fortran_modifier_win32(conf)
141 v.fcshlib_PATTERN = 'cyg%s.dll'
142 v.append_value('LINKFLAGS_fcshlib', ['-Wl,--enable-auto-image-base'])
143 v.FCFLAGS_fcshlib = []
145 # ------------------------------------------------------------------------
148 def check_fortran_dummy_main(self, *k, **kw):
150 Determines if a main function is needed by compiling a code snippet with
151 the C compiler and linking it with the Fortran compiler (useful on unix-like systems)
154 self.fatal('A c compiler is required for check_fortran_dummy_main')
156 lst = ['MAIN__', '__MAIN', '_MAIN', 'MAIN_', 'MAIN']
157 lst.extend([m.lower() for m in lst])
160 self.start_msg('Detecting whether we need a dummy main')
162 kw['fortran_main'] = main
165 fragment = 'int %s() { return 0; }\n' % (main or 'test'),
166 features = 'c fcprogram',
170 self.env.FC_MAIN = -1
173 self.env.FC_MAIN = main
174 self.end_msg('yes %s' % main)
176 except self.errors.ConfigurationError:
179 self.end_msg('not found')
180 self.fatal('could not detect whether fortran requires a dummy main, see the config.log')
182 # ------------------------------------------------------------------------
184 GCC_DRIVER_LINE = re.compile('^Driving:')
185 POSIX_STATIC_EXT = re.compile('\S+\.a')
186 POSIX_LIB_FLAGS = re.compile('-l\S+')
189 def is_link_verbose(self, txt):
190 """Returns True if 'useful' link options can be found in txt"""
191 assert isinstance(txt, str)
192 for line in txt.splitlines():
193 if not GCC_DRIVER_LINE.search(line):
194 if POSIX_STATIC_EXT.search(line) or POSIX_LIB_FLAGS.search(line):
199 def check_fortran_verbose_flag(self, *k, **kw):
201 Checks what kind of verbose (-v) flag works, then sets it to env.FC_VERBOSE_FLAG
203 self.start_msg('fortran link verbose flag')
204 for x in ('-v', '--verbose', '-verbose', '-V'):
207 features = 'fc fcprogram_test',
208 fragment = FC_FRAGMENT2,
209 compile_filename = 'test.f',
212 except self.errors.ConfigurationError:
215 # output is on stderr or stdout (for xlf)
216 if self.is_link_verbose(self.test_bld.err) or self.is_link_verbose(self.test_bld.out):
220 self.end_msg('failure')
221 self.fatal('Could not obtain the fortran link verbose flag (see config.log)')
223 self.env.FC_VERBOSE_FLAG = x
226 # ------------------------------------------------------------------------
228 # linkflags which match those are ignored
229 LINKFLAGS_IGNORED = [r'-lang*', r'-lcrt[a-zA-Z0-9\.]*\.o', r'-lc$', r'-lSystem', r'-libmil', r'-LIST:*', r'-LNO:*']
231 LINKFLAGS_IGNORED.extend([r'-lfrt*', r'-luser32', r'-lkernel32', r'-ladvapi32', r'-lmsvcrt', r'-lshell32', r'-lmingw', r'-lmoldname'])
233 LINKFLAGS_IGNORED.append(r'-lgcc*')
234 RLINKFLAGS_IGNORED = [re.compile(f) for f in LINKFLAGS_IGNORED]
236 def _match_ignore(line):
237 """Returns True if the line should be ignored (Fortran verbose flag test)"""
238 for i in RLINKFLAGS_IGNORED:
243 def parse_fortran_link(lines):
244 """Given the output of verbose link of Fortran compiler, this returns a
245 list of flags necessary for linking using the standard linker."""
248 if not GCC_DRIVER_LINE.match(line):
249 _parse_flink_line(line, final_flags)
252 SPACE_OPTS = re.compile('^-[LRuYz]$')
253 NOSPACE_OPTS = re.compile('^-[RL]')
255 def _parse_flink_token(lexer, token, tmp_flags):
256 # Here we go (convention for wildcard is shell, not regex !)
257 # 1 TODO: we first get some root .a libraries
258 # 2 TODO: take everything starting by -bI:*
259 # 3 Ignore the following flags: -lang* | -lcrt*.o | -lc |
260 # -lgcc* | -lSystem | -libmil | -LANG:=* | -LIST:* | -LNO:*)
261 # 4 take into account -lkernel32
262 # 5 For options of the kind -[[LRuYz]], as they take one argument
263 # after, the actual option is the next token
264 # 6 For -YP,*: take and replace by -Larg where arg is the old
266 # 7 For -[lLR]*: take
269 if _match_ignore(token):
272 elif token.startswith('-lkernel32') and sys.platform == 'cygwin':
273 tmp_flags.append(token)
275 elif SPACE_OPTS.match(token):
276 t = lexer.get_token()
277 if t.startswith('P,'):
279 for opt in t.split(os.pathsep):
280 tmp_flags.append('-L%s' % opt)
282 elif NOSPACE_OPTS.match(token):
283 tmp_flags.append(token)
285 elif POSIX_LIB_FLAGS.match(token):
286 tmp_flags.append(token)
288 # ignore anything not explicitely taken into account
291 t = lexer.get_token()
294 def _parse_flink_line(line, final_flags):
296 lexer = shlex.shlex(line, posix = True)
297 lexer.whitespace_split = True
299 t = lexer.get_token()
302 t = _parse_flink_token(lexer, t, tmp_flags)
304 final_flags.extend(tmp_flags)
308 def check_fortran_clib(self, autoadd=True, *k, **kw):
310 Obtains the flags for linking with the C library
311 if this check works, add uselib='CLIB' to your task generators
313 if not self.env.FC_VERBOSE_FLAG:
314 self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?')
316 self.start_msg('Getting fortran runtime link flags')
319 fragment = FC_FRAGMENT2,
320 compile_filename = 'test.f',
321 features = 'fc fcprogram_test',
322 linkflags = [self.env.FC_VERBOSE_FLAG]
326 if kw.get('mandatory', True):
327 conf.fatal('Could not find the c library flags')
329 out = self.test_bld.err
330 flags = parse_fortran_link(out.splitlines())
331 self.end_msg('ok (%s)' % ' '.join(flags))
332 self.env.LINKFLAGS_CLIB = flags
336 def getoutput(conf, cmd, stdin=False):
338 Obtains Fortran command outputs
340 from waflib import Errors
344 env = dict(os.environ)
346 input = stdin and '\n'.encode() or None
348 out, err = conf.cmd_and_log(cmd, env=env, output=0, input=input)
349 except Errors.WafError as e:
350 # An WafError might indicate an error code during the command
351 # execution, in this case we still obtain the stderr and stdout,
352 # which we can use to find the version string.
353 if not (hasattr(e, 'stderr') and hasattr(e, 'stdout')):
356 # Ignore the return code and return the original
361 conf.fatal('could not determine the compiler version %r' % cmd)
364 # ------------------------------------------------------------------------
376 void %(dummy_func_nounder)s(void);
377 void %(dummy_func_under)s(void);
378 int %(main_func_name)s() {
379 %(dummy_func_nounder)s();
380 %(dummy_func_under)s();
385 @feature('link_main_routines_func')
386 @before_method('process_source')
387 def link_main_routines_tg_method(self):
389 The configuration test declares a unique task generator,
390 so we create other task generators from there for fortran link tests
392 def write_test_file(task):
393 task.outputs[0].write(task.generator.code)
395 bld(rule=write_test_file, target='main.c', code=MAIN_CODE % self.__dict__)
396 bld(rule=write_test_file, target='test.f', code=ROUTINES_CODE)
397 bld(features='fc fcstlib', source='test.f', target='test')
398 bld(features='c fcprogram', source='main.c', target='app', use='test')
400 def mangling_schemes():
402 Generate triplets for use with mangle_name
403 (used in check_fortran_mangling)
404 the order is tuned for gfortan
408 for c in ("lower", "upper"):
411 def mangle_name(u, du, c, name):
412 """Mangle a name from a triplet (used in check_fortran_mangling)"""
413 return getattr(name, c)() + u + (name.find('_') != -1 and du or '')
416 def check_fortran_mangling(self, *k, **kw):
418 Detect the mangling scheme, sets FORTRAN_MANGLING to the triplet found
420 This test will compile a fortran static library, then link a c app against it
423 self.fatal('A c compiler is required for link_main_routines')
425 self.fatal('A fortran compiler is required for link_main_routines')
426 if not self.env.FC_MAIN:
427 self.fatal('Checking for mangling requires self.env.FC_MAIN (execute "check_fortran_dummy_main" first?)')
429 self.start_msg('Getting fortran mangling scheme')
430 for (u, du, c) in mangling_schemes():
433 compile_filename = [],
434 features = 'link_main_routines_func',
437 dummy_func_nounder = mangle_name(u, du, c, 'foobar'),
438 dummy_func_under = mangle_name(u, du, c, 'foo_bar'),
439 main_func_name = self.env.FC_MAIN
441 except self.errors.ConfigurationError:
444 self.end_msg("ok ('%s', '%s', '%s-case')" % (u, du, c))
445 self.env.FORTRAN_MANGLING = (u, du, c)
449 self.fatal('mangler not found')
453 @before_method('propagate_uselib_vars', 'apply_link')
454 def set_lib_pat(self):
455 """Sets the Fortran flags for linking with Python"""
456 self.env.fcshlib_PATTERN = self.env.pyext_PATTERN
459 def detect_openmp(self):
461 Detects openmp flags and sets the OPENMP ``FCFLAGS``/``LINKFLAGS``
463 for x in ('-fopenmp','-openmp','-mp','-xopenmp','-omp','-qsmp=omp'):
466 msg = 'Checking for OpenMP flag %s' % x,
467 fragment = 'program main\n call omp_get_num_threads()\nend program main',
470 uselib_store = 'OPENMP'
472 except self.errors.ConfigurationError:
477 self.fatal('Could not find OpenMP')
480 def check_gfortran_o_space(self):
481 if self.env.FC_NAME != 'GFORTRAN' or int(self.env.FC_VERSION[0]) > 4:
482 # This is for old compilers and only for gfortran.
483 # No idea how other implementations handle this. Be safe and bail out.
486 self.env.FCLNK_TGT_F = ['-o', '']
488 self.check_fc(msg='Checking if the -o link must be split from arguments', fragment=FC_FRAGMENT, features='fc fcshlib')
489 except self.errors.ConfigurationError: