xref: /petsc/config/BuildSystem/config/compilerFlags.py (revision 5574ef43b0ac3357bb9db30b381e3fae1c24bab1)
1import config.base
2import re
3import os
4
5class Configure(config.base.Configure):
6  def __init__(self, framework):
7    config.base.Configure.__init__(self, framework)
8    self.headerPrefix = ''
9    self.substPrefix  = ''
10    self.version = {}
11    self.rejected = {}
12    self.text = ''
13    return
14
15  def __str__(self):
16    return self.text
17
18  def setupHelp(self, help):
19    import nargs
20    help.addArgument('Compiler Flags', '-optionsModule=<module name>', nargs.Arg(None, 'config.compilerOptions', 'The Python module used to determine compiler options and versions'))
21    help.addArgument('Compiler Flags', '-with-debugging=<bool>', nargs.ArgBool(None, 1, 'Specify debugging version of libraries'))
22    help.addArgument('Compiler Flags', '-C_VERSION=<string>',   nargs.Arg(None, 'Unknown', 'The version of the C compiler'))
23    help.addArgument('Compiler Flags', '-CXX_VERSION=<string>', nargs.Arg(None, 'Unknown', 'The version of the C++ compiler'))
24    help.addArgument('Compiler Flags', '-FC_VERSION=<string>',  nargs.Arg(None, 'Unknown', 'The version of the Fortran compiler'))
25    help.addArgument('Compiler Flags', '-CUDA_VERSION=<string>',nargs.Arg(None, 'Unknown', 'The version of the CUDA compiler'))
26    help.addArgument('Compiler Flags', '-HIP_VERSION=<string>',nargs.Arg(None, 'Unknown', 'The version of the HIP compiler'))
27    help.addArgument('Compiler Flags', '-SYCL_VERSION=<string>',nargs.Arg(None, 'Unknown', 'The version of the SYCL compiler'))
28    help.addArgument('Compiler Flags', '-COPTFLAGS=<string>',   nargs.Arg(None, None, 'Override the debugging/optimization flags for the C compiler'))
29    help.addArgument('Compiler Flags', '-CXXOPTFLAGS=<string>', nargs.Arg(None, None, 'Override the debugging/optimization flags for the C++ compiler'))
30    help.addArgument('Compiler Flags', '-FOPTFLAGS=<string>',   nargs.Arg(None, None, 'Override the debugging/optimization flags for the Fortran compiler'))
31    help.addArgument('Compiler Flags', '-CUDAOPTFLAGS=<string>',   nargs.Arg(None, None, 'Override the debugging/optimization flags for the CUDA compiler'))
32    help.addArgument('Compiler Flags', '-HIPOPTFLAGS=<string>',   nargs.Arg(None, None, 'Override the debugging/optimization flags for the HIP compiler'))
33    help.addArgument('Compiler Flags', '-SYCLOPTFLAGS=<string>',   nargs.Arg(None, None, 'Override the debugging/optimization flags for the SYCL compiler'))
34    help.addArgument('Compiler Flags', '-with-gcov=<bool>', nargs.ArgBool(None, value=None, help='Specify that GNUs coverage tool gcov is used', deprecated='--with-coverage'))
35    return
36
37  def setupDependencies(self, framework):
38    config.base.Configure.setupDependencies(self, framework)
39    self.setCompilers = framework.require('config.setCompilers', self)
40    return
41
42  def getOptionalFlagsName(self, language, compilerOnly = 0):
43    if language == 'C':
44      flagsArg = 'COPTFLAGS'
45    elif language == 'Cxx':
46      if compilerOnly:
47        flagsArg = 'CXX_CXXOPTFLAGS'
48      else:
49        flagsArg = 'CXXOPTFLAGS'
50    elif language == 'FC':
51      flagsArg = 'FOPTFLAGS'
52    elif language == 'CUDA':
53      flagsArg = 'CUDAOPTFLAGS'
54    elif language == 'HIP':
55      flagsArg = 'HIPOPTFLAGS'
56    elif language == 'SYCL':
57      flagsArg = 'SYCLOPTFLAGS'
58    else:
59      raise RuntimeError('Unknown language: '+language)
60    return flagsArg
61
62  def findOptFlags(self, flags):
63    if isinstance(flags, str):
64      flags = flags.split()
65
66    return [f for f in flags if f.startswith('-g') or f.startswith('-O') or f in {'-fast'}]
67
68  def hasOptFlags(self,flags):
69    return len(self.findOptFlags(flags)) > 0
70
71  def getOptionsObject(self):
72    '''Get a configure object which will return default options for each compiler'''
73    options = None
74    try:
75      mod     = __import__(self.argDB['optionsModule'], locals(), globals(), ['CompilerOptions'])
76      options = mod.CompilerOptions(self.framework)
77      options.setup()
78    except ImportError:
79      self.logPrint('ERROR: Failed to load user options module '+str(self.argDB['optionsModule']))
80    return options
81
82  def configureCompilerFlags(self):
83    '''Get the default compiler flags'''
84    self.debugging = self.argDB['with-debugging']
85    bopts = ['', '+']
86    if self.debugging:
87      bopts.append('g')
88    else:
89      bopts.append('O')
90
91    options = self.getOptionsObject()
92    if not options:
93      return
94    options.saveLog()
95    for language, compiler in [('C', 'CC'), ('Cxx', 'CXX'), ('FC', 'FC'), ('CUDA', 'CUDAC'), ('HIP', 'HIPC'), ('SYCL', 'SYCLC')]:
96      if not hasattr(self.setCompilers, compiler):
97        continue
98      self.setCompilers.pushLanguage(language)
99      flagsName = config.base.Configure.getCompilerFlagsName(language)
100      try:
101        self.version[language] = self.argDB[language.upper()+'_VERSION']
102        if self.version[language] == 'Unknown':
103          self.version[language] = options.getCompilerVersion(language, self.setCompilers.getCompiler())
104      except RuntimeError:
105        pass
106      self.rejected[language] = []
107      for bopt in bopts:
108        userflags = 0
109        userlangflags = 0
110        if bopt in ['g','O'] and self.getOptionalFlagsName(language) in self.argDB: # check --COPTFLAGS etc
111          # treat user supplied options as single option - as it could include options separated by spaces '-tp k8-64'
112          flags = [self.argDB[self.getOptionalFlagsName(language)]]
113          userflags = 1
114        elif bopt in ['g','O'] and self.hasOptFlags(getattr(self.setCompilers,flagsName)): # check --CFLAGS etc
115          self.logPrint('Optimization options found in '+flagsName+ '. Skipping setting defaults')
116          flags = []
117        elif bopt == '' and flagsName in self.argDB:
118          self.logPrint('Ignoring default options which were overridden using --'+flagsName+ ' ' + self.argDB[flagsName])
119          flags = []
120        elif bopt == '+':
121          if flagsName+'+' in self.argDB:
122            flags = [self.argDB[flagsName+'+']]
123            userlangflags = 1
124          else:
125            continue
126        else:
127          flags = options.getCompilerFlags(language, self.setCompilers.getCompiler(), bopt)
128
129        for testFlag in flags:
130          if isinstance(testFlag, (tuple, list)):
131            testFlag = ' '.join(testFlag)
132          self.logPrint('Trying '+language+' compiler flag '+testFlag)
133          try:
134            self.setCompilers.addCompilerFlag(testFlag)
135          except RuntimeError:
136            if userflags:
137              raise RuntimeError('User provided flags for language '+language+' with '+self.getOptionalFlagsName(language)+': '+self.argDB[self.getOptionalFlagsName(language)]+' are not correct for the compiler')
138            if userlangflags:
139              raise RuntimeError('User provided flags for language '+language+' with '+flagsName+'+: '+self.argDB[flagsName+'+']+' are not correct for the compiler')
140            self.logPrint('Rejected '+language+' compiler flag '+testFlag)
141            self.rejected[language].append(testFlag)
142      self.setCompilers.popLanguage()
143    return
144
145  def outputCompilerMacros(self):
146    '''Cannot use the regular outputCompile because it swallows the output into the -o file'''
147    command = self.getCompilerCmd()
148    if self.compilerDefines: self.framework.outputHeader(self.compilerDefines)
149    self.framework.outputCHeader(self.compilerFixes)
150    f = open(self.compilerSource, 'w')
151    f.close()
152    try:
153      command = command + ' -E -dM '
154      start = command.find('-o ')
155      end = start + 3 + command[start+3:].find(' ')
156      command = command[:start-1]+command[end:]
157      (out, err, ret) = Configure.executeShellCommand(command, log = self.log)
158      if out.find('__AVX2__') > -1 and out.find('__FMA__') > -1:
159        self.text = self.text + 'Intel instruction sets utilizable by compiler:\n'
160        self.text = self.text + '  AVX2\n'
161      if out.find('__AVX512') > -1:
162        self.text = self.text + '  AVX512: '
163        self.text = self.text + ' '.join([i for i in out.split('__') if i.startswith('AVX512')])
164        self.text = self.text + '\n'
165    except:
166      pass
167    for filename in [self.compilerDefines, self.compilerFixes, self.compilerSource, self.compilerObj]:
168      if os.path.isfile(filename): os.remove(filename)
169
170  def checkCompilerMacros(self):
171    '''Save the list of CPP macros defined by the C and C++ compiler, does not work for all compilers'''
172    '''The values will depends on the flags passed to the compiler'''
173    self.outputCompilerMacros()
174    if hasattr(self.setCompilers, 'CXX'):
175      with self.Language('Cxx'):
176        self.outputCompilerMacros()
177    return
178
179  def checkIntelHardwareSupport(self):
180    '''Use Linux/macOS commands to determine what operations the hardware supports'''
181    try:
182      (out, err, ret) = Configure.executeShellCommand('lscpu', log = self.log)
183    except:
184      out = ''
185    if out.find(' avx2 ') > -1 and out.find(' fma ') > -1:
186      self.text = self.text + 'Intel instruction sets found on CPU:\n'
187      self.text = self.text + '  AVX2\n'
188    if out.find(' avx512') > -1:
189      self.text = self.text + '  AVX512: '
190      self.text = self.text + ' '.join([i for i in out.split(' ') if i.startswith('avx512')])
191      self.text = self.text + '\n'
192    return
193
194  def configure(self):
195    self.executeTest(self.configureCompilerFlags)
196    self.executeTest(self.checkIntelHardwareSupport)
197    self.executeTest(self.checkCompilerMacros)
198    return
199