ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/JSOC/localize.py
Revision: 1.8
Committed: Wed Nov 13 19:38:37 2013 UTC (9 years, 10 months ago) by arta
Content type: text/x-python
Branch: MAIN
Changes since 1.7: +6 -4 lines
Log Message:
Use config.local to override configsdp.txt - that way we do not have to edit configsdp.txt which will lead people to checking-in an edited version configsdp.txt

File Contents

# User Rev Content
1 arta 1.1 #!/home/jsoc/bin/linux_x86_64/activepython
2    
3     import sys
4     import getopt
5     import re
6 arta 1.6 import os
7     import stat
8     import xml.etree.ElementTree as ET
9 arta 1.1 from subprocess import check_output, CalledProcessError
10    
11     # Constants
12     VERS_FILE = 'jsoc_version.h'
13     SDP_CFG = 'configsdp.txt'
14     NET_CFG = 'config.local'
15     NET_CFGMAP = 'config.local.map'
16     RET_SUCCESS = 0
17     RET_NOTDRMS = 1
18    
19 arta 1.2 PREFIX = """# This file was auto-generated by localize.py. Please do not edit it directly (running
20     # configure will run localize.py, which will then overwrite any edits manually performed).
21     """
22 arta 1.1
23 arta 1.2 C_PREFIX = """/* This file was auto-generated by localize.py. Please do not edit it directly (running
24     * configure will run localize.py, which will then overwrite any edits manually performed). */
25 arta 1.1 """
26    
27 arta 1.2 PERL_BINPATH = '#!/usr/bin/perl\n'
28    
29     PERL_INTIAL = """package drmsparams;
30    
31     use warnings;
32     use strict;
33     """
34    
35     PERL_FXNS_A = """sub new
36 arta 1.1 {
37     my($clname) = shift;
38    
39     my($self) =
40     {
41     _paramsH => undef
42     };
43    
44     bless($self, $clname);
45     $self->{_paramsH} = $self->initialize();
46    
47     return $self;
48     }
49    
50     sub DESTROY
51     {
52     my($self) = shift;
53     }
54 arta 1.2 """
55    
56     PERL_FXNS_B = """sub get
57     {
58     my($self) = shift;
59     my($name) = shift;
60     my($rv);
61 arta 1.1
62 arta 1.2 if (exists($self->{_paramsH}->{$name}))
63     {
64     return $self->{_paramsH}->{$name};
65     }
66     else
67     {
68     return undef;
69     }
70     }
71     1;"""
72 arta 1.1
73 arta 1.6
74     RULESPREFIX = """# Standard things
75     sp := $(sp).x
76     dirstack_$(sp) := $(d)
77     d := $(dir)
78     """
79    
80     RULESSUFFIX = """dir := $(d)/example
81     -include $(SRCDIR)/$(dir)/Rules.mk
82     dir := $(d)/cookbook
83     -include $(SRCDIR)/$(dir)/Rules.mk
84     dir := $(d)/myproj
85     -include $(SRCDIR)/$(dir)/Rules.mk
86    
87     # Standard things
88     d := $(dirstack_$(sp))
89     sp := $(basename $(sp))
90     """
91    
92     TARGETPREFIX = """$(PROJOBJDIR):\n\t+@[ -d $@ ] || mkdir -p $@"""
93    
94 arta 1.3 ICC_MAJOR = 9
95     ICC_MINOR = 0
96     GCC_MAJOR = 3
97     GCC_MINOR = 0
98     IFORT_MAJOR = 9
99     IFORT_MINOR = 0
100     GFORT_MAJOR = 4
101     GFORT_MINOR = 2
102    
103    
104 arta 1.1 # Read arguments
105     # d - localization directory
106     # b - base name of all parameter files (e.g., -b drmsparams --> drmsparams.h, drmsparams.mk, drmsparams.pm, etc.)
107     # m - make file
108     def GetArgs(args):
109     rv = bool(0)
110     optD = {}
111    
112     try:
113     opts, remainder = getopt.getopt(args, "hd:b:",["dir=", "base="])
114     except getopt.GetoptError:
115     print('Usage:')
116     print('localize.py [-h] -d <localization directory> -b <parameter file base>')
117     rv = bool(1)
118    
119     if rv == bool(0):
120     for opt, arg in opts:
121     if opt == '-h':
122     print('localize.py [-h] -d <localization directory> -b <parameter file base>')
123     elif opt in ("-d", "--dir"):
124     regexp = re.compile(r"(\S+)/?")
125     matchobj = regexp.match(arg)
126     if matchobj is None:
127     rv = bool(1)
128     else:
129     optD['dir'] = matchobj.group(1)
130     elif opt in ("-b", "--base"):
131     optD['base'] = arg
132     else:
133     optD[opt] = arg
134    
135     return optD
136    
137     def createMacroStr(key, val, keyColLen, status):
138     if keyColLen < len(key):
139     status = bool(1)
140     return None
141     else:
142     nsp = keyColLen - len(key)
143     spaces = str()
144     for isp in range(nsp):
145     spaces += ' '
146     status = bool(0)
147     return '#define ' + key + spaces + val + '\n'
148    
149     def createPerlConst(key, val, keyColLen, status):
150     if keyColLen < len(key):
151     status = bool(1)
152     return None
153     else:
154     nsp = keyColLen - len(key)
155     spaces = str()
156     for isp in range(nsp):
157     spaces += ' '
158     status = bool(0)
159     return 'use constant ' + key + ' => ' + spaces + val + ';\n'
160    
161 arta 1.3 def isSupportedPlat(plat):
162     regexp = re.compile(r"\s*(^x86_64|^ia32|^ia64|^avx)", re.IGNORECASE)
163     matchobj = regexp.match(plat);
164    
165     if not matchobj is None:
166     return bool(1);
167     else:
168     return bool(0);
169    
170 arta 1.6 def processMakeParam(mDefs, key, val, platDict, machDict):
171 arta 1.3 varMach = None
172     regexp = re.compile(r"(\S+):(\S+)")
173     matchobj = regexp.match(key)
174     if not matchobj is None:
175     varName = matchobj.group(1)
176     varMach = matchobj.group(2)
177     else:
178     varName = key
179    
180     varValu = val
181    
182     if varMach is None:
183     mDefs.extend(list('\n' + varName + ' = ' + varValu))
184     else:
185     if isSupportedPlat(varMach):
186     # The guard will compare varValu to $JSOC_MACHINE.
187     if not varMach in platDict:
188     platDict[varMach] = {}
189     platDict[varMach][varName] = varValu
190     else:
191     # The guard will compare varValu to $MACHINETYPE (this is just the hostname of the machine on which localize.py is running).
192     if not varMach in machDict:
193     machDict[varMach] = {}
194     machDict[varMach][varName] = varValu
195    
196 arta 1.6 def processParam(cfgfile, line, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, section):
197 arta 1.1 status = 0
198    
199     if ''.join(section) == 'defs' or not cfgfile:
200     matchobj = regexp.match(line)
201     if not matchobj is None:
202     # We have a key-value line
203     keyCfgSp = matchobj.group(1)
204     val = matchobj.group(2)
205    
206     # Must map the indirect name to the actual name
207     if keymap:
208     # Map to actual name only if the keymap is not empty (which signifies NA).
209     if keyCfgSp in keymap:
210     key = keymap[keyCfgSp]
211     elif keyCfgSp == 'LOCAL_CONFIG_SET' or keyCfgSp == 'DRMS_SAMPLE_NAMESPACE':
212 arta 1.6 # Ignore parameters that are not useful and shouldn't have been there in the first place. But
213     # they have been released to the world, so we have to account for them.
214 arta 1.1 return bool(0)
215     elif not cfgfile:
216     # Should not be doing mapping for addenda
217     key = keyCfgSp
218     else:
219     raise Exception('badKeyMapKey', keyCfgSp)
220     else:
221     key = keyCfgSp
222    
223     matchobj = regexpQuote.match(key)
224     if not matchobj is None:
225     quote = matchobj.group(1)
226     key = matchobj.group(2)
227    
228     # master defs dictionary
229     defs[key] = val
230    
231     # C header file
232     if quote == "q":
233     # Add double-quotes
234     cDefs.extend(list(createMacroStr(key, '"' + val + '"', 40, status)))
235     elif quote == "p":
236     # Add parentheses
237     cDefs.extend(list(createMacroStr(key, '(' + val + ')', 40, status)))
238     elif quote == "a":
239     # Leave as-is
240     cDefs.extend(list(createMacroStr(key, val, 40, status)))
241     else:
242     # Unknown quote type
243     raise Exception('badQuoteQual', key)
244    
245     if status:
246     raise Exception('paramNameTooLong', key)
247    
248     # Make file - val should never be quoted; just use as is
249 arta 1.4 mDefsGen.extend(list('\n' + key + ' = ' + val))
250 arta 1.1
251     # Perl file - val should ALWAYS be single-quote quoted
252     # Save const info to a string
253     perlConstSection.extend(list(createPerlConst(key, "'" + val + "'", 40, status)))
254    
255     if status:
256     raise Exception('paramNameTooLong', key)
257    
258     # Save initialization information as a string. Now that we've defined
259     # constants (the names of which are the parameter names)
260     # we can refer to those in the init section. The key variable holds the
261     # name of the constant.
262 arta 1.2 perlInitSection.extend(list('\n $self->{_paramsH}->{' + key + '} = ' + "'" + val + "';"))
263 arta 1.1 else:
264     # No quote qualifier
265     raise Exception('missingQuoteQual', key)
266 arta 1.3 elif ''.join(section) == 'make' and cfgfile:
267     # Configure the remaining make variables defined in the __MAKE__ section of the configuration file. Third-party
268     # library make variables are specified in the __MAKE__ section.
269     matchobj = regexp.match(line)
270     if not matchobj is None:
271     # We have a key-value line
272     key = matchobj.group(1)
273     val = matchobj.group(2)
274    
275     # This information is for making make variables only. We do not need to worry about quoting any values
276     defs[key] = val
277 arta 1.4 processMakeParam(mDefsMake, key, val, platDict, machDict)
278 arta 1.1
279     return bool(0)
280    
281     # We have some extraneous line or a newline - ignore.
282    
283 arta 1.6 def processXML(xml, projRules, projTarget):
284     rv = bool(0)
285    
286     # <projects>
287     root = ET.fromstring(xml)
288    
289     # Iterate through each proj child.
290     for proj in root.iter('proj'):
291     # Rules.mk
292     nameElem = proj.find('name')
293     rulesStr = 'dir := $(d)/' + nameElem.text + '\n-include $(SRCDIR)/$(dir)/Rules.mk\n'
294    
295     # make doesn't support logical operations in ifeq conditionals (you can't do ifeq (A AND B)),
296     # so we need to write:
297     # ifeq (A)
298     # ifeq (B)
299     # <do something>
300     # endif
301     # endif
302    
303     rulesPref = '';
304     rulesSuff = '';
305    
306     filters = proj.find('filters')
307     if filters is not None:
308     for filter in filters.findall('filter'):
309     rulesPref += 'ifeq ($(' + filter.find('name').text + '),' + filter.find('value').text + ')\n'
310     rulesSuff += 'endif\n'
311    
312     if len(rulesPref) > 0 and len(rulesSuff) > 0:
313     projRules.extend(list(rulesPref))
314     projRules.extend(list(rulesStr))
315     projRules.extend(list(rulesSuff))
316     else:
317     projRules.extend(list(rulesStr))
318    
319     # target.mk
320     subdirs = proj.find('subdirs')
321     if subdirs is not None:
322     for subdir in subdirs.findall('subdir'):
323     targetStr = '\n\t+@[ -d $@/' + nameElem.text + '/' + subdir.text + ' ] || mkdir -p $@/' + nameElem.text + '/' + subdir.text
324     projTarget.extend(list(targetStr))
325    
326     return rv
327    
328     def determineSection(line, regexpDefs, regexpMake, regexpProjMkRules, regexpProj, regexpProjCfg):
329     matchobj = regexpDefs.match(line)
330     if not matchobj is None:
331     return 'defs'
332    
333     matchobj = regexpMake.match(line)
334     if not matchobj is None:
335     return 'make'
336    
337     matchobj = regexpProjMkRules.match(line)
338     if not matchobj is None:
339     return 'projmkrules'
340    
341     matchobj = regexpProj.match(line)
342     if not matchobj is None:
343     return 'proj'
344    
345     matchobj = regexpProjCfg.match(line)
346     if not matchobj is None:
347     return 'projcfg'
348    
349     return None
350    
351 arta 1.1 # defs is a dictionary containing all parameters (should they be needed in this script)
352 arta 1.6 # projCfg is the list containing the configure script content.
353     # projMkRules is the list containing the make_basic.mk content.
354     # projRules is the list containing the Rules.mk content.
355     # projTargert is the list containing the target.mk content.
356     def parseConfig(fin, keymap, addenda, defs, cDefs, mDefsGen, mDefsMake, projCfg, projMkRules, projRules, projTarget, perlConstSection, perlInitSection):
357 arta 1.1 rv = bool(0)
358    
359     # Open required config file (config.local)
360     try:
361     # Examine each line, looking for key=value pairs.
362     regexpDefs = re.compile(r"^__DEFS__")
363     regexpMake = re.compile(r"^__MAKE__")
364 arta 1.6 regexpProjMkRules = re.compile(r"__PROJ_MK_RULES__")
365     regexpProj = re.compile(r"^__PROJ__")
366     regexpProjCfg = re.compile(r"^__PROJCFG__")
367 arta 1.1 regexpComm = re.compile(r"^\s*#")
368 arta 1.6 regexpSp = re.compile(r"^s*$")
369 arta 1.1 regexpQuote = re.compile(r"^\s*(\w):(.+)")
370 arta 1.6 regexpCustMkBeg = re.compile(r"^_CUST_")
371     regexpCustMkEnd = re.compile(r"^_ENDCUST_")
372     regexpDiv = re.compile(r"^__")
373 arta 1.3 regexp = re.compile(r"^\s*(\S+)\s+(\S+)")
374 arta 1.1
375 arta 1.3 platDict = {}
376     machDict = {}
377 arta 1.6
378     xml = None
379 arta 1.1
380     # Process the parameters in the configuration file
381     if not fin is None:
382     for line in fin:
383 arta 1.6 matchobj = regexpComm.match(line)
384     if not matchobj is None:
385     # Skip comment line
386     continue
387    
388     matchobj = regexpSp.match(line)
389     if not matchobj is None:
390     # Skip whitespace line
391     continue
392 arta 1.1
393 arta 1.6 newSection = determineSection(line, regexpDefs, regexpMake, regexpProjMkRules, regexpProj, regexpProjCfg)
394     if not newSection is None:
395     section = newSection
396    
397     if section == 'make':
398     # There are some blocks of lines in the __MAKE__ section that must be copied ver batim to the output make file.
399     # The blocks are defined by _CUST_/_ENDCUST_ tags.
400     matchobj = regexpCustMkBeg.match(line)
401    
402     if not matchobj is None:
403     mDefsMake.extend(list('\n'))
404     for line in fin:
405     matchobj = regexpCustMkEnd.match(line)
406     if not matchobj is None:
407     break;
408     mDefsMake.extend(list(line))
409     newSection = determineSection(line, regexpDefs, regexpMake, regexpProjMkRules, regexpProj, regexpProjCfg)
410     if not newSection is None:
411     section = newSection
412     continue
413     # Intentional fall through to next if statement
414     if section == 'defs' or section == 'make':
415     iscfg = bool(1)
416     ppRet = processParam(iscfg, line, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, section)
417    
418     if ppRet:
419     break;
420     elif section == 'projcfg':
421     # Copy the line ver batim to the projCfg list (configure)
422     for line in fin:
423     matchobj = regexpDiv.match(line)
424     if not matchobj is None:
425     break;
426     projCfg.extend(list(line))
427     newSection = determineSection(line, regexpDefs, regexpMake, regexpProjMkRules, regexpProj, regexpProjCfg)
428     if not newSection is None:
429     section = newSection
430     continue
431     elif section == 'projmkrules':
432     # Copy the line ver batim to the projMkRules list (make_basic.mk)
433     for line in fin:
434     matchobj = regexpDiv.match(line)
435     if not matchobj is None:
436     break;
437     projMkRules.extend(list(line))
438     newSection = determineSection(line, regexpDefs, regexpMake, regexpProjMkRules, regexpProj, regexpProjCfg)
439     if not newSection is None:
440     section = newSection
441     continue
442     elif section == 'proj':
443     # Must parse xml and use the project-specific information to populate the Rules.mk and target.mk files.
444     # Collect all xml lines for now, then process after file-read loop.
445     if xml is None:
446     xml = line
447     else:
448     xml += line
449     else:
450     # Unknown section
451     raise Exception('unknownSection', section)
452 arta 1.1 except Exception as exc:
453 arta 1.6 msg = exc.args[0]
454 arta 1.1 if msg == 'badKeyMapKey':
455     # If we are here, then there was a non-empty keymap, and the parameter came from
456     # the configuration file.
457 arta 1.6 violator = exc.args[1]
458 arta 1.1 print('Unknown parameter name ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
459     rv = bool(1)
460     elif msg == 'badQuoteQual':
461     # The bad quote qualifier came from the configuration file, not the addenda, since
462     # we will have fixed any bad qualifiers in the addenda (which is populated by code).
463 arta 1.6 violator = exc.args[1]
464 arta 1.1 print('Unknown quote qualifier ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
465     rv = bool(1)
466     elif msg == 'missingQuoteQual':
467 arta 1.6 violator = exc.args[1]
468 arta 1.1 print('Missing quote qualifier for parameter ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
469     rv = bool(1)
470     elif msg == 'paramNameTooLong':
471 arta 1.6 violator = exc.args[1]
472 arta 1.1 print('Macro name ' + "'" + violator + "' is too long.", file=sys.stderr)
473     rv = bool(1)
474 arta 1.6 elif msg == 'unknownSection':
475     violator = exc.args[1]
476     print('Unknown section ' + "'" + violator + "' in configuration file.", file=sys.stderr)
477     rv = bool(1)
478 arta 1.1 else:
479     # re-raise the exception
480     raise
481    
482 arta 1.6 if not rv:
483     if not xml is None:
484     # Process xml.
485     projRules.extend(list(RULESPREFIX))
486     projTarget.extend(list(TARGETPREFIX))
487     rv = processXML(xml, projRules, projTarget)
488     projRules.extend(RULESSUFFIX)
489    
490     # Process addenda - these are parameters that are not configurable and must be set in the
491     # NetDRMS build.
492     if not rv:
493     iscfg = bool(0)
494     for key in addenda:
495     item = key + ' ' + addenda[key]
496     ppRet = processParam(iscfg, item, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, 'defs')
497     if ppRet:
498     break;
499    
500 arta 1.4 # Put information collected in platDict and machDict into mDefs. Must do this here, and not in processParam, since
501     # we need to parse all platform-specific make variables before grouping them into platform categories.
502 arta 1.3 if not rv:
503     for plat in platDict:
504 arta 1.4 mDefsMake.extend(list('\nifeq ($(JSOC_MACHINE), linux_' + plat.lower() + ')'))
505 arta 1.3 for var in platDict[plat]:
506 arta 1.4 mDefsMake.extend(list('\n' + var + ' = ' + platDict[plat][var]))
507     mDefsMake.extend(list('\nendif\n'))
508 arta 1.3
509     if not rv:
510     for mach in machDict:
511 arta 1.4 mDefsMake.extend(list('\nifeq ($(MACHTYPE), ' + mach + ')'))
512 arta 1.7 for var in machDict[mach]:
513     mDefsMake.extend(list('\n' + var + ' = ' + machDict[mach][var]))
514 arta 1.4 mDefsMake.extend(list('\nendif\n'))
515 arta 1.3
516 arta 1.1 return rv
517    
518     def getMgrUIDLine(defs, uidParam):
519     rv = bool(0)
520    
521     cmd = 'id -u ' + defs['SUMS_MANAGER']
522     try:
523     ret = check_output(cmd, shell=True)
524     uidParam['q:SUMS_MANAGER_UID'] = ret.decode("utf-8")
525     except ValueError:
526     print('Unable to run cmd: ' + cmd + '.')
527     rv = bool(1)
528     except CalledProcessError:
529     print('Command ' + "'" + cmd + "'" + ' ran improperly.')
530     rv = bool(1)
531    
532     return rv
533    
534 arta 1.3 def isVersion(maj, min, majDef, minDef):
535     res = 0
536    
537     if maj > majDef or (maj == majDef and min >= minDef):
538     res = 1
539    
540     return res
541    
542     def configureComps(defs, mDefs):
543     rv = bool(0)
544 arta 1.6 autoConfig = bool(1)
545    
546     if 'AUTOSELCOMP' in defs:
547     autoConfig = (not defs['AUTOSELCOMP'] == '0')
548 arta 1.3
549     if autoConfig:
550     hasicc = bool(0)
551     hasgcc = bool(0)
552     hasifort = bool(0)
553     hasgfort = bool(0)
554    
555     # Try icc.
556     cmd = 'icc -V 2>&1'
557     try:
558     ret = check_output(cmd, shell=True)
559     ret = ret.decode("utf-8")
560     except CalledProcessError:
561     print('Command ' + "'" + cmd + "'" + ' ran improperly.')
562     rv = bool(1)
563    
564 arta 1.6 if not rv:
565 arta 1.3 regexp = re.compile(r".+Version\s+(\d+)[.](\d+)", re.DOTALL)
566     matchobj = regexp.match(ret)
567     if matchobj is None:
568     raise Exception('unexpectedIccRet', ret)
569     else:
570     major = matchobj.group(1)
571     minor = matchobj.group(2)
572     if isVersion(int(major), int(minor), ICC_MAJOR, ICC_MINOR):
573     hasicc = bool(1)
574    
575     # Try gcc.
576     if not hasicc:
577     cmd = 'gcc -v 2>&1'
578     try:
579     ret = check_output(cmd, shell=True)
580     ret = ret.decode("utf-8")
581     except CalledProcessError:
582     print('Command ' + "'" + cmd + "'" + ' ran improperly.')
583     rv = bool(1)
584    
585     if rv == bool(0):
586     regexp = re.compile(r".+gcc\s+version\s+(\d+)\.(\d+)", re.DOTALL)
587     matchobj = regexp.match(ret)
588     if matchobj is None:
589     raise Exception('unexpectedGccRet', ret)
590     else:
591     major = matchobj.group(1)
592     minor = matchobj.group(2)
593     if isVersion(int(major), int(minor), GCC_MAJOR, GCC_MINOR):
594     hasgcc = bool(1)
595    
596     # Try ifort.
597     cmd = 'ifort -V 2>&1'
598     try:
599     ret = check_output(cmd, shell=True)
600     ret = ret.decode("utf-8")
601     except CalledProcessError:
602     print('Command ' + "'" + cmd + "'" + ' ran improperly.')
603     rv = bool(1)
604    
605 arta 1.6 if not rv:
606 arta 1.3 regexp = re.compile(r".+Version\s+(\d+)\.(\d+)", re.DOTALL)
607     matchobj = regexp.match(ret)
608     if matchobj is None:
609     raise Exception('unexpectedIfortRet', ret)
610     else:
611     major = matchobj.group(1)
612     minor = matchobj.group(2)
613     if isVersion(int(major), int(minor), IFORT_MAJOR, IFORT_MINOR):
614     hasifort = bool(1)
615    
616     # Try gfortran
617     if not hasifort:
618     cmd = 'gfortran -v 2>&1'
619     try:
620     ret = check_output(cmd, shell=True)
621     ret = ret.decode("utf-8")
622     except CalledProcessError:
623     print('Command ' + "'" + cmd + "'" + ' ran improperly.')
624     rv = bool(1)
625    
626     if rv == bool(0):
627     regexp = re.compile(r".+gcc\s+version\s+(\d+)\.(\d+)", re.DOTALL)
628     matchobj = regexp.match(ret)
629     if matchobj is None:
630     raise Exception('unexpectedGfortranRet', ret)
631     else:
632     major = matchobj.group(1)
633     minor = matchobj.group(2)
634     if isVersion(int(major), int(minor), GFORT_MAJOR, GFORT_MINOR):
635     hasgfort = bool(1)
636    
637     # Append the compiler make variables to the make file
638     if not hasicc and not hasgcc:
639     print('Fatal error: Acceptable C compiler not found! You will be unable to build the DRMS library.', file=sys.stderr)
640     rv = bool(1)
641     elif hasicc:
642     mDefs.extend(list('\nCOMPILER = icc'))
643     else:
644     mDefs.extend(list('\nCOMPILER = gcc'))
645    
646     if not hasifort and not hasgfort:
647     print('Warning: Acceptable Fortran compiler not found! Fortran interface will not be built, and you will be unable to build Fortran modules.', file=sys.stderr)
648     elif hasifort:
649     mDefs.extend(list('\nFCOMPILER = ifort'))
650     else:
651     mDefs.extend(list('\nFCOMPILER = gfortran'))
652    
653     # Environment overrides. These get written, regardless of the disposition of auto-configuration.
654 arta 1.7 mDefs.extend(list('\nifneq ($(JSOC_COMPILER,))\n COMPILER = $(JSOC_COMPILER)\nendif'))
655     mDefs.extend(list('\nifneq ($(JSOC_FCOMPILER,))\n FCOMPILER = $(JSOC_FCOMPILER)\nendif'))
656 arta 1.3
657     return rv
658    
659 arta 1.6 def writeParamsFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, mDefsComps, perlConstSection, perlInitSection):
660 arta 1.1 rv = bool(0)
661 arta 1.4
662     # Merge mDefsGen, mDefsMake, and mDefsComps into a single string with compiler configuration first, general parameters next, then
663     # make-specific make variables (e.g., third-party library information) last.
664     mDefs = '\n# Compiler Selection\n' + ''.join(mDefsComps) + '\n\n# General Parameters\n' + ''.join(mDefsGen) + '\n\n# Parameters to Configure make\n' + ''.join(mDefsMake)
665 arta 1.1
666     try:
667     with open(cfile, 'w') as cout, open(mfile, 'w') as mout, open(pfile, 'w') as pout:
668     # C file of macros
669 arta 1.2 print(C_PREFIX, file=cout)
670     print('/* This file contains a set of preprocessor macros - one for each configuration parameter. */\n', file=cout)
671 arta 1.1 buf = '__' + base.upper() + '_H'
672     print('#ifndef ' + buf, file=cout)
673     print('#define ' + buf, file=cout)
674     print(''.join(cDefs), file=cout)
675     print('#endif', file=cout)
676    
677     # Make file of make variables
678 arta 1.2 print(PREFIX, file=mout)
679 arta 1.4 print('# This file contains a set of make-variable values. The first section contains compiler-selection variables, the second contains general configuration variables, and the third section contains variables that configure how make is run.', file=mout)
680     print(mDefs, file=mout)
681 arta 1.1
682 arta 1.2 # Perl module
683     print(PERL_BINPATH, file=pout)
684     print(PREFIX, file=pout)
685     print('# This file contains a set of constants - one for each configuration parameter.\n', file=pout)
686     print(PERL_INTIAL, file=pout)
687 arta 1.1 print(''.join(perlConstSection), file=pout)
688 arta 1.2 print(PERL_FXNS_A, file=pout)
689 arta 1.1 print('sub initialize', file=pout)
690     print('{', file=pout)
691 arta 1.2 print(' my($self) = shift;', file=pout, end='')
692 arta 1.1 print('', file=pout)
693     print(''.join(perlInitSection), file=pout)
694 arta 1.2 print('}\n', file=pout)
695     print(PERL_FXNS_B, file=pout)
696 arta 1.1 except IOError as exc:
697 arta 1.6 type, value, traceback = sys.exc_info()
698     print(exc.strerror, file=sys.stderr)
699     print('Unable to open ' + "'" + value.filename + "'.", file=sys.stderr)
700     rv = bool(1)
701    
702     return rv
703    
704     def writeProjFiles(pCfile, pMfile, pRfile, pTfile, projCfg, projMkRules, projRules, projTarget):
705     rv = bool(0)
706    
707     try:
708     if projCfg:
709     with open(pCfile, 'w') as cout:
710     # configure
711     print(''.join(projCfg), file=cout)
712    
713     if projMkRules:
714     with open(pMfile, 'w') as mout:
715     # make_basic.mk
716     print(PREFIX, file=mout)
717     print(''.join(projMkRules), file=mout)
718    
719     if projRules:
720     with open(pRfile, 'w') as rout:
721     # Rules.mk
722     print(PREFIX, file=rout)
723     print(''.join(projRules), file=rout)
724    
725     if projTarget:
726     with open(pTfile, 'w') as tout:
727     # target.mk
728     print(PREFIX, file=tout)
729     print(''.join(projTarget), file=tout)
730     except IOError as exc:
731     type, value, traceback = sys.exc_info()
732     print(exc.strerror, file=sys.stderr)
733     print('Unable to open ' + "'" + value.filename + "'.", file=sys.stderr)
734 arta 1.1 rv = bool(1)
735    
736 arta 1.6 if not rv:
737     try:
738     os.chmod(pCfile, stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH)
739     except OSError as exc:
740     print(exc.strerror, file=sys.stderr)
741     rv = bool(1)
742    
743 arta 1.1 return rv
744 arta 1.3
745 arta 1.6 def configureNet(cfgfile, cfile, mfile, pfile, pCfile, pMfile, pRfile, pTfile, base, keymap):
746 arta 1.1 rv = bool(0)
747    
748     defs = {}
749     cDefs = list()
750 arta 1.4 mDefsGen = list()
751     mDefsMake = list()
752     mDefsComps = list()
753 arta 1.6 projCfg = list()
754     projMkRules = list()
755     projRules = list()
756     projTarget = list()
757 arta 1.1 perlConstSection = list()
758     perlInitSection = list()
759 arta 1.2 addenda = {}
760    
761 arta 1.6 # There are three parameters that were not included in the original config.local parameter set, for some reason.
762     # Due to this omission, then are not configurable, and must be set in the script.
763 arta 1.2 addenda['a:USER'] = 'NULL'
764     addenda['a:PASSWD'] = 'NULL'
765     addenda['p:DSDS_SUPPORT'] = '0'
766 arta 1.6
767     # This parameter is not configurable. BUILD_TYPE is used to distinguish between a NetDRMS and an JSOC-SDP build.
768 arta 1.2 addenda['a:BUILD_TYPE'] = 'NETDRMS' # Means a non-Stanford build. This will set two additional macros used by make:
769     # __LOCALIZED_DEFS__ and NETDRMS_BUILD. The former is to support legacy code
770     # which incorrectly used this macro, and the latter is for future use.
771     # __LOCALIZED_DEFS__ is deprecated and should not be used in new code.
772 arta 1.1
773     try:
774     with open(cfgfile, 'r') as fin:
775 arta 1.3 # Process configuration parameters
776 arta 1.6 rv = parseConfig(fin, keymap, addenda, defs, cDefs, mDefsGen, mDefsMake, projCfg, projMkRules, projRules, projTarget, perlConstSection, perlInitSection)
777     if not rv:
778 arta 1.3 # Must add a parameter for the SUMS_MANAGER UID (for some reason). This must be done after the
779     # config file is processed since an input to getMgrUIDLine() is one of the config file's
780     # parameter values.
781 arta 1.1 uidParam = {}
782     rv = getMgrUIDLine(defs, uidParam)
783     if rv == bool(0):
784 arta 1.6 rv = parseConfig(None, keymap, uidParam, defs, cDefs, mDefsGen, None, projCfg, projMkRules, projRules, projTarget, perlConstSection, perlInitSection)
785 arta 1.3
786     # Configure the compiler-selection make variables.
787 arta 1.6 if not rv:
788 arta 1.4 rv = configureComps(defs, mDefsComps)
789 arta 1.1
790 arta 1.3 # Write out the parameter files.
791 arta 1.6 if not rv:
792     rv = writeParamsFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, mDefsComps, perlConstSection, perlInitSection)
793    
794     # Write out the project-specific make files (make_basic.mk, Rules.mk, and target.mk).
795     if not rv:
796     rv = writeProjFiles(pCfile, pMfile, pRfile, pTfile, projCfg, projMkRules, projRules, projTarget)
797    
798 arta 1.1 except IOError as exc:
799 arta 1.6 print(exc.strerror, file=sys.stderr)
800     print('Unable to read configuration file ' + cfgfile + '.', file=sys.stderr)
801 arta 1.3 except Exception as exc:
802     type, msg = exc.args
803     if type == 'unexpectedIccRet':
804     print('icc -V returned this unexpected message:\n' + msg, file=sys.stderr)
805     rv = bool(1)
806     elif type == 'unexpectedGccRet':
807     print('gcc -v returned this unexpected message:\n' + msg, file=sys.stderr)
808     rv = bool(1)
809     elif type == 'unexpectedIfortRet':
810     print('ifort -V returned this unexpected message:\n' + msg, file=sys.stderr)
811     rv = bool(1)
812     elif type == 'unexpectedGfortranRet':
813     print('gfortran -v returned this unexpected message:\n' + msg, file=sys.stderr)
814     rv = bool(1)
815     else:
816     # re-raise the exception
817     raise
818 arta 1.1
819 arta 1.2 return rv
820 arta 1.1
821 arta 1.6 def configureSdp(cfgfile, cfile, mfile, pfile, pCfile, pMfile, pRfile, pTfile, base):
822 arta 1.1 rv = bool(0)
823    
824     defs = {}
825     cDefs = list()
826 arta 1.5 mDefsGen = list()
827     mDefsMake = list()
828 arta 1.6 projCfg = list()
829     projMkRules = list()
830     projRules = list()
831     projTarget = list()
832 arta 1.5 mDefsComps = list()
833 arta 1.1 perlConstSection = list()
834     perlInitSection = list()
835 arta 1.2 addenda = {}
836    
837 arta 1.6 # There are three parameters that were not included in the original config.local parameter set, for some reason.
838     # Due to this omission, then are not configurable, and must be set in the script.
839 arta 1.5 addenda['a:USER'] = 'NULL'
840     addenda['a:PASSWD'] = 'NULL'
841     addenda['p:DSDS_SUPPORT'] = '1'
842 arta 1.6
843     # This parameter is not configurable. BUILD_TYPE is used to distinguish between a NetDRMS and an JSOC-SDP build.
844 arta 1.5 addenda['a:BUILD_TYPE'] = 'JSOC_SDP' # Means a Stanford build. This will set one additional macro used by make: JSOC_SDP_BUILD.
845 arta 1.1
846     try:
847     with open(cfgfile, 'r') as fin:
848 arta 1.6 rv = parseConfig(fin, None, addenda, defs, cDefs, mDefsGen, mDefsMake, projCfg, projMkRules, projRules, projTarget, perlConstSection, perlInitSection)
849    
850     if not rv:
851 arta 1.2 # Must add a parameter for the SUMS_MANAGER UID (for some reason)
852     uidParam = {}
853     rv = getMgrUIDLine(defs, uidParam)
854 arta 1.6 if not rv:
855     rv = parseConfig(None, None, uidParam, defs, cDefs, mDefsGen, None, projCfg, projMkRules, projRules, projTarget, perlConstSection, perlInitSection)
856    
857 arta 1.5 # Configure the compiler-selection make variables.
858 arta 1.6 if not rv:
859 arta 1.5 rv = configureComps(defs, mDefsComps)
860 arta 1.3
861 arta 1.6 # Write out the parameter files.
862     if not rv:
863     rv = writeParamsFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, mDefsComps, perlConstSection, perlInitSection)
864    
865     # Write out the project-specific make files (make_basic.mk, Rules.mk, and target.mk).
866     if not rv:
867     rv = writeProjFiles(pCfile, pMfile, pRfile, pTfile, projCfg, projMkRules, projRules, projTarget)
868 arta 1.1 except IOError as exc:
869 arta 1.6 print(exc.strerror, file=sys.stderr)
870     print('Unable to read configuration file ' + cfgfile + '.', file=sys.stderr)
871 arta 1.5 except Exception as exc:
872 arta 1.6 type = exc.args[0]
873 arta 1.5 if type == 'unexpectedIccRet':
874 arta 1.6 msg = exc.args[1]
875 arta 1.5 print('icc -V returned this unexpected message:\n' + msg, file=sys.stderr)
876     rv = bool(1)
877     elif type == 'unexpectedGccRet':
878 arta 1.6 msg = exc.args[1]
879 arta 1.5 print('gcc -v returned this unexpected message:\n' + msg, file=sys.stderr)
880     rv = bool(1)
881     elif type == 'unexpectedIfortRet':
882 arta 1.6 msg = exc.args[1]
883 arta 1.5 print('ifort -V returned this unexpected message:\n' + msg, file=sys.stderr)
884     rv = bool(1)
885     elif type == 'unexpectedGfortranRet':
886 arta 1.6 msg = exc.args[1]
887 arta 1.5 print('gfortran -v returned this unexpected message:\n' + msg, file=sys.stderr)
888     rv = bool(1)
889     else:
890     # re-raise the exception
891     raise
892    
893 arta 1.2 return rv
894    
895 arta 1.1 # Beginning of program
896     rv = RET_SUCCESS
897     net = bool(1)
898    
899     # Parse arguments
900     if __name__ == "__main__":
901     optD = GetArgs(sys.argv[1:])
902    
903     if not(optD is None):
904     # Ensure we are configuring a DRMS tree
905     cdir = os.path.realpath(os.getcwd())
906     versfile = cdir + '/base/' + VERS_FILE
907    
908     if not os.path.isfile(versfile):
909     rv = RET_NOTDRMS
910    
911     # Determine whether we are localizing a Stanford build, or a NetDRMS build. If configsdp.txt exists, then
912     # it is a Stanford build, otherwise it is a NetDRMS build.
913     if rv == RET_SUCCESS:
914     stanfordFile = cdir + '/' + SDP_CFG
915     if os.path.isfile(stanfordFile):
916     net = bool(0)
917    
918     cfile = optD['dir'] + '/' + optD['base'] + '.h'
919     mfile = optD['dir'] + '/' + optD['base'] + '.mk'
920     pfile = optD['dir'] + '/' + optD['base'] + '.pm'
921 arta 1.6 pCfile = optD['dir'] + '/configure'
922     pMfile = optD['dir'] + '/make_basic.mk'
923     pRfile = optD['dir'] + '/Rules.mk'
924     pTfile = optD['dir'] + '/target.mk'
925 arta 1.1
926     if net:
927     try:
928     with open(NET_CFGMAP, 'r') as fin:
929     regexpComm = re.compile(r"^\s*#")
930     regexp = re.compile(r"^\s*(\S+)\s+(\w:\S+)")
931     # Must map from config.local namespace to DRMS namespace (e.g., the names used for the C macros)
932     keymap = {}
933     for line in fin:
934     matchobj = regexpComm.match(line)
935     if not matchobj is None:
936     # Skip comment line
937     continue
938    
939     matchobj = regexp.match(line)
940     if not(matchobj is None):
941     # We have a key-value line
942     key = matchobj.group(1)
943     val = matchobj.group(2)
944     keymap[key] = val
945     except OSError:
946     sys.stderr.write('Unable to read configuration map-file ' + NET_CFGMAP + '.')
947     rv = bool(1)
948    
949     # We also need to set the UID of the SUMS manager. We have the name of the
950     # SUMS manager (it is in the configuration file)
951 arta 1.6 configureNet(NET_CFG, cfile, mfile, pfile, pCfile, pMfile, pRfile, pTfile, optD['base'], keymap)
952 arta 1.1 else:
953 arta 1.8 # A Stanford user can override the parameters in configsdp.txt by copying that file to config.local,
954     # and then editing config.local. So, if config.local exists, use that.
955     if os.path.isfile(cdir + '/' + NET_CFG):
956     configureSdp(NET_CFG, cfile, mfile, pfile, pCfile, pMfile, pRfile, pTfile, optD['base'])
957     else:
958     configureSdp(SDP_CFG, cfile, mfile, pfile, pCfile, pMfile, pRfile, pTfile, optD['base'])