ec-conf3 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. import errno
  5. import getopt
  6. import stat
  7. import re
  8. import operator
  9. from xml.etree import ElementTree, ElementInclude
  10. import xml.sax
  11. def info(message, level=1):
  12. """ @brief Print info to stdout
  13. @param message string to print
  14. """
  15. if VERBOSE >= level:
  16. sys.stderr.write("*II* %s\n" % message)
  17. def warning(message):
  18. """ @brief Print warning to stdout
  19. @param message string to print
  20. """
  21. if ERROR_ON_WARNING:
  22. error(message)
  23. if WARNING:
  24. sys.stderr.write("*WW* %s\n" % message)
  25. def error(message):
  26. """ @brief Print error to stdout
  27. @param message string to print
  28. """
  29. print("*EE* %s " % message)
  30. sys.exit(1)
  31. def usage(myname):
  32. print("\nUsage: %s OPTIONS <XML_FILE>\n" % myname)
  33. print("Read configuration from an XML file and create config files.")
  34. print("""
  35. %s reads a data base of configuration parameters from an XML file.
  36. Subsequently, a number of template files are processed in order to create
  37. configuration files (targets). See ec-conf User Guide for more information.""" % myname)
  38. print("""
  39. Options: -h|--help Print this help screen.
  40. -p|--platform Set the active platform.
  41. -l|--list-platforms Lists all platforms, which are defined in the XML file, on stdout.
  42. -d|--prefix=<PATH> Creates the directory PATH (if non-existing) and writes all target
  43. files within that directory. Also sets a configuration parameter
  44. named PLT:ACTIVE:PREFIXDIR for use in the template files.
  45. -o|--overwrite-parameter <NAME>=<VALUE>
  46. Allows to set new values to configuration parameters from the
  47. command line, overwriting the values from the XML file.
  48. <NAME> must be given in the form
  49. 'COMPONENT_TYPE:COMPONENT_NAME:PARAMETER_NAME' corresponding to
  50. the place-holder syntax of ec-conf. See ec-conf user guide for details.
  51. Multiple parameters can be overwritten by repeating this option.
  52. -r|--retrieve-parameter <NAME>
  53. Allows to retrieve the value of a configuration parameter in the XML
  54. file from the command line. <NAME> must be given in the form
  55. 'COMPONENT_TYPE:COMPONENT_NAME:PARAMETER_NAME' corresponding to
  56. the place-holder syntax of ec-conf. See ec-conf user guide for details.
  57. All other actions (-x, -l, -g, translations) are cancelled.
  58. -x|--write-xml Writes the content of <XML_FILE> in XML format to stdout.
  59. This can be used to normalise the XML file and for test purposes.
  60. -g|--gui Starts the graphical user interface. Turns off -x and -l.
  61. -v|--verbose Produces verbose output (stderr). To increase verbosity,
  62. use more than once.
  63. -w|--no-warning Turns off warnings (however, errors are displayed).
  64. -e|--error-on-warning Turns warnings into errors (i.e. ec-conf will stop).
  65. """)
  66. sys.exit(2)
  67. class TextNode(object):
  68. """Represents an XML node that contains only text, i.e. is not named and has no child nodes"""
  69. def __init__(self):
  70. self.text = ""
  71. def add_text(self, content):
  72. self.text += content
  73. class NamedNode(object):
  74. """Represents an XML node with a certain type according to the document
  75. definition. Base class for Configuration, Translation, Platform, Model,
  76. and Parameter"""
  77. def __init__(self, attr=None):
  78. self.name = None
  79. self.description = None
  80. if attr:
  81. if 'name' in list(attr.keys()):
  82. self.name = attr['name']
  83. info('Create named node: ' + self.name +
  84. ' (' + self.__class__.__name__ + ')', level=2)
  85. def set(self, key, value):
  86. self.__dict__.__setitem__(key, value)
  87. def xml(self, level=0):
  88. """Provides XML representation of the NamedNode as a string"""
  89. tabwidth = 4
  90. xml_string = ' ' * level * tabwidth
  91. xml_string += '<' + self.__class__.__name__
  92. if 'name' in self.__dict__ and self.name:
  93. xml_string += ' name="' + self.name + '"'
  94. xml_string += '>\n'
  95. for element in 'description', 'template', 'target', 'properties', 'type', 'value':
  96. if element in self.__dict__:
  97. xml_string += ' ' * (level + 1) * tabwidth
  98. xml_string += '<' + element.capitalize() + '>'
  99. if self.__dict__[element]:
  100. xml_string += self.__dict__[element]
  101. xml_string += '</' + element.capitalize() + '>\n'
  102. if level <= 1:
  103. xml_string += '\n'
  104. for element in 'translation', 'platform', 'model', 'parameter':
  105. if element in self.__dict__:
  106. for child_element in self.__dict__[element]:
  107. xml_string += child_element.xml(level + 1)
  108. xml_string += ' ' * level * tabwidth
  109. xml_string += '</' + self.__class__.__name__ + '>\n'
  110. if level > 0 and level <= 2:
  111. xml_string += '\n'
  112. return xml_string
  113. def plt(self, name=None):
  114. """Provides short-hand access to the Platform member with name 'name'."""
  115. if 'platform' in self.__dict__:
  116. if name:
  117. for plt in self.platform:
  118. if plt.name == name:
  119. return plt
  120. else:
  121. return self.platform
  122. return None
  123. def mod(self, name=None):
  124. """Provides short-hand access to the Model member with name 'name'."""
  125. if 'model' in self.__dict__:
  126. if name:
  127. for mod in self.model:
  128. if mod.name == name:
  129. return mod
  130. else:
  131. return self.model
  132. return None
  133. def par(self, name=None):
  134. """Provides short-hand access to the Parameter member with name 'name'."""
  135. if 'parameter' in self.__dict__:
  136. if name:
  137. for par in self.parameter:
  138. if par.name == name:
  139. return par
  140. else:
  141. return self.parameter
  142. return None
  143. class Translation(NamedNode):
  144. """Represents a Translation element of the XML document type"""
  145. def __init__(self, attr):
  146. super(Translation, self).__init__(attr)
  147. self.template = None
  148. self.target = None
  149. self.properties = ''
  150. self.is_active = 1
  151. def set_target(self, target):
  152. self.target = target
  153. class Platform(NamedNode):
  154. """Represents a Platform element of the XML document type"""
  155. def __init__(self, attr):
  156. super(Platform, self).__init__(attr)
  157. self.parameter = []
  158. self.translation = []
  159. class Model(NamedNode):
  160. """Represents a Parameter element of the XML document type"""
  161. def __init__(self, attr):
  162. super(Model, self).__init__(attr)
  163. self.parameter = []
  164. class Parameter(NamedNode):
  165. """Represents a Parameter element of the XML document type"""
  166. def __init__(self, attr):
  167. super(Parameter, self).__init__(attr)
  168. self.type = None
  169. self.value = None
  170. class Configuration(NamedNode, xml.sax.handler.ContentHandler):
  171. """Represents a Parameter element (which is the root element) of the XML document type"""
  172. def __init__(self):
  173. self.translation = []
  174. self.platform = []
  175. self.model = []
  176. self.xml_file = None
  177. self.active_platform = None
  178. self.__stack = []
  179. self.__types = {'Translation': Translation,
  180. 'Platform': Platform,
  181. 'Model': Model,
  182. 'Parameter': Parameter}
  183. def xml(self, level=0):
  184. return '<?xml version="1.0"?>\n\n' \
  185. + super(Configuration, self).xml(level)
  186. def startElement(self, tag, attributes):
  187. info("Processing XML element '" + str(tag) + "'", level=3)
  188. if self.__stack:
  189. if tag in self.__types:
  190. info("Adding NamedNode for element '" + str(tag) + "'", level=3)
  191. self.__stack.append(self.__types[tag](attributes))
  192. else:
  193. info("Adding TextNode for element '" + str(tag) + "'", level=3)
  194. self.__stack.append(TextNode())
  195. else:
  196. self.__stack.append(self)
  197. def characters(self, content):
  198. if isinstance(self.__stack[-1], TextNode):
  199. self.__stack[-1].add_text(content)
  200. def endElement(self, tag):
  201. element = self.__stack.pop()
  202. if self.__stack:
  203. if isinstance(element, TextNode):
  204. self.__stack[-1].__dict__[tag.lower()] = element.text
  205. else:
  206. self.__stack[-1].__dict__[tag.lower()].append(element)
  207. def parse(self, file):
  208. info("Parsing XML file '%s'" % file)
  209. # A short helper function for xml.etree.ElementInclude.include
  210. # that parses include files and catches parse errors
  211. def include_loader(href, parse, encoding=None):
  212. if parse != "xml":
  213. error("Only XML includes allowed in xi:include! (see file '%s')" % href)
  214. try:
  215. with open(href) as file:
  216. data = ElementTree.parse(file).getroot()
  217. except IOError as e:
  218. error("Can't open include file '%s' for parsing: %s" % (href, e))
  219. except ElementTree.ParseError as e:
  220. error("XML parse error in include file '%s': %s" % (href, e))
  221. return data
  222. # First parsing stage with xml.etree for include processing
  223. try:
  224. tree = ElementTree.parse(file)
  225. except IOError:
  226. error("Can't open file '%s' for parsing" % file)
  227. except ElementTree.ParseError as e:
  228. error("XML parse error in file '%s': %s" % (file, e))
  229. # Process XML include files
  230. tree_root = tree.getroot()
  231. ElementInclude.include(tree_root, loader=include_loader)
  232. # Second parsing stage with xml.sax to fill data structures
  233. # Since errors are catched in the first parsing stage, we assume
  234. # everything is fine here.
  235. xml.sax.parseString(ElementTree.tostring(tree_root, encoding="UTF-8"),self)
  236. info("Finished parsing '%s': Translation: %d Platform: %d Model: %d" % (
  237. file, len(self.translation), len(self.platform), len(self.model)))
  238. self.xml_file = file
  239. def translate(self, translation):
  240. rpm_ops = {
  241. 'ADD': operator.add,
  242. 'SUB': operator.sub,
  243. 'MUL': operator.mul,
  244. 'DIV': operator.truediv,
  245. 'POW': operator.pow,
  246. 'MOD': operator.mod
  247. }
  248. subst_re = re.compile(r"\[\[\[(?P<var>[a-zA-Z0-9_:,]+)\]\]\]")
  249. var_re = re.compile(r"^(\w{3}):([a-zA-Z0-9_]+):([a-zA-Z0-9_]+)$")
  250. def parse_var(string):
  251. # Shortcut for the 'prefixdir' parameter in template file
  252. if string.lower() == 'plt:active:prefixdir':
  253. return targetPrefixDir if targetPrefixDir else '[[[' + string + ']]]'
  254. while True:
  255. match = var_re.search(string)
  256. if not match:
  257. break
  258. (category, component, parameter) = match.groups()
  259. if category.lower() == 'plt' and component.lower() == 'active':
  260. component = self.active_platform
  261. try:
  262. string = getattr(self, category.lower())(
  263. component).par(parameter).value
  264. except BaseException:
  265. warning(
  266. "Unable to process '%s' (Line %d in '%s')" %
  267. (string, line_number, template))
  268. return '[[[' + string + ']]]'
  269. return string
  270. def parse_rpn(string):
  271. stack = []
  272. for token in string.split(','):
  273. token = parse_var(token)
  274. if not token:
  275. info(
  276. "Substitute expression with empty string (Line %d in '%s')" %
  277. (line_number, template))
  278. try:
  279. result = int(token)
  280. except ValueError:
  281. try:
  282. result = float(token)
  283. except ValueError:
  284. if token in list(rpm_ops.keys()):
  285. try:
  286. result = rpm_ops[token](
  287. stack.pop(-2), stack.pop())
  288. except IndexError:
  289. warning(
  290. "Too few arguments to execute '%s' (Line %d in '%s')" %
  291. (token, line_number, template))
  292. return "[[[" + string + "]]]"
  293. except BaseException:
  294. warning(
  295. "Unable to execute '%s' (Line %d in '%s')" %
  296. (token, line_number, template))
  297. return "[[[" + string + "]]]"
  298. else:
  299. result = token
  300. stack.append(result)
  301. if len(stack) > 1:
  302. warning(
  303. "Too many operands in '%s' (Line %d in '%s')" %
  304. (string, line_number, template))
  305. return "[[[" + string + "]]]"
  306. return result
  307. template = translation.template
  308. target = translation.target.strip()
  309. info("Translate: '%s' --> '%s'" % (template, target))
  310. try:
  311. input = open(template, 'r')
  312. except IOError:
  313. error("Can't open template file '%s' for reading" % template)
  314. if target:
  315. if targetPrefixDir:
  316. target = os.path.join(targetPrefixDir, target)
  317. path = os.path.dirname(target)
  318. try:
  319. os.makedirs(path)
  320. info("Created target prefix directory '%s'" % path)
  321. except OSError as exc:
  322. if exc.errno == errno.EEXIST and os.path.isdir(path):
  323. info(
  324. "Target prefix directory '%s' exists already" %
  325. (path))
  326. else:
  327. error(
  328. "Could not create target prefix directory '%s'" %
  329. (path))
  330. try:
  331. output = open(target, 'w')
  332. except IOError:
  333. error("Can't open target file '%s' for writing" % target)
  334. if 'executable' in translation.properties.split(','):
  335. os.chmod(target, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
  336. stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
  337. stat.S_IWUSR)
  338. else:
  339. output = sys.stdout
  340. line_number = 0
  341. for line in input:
  342. line_number += 1
  343. ptr = 0
  344. buf = ''
  345. for match in subst_re.finditer(line):
  346. buf += line[ptr:match.start()]
  347. var = match.groupdict()['var']
  348. try:
  349. buf += str(parse_rpn(var))
  350. except UnicodeEncodeError as e:
  351. error('Invalid character in XML file!\n'
  352. '*EE* Look up the \\uXXXX character code from the following message:\n'
  353. '*EE* %s' % str(e))
  354. ptr = match.end()
  355. buf += line[ptr:]
  356. output.write(buf)
  357. input.close()
  358. if target:
  359. output.close()
  360. def translate_all(self):
  361. for t in self.translation:
  362. self.translate(t)
  363. if self.active_platform:
  364. for t in self.plt(self.active_platform).translation:
  365. self.translate(t)
  366. def start_gui(cfg):
  367. import tkinter
  368. import tkinter.filedialog
  369. import tkinter.messagebox
  370. class VerticalScrolledFrame(tkinter.Frame):
  371. """A pure Tkinter scrollable frame that actually works!
  372. * Use the 'interior' attribute to place widgets inside the scrollable frame
  373. * Construct and pack/place/grid normally
  374. * This frame only allows vertical scrolling
  375. http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
  376. """
  377. def __init__(self, parent, *args, **kw):
  378. tkinter.Frame.__init__(self, parent, *args, **kw)
  379. # create a canvas object and a vertical scrollbar for scrolling it
  380. vscrollbar = tkinter.Scrollbar(self, orient=tkinter.VERTICAL)
  381. vscrollbar.pack(
  382. fill=tkinter.Y,
  383. side=tkinter.RIGHT,
  384. expand=tkinter.FALSE)
  385. canvas = tkinter.Canvas(self, bd=0, highlightthickness=0,
  386. yscrollcommand=vscrollbar.set)
  387. canvas.pack(
  388. side=tkinter.LEFT,
  389. fill=tkinter.BOTH,
  390. expand=tkinter.TRUE)
  391. vscrollbar.config(command=canvas.yview)
  392. # reset the view
  393. canvas.xview_moveto(0)
  394. canvas.yview_moveto(0)
  395. # create a frame inside the canvas which will be scrolled with it
  396. self.interior = interior = tkinter.Frame(canvas)
  397. interior_id = canvas.create_window(0, 0, window=interior,
  398. anchor=tkinter.NW)
  399. # track changes to the canvas and frame width and sync them,
  400. # also updating the scrollbar
  401. def _configure_interior(event):
  402. # update the scrollbars to match the size of the inner frame
  403. size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
  404. canvas.config(scrollregion="0 0 %s %s" % size)
  405. if interior.winfo_reqwidth() != canvas.winfo_width():
  406. # update the canvas's width to fit the inner frame
  407. canvas.config(width=interior.winfo_reqwidth())
  408. interior.bind('<Configure>', _configure_interior)
  409. def _configure_canvas(event):
  410. if interior.winfo_reqwidth() != canvas.winfo_width():
  411. # update the inner frame's width to fill the canvas
  412. canvas.itemconfigure(
  413. interior_id, width=canvas.winfo_width())
  414. canvas.bind('<Configure>', _configure_canvas)
  415. return
  416. class GUI(tkinter.Tk):
  417. def __init__(self, cfg):
  418. tkinter.Tk.__init__(self)
  419. self.columnconfigure(1, weight=1)
  420. self.rowconfigure(0, pad=10)
  421. self.rowconfigure(2, weight=1)
  422. self.__cfg = cfg
  423. self.__var_list = []
  424. self.__var_dict = {}
  425. for t in self.__cfg.translation + \
  426. [t for p in self.__cfg.platform for t in p.translation]:
  427. v = tkinter.IntVar()
  428. v.set(1)
  429. v.trace(
  430. 'w', lambda n, i, m, t=t: t.set(
  431. 'is_active', int(
  432. self.globalgetvar(n))))
  433. self.__add_var(v, t)
  434. self.__active_component = tkinter.StringVar()
  435. self.__status = tkinter.StringVar()
  436. self.__component_frame = None
  437. self.__parameter_frame = None
  438. self.__translation_frame = None
  439. self.__init_top_panel()
  440. self.__init_status_panel()
  441. self.__init_main_panel()
  442. self.grid_propagate(flag=0)
  443. self.__set_status(
  444. "Welcome to EC-CONF's graphical user interface!",
  445. time=3000)
  446. def __add_var(self, var, obj=None):
  447. self.__var_list.append(var)
  448. if obj:
  449. self.__var_dict[obj] = len(self.__var_list) - 1
  450. def __set_status(self, message, time=8000):
  451. try:
  452. self.after_cancel(self.__status_after_id)
  453. except BaseException:
  454. pass
  455. self.__default_status_message = 'Basic usage: SELECT XML database' \
  456. ' and template/target files, CONFIGURE parameters, and CREATE' \
  457. ' the configuration files.'
  458. self.__status.set(message)
  459. self.__status_after_id = self.after(
  460. time, self.__status.set, self.__default_status_message)
  461. def __init_top_panel(self):
  462. w = tkinter.Label(text='The active platform is:')
  463. w.grid(row=0, column=0)
  464. if not self.__cfg.active_platform:
  465. self.__cfg.active_platform = self.__cfg.platform[0].name
  466. v = tkinter.StringVar()
  467. v.set(self.__cfg.active_platform)
  468. v.trace(
  469. 'w',
  470. lambda n, i, m: self.__set_status(
  471. "Active platform changed to '"
  472. + self.__cfg.active_platform
  473. + "'"))
  474. v.trace(
  475. 'w',
  476. lambda n, i, m: self.__fill_parameter_frame(
  477. self.__parameter_frame))
  478. v.trace(
  479. 'w',
  480. lambda n, i, m: self.__fill_component_frame(
  481. self.__component_frame))
  482. v.trace(
  483. 'w',
  484. lambda n, i, m: self.__fill_translation_frame(
  485. self.__translation_frame))
  486. v.trace('w', lambda n, i, m: self.__cfg.set(
  487. 'active_platform', self.globalgetvar(n)))
  488. self.__add_var(v)
  489. w = tkinter.OptionMenu(None, v, *
  490. [p.name for p in self.__cfg.platform])
  491. w.grid(row=0, column=1, sticky='W', padx=8)
  492. w = tkinter.Button(text='Select', width=10, height=2, bg='tan')
  493. w['command'] = self.__do_select
  494. w.grid(row=0, column=2, padx=8)
  495. w = tkinter.Button(text='Configure', width=10, height=2, bg='tan')
  496. w['command'] = self.__do_configure
  497. w.grid(row=0, column=3, padx=8)
  498. w = tkinter.Button(
  499. text='Create!',
  500. width=10,
  501. height=2,
  502. bg='darkgrey',
  503. fg='white')
  504. w['command'] = self.__do_create
  505. w.grid(row=0, column=4, padx=8)
  506. def __init_status_panel(self):
  507. w = tkinter.Label(
  508. textvariable=self.__status,
  509. height=2,
  510. bg='orange')
  511. w.grid(row=1, column=0, columnspan=5, sticky='EW', pady=5)
  512. def __init_main_panel(self):
  513. self.__init_select_panel()
  514. self.__init_configure_panel()
  515. self.__select_panel.grid_remove()
  516. self.__configure_panel.grid_remove()
  517. self.__active_main_panel = self.__select_panel
  518. self.__active_main_panel.grid()
  519. def __init_select_panel(self):
  520. self.__select_panel = tkinter.Frame()
  521. self.__select_panel.grid(
  522. row=2, column=0, columnspan=5, sticky='NEWS')
  523. self.__select_panel.columnconfigure(0, weight=1)
  524. self.__select_panel.rowconfigure(2, weight=1)
  525. f = tkinter.LabelFrame(
  526. self.__select_panel,
  527. text='XML database file')
  528. f.grid(sticky='NEWS')
  529. f.columnconfigure(1, weight=1)
  530. w = tkinter.Label(f, text='The current file is: ')
  531. w.grid(row=0, column=0, sticky='W')
  532. v = tkinter.StringVar()
  533. v.set(self.__cfg.xml_file)
  534. v.trace(
  535. 'w', lambda n, i, m: self.__cfg.set(
  536. 'xml_file', self.globalgetvar(n)))
  537. self.__add_var(v, 'xml_file')
  538. w = tkinter.Label(f, textvariable=v, bg='darkgrey', fg='white')
  539. w.grid(row=0, column=1, sticky='W')
  540. w = tkinter.Button(f, text='Save as', width=8)
  541. w['command'] = lambda: self.__save_as_xml_file()
  542. w.grid(row=0, column=2, sticky='E', padx=4, pady=5)
  543. w = tkinter.Button(f, text='Save', width=8)
  544. w['command'] = lambda: self.__save_xml_file()
  545. w.grid(row=0, column=3, sticky='E', padx=4, pady=5)
  546. tkinter.Frame(self.__select_panel).grid(pady=5)
  547. self.__translation_frame = tkinter.LabelFrame(
  548. self.__select_panel, text='Templates and Targets')
  549. self.__translation_frame.grid(sticky='NEWS')
  550. self.__translation_frame.columnconfigure(1, weight=1)
  551. self.__translation_frame.columnconfigure(3, weight=9)
  552. self.__fill_translation_frame(self.__translation_frame)
  553. def __init_configure_panel(self):
  554. self.__configure_panel = tkinter.Frame()
  555. self.__configure_panel.grid(
  556. row=2, column=0, columnspan=5, sticky='NEWS')
  557. self.__configure_panel.rowconfigure(0, weight=1)
  558. self.__configure_panel.columnconfigure(2, weight=1)
  559. # Component frame
  560. self.__component_frame = tkinter.LabelFrame(
  561. self.__configure_panel, text='Configurable components')
  562. self.__component_frame.grid(sticky='NEWS')
  563. self.__fill_component_frame(self.__component_frame)
  564. # Spacer frame
  565. f = tkinter.Frame(self.__configure_panel)
  566. f.grid(row=0, column=1, padx=2)
  567. # Parameter frame
  568. f = tkinter.LabelFrame(
  569. self.__configure_panel,
  570. text='Configuration parameters')
  571. f.grid(row=0, column=2, sticky='NEWS')
  572. f = VerticalScrolledFrame(f)
  573. f.pack(fill=tkinter.BOTH, expand=tkinter.TRUE)
  574. self.__parameter_frame = f.interior
  575. self.__parameter_frame.columnconfigure(2, weight=1)
  576. self.__fill_parameter_frame(self.__parameter_frame)
  577. def __fill_translation_frame(self, frame):
  578. for w in list(frame.children.values()):
  579. w.destroy()
  580. v = tkinter.IntVar()
  581. v.set(1)
  582. v.trace('w', lambda n,
  583. i,
  584. m: [self.__var_list[self.__var_dict[t]].set(self.globalgetvar(n))
  585. for t in self.__cfg.translation +
  586. self.__cfg.plt(self.__cfg.active_platform).translation])
  587. self.__add_var(v)
  588. w = tkinter.Checkbutton(
  589. frame, text='Activate/deactivate all', variable=v)
  590. w.grid(row=0, column=0, sticky='W', pady=5)
  591. r = 1
  592. for t in self.__cfg.translation + \
  593. self.__cfg.plt(self.__cfg.active_platform).translation:
  594. w = tkinter.Checkbutton(
  595. frame, text=t.description, variable=self.__var_list[self.__var_dict[t]])
  596. w.grid(row=r, column=0, sticky='W', pady=5)
  597. w = tkinter.Label(
  598. frame,
  599. text=t.template,
  600. bg='darkgrey',
  601. fg='white')
  602. w.grid(row=r, column=1, sticky='E')
  603. w = tkinter.Label(frame, text=' --> ')
  604. w.grid(row=r, column=2)
  605. v = tkinter.StringVar()
  606. v.set(t.target)
  607. v.trace(
  608. 'w', lambda n, i, m, t=t: t.set_target(
  609. self.globalgetvar(n)))
  610. self.__add_var(v)
  611. w = tkinter.Entry(frame, textvariable=v)
  612. w.grid(row=r, column=3, sticky='EW')
  613. r += 1
  614. def __fill_component_frame(self, frame):
  615. for w in list(frame.children.values()):
  616. w.destroy()
  617. if self.__cfg.active_platform:
  618. self.__active_component.set(self.__cfg.active_platform)
  619. w = tkinter.Label(frame, text='Active platform')
  620. w.pack(anchor='w')
  621. c = self.__cfg.plt(self.__cfg.active_platform)
  622. w = tkinter.Radiobutton(
  623. frame,
  624. text=c.name,
  625. variable=self.__active_component,
  626. value=c.name)
  627. w['command'] = lambda: self.__fill_parameter_frame(
  628. self.__parameter_frame)
  629. w.pack(anchor='w', pady=5)
  630. if self.__cfg.model:
  631. if not self.__active_component.get():
  632. self.set(self.__cfg.model[0].name)
  633. w = tkinter.Label(frame, text='Configurable models')
  634. w.pack(anchor='w')
  635. for c in self.__cfg.model:
  636. w = tkinter.Radiobutton(
  637. frame, text=c.name, variable=self.__active_component, value=c.name)
  638. w['command'] = lambda: self.__fill_parameter_frame(
  639. self.__parameter_frame)
  640. w.pack(anchor='w', pady=5)
  641. def __fill_parameter_frame(self, frame):
  642. for w in list(frame.children.values()):
  643. w.destroy()
  644. for (name, component) in [(c.name, c)
  645. for c in self.__cfg.platform + self.__cfg.model]:
  646. if self.__active_component.get() == name:
  647. break
  648. r = 0
  649. for p in component.par():
  650. w = tkinter.Label(
  651. frame, text=p.description, anchor="w", width=35)
  652. w.grid(row=r, column=0, sticky='W')
  653. w = tkinter.Label(frame, text='[' + str(p.name) + ']')
  654. w.grid(row=r, column=1, sticky='W', padx=20)
  655. v = tkinter.StringVar()
  656. v.set(p.value)
  657. v.trace(
  658. 'w', lambda n, i, m, p=p: p.set(
  659. 'value', self.globalgetvar(n)))
  660. self.__add_var(v)
  661. w = tkinter.Entry(frame, textvariable=v)
  662. w.grid(row=r, column=2, sticky='EW')
  663. r += 1
  664. self.__set_status(
  665. 'Configure parameters for component \'' +
  666. self.__active_component.get() +
  667. '\'')
  668. def __do_select(self):
  669. self.__active_main_panel.grid_remove()
  670. self.__active_main_panel = self.__select_panel
  671. self.__active_main_panel.grid()
  672. self.__set_status(
  673. 'Select the XML data base file and active translations in the panel below.')
  674. def __do_configure(self):
  675. self.__active_main_panel.grid_remove()
  676. self.__active_main_panel = self.__configure_panel
  677. self.__active_main_panel.grid()
  678. self.__set_status('Configure the configuration parameters for the'
  679. ' available components in the panel below.')
  680. def __do_create(self):
  681. fw = []
  682. for t in self.__cfg.translation + \
  683. self.__cfg.plt(self.__cfg.active_platform).translation:
  684. if t.is_active:
  685. self.__cfg.translate(t)
  686. fw.append(t.target)
  687. if fw:
  688. msg = 'Active target files written: ' + fw.pop()
  689. while fw:
  690. msg += ', ' + fw.pop()
  691. else:
  692. msg = 'No targets where written'
  693. self.__set_status(msg)
  694. def __save_as_xml_file(self):
  695. f = tkinter.filedialog.asksaveasfilename(
  696. title='Select a file name for saving:', filetypes=[
  697. ('XML files', '*.xml'), ('All files', '*')])
  698. if f:
  699. try:
  700. self.__var_list[self.__var_dict['xml_file']].set(
  701. os.path.relpath(f))
  702. except AttributeError:
  703. self.__var_list[self.__var_dict['xml_file']].set(
  704. os.path.realpath(f))
  705. self.__save_xml_file()
  706. else:
  707. self.__set_status("Current XML file NOT saved")
  708. def __save_xml_file(self):
  709. if os.path.isfile(self.__cfg.xml_file):
  710. msg = "The file '" + self.__cfg.xml_file + \
  711. "' exists. Do you want to replace it?"
  712. if not tkinter.messagebox.askyesno('Save XML file', msg):
  713. return
  714. try:
  715. f = open(self.__cfg.xml_file, 'w')
  716. except IOError:
  717. msg = "The file '" + self.__cfg.xml_file + "' could not be opened for writing"
  718. tkinter.messagebox.showerror('Save XML file', msg)
  719. self.__set_status("XML database NOT saved")
  720. return
  721. f.write(self.__cfg.xml())
  722. f.close()
  723. self.__set_status(
  724. "XML database saved to file '" +
  725. self.__cfg.xml_file +
  726. "'")
  727. root = GUI(cfg)
  728. root.title('ec-conf GUI')
  729. min_window_width = min(900, int(0.9 * root.winfo_screenwidth()))
  730. min_window_height = min(800, int(0.9 * root.winfo_screenheight()))
  731. root.minsize(min_window_width, min_window_height)
  732. root.resizable()
  733. root.mainloop()
  734. if __name__ == "__main__":
  735. # Try to get command line options and arguments
  736. try:
  737. opts, args = getopt.getopt(sys.argv[1:],
  738. "hp:gd:o:r:xlvwe",
  739. ["help", "platform=", "gui", "prefix=",
  740. "overwrite-parameter=", "retrieve-parameter",
  741. "write-xml", "list-platforms",
  742. "verbose", "no-warning", "error-on-warning"])
  743. except getopt.GetoptError:
  744. usage(os.path.split(sys.argv[0])[-1])
  745. # Default values, to be overwritten by command line options
  746. WARNING = True
  747. ERROR_ON_WARNING = False
  748. VERBOSE = 0
  749. writeXML = False
  750. listPlatforms = False
  751. wantGUI = False
  752. platform = None
  753. targetPrefixDir = None
  754. overwriteParameters = []
  755. retrieveParameter = None
  756. # Parse command line options
  757. for opt, arg in opts:
  758. if opt in ('-h', '--help'):
  759. usage(os.path.split(sys.argv[0])[-1])
  760. elif opt in ('-p', '--platform'):
  761. platform = arg
  762. elif opt in ('-g', '--gui'):
  763. wantGUI = True
  764. elif opt in ('-d', '--prefix'):
  765. targetPrefixDir = arg
  766. elif opt in ('-o', '--overwrite-parameter'):
  767. overwriteParameters.append(arg)
  768. elif opt in ('-r', '--retrieve-parameter'):
  769. retrieveParameter = arg
  770. elif opt in ('-x', '--write-xml'):
  771. writeXML = True
  772. elif opt in ('-l', '--list-platforms'):
  773. listPlatforms = True
  774. elif opt in ('-v', '--verbose'):
  775. VERBOSE += 1
  776. elif opt in ('-w', '--no-warning'):
  777. WARNING = False
  778. elif opt in ('-e', '--error-on-warning'):
  779. ERROR_ON_WARNING = True
  780. # The XML file is all that should be left on the command line
  781. if len(args) != 1:
  782. usage(os.path.split(sys.argv[0])[-1])
  783. # Create the Configuration object and fill the data structures by parsing
  784. # the XML file
  785. cfg = Configuration()
  786. cfg.parse(args[0])
  787. # If a platform was given on the command line, try to set it
  788. if platform:
  789. if cfg.plt(platform):
  790. cfg.active_platform = platform
  791. else:
  792. error(
  793. "Platform '%s' not defined in the configuration file '%s'" %
  794. (platform, args[0]))
  795. elif not (wantGUI or listPlatforms):
  796. warning("No active platform given")
  797. # Overwrite parameters given explicitely on the command line
  798. for arg in overwriteParameters:
  799. # split the name=value pair but make sure additional '=' are preserved
  800. name = arg.split('=')[0]
  801. value = '='.join(arg.split('=')[1:])
  802. # Maybe we want to allow overwriting with empty values?!
  803. # if so, remove the following if block
  804. if not value:
  805. warning("Parameter '%s' in --overwrite-parameter has no value" % name)
  806. continue
  807. try:
  808. (category, component, parameter) = name.split(':')
  809. except ValueError:
  810. warning(
  811. "Malformed parameter name given to --overwrite-parameter: '%s'" % name)
  812. continue
  813. if category.lower() == 'plt' and component.lower() == 'active':
  814. obs = [x for x in cfg.platform if x.name == cfg.active_platform]
  815. elif category.lower() == 'model':
  816. obs = [x for x in cfg.model if x.name == component]
  817. else:
  818. error("Do not know how to find: '%s'" % name)
  819. if len(obs) == 1:
  820. ob = obs[0]
  821. try:
  822. ob.par(parameter).value = value
  823. info("Overwriting parameter '%s' with value '%s'" % (name, value))
  824. except AttributeError:
  825. warning(
  826. "Non-existing parameter name given to --overwrite-parameter: '%s'" % name)
  827. continue
  828. else:
  829. warning("Found %s parameters: '%s'" % (str(len(obs)), name))
  830. # Echo
  831. if retrieveParameter:
  832. try:
  833. (category, component, parameter) = retrieveParameter.split(':')
  834. except ValueError:
  835. warning(
  836. "Malformed parameter name given to --retrieve-parameter: '%s'" % retrieveParameter)
  837. if category.lower() == 'plt' and component.lower() == 'active':
  838. obs = [x for x in cfg.platform if x.name == cfg.active_platform]
  839. elif category.lower() == 'model':
  840. obs = [x for x in cfg.model if x.name == component]
  841. else:
  842. error("Do not know how to retrieve: '%s'" % retrieveParameter)
  843. if len(obs) == 1:
  844. ob = obs[0]
  845. else:
  846. error("Found %s parameters to retrieve: '%s'" % (str(len(obs)), retrieveParameter))
  847. try:
  848. print(ob.par(parameter).value)
  849. except AttributeError:
  850. error(
  851. "Non-existing parameter name given to --retrieve-parameter: '%s'" % retrieveParameter)
  852. else:
  853. # Select activity to be done according to the command line options
  854. # Default is to translate all Translations in the Configuration
  855. if wantGUI:
  856. info("Starting GUI")
  857. start_gui(cfg)
  858. elif listPlatforms:
  859. if cfg.plt():
  860. print('\n'.join([p.name for p in cfg.plt()]))
  861. else:
  862. warning("No platforms defined in XML file")
  863. elif writeXML:
  864. sys.stdout.write(cfg.xml())
  865. else:
  866. cfg.translate_all()