1 arta 1.1 #!/home/jsoc/bin/linux_x86_64/activepython
2
3 import os.path
4 import sys
5 import getopt
6 import re
7 from subprocess import check_output, CalledProcessError
8
9 # Constants
10 VERS_FILE = 'jsoc_version.h'
11 SDP_CFG = 'configsdp.txt'
12 NET_CFG = 'config.local'
13 NET_CFGMAP = 'config.local.map'
14 RET_SUCCESS = 0
15 RET_NOTDRMS = 1
16
|
17 arta 1.2 PREFIX = """# This file was auto-generated by localize.py. Please do not edit it directly (running
18 # configure will run localize.py, which will then overwrite any edits manually performed).
19 """
|
20 arta 1.1
|
21 arta 1.2 C_PREFIX = """/* This file was auto-generated by localize.py. Please do not edit it directly (running
22 * configure will run localize.py, which will then overwrite any edits manually performed). */
|
23 arta 1.1 """
24
|
25 arta 1.2 PERL_BINPATH = '#!/usr/bin/perl\n'
26
27 PERL_INTIAL = """package drmsparams;
28
29 use warnings;
30 use strict;
31 """
32
33 PERL_FXNS_A = """sub new
|
34 arta 1.1 {
35 my($clname) = shift;
36
37 my($self) =
38 {
39 _paramsH => undef
40 };
41
42 bless($self, $clname);
43 $self->{_paramsH} = $self->initialize();
44
45 return $self;
46 }
47
48 sub DESTROY
49 {
50 my($self) = shift;
51 }
|
52 arta 1.2 """
53
54 PERL_FXNS_B = """sub get
55 {
56 my($self) = shift;
57 my($name) = shift;
58 my($rv);
|
59 arta 1.1
|
60 arta 1.2 if (exists($self->{_paramsH}->{$name}))
61 {
62 return $self->{_paramsH}->{$name};
63 }
64 else
65 {
66 return undef;
67 }
68 }
69 1;"""
|
70 arta 1.1
71 # Read arguments
72 # d - localization directory
73 # b - base name of all parameter files (e.g., -b drmsparams --> drmsparams.h, drmsparams.mk, drmsparams.pm, etc.)
74 # m - make file
75 def GetArgs(args):
76 rv = bool(0)
77 optD = {}
78
79 try:
80 opts, remainder = getopt.getopt(args, "hd:b:",["dir=", "base="])
81 except getopt.GetoptError:
82 print('Usage:')
83 print('localize.py [-h] -d <localization directory> -b <parameter file base>')
84 rv = bool(1)
85
86 if rv == bool(0):
87 for opt, arg in opts:
88 if opt == '-h':
89 print('localize.py [-h] -d <localization directory> -b <parameter file base>')
90 elif opt in ("-d", "--dir"):
91 arta 1.1 regexp = re.compile(r"(\S+)/?")
92 matchobj = regexp.match(arg)
93 if matchobj is None:
94 rv = bool(1)
95 else:
96 optD['dir'] = matchobj.group(1)
97 elif opt in ("-b", "--base"):
98 optD['base'] = arg
99 else:
100 optD[opt] = arg
101
102 return optD
103
104 def createMacroStr(key, val, keyColLen, status):
105 if keyColLen < len(key):
106 status = bool(1)
107 return None
108 else:
109 nsp = keyColLen - len(key)
110 spaces = str()
111 for isp in range(nsp):
112 arta 1.1 spaces += ' '
113 status = bool(0)
114 return '#define ' + key + spaces + val + '\n'
115
116 def createPerlConst(key, val, keyColLen, status):
117 if keyColLen < len(key):
118 status = bool(1)
119 return None
120 else:
121 nsp = keyColLen - len(key)
122 spaces = str()
123 for isp in range(nsp):
124 spaces += ' '
125 status = bool(0)
126 return 'use constant ' + key + ' => ' + spaces + val + ';\n'
127
128 def processParam(cfgfile, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section):
129 status = 0
130
131 matchobj = regexpComm.match(line)
132 if not matchobj is None:
133 arta 1.1 # Skip comment line
134 return bool(0)
135
136 matchobj = regexpDefs.match(line)
137 if not matchobj is None:
138 section.extend(list('defs'))
139 return bool(0)
140
141 matchobj = regexpMake.match(line)
142 if not matchobj is None:
143 section = 'make'
144 return bool(1)
145
146 if ''.join(section) == 'defs' or not cfgfile:
147 matchobj = regexp.match(line)
148 if not matchobj is None:
149 # We have a key-value line
150 keyCfgSp = matchobj.group(1)
151 val = matchobj.group(2)
152
153 # Must map the indirect name to the actual name
154 arta 1.1 if keymap:
155 # Map to actual name only if the keymap is not empty (which signifies NA).
156 if keyCfgSp in keymap:
157 key = keymap[keyCfgSp]
158 elif keyCfgSp == 'LOCAL_CONFIG_SET' or keyCfgSp == 'DRMS_SAMPLE_NAMESPACE':
159 # Ignore parameters that are not useful and shouldn't have been there in the first place
160 return bool(0)
161 elif not cfgfile:
162 # Should not be doing mapping for addenda
163 key = keyCfgSp
164 else:
165 raise Exception('badKeyMapKey', keyCfgSp)
166 else:
167 key = keyCfgSp
168
169 matchobj = regexpQuote.match(key)
170 if not matchobj is None:
171 quote = matchobj.group(1)
172 key = matchobj.group(2)
173
174 # master defs dictionary
175 arta 1.1 defs[key] = val
176
177 # C header file
178 if quote == "q":
179 # Add double-quotes
180 cDefs.extend(list(createMacroStr(key, '"' + val + '"', 40, status)))
181 elif quote == "p":
182 # Add parentheses
183 cDefs.extend(list(createMacroStr(key, '(' + val + ')', 40, status)))
184 elif quote == "a":
185 # Leave as-is
186 cDefs.extend(list(createMacroStr(key, val, 40, status)))
187 else:
188 # Unknown quote type
189 raise Exception('badQuoteQual', key)
190
191 if status:
192 raise Exception('paramNameTooLong', key)
193
194 # Make file - val should never be quoted; just use as is
|
195 arta 1.2 mDefs.extend(list('\n' + key + ' = ' + val))
|
196 arta 1.1
197 # Perl file - val should ALWAYS be single-quote quoted
198 # Save const info to a string
199 perlConstSection.extend(list(createPerlConst(key, "'" + val + "'", 40, status)))
200
201 if status:
202 raise Exception('paramNameTooLong', key)
203
204 # Save initialization information as a string. Now that we've defined
205 # constants (the names of which are the parameter names)
206 # we can refer to those in the init section. The key variable holds the
207 # name of the constant.
|
208 arta 1.2 perlInitSection.extend(list('\n $self->{_paramsH}->{' + key + '} = ' + "'" + val + "';"))
|
209 arta 1.1 else:
210 # No quote qualifier
211 raise Exception('missingQuoteQual', key)
212
213 return bool(0)
214
215 # We have some extraneous line or a newline - ignore.
216
217 # defs is a dictionary containing all parameters (should they be needed in this script)
|
218 arta 1.2 def parseConfig(fin, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection):
|
219 arta 1.1 rv = bool(0)
220
221 # Open required config file (config.local)
222 try:
223 # Examine each line, looking for key=value pairs.
224 regexpDefs = re.compile(r"^__DEFS__")
225 regexpMake = re.compile(r"^__MAKE__")
226 regexpComm = re.compile(r"^\s*#")
227 regexpQuote = re.compile(r"^\s*(\w):(.+)")
228 regexp = re.compile(r"^\s*(\S+)\s+(.+)")
229
230 section = list()
231
232 # Process the parameters in the configuration file
233 iscfg = bool(1)
234 if not fin is None:
235 for line in fin:
236 ppRet = processParam(iscfg, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section)
237 if ppRet:
238 break;
239
240 arta 1.1 # Process addenda - these are parameters that are not configurable and must be set in the
241 # NetDRMS build.
242 iscfg = bool(0)
243 for key in addenda:
244 item = key + ' ' + addenda[key]
245 ppRet = processParam(iscfg, item, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section)
246 if ppRet:
247 break;
248
249 except Exception as exc:
250 msg, violator = exc.args
251 if msg == 'badKeyMapKey':
252 # If we are here, then there was a non-empty keymap, and the parameter came from
253 # the configuration file.
254 print('Unknown parameter name ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
255 rv = bool(1)
256 elif msg == 'badQuoteQual':
257 # The bad quote qualifier came from the configuration file, not the addenda, since
258 # we will have fixed any bad qualifiers in the addenda (which is populated by code).
259 print('Unknown quote qualifier ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
260 rv = bool(1)
261 arta 1.1 elif msg == 'missingQuoteQual':
262 print('Missing quote qualifier for parameter ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
263 rv = bool(1)
264 elif msg == 'paramNameTooLong':
265 print('Macro name ' + "'" + violator + "' is too long.", file=sys.stderr)
266 rv = bool(1)
267 else:
268 # re-raise the exception
269 raise
270
271 return rv
272
273 def getMgrUIDLine(defs, uidParam):
274 rv = bool(0)
275
276 cmd = 'id -u ' + defs['SUMS_MANAGER']
277 try:
278 ret = check_output(cmd, shell=True)
279 uidParam['q:SUMS_MANAGER_UID'] = ret.decode("utf-8")
280 except ValueError:
281 print('Unable to run cmd: ' + cmd + '.')
282 arta 1.1 rv = bool(1)
283 except CalledProcessError:
284 print('Command ' + "'" + cmd + "'" + ' ran improperly.')
285 rv = bool(1)
286
287 return rv
288
289 def writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection):
290 rv = bool(0)
291
292 try:
293 with open(cfile, 'w') as cout, open(mfile, 'w') as mout, open(pfile, 'w') as pout:
294 # C file of macros
|
295 arta 1.2 print(C_PREFIX, file=cout)
296 print('/* This file contains a set of preprocessor macros - one for each configuration parameter. */\n', file=cout)
|
297 arta 1.1 buf = '__' + base.upper() + '_H'
298 print('#ifndef ' + buf, file=cout)
299 print('#define ' + buf, file=cout)
300 print(''.join(cDefs), file=cout)
301 print('#endif', file=cout)
302
303 # Make file of make variables
|
304 arta 1.2 print(PREFIX, file=mout)
|
305 arta 1.1 print('# This file contains a set of make-variable values - one for each configuration parameter.', file=mout)
306 print(''.join(mDefs), file=mout)
307
|
308 arta 1.2 # Perl module
309 print(PERL_BINPATH, file=pout)
310 print(PREFIX, file=pout)
311 print('# This file contains a set of constants - one for each configuration parameter.\n', file=pout)
312 print(PERL_INTIAL, file=pout)
|
313 arta 1.1 print(''.join(perlConstSection), file=pout)
|
314 arta 1.2 print(PERL_FXNS_A, file=pout)
|
315 arta 1.1 print('sub initialize', file=pout)
316 print('{', file=pout)
|
317 arta 1.2 print(' my($self) = shift;', file=pout, end='')
|
318 arta 1.1 print('', file=pout)
319 print(''.join(perlInitSection), file=pout)
|
320 arta 1.2 print('}\n', file=pout)
321 print(PERL_FXNS_B, file=pout)
|
322 arta 1.1 except IOError as exc:
323 sys.stderr.write(exc.strerror)
324 sys.stderr.write('Unable to open a parameter vile.')
325 rv = bool(1)
326
327 return rv
328
|
329 arta 1.2 def configureNet(cfgfile, cfile, mfile, pfile, base, keymap):
|
330 arta 1.1 rv = bool(0)
331
332 defs = {}
333 cDefs = list()
334 mDefs = list()
335 perlConstSection = list()
336 perlInitSection = list()
|
337 arta 1.2 addenda = {}
338
339 # There are three parameters that are not configurable and must be set.
340 addenda['a:USER'] = 'NULL'
341 addenda['a:PASSWD'] = 'NULL'
342 addenda['p:DSDS_SUPPORT'] = '0'
343 addenda['a:BUILD_TYPE'] = 'NETDRMS' # Means a non-Stanford build. This will set two additional macros used by make:
344 # __LOCALIZED_DEFS__ and NETDRMS_BUILD. The former is to support legacy code
345 # which incorrectly used this macro, and the latter is for future use.
346 # __LOCALIZED_DEFS__ is deprecated and should not be used in new code.
|
347 arta 1.1
348 try:
349 with open(cfgfile, 'r') as fin:
|
350 arta 1.2 rv = parseConfig(fin, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection)
|
351 arta 1.1 if rv == bool(0):
352 # Must add a parameter for the SUMS_MANAGER UID (for some reason)
353 uidParam = {}
354 rv = getMgrUIDLine(defs, uidParam)
355 if rv == bool(0):
|
356 arta 1.2 rv = parseConfig(None, keymap, uidParam, defs, cDefs, mDefs, perlConstSection, perlInitSection)
|
357 arta 1.1
358 if rv == bool(0):
359 rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection)
360 except IOError as exc:
361 sys.stderr.write(exc.strerror)
362 sys.stderr.write('Unable to read configuration file ' + cfgfile + '.')
363
|
364 arta 1.2 return rv
|
365 arta 1.1
|
366 arta 1.2 def configureSdp(cfgfile, cfile, mfile, pfile, base, keymap):
|
367 arta 1.1 rv = bool(0)
368
369 defs = {}
370 cDefs = list()
371 mDefs = list()
372 perlConstSection = list()
373 perlInitSection = list()
|
374 arta 1.2 addenda = {}
375
376 addenda['a:BUILD_TYPE'] = 'JSOC_SDP'
|
377 arta 1.1
378 try:
379 with open(cfgfile, 'r') as fin:
|
380 arta 1.2 rv = parseConfig(cfgfile, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection)
|
381 arta 1.1 if rv == bool(0):
|
382 arta 1.2 # Must add a parameter for the SUMS_MANAGER UID (for some reason)
383 uidParam = {}
384 rv = getMgrUIDLine(defs, uidParam)
385 if rv == bool(0):
386 rv = parseConfig(None, keymap, uidParam, defs, cDefs, mDefs, perlConstSection, perlInitSection)
387
|
388 arta 1.1 rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection)
389 except IOError as exc:
390 sys.stderr.write(exc.strerror)
391 sys.stderr.write('Unable to read configuration file ' + cfgfile + '.')
392
|
393 arta 1.2 return rv
394
|
395 arta 1.1 # Beginning of program
396 rv = RET_SUCCESS
397 net = bool(1)
398
399 # Parse arguments
400 if __name__ == "__main__":
401 optD = GetArgs(sys.argv[1:])
402
403 if not(optD is None):
404 # Ensure we are configuring a DRMS tree
405 cdir = os.path.realpath(os.getcwd())
406 versfile = cdir + '/base/' + VERS_FILE
407
408 if not os.path.isfile(versfile):
409 rv = RET_NOTDRMS
410
411 # Determine whether we are localizing a Stanford build, or a NetDRMS build. If configsdp.txt exists, then
412 # it is a Stanford build, otherwise it is a NetDRMS build.
413 if rv == RET_SUCCESS:
414 stanfordFile = cdir + '/' + SDP_CFG
415 if os.path.isfile(stanfordFile):
416 arta 1.1 net = bool(0)
417
418 cfile = optD['dir'] + '/' + optD['base'] + '.h'
419 mfile = optD['dir'] + '/' + optD['base'] + '.mk'
420 pfile = optD['dir'] + '/' + optD['base'] + '.pm'
421
422 if net:
423 try:
424 with open(NET_CFGMAP, 'r') as fin:
425 regexpComm = re.compile(r"^\s*#")
426 regexp = re.compile(r"^\s*(\S+)\s+(\w:\S+)")
427 # Must map from config.local namespace to DRMS namespace (e.g., the names used for the C macros)
428 keymap = {}
429 for line in fin:
430 matchobj = regexpComm.match(line)
431 if not matchobj is None:
432 # Skip comment line
433 continue
434
435 matchobj = regexp.match(line)
436 if not(matchobj is None):
437 arta 1.1 # We have a key-value line
438 key = matchobj.group(1)
439 val = matchobj.group(2)
440 keymap[key] = val
441 except OSError:
442 sys.stderr.write('Unable to read configuration map-file ' + NET_CFGMAP + '.')
443 rv = bool(1)
444
|
445 arta 1.2
|
446 arta 1.1
447 # We also need to set the UID of the SUMS manager. We have the name of the
448 # SUMS manager (it is in the configuration file)
|
449 arta 1.2 configureNet(NET_CFG, cfile, mfile, pfile, optD['base'], keymap)
|
450 arta 1.1 else:
|
451 arta 1.2 configureSdp(SDP_CFG, cfile, mfile, pfile, optD['base'], {})
|
452 arta 1.1
453
454
|