third_party:waf: update to upstream 2.0.4 release
[amitay/samba.git] / third_party / waf / waflib / Tools / fc_config.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #! /usr/bin/env python
6 # encoding: utf-8
7 # DC 2008
8 # Thomas Nagy 2016-2018 (ita)
9
10 """
11 Fortran configuration helpers
12 """
13
14 import re, os, sys, shlex
15 from waflib.Configure import conf
16 from waflib.TaskGen import feature, before_method
17
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?
20
21 @conf
22 def fc_flags(conf):
23         """
24         Defines common fortran configuration flags and file extensions
25         """
26         v = conf.env
27
28         v.FC_SRC_F    = []
29         v.FC_TGT_F    = ['-c', '-o']
30         v.FCINCPATH_ST  = '-I%s'
31         v.FCDEFINES_ST  = '-D%s'
32
33         if not v.LINK_FC:
34                 v.LINK_FC = v.FC
35
36         v.FCLNK_SRC_F = []
37         v.FCLNK_TGT_F = ['-o']
38
39         v.FCFLAGS_fcshlib   = ['-fpic']
40         v.LINKFLAGS_fcshlib = ['-shared']
41         v.fcshlib_PATTERN   = 'lib%s.so'
42
43         v.fcstlib_PATTERN   = 'lib%s.a'
44
45         v.FCLIB_ST       = '-l%s'
46         v.FCLIBPATH_ST   = '-L%s'
47         v.FCSTLIB_ST     = '-l%s'
48         v.FCSTLIBPATH_ST = '-L%s'
49         v.FCSTLIB_MARKER = '-Wl,-Bstatic'
50         v.FCSHLIB_MARKER = '-Wl,-Bdynamic'
51
52         v.SONAME_ST      = '-Wl,-h,%s'
53
54 @conf
55 def fc_add_flags(conf):
56         """
57         Adds FCFLAGS / LDFLAGS / LINKFLAGS from os.environ to conf.env
58         """
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)
63
64 @conf
65 def check_fortran(self, *k, **kw):
66         """
67         Compiles a Fortran program to ensure that the settings are correct
68         """
69         self.check_cc(
70                 fragment         = FC_FRAGMENT,
71                 compile_filename = 'test.f',
72                 features         = 'fc fcprogram',
73                 msg              = 'Compiling a simple fortran app')
74
75 @conf
76 def check_fc(self, *k, **kw):
77         """
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`)
80         """
81         kw['compiler'] = 'fc'
82         if not 'compile_mode' in kw:
83                 kw['compile_mode'] = 'fc'
84         if not 'type' in kw:
85                 kw['type'] = 'fcprogram'
86         if not 'compile_filename' in kw:
87                 kw['compile_filename'] = 'test.f90'
88         if not 'code' in kw:
89                 kw['code'] = FC_FRAGMENT
90         return self.check(*k, **kw)
91
92 # ------------------------------------------------------------------------
93 # --- These are the default platform modifiers, refactored here for
94 #     convenience.  gfortran and g95 have much overlap.
95 # ------------------------------------------------------------------------
96
97 @conf
98 def fortran_modifier_darwin(conf):
99         """
100         Defines Fortran flags and extensions for OSX systems
101         """
102         v = conf.env
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']
108
109         v.LINKFLAGS_fcstlib = []
110
111         v.FCSHLIB_MARKER    = ''
112         v.FCSTLIB_MARKER    = ''
113         v.SONAME_ST         = ''
114
115 @conf
116 def fortran_modifier_win32(conf):
117         """
118         Defines Fortran flags for Windows platforms
119         """
120         v = conf.env
121         v.fcprogram_PATTERN = v.fcprogram_test_PATTERN  = '%s.exe'
122
123         v.fcshlib_PATTERN   = '%s.dll'
124         v.implib_PATTERN    = '%s.dll.a'
125         v.IMPLIB_ST         = '-Wl,--out-implib,%s'
126
127         v.FCFLAGS_fcshlib   = []
128
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'])
133
134 @conf
135 def fortran_modifier_cygwin(conf):
136         """
137         Defines Fortran flags for use on cygwin
138         """
139         fortran_modifier_win32(conf)
140         v = conf.env
141         v.fcshlib_PATTERN = 'cyg%s.dll'
142         v.append_value('LINKFLAGS_fcshlib', ['-Wl,--enable-auto-image-base'])
143         v.FCFLAGS_fcshlib = []
144
145 # ------------------------------------------------------------------------
146
147 @conf
148 def check_fortran_dummy_main(self, *k, **kw):
149         """
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)
152         """
153         if not self.env.CC:
154                 self.fatal('A c compiler is required for check_fortran_dummy_main')
155
156         lst = ['MAIN__', '__MAIN', '_MAIN', 'MAIN_', 'MAIN']
157         lst.extend([m.lower() for m in lst])
158         lst.append('')
159
160         self.start_msg('Detecting whether we need a dummy main')
161         for main in lst:
162                 kw['fortran_main'] = main
163                 try:
164                         self.check_cc(
165                                 fragment = 'int %s() { return 0; }\n' % (main or 'test'),
166                                 features = 'c fcprogram',
167                                 mandatory = True
168                         )
169                         if not main:
170                                 self.env.FC_MAIN = -1
171                                 self.end_msg('no')
172                         else:
173                                 self.env.FC_MAIN = main
174                                 self.end_msg('yes %s' % main)
175                         break
176                 except self.errors.ConfigurationError:
177                         pass
178         else:
179                 self.end_msg('not found')
180                 self.fatal('could not detect whether fortran requires a dummy main, see the config.log')
181
182 # ------------------------------------------------------------------------
183
184 GCC_DRIVER_LINE = re.compile('^Driving:')
185 POSIX_STATIC_EXT = re.compile('\S+\.a')
186 POSIX_LIB_FLAGS = re.compile('-l\S+')
187
188 @conf
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):
195                                 return True
196         return False
197
198 @conf
199 def check_fortran_verbose_flag(self, *k, **kw):
200         """
201         Checks what kind of verbose (-v) flag works, then sets it to env.FC_VERBOSE_FLAG
202         """
203         self.start_msg('fortran link verbose flag')
204         for x in ('-v', '--verbose', '-verbose', '-V'):
205                 try:
206                         self.check_cc(
207                                 features = 'fc fcprogram_test',
208                                 fragment = FC_FRAGMENT2,
209                                 compile_filename = 'test.f',
210                                 linkflags = [x],
211                                 mandatory=True)
212                 except self.errors.ConfigurationError:
213                         pass
214                 else:
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):
217                                 self.end_msg(x)
218                                 break
219         else:
220                 self.end_msg('failure')
221                 self.fatal('Could not obtain the fortran link verbose flag (see config.log)')
222
223         self.env.FC_VERBOSE_FLAG = x
224         return x
225
226 # ------------------------------------------------------------------------
227
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:*']
230 if os.name == 'nt':
231         LINKFLAGS_IGNORED.extend([r'-lfrt*', r'-luser32', r'-lkernel32', r'-ladvapi32', r'-lmsvcrt', r'-lshell32', r'-lmingw', r'-lmoldname'])
232 else:
233         LINKFLAGS_IGNORED.append(r'-lgcc*')
234 RLINKFLAGS_IGNORED = [re.compile(f) for f in LINKFLAGS_IGNORED]
235
236 def _match_ignore(line):
237         """Returns True if the line should be ignored (Fortran verbose flag test)"""
238         for i in RLINKFLAGS_IGNORED:
239                 if i.match(line):
240                         return True
241         return False
242
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."""
246         final_flags = []
247         for line in lines:
248                 if not GCC_DRIVER_LINE.match(line):
249                         _parse_flink_line(line, final_flags)
250         return final_flags
251
252 SPACE_OPTS = re.compile('^-[LRuYz]$')
253 NOSPACE_OPTS = re.compile('^-[RL]')
254
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
265         #   argument
266         #   7 For -[lLR]*: take
267
268         # step 3
269         if _match_ignore(token):
270                 pass
271         # step 4
272         elif token.startswith('-lkernel32') and sys.platform == 'cygwin':
273                 tmp_flags.append(token)
274         # step 5
275         elif SPACE_OPTS.match(token):
276                 t = lexer.get_token()
277                 if t.startswith('P,'):
278                         t = t[2:]
279                 for opt in t.split(os.pathsep):
280                         tmp_flags.append('-L%s' % opt)
281         # step 6
282         elif NOSPACE_OPTS.match(token):
283                 tmp_flags.append(token)
284         # step 7
285         elif POSIX_LIB_FLAGS.match(token):
286                 tmp_flags.append(token)
287         else:
288                 # ignore anything not explicitely taken into account
289                 pass
290
291         t = lexer.get_token()
292         return t
293
294 def _parse_flink_line(line, final_flags):
295         """private"""
296         lexer = shlex.shlex(line, posix = True)
297         lexer.whitespace_split = True
298
299         t = lexer.get_token()
300         tmp_flags = []
301         while t:
302                 t = _parse_flink_token(lexer, t, tmp_flags)
303
304         final_flags.extend(tmp_flags)
305         return final_flags
306
307 @conf
308 def check_fortran_clib(self, autoadd=True, *k, **kw):
309         """
310         Obtains the flags for linking with the C library
311         if this check works, add uselib='CLIB' to your task generators
312         """
313         if not self.env.FC_VERBOSE_FLAG:
314                 self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?')
315
316         self.start_msg('Getting fortran runtime link flags')
317         try:
318                 self.check_cc(
319                         fragment = FC_FRAGMENT2,
320                         compile_filename = 'test.f',
321                         features = 'fc fcprogram_test',
322                         linkflags = [self.env.FC_VERBOSE_FLAG]
323                 )
324         except Exception:
325                 self.end_msg(False)
326                 if kw.get('mandatory', True):
327                         conf.fatal('Could not find the c library flags')
328         else:
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
333                 return flags
334         return []
335
336 def getoutput(conf, cmd, stdin=False):
337         """
338         Obtains Fortran command outputs
339         """
340         from waflib import Errors
341         if conf.env.env:
342                 env = conf.env.env
343         else:
344                 env = dict(os.environ)
345                 env['LANG'] = 'C'
346         input = stdin and '\n'.encode() or None
347         try:
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')):
354                         raise e
355                 else:
356                         # Ignore the return code and return the original
357                         # stdout and stderr.
358                         out = e.stdout
359                         err = e.stderr
360         except Exception:
361                 conf.fatal('could not determine the compiler version %r' % cmd)
362         return (out, err)
363
364 # ------------------------------------------------------------------------
365
366 ROUTINES_CODE = """\
367       subroutine foobar()
368       return
369       end
370       subroutine foo_bar()
371       return
372       end
373 """
374
375 MAIN_CODE = """
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();
381   return 0;
382 }
383 """
384
385 @feature('link_main_routines_func')
386 @before_method('process_source')
387 def link_main_routines_tg_method(self):
388         """
389         The configuration test declares a unique task generator,
390         so we create other task generators from there for fortran link tests
391         """
392         def write_test_file(task):
393                 task.outputs[0].write(task.generator.code)
394         bld = self.bld
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')
399
400 def mangling_schemes():
401         """
402         Generate triplets for use with mangle_name
403         (used in check_fortran_mangling)
404         the order is tuned for gfortan
405         """
406         for u in ('_', ''):
407                 for du in ('', '_'):
408                         for c in ("lower", "upper"):
409                                 yield (u, du, c)
410
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 '')
414
415 @conf
416 def check_fortran_mangling(self, *k, **kw):
417         """
418         Detect the mangling scheme, sets FORTRAN_MANGLING to the triplet found
419
420         This test will compile a fortran static library, then link a c app against it
421         """
422         if not self.env.CC:
423                 self.fatal('A c compiler is required for link_main_routines')
424         if not self.env.FC:
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?)')
428
429         self.start_msg('Getting fortran mangling scheme')
430         for (u, du, c) in mangling_schemes():
431                 try:
432                         self.check_cc(
433                                 compile_filename   = [],
434                                 features           = 'link_main_routines_func',
435                                 msg                = 'nomsg',
436                                 errmsg             = 'nomsg',
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
440                         )
441                 except self.errors.ConfigurationError:
442                         pass
443                 else:
444                         self.end_msg("ok ('%s', '%s', '%s-case')" % (u, du, c))
445                         self.env.FORTRAN_MANGLING = (u, du, c)
446                         break
447         else:
448                 self.end_msg(False)
449                 self.fatal('mangler not found')
450         return (u, du, c)
451
452 @feature('pyext')
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
457
458 @conf
459 def detect_openmp(self):
460         """
461         Detects openmp flags and sets the OPENMP ``FCFLAGS``/``LINKFLAGS``
462         """
463         for x in ('-fopenmp','-openmp','-mp','-xopenmp','-omp','-qsmp=omp'):
464                 try:
465                         self.check_fc(
466                                 msg          = 'Checking for OpenMP flag %s' % x,
467                                 fragment     = 'program main\n  call omp_get_num_threads()\nend program main',
468                                 fcflags      = x,
469                                 linkflags    = x,
470                                 uselib_store = 'OPENMP'
471                         )
472                 except self.errors.ConfigurationError:
473                         pass
474                 else:
475                         break
476         else:
477                 self.fatal('Could not find OpenMP')
478
479 @conf
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.
484                 return
485         self.env.stash()
486         self.env.FCLNK_TGT_F = ['-o', '']
487         try:
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:
490                 self.env.revert()
491         else:
492                 self.env.commit()