fortran.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. # Copyright 2012-2017 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. from pathlib import Path
  12. import typing as T
  13. import subprocess, os
  14. from .. import coredata
  15. from .compilers import (
  16. clike_debug_args,
  17. Compiler,
  18. )
  19. from .mixins.clike import CLikeCompiler
  20. from .mixins.gnu import (
  21. GnuCompiler, gnulike_buildtype_args, gnu_optimization_args,
  22. )
  23. from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
  24. from .mixins.clang import ClangCompiler
  25. from .mixins.elbrus import ElbrusCompiler
  26. from .mixins.pgi import PGICompiler
  27. from .. import mlog
  28. from mesonbuild.mesonlib import (
  29. version_compare, EnvironmentException, MesonException, MachineChoice, LibType
  30. )
  31. if T.TYPE_CHECKING:
  32. from ..envconfig import MachineInfo
  33. class FortranCompiler(CLikeCompiler, Compiler):
  34. language = 'fortran'
  35. def __init__(self, exelist, version, for_machine: MachineChoice,
  36. is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs):
  37. Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
  38. CLikeCompiler.__init__(self, is_cross, exe_wrapper)
  39. self.id = 'unknown'
  40. def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
  41. raise MesonException('Fortran does not have "has_function" capability.\n'
  42. 'It is better to test if a Fortran capability is working like:\n\n'
  43. "meson.get_compiler('fortran').links('block; end block; end program')\n\n"
  44. 'that example is to see if the compiler has Fortran 2008 Block element.')
  45. def sanity_check(self, work_dir: Path, environment):
  46. """
  47. Check to be sure a minimal program can compile and execute
  48. with this compiler & platform.
  49. """
  50. work_dir = Path(work_dir)
  51. source_name = work_dir / 'sanitycheckf.f90'
  52. binary_name = work_dir / 'sanitycheckf'
  53. if binary_name.is_file():
  54. binary_name.unlink()
  55. source_name.write_text('print *, "Fortran compilation is working."; end')
  56. extra_flags = []
  57. extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
  58. extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
  59. extra_flags += self.get_always_args()
  60. # %% build the test executable "sanitycheckf"
  61. # cwd=work_dir is necessary on Windows especially for Intel compilers to avoid error: cannot write on sanitycheckf.obj
  62. # this is a defect with how Windows handles files and ifort's object file-writing behavior vis concurrent ProcessPoolExecutor.
  63. # This simple workaround solves the issue.
  64. # FIXME: cwd=str(work_dir) is for Python 3.5 on Windows, when 3.5 is deprcated, this can become cwd=work_dir
  65. returncode = subprocess.run(self.exelist + extra_flags + [str(source_name), '-o', str(binary_name)],
  66. cwd=str(work_dir)).returncode
  67. if returncode != 0:
  68. raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
  69. if self.is_cross:
  70. if self.exe_wrapper is None:
  71. # Can't check if the binaries run so we have to assume they do
  72. return
  73. cmdlist = self.exe_wrapper + [str(binary_name)]
  74. else:
  75. cmdlist = [str(binary_name)]
  76. # %% Run the test executable
  77. try:
  78. returncode = subprocess.run(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
  79. if returncode != 0:
  80. raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string())
  81. except OSError:
  82. raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string())
  83. def get_std_warn_args(self, level):
  84. return FortranCompiler.std_warn_args
  85. def get_buildtype_args(self, buildtype):
  86. return gnulike_buildtype_args[buildtype]
  87. def get_optimization_args(self, optimization_level):
  88. return gnu_optimization_args[optimization_level]
  89. def get_debug_args(self, is_debug):
  90. return clike_debug_args[is_debug]
  91. def get_dependency_gen_args(self, outtarget, outfile):
  92. return []
  93. def get_preprocess_only_args(self):
  94. return ['-cpp'] + super().get_preprocess_only_args()
  95. def get_module_incdir_args(self):
  96. return ('-I', )
  97. def get_module_outdir_args(self, path):
  98. return ['-module', path]
  99. def compute_parameters_with_absolute_paths(self, parameter_list, build_dir):
  100. for idx, i in enumerate(parameter_list):
  101. if i[:2] == '-I' or i[:2] == '-L':
  102. parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
  103. return parameter_list
  104. def module_name_to_filename(self, module_name: str) -> str:
  105. if '_' in module_name: # submodule
  106. s = module_name.lower()
  107. if self.id in ('gcc', 'intel', 'intel-cl'):
  108. filename = s.replace('_', '@') + '.smod'
  109. elif self.id in ('pgi', 'flang'):
  110. filename = s.replace('_', '-') + '.mod'
  111. else:
  112. filename = s + '.mod'
  113. else: # module
  114. filename = module_name.lower() + '.mod'
  115. return filename
  116. def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
  117. code = 'stop; end program'
  118. return self.find_library_impl(libname, env, extra_dirs, code, libtype)
  119. def has_multi_arguments(self, args: T.Sequence[str], env):
  120. for arg in args[:]:
  121. # some compilers, e.g. GCC, don't warn for unsupported warning-disable
  122. # flags, so when we are testing a flag like "-Wno-forgotten-towel", also
  123. # check the equivalent enable flag too "-Wforgotten-towel"
  124. # GCC does error for "-fno-foobar"
  125. if arg.startswith('-Wno-'):
  126. args.append('-W' + arg[5:])
  127. if arg.startswith('-Wl,'):
  128. mlog.warning('{} looks like a linker argument, '
  129. 'but has_argument and other similar methods only '
  130. 'support checking compiler arguments. Using them '
  131. 'to check linker arguments are never supported, '
  132. 'and results are likely to be wrong regardless of '
  133. 'the compiler you are using. has_link_argument or '
  134. 'other similar method can be used instead.'
  135. .format(arg))
  136. code = 'stop; end program'
  137. return self.has_arguments(args, env, code, mode='compile')
  138. class GnuFortranCompiler(GnuCompiler, FortranCompiler):
  139. def __init__(self, exelist, version, for_machine: MachineChoice,
  140. is_cross, info: 'MachineInfo', exe_wrapper=None,
  141. defines=None, **kwargs):
  142. FortranCompiler.__init__(self, exelist, version, for_machine,
  143. is_cross, info, exe_wrapper, **kwargs)
  144. GnuCompiler.__init__(self, defines)
  145. default_warn_args = ['-Wall']
  146. self.warn_args = {'0': [],
  147. '1': default_warn_args,
  148. '2': default_warn_args + ['-Wextra'],
  149. '3': default_warn_args + ['-Wextra', '-Wpedantic', '-fimplicit-none']}
  150. def get_options(self):
  151. opts = FortranCompiler.get_options(self)
  152. fortran_stds = ['legacy', 'f95', 'f2003']
  153. if version_compare(self.version, '>=4.4.0'):
  154. fortran_stds += ['f2008']
  155. if version_compare(self.version, '>=8.0.0'):
  156. fortran_stds += ['f2018']
  157. opts.update({
  158. 'std': coredata.UserComboOption(
  159. 'Fortran language standard to use',
  160. ['none'] + fortran_stds,
  161. 'none',
  162. ),
  163. })
  164. return opts
  165. def get_option_compile_args(self, options) -> T.List[str]:
  166. args = []
  167. std = options['std']
  168. if std.value != 'none':
  169. args.append('-std=' + std.value)
  170. return args
  171. def get_dependency_gen_args(self, outtarget, outfile) -> T.List[str]:
  172. # Disabled until this is fixed:
  173. # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62162
  174. # return ['-cpp', '-MD', '-MQ', outtarget]
  175. return []
  176. def get_module_outdir_args(self, path: str) -> T.List[str]:
  177. return ['-J' + path]
  178. def language_stdlib_only_link_flags(self) -> T.List[str]:
  179. return ['-lgfortran', '-lm']
  180. class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler):
  181. def __init__(self, exelist, version, for_machine: MachineChoice,
  182. is_cross, info: 'MachineInfo', exe_wrapper=None,
  183. defines=None, **kwargs):
  184. GnuFortranCompiler.__init__(self, exelist, version, for_machine,
  185. is_cross, info, exe_wrapper, defines,
  186. **kwargs)
  187. ElbrusCompiler.__init__(self)
  188. class G95FortranCompiler(FortranCompiler):
  189. LINKER_PREFIX = '-Wl,'
  190. def __init__(self, exelist, version, for_machine: MachineChoice,
  191. is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs):
  192. FortranCompiler.__init__(self, exelist, version, for_machine,
  193. is_cross, info, exe_wrapper, **kwargs)
  194. self.id = 'g95'
  195. default_warn_args = ['-Wall']
  196. self.warn_args = {'0': [],
  197. '1': default_warn_args,
  198. '2': default_warn_args + ['-Wextra'],
  199. '3': default_warn_args + ['-Wextra', '-pedantic']}
  200. def get_module_outdir_args(self, path: str) -> T.List[str]:
  201. return ['-fmod=' + path]
  202. def get_no_warn_args(self):
  203. # FIXME: Confirm that there's no compiler option to disable all warnings
  204. return []
  205. class SunFortranCompiler(FortranCompiler):
  206. LINKER_PREFIX = '-Wl,'
  207. def __init__(self, exelist, version, for_machine: MachineChoice,
  208. is_cross, info: 'MachineInfo', exe_wrapper=None,
  209. **kwargs):
  210. FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs)
  211. self.id = 'sun'
  212. def get_dependency_gen_args(self, outtarget, outfile) -> T.List[str]:
  213. return ['-fpp']
  214. def get_always_args(self):
  215. return []
  216. def get_warn_args(self, level):
  217. return []
  218. def get_module_incdir_args(self):
  219. return ('-M', )
  220. def get_module_outdir_args(self, path: str) -> T.List[str]:
  221. return ['-moddir=' + path]
  222. def openmp_flags(self) -> T.List[str]:
  223. return ['-xopenmp']
  224. class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
  225. def __init__(self, exelist, version, for_machine: MachineChoice,
  226. is_cross, info: 'MachineInfo', exe_wrapper=None,
  227. **kwargs):
  228. self.file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp')
  229. FortranCompiler.__init__(self, exelist, version, for_machine,
  230. is_cross, info, exe_wrapper, **kwargs)
  231. # FIXME: Add support for OS X and Windows in detect_fortran_compiler so
  232. # we are sent the type of compiler
  233. IntelGnuLikeCompiler.__init__(self)
  234. self.id = 'intel'
  235. default_warn_args = ['-warn', 'general', '-warn', 'truncated_source']
  236. self.warn_args = {'0': [],
  237. '1': default_warn_args,
  238. '2': default_warn_args + ['-warn', 'unused'],
  239. '3': ['-warn', 'all']}
  240. def get_options(self):
  241. opts = FortranCompiler.get_options(self)
  242. fortran_stds = ['legacy', 'f95', 'f2003', 'f2008', 'f2018']
  243. opts.update({
  244. 'std': coredata.UserComboOption(
  245. 'Fortran language standard to use',
  246. ['none'] + fortran_stds,
  247. 'none',
  248. ),
  249. })
  250. return opts
  251. def get_option_compile_args(self, options) -> T.List[str]:
  252. args = []
  253. std = options['std']
  254. stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
  255. if std.value != 'none':
  256. args.append('-stand=' + stds[std.value])
  257. return args
  258. def get_preprocess_only_args(self) -> T.List[str]:
  259. return ['-cpp', '-EP']
  260. def get_always_args(self):
  261. """Ifort doesn't have -pipe."""
  262. val = super().get_always_args()
  263. val.remove('-pipe')
  264. return val
  265. def language_stdlib_only_link_flags(self) -> T.List[str]:
  266. return ['-lifcore', '-limf']
  267. def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
  268. return ['-gen-dep=' + outtarget, '-gen-depformat=make']
  269. class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
  270. file_suffixes = ['f90', 'f', 'for', 'ftn', 'fpp']
  271. always_args = ['/nologo']
  272. def __init__(self, exelist, version, for_machine: MachineChoice,
  273. is_cross, target: str, info: 'MachineInfo', exe_wrapper=None,
  274. **kwargs):
  275. FortranCompiler.__init__(self, exelist, version, for_machine,
  276. is_cross, info, exe_wrapper, **kwargs)
  277. IntelVisualStudioLikeCompiler.__init__(self, target)
  278. default_warn_args = ['/warn:general', '/warn:truncated_source']
  279. self.warn_args = {'0': [],
  280. '1': default_warn_args,
  281. '2': default_warn_args + ['/warn:unused'],
  282. '3': ['/warn:all']}
  283. def get_options(self):
  284. opts = FortranCompiler.get_options(self)
  285. fortran_stds = ['legacy', 'f95', 'f2003', 'f2008', 'f2018']
  286. opts.update({
  287. 'std': coredata.UserComboOption(
  288. 'Fortran language standard to use',
  289. ['none'] + fortran_stds,
  290. 'none',
  291. ),
  292. })
  293. return opts
  294. def get_option_compile_args(self, options) -> T.List[str]:
  295. args = []
  296. std = options['std']
  297. stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
  298. if std.value != 'none':
  299. args.append('/stand:' + stds[std.value])
  300. return args
  301. def get_module_outdir_args(self, path) -> T.List[str]:
  302. return ['/module:' + path]
  303. class PathScaleFortranCompiler(FortranCompiler):
  304. def __init__(self, exelist, version, for_machine: MachineChoice,
  305. is_cross, info: 'MachineInfo', exe_wrapper=None,
  306. **kwargs):
  307. FortranCompiler.__init__(self, exelist, version, for_machine,
  308. is_cross, info, exe_wrapper, **kwargs)
  309. self.id = 'pathscale'
  310. default_warn_args = ['-fullwarn']
  311. self.warn_args = {'0': [],
  312. '1': default_warn_args,
  313. '2': default_warn_args,
  314. '3': default_warn_args}
  315. def openmp_flags(self) -> T.List[str]:
  316. return ['-mp']
  317. class PGIFortranCompiler(PGICompiler, FortranCompiler):
  318. def __init__(self, exelist, version, for_machine: MachineChoice,
  319. is_cross, info: 'MachineInfo', exe_wrapper=None,
  320. **kwargs):
  321. FortranCompiler.__init__(self, exelist, version, for_machine,
  322. is_cross, info, exe_wrapper, **kwargs)
  323. PGICompiler.__init__(self)
  324. default_warn_args = ['-Minform=inform']
  325. self.warn_args = {'0': [],
  326. '1': default_warn_args,
  327. '2': default_warn_args,
  328. '3': default_warn_args + ['-Mdclchk']}
  329. def language_stdlib_only_link_flags(self) -> T.List[str]:
  330. return ['-lpgf90rtl', '-lpgf90', '-lpgf90_rpm1', '-lpgf902',
  331. '-lpgf90rtl', '-lpgftnrtl', '-lrt']
  332. class FlangFortranCompiler(ClangCompiler, FortranCompiler):
  333. def __init__(self, exelist, version, for_machine: MachineChoice,
  334. is_cross, info: 'MachineInfo', exe_wrapper=None,
  335. **kwargs):
  336. FortranCompiler.__init__(self, exelist, version, for_machine,
  337. is_cross, info, exe_wrapper, **kwargs)
  338. ClangCompiler.__init__(self)
  339. self.id = 'flang'
  340. default_warn_args = ['-Minform=inform']
  341. self.warn_args = {'0': [],
  342. '1': default_warn_args,
  343. '2': default_warn_args,
  344. '3': default_warn_args}
  345. def language_stdlib_only_link_flags(self) -> T.List[str]:
  346. return ['-lflang', '-lpgmath']
  347. class Open64FortranCompiler(FortranCompiler):
  348. def __init__(self, exelist, version, for_machine: MachineChoice,
  349. is_cross, info: 'MachineInfo', exe_wrapper=None,
  350. **kwargs):
  351. FortranCompiler.__init__(self, exelist, version, for_machine,
  352. is_cross, info, exe_wrapper, **kwargs)
  353. self.id = 'open64'
  354. default_warn_args = ['-fullwarn']
  355. self.warn_args = {'0': [],
  356. '1': default_warn_args,
  357. '2': default_warn_args,
  358. '3': default_warn_args}
  359. def openmp_flags(self) -> T.List[str]:
  360. return ['-mp']
  361. class NAGFortranCompiler(FortranCompiler):
  362. def __init__(self, exelist, version, for_machine: MachineChoice,
  363. is_cross, info: 'MachineInfo', exe_wrapper=None,
  364. **kwargs):
  365. FortranCompiler.__init__(self, exelist, version, for_machine,
  366. is_cross, info, exe_wrapper, **kwargs)
  367. self.id = 'nagfor'
  368. def get_warn_args(self, level):
  369. return []
  370. def get_module_outdir_args(self, path) -> T.List[str]:
  371. return ['-mdir', path]
  372. def openmp_flags(self) -> T.List[str]:
  373. return ['-openmp']