PageRenderTime 81ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/Code/Tools/waf-1.7.13/waflib/Tools/fc_config.py

https://gitlab.com/dahbearz/CRYENGINE
Python | 468 lines | 426 code | 17 blank | 25 comment | 10 complexity | ecbfab104b952ef5f855ee9de6c7e5b6 MD5 | raw file
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # DC 2008
  4. # Thomas Nagy 2010 (ita)
  5. """
  6. Fortran configuration helpers
  7. """
  8. import re, shutil, os, sys, string, shlex
  9. from waflib.Configure import conf
  10. from waflib.TaskGen import feature, after_method, before_method
  11. from waflib import Build, Utils
  12. FC_FRAGMENT = ' program main\n end program main\n'
  13. FC_FRAGMENT2 = ' PROGRAM MAIN\n END\n' # what's the actual difference between these?
  14. @conf
  15. def fc_flags(conf):
  16. """
  17. Define common fortran configuration flags and file extensions
  18. """
  19. v = conf.env
  20. v['FC_SRC_F'] = []
  21. v['FC_TGT_F'] = ['-c', '-o']
  22. v['FCINCPATH_ST'] = '-I%s'
  23. v['FCDEFINES_ST'] = '-D%s'
  24. if not v['LINK_FC']: v['LINK_FC'] = v['FC']
  25. v['FCLNK_SRC_F'] = []
  26. v['FCLNK_TGT_F'] = ['-o']
  27. v['FCFLAGS_fcshlib'] = ['-fpic']
  28. v['LINKFLAGS_fcshlib'] = ['-shared']
  29. v['fcshlib_PATTERN'] = 'lib%s.so'
  30. v['fcstlib_PATTERN'] = 'lib%s.a'
  31. v['FCLIB_ST'] = '-l%s'
  32. v['FCLIBPATH_ST'] = '-L%s'
  33. v['FCSTLIB_ST'] = '-l%s'
  34. v['FCSTLIBPATH_ST'] = '-L%s'
  35. v['FCSTLIB_MARKER'] = '-Wl,-Bstatic'
  36. v['FCSHLIB_MARKER'] = '-Wl,-Bdynamic'
  37. v['SONAME_ST'] = '-Wl,-h,%s'
  38. @conf
  39. def fc_add_flags(conf):
  40. """
  41. FCFLAGS?
  42. """
  43. conf.add_os_flags('FCFLAGS')
  44. conf.add_os_flags('LDFLAGS', 'LINKFLAGS')
  45. @conf
  46. def check_fortran(self, *k, **kw):
  47. """See if the fortran compiler works by compiling a simple fortran program"""
  48. self.check_cc(
  49. fragment = FC_FRAGMENT,
  50. compile_filename = 'test.f',
  51. features = 'fc fcprogram',
  52. msg = 'Compiling a simple fortran app')
  53. @conf
  54. def check_fc(self, *k, **kw):
  55. """
  56. Same as :py:func:`waflib.Tools.c_config.check` but default to the *Fortran* programming language
  57. (Overriding the C defaults in :py:func:`waflib.Tools.c_config.validate_c` here)
  58. """
  59. kw['compiler'] = 'fc'
  60. if not 'compile_mode' in kw:
  61. kw['compile_mode'] = 'fc'
  62. if not 'type' in kw:
  63. kw['type'] = 'fcprogram'
  64. if not 'compile_filename' in kw:
  65. kw['compile_filename'] = 'test.f90'
  66. if not 'code' in kw:
  67. kw['code'] = FC_FRAGMENT
  68. return self.check(*k, **kw)
  69. # ------------------------------------------------------------------------
  70. # --- These are the default platform modifiers, refactored here for
  71. # convenience. gfortran and g95 have much overlap.
  72. # ------------------------------------------------------------------------
  73. @conf
  74. def fortran_modifier_darwin(conf):
  75. """
  76. Define fortran flags and extensions for the OSX systems
  77. """
  78. v = conf.env
  79. v['FCFLAGS_fcshlib'] = ['-fPIC']
  80. v['LINKFLAGS_fcshlib'] = ['-dynamiclib', '-Wl,-compatibility_version,1', '-Wl,-current_version,1']
  81. v['fcshlib_PATTERN'] = 'lib%s.dylib'
  82. v['FRAMEWORKPATH_ST'] = '-F%s'
  83. v['FRAMEWORK_ST'] = '-framework %s'
  84. v['LINKFLAGS_fcstlib'] = []
  85. v['FCSHLIB_MARKER'] = ''
  86. v['FCSTLIB_MARKER'] = ''
  87. v['SONAME_ST'] = ''
  88. @conf
  89. def fortran_modifier_win32(conf):
  90. """Define fortran flags for the windows platforms"""
  91. v = conf.env
  92. v['fcprogram_PATTERN'] = v['fcprogram_test_PATTERN'] = '%s.exe'
  93. v['fcshlib_PATTERN'] = '%s.dll'
  94. v['implib_PATTERN'] = 'lib%s.dll.a'
  95. v['IMPLIB_ST'] = '-Wl,--out-implib,%s'
  96. v['FCFLAGS_fcshlib'] = []
  97. v.append_value('FCFLAGS_fcshlib', ['-DDLL_EXPORT']) # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea
  98. # Auto-import is enabled by default even without this option,
  99. # but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages
  100. # that the linker emits otherwise.
  101. v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import'])
  102. @conf
  103. def fortran_modifier_cygwin(conf):
  104. """Define fortran flags for use on cygwin"""
  105. fortran_modifier_win32(conf)
  106. v = conf.env
  107. v['fcshlib_PATTERN'] = 'cyg%s.dll'
  108. v.append_value('LINKFLAGS_fcshlib', ['-Wl,--enable-auto-image-base'])
  109. v['FCFLAGS_fcshlib'] = []
  110. # ------------------------------------------------------------------------
  111. @conf
  112. def check_fortran_dummy_main(self, *k, **kw):
  113. """
  114. Guess if a main function is needed by compiling a code snippet with
  115. the C compiler and link with the Fortran compiler
  116. TODO: (DC)
  117. - handling dialects (F77, F90, etc... -> needs core support first)
  118. - fix dummy main check (AC_FC_DUMMY_MAIN vs AC_FC_MAIN)
  119. TODO: what does the above mean? (ita)
  120. """
  121. if not self.env.CC:
  122. self.fatal('A c compiler is required for check_fortran_dummy_main')
  123. lst = ['MAIN__', '__MAIN', '_MAIN', 'MAIN_', 'MAIN']
  124. lst.extend([m.lower() for m in lst])
  125. lst.append('')
  126. self.start_msg('Detecting whether we need a dummy main')
  127. for main in lst:
  128. kw['fortran_main'] = main
  129. try:
  130. self.check_cc(
  131. fragment = 'int %s() { return 0; }\n' % (main or 'test'),
  132. features = 'c fcprogram',
  133. mandatory = True
  134. )
  135. if not main:
  136. self.env.FC_MAIN = -1
  137. self.end_msg('no')
  138. else:
  139. self.env.FC_MAIN = main
  140. self.end_msg('yes %s' % main)
  141. break
  142. except self.errors.ConfigurationError:
  143. pass
  144. else:
  145. self.end_msg('not found')
  146. self.fatal('could not detect whether fortran requires a dummy main, see the config.log')
  147. # ------------------------------------------------------------------------
  148. GCC_DRIVER_LINE = re.compile('^Driving:')
  149. POSIX_STATIC_EXT = re.compile('\S+\.a')
  150. POSIX_LIB_FLAGS = re.compile('-l\S+')
  151. @conf
  152. def is_link_verbose(self, txt):
  153. """Return True if 'useful' link options can be found in txt"""
  154. assert isinstance(txt, str)
  155. for line in txt.splitlines():
  156. if not GCC_DRIVER_LINE.search(line):
  157. if POSIX_STATIC_EXT.search(line) or POSIX_LIB_FLAGS.search(line):
  158. return True
  159. return False
  160. @conf
  161. def check_fortran_verbose_flag(self, *k, **kw):
  162. """
  163. Check what kind of verbose (-v) flag works, then set it to env.FC_VERBOSE_FLAG
  164. """
  165. self.start_msg('fortran link verbose flag')
  166. for x in ['-v', '--verbose', '-verbose', '-V']:
  167. try:
  168. self.check_cc(
  169. features = 'fc fcprogram_test',
  170. fragment = FC_FRAGMENT2,
  171. compile_filename = 'test.f',
  172. linkflags = [x],
  173. mandatory=True
  174. )
  175. except self.errors.ConfigurationError:
  176. pass
  177. else:
  178. # output is on stderr or stdout (for xlf)
  179. if self.is_link_verbose(self.test_bld.err) or self.is_link_verbose(self.test_bld.out):
  180. self.end_msg(x)
  181. break
  182. else:
  183. self.end_msg('failure')
  184. self.fatal('Could not obtain the fortran link verbose flag (see config.log)')
  185. self.env.FC_VERBOSE_FLAG = x
  186. return x
  187. # ------------------------------------------------------------------------
  188. # linkflags which match those are ignored
  189. LINKFLAGS_IGNORED = [r'-lang*', r'-lcrt[a-zA-Z0-9\.]*\.o', r'-lc$', r'-lSystem', r'-libmil', r'-LIST:*', r'-LNO:*']
  190. if os.name == 'nt':
  191. LINKFLAGS_IGNORED.extend([r'-lfrt*', r'-luser32', r'-lkernel32', r'-ladvapi32', r'-lmsvcrt', r'-lshell32', r'-lmingw', r'-lmoldname'])
  192. else:
  193. LINKFLAGS_IGNORED.append(r'-lgcc*')
  194. RLINKFLAGS_IGNORED = [re.compile(f) for f in LINKFLAGS_IGNORED]
  195. def _match_ignore(line):
  196. """Returns True if the line should be ignored (fortran test for verbosity)."""
  197. for i in RLINKFLAGS_IGNORED:
  198. if i.match(line):
  199. return True
  200. return False
  201. def parse_fortran_link(lines):
  202. """Given the output of verbose link of Fortran compiler, this returns a
  203. list of flags necessary for linking using the standard linker."""
  204. # TODO: On windows ?
  205. final_flags = []
  206. for line in lines:
  207. if not GCC_DRIVER_LINE.match(line):
  208. _parse_flink_line(line, final_flags)
  209. return final_flags
  210. SPACE_OPTS = re.compile('^-[LRuYz]$')
  211. NOSPACE_OPTS = re.compile('^-[RL]')
  212. def _parse_flink_line(line, final_flags):
  213. """private"""
  214. lexer = shlex.shlex(line, posix = True)
  215. lexer.whitespace_split = True
  216. t = lexer.get_token()
  217. tmp_flags = []
  218. while t:
  219. def parse(token):
  220. # Here we go (convention for wildcard is shell, not regex !)
  221. # 1 TODO: we first get some root .a libraries
  222. # 2 TODO: take everything starting by -bI:*
  223. # 3 Ignore the following flags: -lang* | -lcrt*.o | -lc |
  224. # -lgcc* | -lSystem | -libmil | -LANG:=* | -LIST:* | -LNO:*)
  225. # 4 take into account -lkernel32
  226. # 5 For options of the kind -[[LRuYz]], as they take one argument
  227. # after, the actual option is the next token
  228. # 6 For -YP,*: take and replace by -Larg where arg is the old
  229. # argument
  230. # 7 For -[lLR]*: take
  231. # step 3
  232. if _match_ignore(token):
  233. pass
  234. # step 4
  235. elif token.startswith('-lkernel32') and sys.platform == 'cygwin':
  236. tmp_flags.append(token)
  237. # step 5
  238. elif SPACE_OPTS.match(token):
  239. t = lexer.get_token()
  240. if t.startswith('P,'):
  241. t = t[2:]
  242. for opt in t.split(os.pathsep):
  243. tmp_flags.append('-L%s' % opt)
  244. # step 6
  245. elif NOSPACE_OPTS.match(token):
  246. tmp_flags.append(token)
  247. # step 7
  248. elif POSIX_LIB_FLAGS.match(token):
  249. tmp_flags.append(token)
  250. else:
  251. # ignore anything not explicitely taken into account
  252. pass
  253. t = lexer.get_token()
  254. return t
  255. t = parse(t)
  256. final_flags.extend(tmp_flags)
  257. return final_flags
  258. @conf
  259. def check_fortran_clib(self, autoadd=True, *k, **kw):
  260. """
  261. Obtain the flags for linking with the C library
  262. if this check works, add uselib='CLIB' to your task generators
  263. """
  264. if not self.env.FC_VERBOSE_FLAG:
  265. self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?')
  266. self.start_msg('Getting fortran runtime link flags')
  267. try:
  268. self.check_cc(
  269. fragment = FC_FRAGMENT2,
  270. compile_filename = 'test.f',
  271. features = 'fc fcprogram_test',
  272. linkflags = [self.env.FC_VERBOSE_FLAG]
  273. )
  274. except Exception:
  275. self.end_msg(False)
  276. if kw.get('mandatory', True):
  277. conf.fatal('Could not find the c library flags')
  278. else:
  279. out = self.test_bld.err
  280. flags = parse_fortran_link(out.splitlines())
  281. self.end_msg('ok (%s)' % ' '.join(flags))
  282. self.env.LINKFLAGS_CLIB = flags
  283. return flags
  284. return []
  285. def getoutput(conf, cmd, stdin=False):
  286. """
  287. TODO a bit redundant, can be removed anytime
  288. """
  289. if stdin:
  290. stdin = Utils.subprocess.PIPE
  291. else:
  292. stdin = None
  293. env = conf.env.env or None
  294. try:
  295. p = Utils.subprocess.Popen(cmd, stdin=stdin, stdout=Utils.subprocess.PIPE, stderr=Utils.subprocess.PIPE, env=env)
  296. if stdin:
  297. p.stdin.write('\n'.encode())
  298. out, err = p.communicate()
  299. except Exception:
  300. conf.fatal('could not determine the compiler version %r' % cmd)
  301. if not isinstance(out, str):
  302. out = out.decode(sys.stdout.encoding or 'iso8859-1')
  303. if not isinstance(err, str):
  304. err = err.decode(sys.stdout.encoding or 'iso8859-1')
  305. return (out, err)
  306. # ------------------------------------------------------------------------
  307. ROUTINES_CODE = """\
  308. subroutine foobar()
  309. return
  310. end
  311. subroutine foo_bar()
  312. return
  313. end
  314. """
  315. MAIN_CODE = """
  316. void %(dummy_func_nounder)s(void);
  317. void %(dummy_func_under)s(void);
  318. int %(main_func_name)s() {
  319. %(dummy_func_nounder)s();
  320. %(dummy_func_under)s();
  321. return 0;
  322. }
  323. """
  324. @feature('link_main_routines_func')
  325. @before_method('process_source')
  326. def link_main_routines_tg_method(self):
  327. """
  328. The configuration test declares a unique task generator,
  329. so we create other task generators from there for fortran link tests
  330. """
  331. def write_test_file(task):
  332. task.outputs[0].write(task.generator.code)
  333. bld = self.bld
  334. bld(rule=write_test_file, target='main.c', code=MAIN_CODE % self.__dict__)
  335. bld(rule=write_test_file, target='test.f', code=ROUTINES_CODE)
  336. bld(features='fc fcstlib', source='test.f', target='test')
  337. bld(features='c fcprogram', source='main.c', target='app', use='test')
  338. def mangling_schemes():
  339. """
  340. Generate triplets for use with mangle_name
  341. (used in check_fortran_mangling)
  342. the order is tuned for gfortan
  343. """
  344. for u in ['_', '']:
  345. for du in ['', '_']:
  346. for c in ["lower", "upper"]:
  347. yield (u, du, c)
  348. def mangle_name(u, du, c, name):
  349. """Mangle a name from a triplet (used in check_fortran_mangling)"""
  350. return getattr(name, c)() + u + (name.find('_') != -1 and du or '')
  351. @conf
  352. def check_fortran_mangling(self, *k, **kw):
  353. """
  354. Detect the mangling scheme, sets FORTRAN_MANGLING to the triplet found
  355. This test will compile a fortran static library, then link a c app against it
  356. """
  357. if not self.env.CC:
  358. self.fatal('A c compiler is required for link_main_routines')
  359. if not self.env.FC:
  360. self.fatal('A fortran compiler is required for link_main_routines')
  361. if not self.env.FC_MAIN:
  362. self.fatal('Checking for mangling requires self.env.FC_MAIN (execute "check_fortran_dummy_main" first?)')
  363. self.start_msg('Getting fortran mangling scheme')
  364. for (u, du, c) in mangling_schemes():
  365. try:
  366. self.check_cc(
  367. compile_filename = [],
  368. features = 'link_main_routines_func',
  369. msg = 'nomsg',
  370. errmsg = 'nomsg',
  371. mandatory=True,
  372. dummy_func_nounder = mangle_name(u, du, c, "foobar"),
  373. dummy_func_under = mangle_name(u, du, c, "foo_bar"),
  374. main_func_name = self.env.FC_MAIN
  375. )
  376. except self.errors.ConfigurationError:
  377. pass
  378. else:
  379. self.end_msg("ok ('%s', '%s', '%s-case')" % (u, du, c))
  380. self.env.FORTRAN_MANGLING = (u, du, c)
  381. break
  382. else:
  383. self.end_msg(False)
  384. self.fatal('mangler not found')
  385. return (u, du, c)
  386. @feature('pyext')
  387. @before_method('propagate_uselib_vars', 'apply_link')
  388. def set_lib_pat(self):
  389. """Set the fortran flags for linking with the python library"""
  390. self.env['fcshlib_PATTERN'] = self.env['pyext_PATTERN']
  391. @conf
  392. def detect_openmp(self):
  393. for x in ['-fopenmp','-openmp','-mp','-xopenmp','-omp','-qsmp=omp']:
  394. try:
  395. self.check_fc(
  396. msg='Checking for OpenMP flag %s' % x,
  397. fragment='program main\n call omp_get_num_threads()\nend program main',
  398. fcflags=x,
  399. linkflags=x,
  400. uselib_store='OPENMP'
  401. )
  402. except self.errors.ConfigurationError:
  403. pass
  404. else:
  405. break
  406. else:
  407. self.fatal('Could not find OpenMP')