Setting to standard shell
[WPS.git] / arch / configure_reader.py
blob45f3ae70e5677eebe3e285a9545244b9e5993355
1 #!/usr/bin/env python3
3 import sys
4 import re
5 import inspect
6 import platform
8 archBlock = re.compile( r"(?:#[ ]*)(ARCH(?:.*\n)*?)(?:#{5,})", re.I )
9 kvPair = re.compile( r"^(\w+)(?:[ \t]*=[ \t]*)(.*?)$", re.I | re.M )
10 # Make this gnarly and complicated since configure.defaults has no standard formatting
11 # v start v OS V typical v MACOS
12 osAndArch = re.compile( r"^ARCH[ ]+(\w+)[ ]+((?:\w+.*?),|(?:[(].*?[)]))", re.I )
13 # Just grab the first two words, thats what you get
14 osAndArchAlt = re.compile( r"^ARCH[ ]+(\w+)[ ]+(\w+)", re.I )
16 referenceVar = re.compile( r"[$][(](\w+)[)]", re.I )
18 class Stanza():
20 def __init__( self, lines ) :
21 self.lines_ = lines
22 self.os_ = None
23 self.archs_ = []
24 self.kvPairs_ = {}
26 def parse( self ) :
27 # First get os & archs
28 osarchMatch = osAndArch.match( self.lines_ )
30 if osarchMatch is None :
31 osarchMatch = osAndArchAlt.match( self.lines_ )
32 if osarchMatch is None :
33 print( "Could not find OS and architecture info in " + self.lines_.partition("\n")[0] )
35 self.os_ = osarchMatch.group(1)
36 self.archs_ = osarchMatch.group(2).strip(",").split( " " )
38 for kvPairMatch in kvPair.finditer( self.lines_ ) :
39 self.kvPairs_[ kvPairMatch.group(1) ] = kvPairMatch.group(2)
40 self.removeComments( kvPairMatch.group(1) )
42 # Now sanitize
43 self.sanitize()
45 def dereference( self, field, fatal=False ) :
46 if field in self.kvPairs_ :
47 prevField = self.kvPairs_[field]
49 for refVarIter in referenceVar.finditer( prevField ) :
50 if refVarIter is not None :
51 # Grab group 1 and check that it is in our kv pairs
52 refVar = refVarIter.group(1)
53 if refVar not in self.kvPairs_ :
54 if fatal :
55 print( "Could not rereference : " + refVar )
56 exit(1)
57 else:
58 continue
60 # Recursively deref
61 self.dereference( refVar, fatal )
63 # Replace in original
64 self.kvPairs_[field] = self.kvPairs_[field].replace(
65 "$({var})".format( var=refVar ),
66 self.kvPairs_[refVar]
69 def removeReferences( self, field, specifics=[] ) :
70 if field in self.kvPairs_ :
71 if specifics :
72 for specific in specifics :
73 self.kvPairs_[ field ] = self.kvPairs_[ field ].replace(
74 "$({var})".format( var=specific ),
77 else :
78 self.kvPairs_[ field ] = referenceVar.sub( "", self.kvPairs_[ field ] )
81 def removeComments( self, field ) :
82 if field in self.kvPairs_ :
83 self.kvPairs_[ field ] = self.kvPairs_[ field ].split( "#", 1 )[0]
85 def splitIntoFieldAndFlags( self, field ) :
86 # Fix flags being mixed with programs
87 if field in self.kvPairs_ :
88 fieldValue = self.kvPairs_[ field ]
90 self.kvPairs_[field] = fieldValue.partition(" ")[0]
91 self.kvPairs_[field + "_FLAGS"] = fieldValue.partition(" ")[1]
93 def sanitize( self ) :
94 # Fix problematic variables
95 self.dereference( "DM_FC" )
96 self.dereference( "DM_CC" )
97 # self.removeReferences( "FCBASEOPTS_NO_G" )
98 # Get rid of all these mixed up flags, these are handled by cmake natively or
99 # just in the wrong place
100 self.removeReferences( "FFLAGS", [ "FORMAT_FREE", "FORMAT_FIXED" ] )
101 self.removeReferences( "F77FLAGS", [ "FORMAT_FREE", "FORMAT_FIXED" ] )
102 # # Now deref
103 # self.dereference( "FCBASEOPTS" )
105 # Now fix certain ones that are mixing programs with flags all mashed into one option
106 self.splitIntoFieldAndFlags( "DM_FC" )
107 self.splitIntoFieldAndFlags( "DM_CC" )
108 self.splitIntoFieldAndFlags( "M4" )
110 # Remove rogue compile commands that should *NOT* even be here
111 keysToSanitize = [
112 "COMPRESSION_LIBS",
113 "COMPRESSION_INC",
114 "FDEFS",
115 "SFC",
116 "SCC",
117 "DM_FC",
118 "DM_CC",
119 "FC",
120 "CC",
121 "LD",
122 "FFLAGS",
123 "F77FLAGS",
124 "FORMAT_FREE",
125 "FORMAT_FIXED",
126 "FCSUFFIX",
127 "FNGFLAGS",
128 "LDFLAGS",
129 "CFLAGS",
130 "CPP",
131 "CPPFLAGS",
132 "ARFLAGS"
135 for keyToSan in keysToSanitize :
136 if keyToSan in self.kvPairs_ :
137 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_COMP_L", "" )
138 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_COMP_I", "" )
139 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_FC", "" )
140 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_CC", "" )
141 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_FDEFS", "" )
142 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_MPI", "" )
143 self.kvPairs_[ keyToSan ] = self.kvPairs_[ keyToSan ].replace( "CONFIGURE_COMPAT_FLAGS", "" )
145 # Now deref all the rest
146 for key in self.kvPairs_ :
147 self.dereference( key )
148 # And for final measure strip
149 self.kvPairs_[ key ] = self.kvPairs_[ key ].strip()
151 def __str__( self ):
152 # base = """OS {os:<8} ARCHITECTURES {archs:<20}
153 # >> SFC = {SFC:<12}
154 # >> SCC = {SCC:<12}
155 # >> CCOMP = {CCOMP:<12}
156 # >> DM_FC = {DM_FC:<12}
157 # >> DM_CC = {DM_CC:<12}
158 # """
159 # print( self.kvPairs_ )
160 base = """{rec} {os:<10} {SFC:<11} / {SCC:<11} / {DM_FC:<11} / {DM_CC:<11}"""
161 text = inspect.cleandoc( base ).format(
162 os=str(self.os_),
163 rec=( "!!" if platform.system().lower() != self.os_.lower() else "Ok" ),
164 # archs=str(self.archs_),
165 SFC=str( self.kvPairs_["SFC"].partition(" ")[0] ),
166 SCC=str( self.kvPairs_["SCC"].partition(" ")[0] ),
167 # CCOMP=str( self.kvPairs_["CCOMP"].partition(" ")[0] ),
168 DM_FC=str( self.kvPairs_["DM_FC"].partition(" ")[0] ),
169 DM_CC=str( self.kvPairs_["DM_CC"].partition(" ")[0] )
171 # text += "\n" + "\n".join( [ "{key:<18} = {value}".format( key=key, value=value) for key, value in self.kvPairs_.items() ] )
172 return text
174 @staticmethod
175 def findFirstDifference( rhStanza, lhStanza, maxLength=32 ) :
176 diff = False
177 value = ""
178 valuesToCheck = [
179 "ARCH_LOCAL",
180 "BYTESWAPIO",
181 "CFLAGS_LOCAL",
182 "CFLAGS",
183 "FFLAGS",
184 "DM_CC",
185 "DM_FC",
186 "DM_FC_FLAGS",
187 "DM_CC_FLAGS",
188 "FCBASEOPTS",
189 "FCDEBUG",
190 "FCNOOPT",
191 "FCOPTIM",
192 "M4_FLAGS",
193 "SCC",
194 "SFC"
196 for rhKey, rhValue in rhStanza.kvPairs_.items() :
197 if rhKey in valuesToCheck and rhKey in lhStanza.kvPairs_ :
198 # Qualifies for difference
199 if rhValue != lhStanza.kvPairs_[rhKey] :
200 diff = True
201 value = "{key:<12} = {value}".format( key=rhKey, value=lhStanza.kvPairs_[rhKey] )
203 # Truncate
204 value = ( value[:maxLength] + "..." ) if len( value ) > maxLength else value
206 return diff, value
209 def getStringOptionSelection( topLevelCmake, searchString ) :
210 topLevelCmakeFP = open( topLevelCmake, "r" )
211 topLevelCmakeLines = topLevelCmakeFP.read()
212 topLevelCmakeFP.close()
214 stringOptionsMatch = re.search(
215 r"set\s*[(]\s*" + searchString + r"\s*(.*?)[)]",
216 topLevelCmakeLines,
217 re.I | re.S | re.M
219 if stringOptionsMatch is None :
220 print( "Syntax error in parsing " + searchString + " from " + topLevelCmake )
221 exit(1)
223 options = [ option.split( "#", 1 )[0].strip() for option in stringOptionsMatch.group(1).split( "\n" ) ]
224 # Weed out empties
225 options = [ option for option in options if option ]
227 optionsFmt = ", ".join( [ "{idx} : {opt}".format( idx=options.index( opt ), opt=opt ) for opt in options ] )
228 selection = int( input( "Select string options [0-{max}] ({opts}) : ".format( max=len(options)-1, opts=optionsFmt ) ) )
230 if selection < 0 or selection > len(options) :
231 print( "Invalid option selection for " + searchString + "!" )
232 exit(1)
234 return options[selection]
236 def generateCMakeToolChainFile( cmakeToolChainTemplate, output, stanza, optionsDict={} ) :
237 cmakeToolChainTemplateFP = open( cmakeToolChainTemplate, "r" )
238 cmakeToolChainTemplateLines = cmakeToolChainTemplateFP.read()
239 cmakeToolChainTemplateFP.close()
241 configStanza = cmakeToolChainTemplateLines.format(
242 CPPFLAGS=stanza.kvPairs_["CPPFLAGS"],
243 # BYTESWAPIO=stanza.kvPairs_["BYTESWAPIO"],
244 CFLAGS=stanza.kvPairs_["CFLAGS"],
245 FFLAGS=stanza.kvPairs_["FFLAGS"],
246 DM_CC=stanza.kvPairs_["DM_CC"],
247 DM_FC=stanza.kvPairs_["DM_FC"],
248 DM_FC_FLAGS=stanza.kvPairs_["DM_FC_FLAGS"],
249 DM_CC_FLAGS=stanza.kvPairs_["DM_CC_FLAGS"],
250 # FCBASEOPTS=stanza.kvPairs_["FCBASEOPTS"],
251 # FCDEBUG=stanza.kvPairs_["FCDEBUG"],
252 # FCNOOPT=stanza.kvPairs_["FCNOOPT"],
253 # FCOPTIM=stanza.kvPairs_["FCOPTIM"],
254 # M4_FLAGS=stanza.kvPairs_["M4_FLAGS"],
255 SCC=stanza.kvPairs_["SCC"],
256 SFC=stanza.kvPairs_["SFC"]
259 # Extra stufff not from stanza but options
260 fmtOption = "set( {opt:<32} {value:<12} CACHE STRING \"Set by configuration\" FORCE )"
261 configStanza += "\n" + "\n".join( [ fmtOption.format( opt=key, value=value ) for key, value in optionsDict.items() ] )
263 outputFP = open( output, "w" )
264 outputFP.write( configStanza )
265 outputFP.close()
267 def main() :
268 configFile = sys.argv[1]
269 cmakeTemplateFile = sys.argv[2]
270 cmakeConfigFile = sys.argv[3]
271 cmakeFile = sys.argv[4]
273 # coreOption = getStringOptionSelection( cmakeFile, "WRF_CORE_OPTIONS" )
274 # nestingOption = getStringOptionSelection( cmakeFile, "WRF_NESTING_OPTIONS" )
275 # caseOption = getStringOptionSelection( cmakeFile, "WRF_CASE_OPTIONS" )
277 # These are yes
278 yesValues = [ "yes", "y", "true", "1" ]
280 #!TODO Expand this for all wrf options
281 useMPI = input( "[DM] Use MPI? [Y/n] : " ).lower() in yesValues
282 useOpenMP = input( "[SM] Use OpenMP? [Y/n] : " ).lower() in yesValues
283 buildExt = input( "[EXT] Build external? [Y/n] : " ).lower() in yesValues
286 fp = open( configFile, 'r' )
287 lines = fp.read()
288 fp.close()
290 # Now grab the blocks and parse
291 stanzas = []
292 uniqueConfigs = {}
293 stanzaIdx = 0
294 for stanzaBlock in archBlock.finditer( lines ) :
295 stanza = Stanza( stanzaBlock.group(1) )
296 stanza.parse()
298 stanzas.append( stanza )
299 stanzaConfig = str( stanza )
300 stanzaId = "{idx:<3} ".format( idx=stanzaIdx )
301 if stanzaConfig not in uniqueConfigs :
302 uniqueConfigs[ stanzaConfig ] = { "stanza" : stanza, "idx" : stanzaIdx }
303 print( stanzaId + stanzaConfig + "[first entry]" )
304 else :
305 diff, value = Stanza.findFirstDifference( uniqueConfigs[ stanzaConfig ]["stanza"], stanza )
306 if diff :
307 print( stanzaId + stanzaConfig + "@{idx} diff => {value}".format( idx=uniqueConfigs[ stanzaConfig ][ "idx" ], value=value ) )
308 else :
309 print( stanzaId + stanzaConfig + "[no difference]" )
310 stanzaIdx += 1
312 print( "!! - Not recommended for your system" )
314 idxSelection = int( input( "Select configuration [0-{stop}] (note !!) : ".format( stop=( stanzaIdx-1) ) ) )
315 if idxSelection < 0 or idxSelection > stanzaIdx - 1 :
316 print( "Invalid configuration selection!" )
317 exit(1)
319 additionalOptions = {
320 # "WRF_CORE" : coreOption,
321 # "WRF_NESTING" : nestingOption,
322 # "WRF_CASE" : caseOption,
323 "USE_OPENMP" : "ON" if useOpenMP else "OFF",
324 "USE_MPI" : "ON" if useMPI else "OFF",
325 "BUILD_EXTERNALS" : "ON" if buildExt else "OFF"
327 generateCMakeToolChainFile( cmakeTemplateFile, cmakeConfigFile, stanzas[idxSelection], additionalOptions )
332 if __name__ == '__main__' :
333 main()