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