3 # Copyright (C) 2010-2013 CEA/DEN
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 # Compatibility python 2/3 for input function
29 # input stays input for python 3 and input = raw_input for python 2
35 # Python 2/3 compatibility for execfile function
40 def execfile(somefile, global_vars, local_vars):
41 with open(somefile) as f:
42 code = compile(f.read(), somefile, "exec")
43 exec(code, global_vars, local_vars)
46 parser = src.options.Options()
53 """REQUIRED: the name of the module to create.
54 \tThe name must be a single word in upper case with only alphanumeric characters.
55 \tWhen generating a c++ component the module's """
56 """name must be suffixed with 'CPP'."""
60 "t", "template", "string", "template", _("REQUIRED: the template to use.")
63 "", "target", "string", "target", _("REQUIRED: where to create the module.")
71 """Optional: dictionary to generate the configuration for salomeTools.
72 \tFormat is: --param param1=value1,param2=value2... without spaces
73 \tNote that when using this option you must supply all the """
74 """values otherwise an error will be raised."""
82 _("Optional: Get information on the template."),
88 def __init__(self, param_def, compo_name, dico=None):
91 self.check_method = None
93 if isinstance(param_def, str):
95 elif isinstance(param_def, tuple):
96 self.name = param_def[0]
97 if len(param_def) > 1:
99 self.default = param_def[1] % dico
101 self.default = param_def[1]
102 if len(param_def) > 2:
103 self.prompt = param_def[2]
104 if len(param_def) > 3:
105 self.check_method = param_def[3]
107 raise src.SatException(_("ERROR in template parameter definition"))
109 self.raw_prompt = self.prompt
110 if len(self.prompt) == 0:
111 self.prompt = _("value for '%s'") % self.name
113 if len(self.default) > 0:
114 self.prompt += "[%s] " % self.default
116 def check_value(self, val):
117 if self.check_method is None:
119 return len(val) > 0 and self.check_method(val)
122 def get_dico_param(dico, key, default):
128 class TemplateSettings:
129 def __init__(self, compo_name, settings_file, target):
130 self.compo_name = compo_name
136 execfile(settings_file, gdic, ldic)
138 # check required parameters in template.info
140 for pp in ["file_subst", "parameters"]:
142 missing.append("'%s'" % pp)
144 raise src.SatException(
145 _("Bad format in settings file! %s not defined.") % ", ".join(missing)
148 self.file_subst = ldic["file_subst"]
149 self.parameters = ldic["parameters"]
150 self.info = get_dico_param(ldic, "info", "").strip()
151 self.pyconf = get_dico_param(ldic, "pyconf", "")
152 self.post_command = get_dico_param(ldic, "post_command", "")
154 # get the delimiter for the template
155 self.delimiter_char = get_dico_param(ldic, "delimiter", ":sat:")
157 # get the ignore filter
158 self.ignore_filters = [l.strip() for l in ldic["ignore_filters"].split(",")]
160 def has_pyconf(self):
161 return len(self.pyconf) > 0
163 def get_pyconf_parameters(self):
164 if len(self.pyconf) == 0:
166 return re.findall("%\((?P<name>\S[^\)]*)", self.pyconf)
169 # Check if the file needs to be parsed.
170 def check_file_for_substitution(self, file_):
171 for filter_ in self.ignore_filters:
172 if fnmatch.fnmatchcase(file_, filter_):
176 def check_user_values(self, values):
180 # create a list of all parameters (pyconf + list))
181 pnames = self.get_pyconf_parameters()
182 for p in self.parameters:
183 tp = TParam(p, self.compo_name)
184 pnames.append(tp.name)
187 pnames = list(set(pnames)) # remove duplicates
189 known_values = ["name", "Name", "NAME", "target", self.file_subst]
190 known_values.extend(values.keys())
193 if p not in known_values:
197 raise src.SatException(_("Missing parameters: %s") % ", ".join(missing))
199 def get_parameters(self, conf_values=None):
200 if self.dico is not None:
203 self.check_user_values(conf_values)
205 # create dictionary with default values
207 dico["name"] = self.compo_name.lower()
208 dico["Name"] = self.compo_name.capitalize()
209 dico["NAME"] = self.compo_name
210 dico["target"] = self.target
211 dico[self.file_subst] = self.compo_name
212 # add user values if any
213 if conf_values is not None:
214 for p in conf_values.keys():
215 dico[p] = conf_values[p]
217 # ask user for values
218 for p in self.parameters:
219 tp = TParam(p, self.compo_name, dico)
224 while not tp.check_value(val):
225 val = input(tp.prompt)
226 if len(val) == 0 and len(tp.default) > 0:
230 # ask for missing value for pyconf
231 pyconfparam = self.get_pyconf_parameters()
232 for p in filter(lambda l: not (l in dico), pyconfparam):
235 rep = input("%s? " % p)
242 def search_template(config, template):
244 template_src_dir = ""
245 if os.path.isabs(template):
246 if os.path.exists(template):
247 template_src_dir = template
249 # look in template directory
250 for td in [os.path.join(config.VARS.datadir, "templates")]:
251 zz = os.path.join(td, template)
252 if os.path.exists(zz):
253 template_src_dir = zz
256 if len(template_src_dir) == 0:
257 raise src.SatException(_("Template not found: %s") % template)
259 return template_src_dir
263 # Prepares a module from a template.
264 def prepare_from_template(config, name, template, target_dir, conf_values, logger):
265 template_src_dir = search_template(config, template)
269 if os.path.isfile(template_src_dir):
271 " " + _("Extract template %s\n") % src.printcolors.printcInfo(template), 4
273 src.system.archive_extract(template_src_dir, target_dir)
276 " " + _("Copy template %s\n") % src.printcolors.printcInfo(template), 4
278 shutil.copytree(template_src_dir, target_dir)
279 logger.write("\n", 5)
282 if name.endswith("CPP"):
283 compo_name = name[:-3]
286 settings_file = os.path.join(target_dir, "template.info")
287 if not os.path.exists(settings_file):
288 raise src.SatException(_("Settings file not found"))
289 tsettings = TemplateSettings(compo_name, settings_file, target_dir)
291 # first rename the files
292 logger.write(" " + src.printcolors.printcLabel(_("Rename files\n")), 4)
293 for root, dirs, files in os.walk(target_dir):
295 ff = fic.replace(tsettings.file_subst, compo_name)
297 if os.path.exists(os.path.join(root, ff)):
298 raise src.SatException(
299 _("Destination file already exists: %s")
300 % os.path.join(root, ff)
302 logger.write(" %s -> %s\n" % (fic, ff), 5)
303 os.rename(os.path.join(root, fic), os.path.join(root, ff))
305 # rename the directories
306 logger.write("\n", 5)
307 logger.write(" " + src.printcolors.printcLabel(_("Rename directories\n")), 4)
308 for root, dirs, files in os.walk(target_dir, topdown=False):
310 dd = rep.replace(tsettings.file_subst, compo_name)
312 if os.path.exists(os.path.join(root, dd)):
313 raise src.SatException(
314 _("Destination directory " "already exists: %s")
315 % os.path.join(root, dd)
317 logger.write(" %s -> %s\n" % (rep, dd), 5)
318 os.rename(os.path.join(root, rep), os.path.join(root, dd))
320 # ask for missing parameters
321 logger.write("\n", 5)
323 " " + src.printcolors.printcLabel(_("Make substitution in files\n")), 4
325 logger.write(" " + _("Delimiter =") + " %s\n" % tsettings.delimiter_char, 5)
327 " " + _("Ignore Filters =") + " %s\n" % ", ".join(tsettings.ignore_filters),
330 dico = tsettings.get_parameters(conf_values)
331 logger.write("\n", 3)
333 # override standard string.Template class to use the desire delimiter
334 class CompoTemplate(string.Template):
335 delimiter = tsettings.delimiter_char
338 logger.write("\n", 5, True)
339 pathlen = len(target_dir) + 1
340 for root, dirs, files in os.walk(target_dir):
342 fpath = os.path.join(root, fic)
343 if not tsettings.check_file_for_substitution(fpath[pathlen:]):
344 logger.write(" - %s\n" % fpath[pathlen:], 5)
347 with open(fpath, "r") as f:
349 # make the substitution
350 template = CompoTemplate(m)
351 d = template.safe_substitute(dico)
356 with open(fpath, "w") as f:
358 logger.write(" %s %s\n" % (changed, fpath[pathlen:]), 5)
360 if not tsettings.has_pyconf:
362 src.printcolors.printcWarning(
363 _("Definition for sat not found in settings file.")
369 definition = tsettings.pyconf % dico
370 pyconf_file = os.path.join(target_dir, name + ".pyconf")
371 f = open(pyconf_file, "w")
375 _("Create configuration file: ")
376 + src.printcolors.printcInfo(pyconf_file)
381 if len(tsettings.post_command) > 0:
382 cmd = tsettings.post_command % dico
383 logger.write("\n", 5, True)
385 _("Run post command: ") + src.printcolors.printcInfo(cmd) + "\n", 3
388 p = subprocess.Popen(cmd, shell=True, cwd=target_dir)
395 def get_template_info(config, template_name, logger):
396 sources = search_template(config, template_name)
397 src.printcolors.print_value(logger, _("Template"), sources)
400 tmpdir = os.path.join(config.VARS.tmp_root, "tmp_template")
401 settings_file = os.path.join(tmpdir, "template.info")
402 if os.path.exists(tmpdir):
403 shutil.rmtree(tmpdir)
404 if os.path.isdir(sources):
405 shutil.copytree(sources, tmpdir)
407 src.system.archive_extract(sources, tmpdir)
408 settings_file = os.path.join(tmpdir, "template.info")
410 if not os.path.exists(settings_file):
411 raise src.SatException(_("Settings file not found"))
412 tsettings = TemplateSettings("NAME", settings_file, "target")
414 logger.write("\n", 3)
415 if len(tsettings.info) == 0:
417 src.printcolors.printcWarning(_("No information for this template.")), 3
420 logger.write(tsettings.info, 3)
422 logger.write("\n", 3)
423 logger.write("= Configuration", 3)
424 src.printcolors.print_value(logger, "file substitution key", tsettings.file_subst)
425 src.printcolors.print_value(logger, "subsitution key", tsettings.delimiter_char)
426 if len(tsettings.ignore_filters) > 0:
427 src.printcolors.print_value(
428 logger, "Ignore Filter", ", ".join(tsettings.ignore_filters)
431 logger.write("\n", 3)
432 logger.write("= Parameters", 3)
434 for pp in tsettings.parameters:
435 tt = TParam(pp, "NAME")
436 pnames.append(tt.name)
437 src.printcolors.print_value(logger, "Name", tt.name)
438 src.printcolors.print_value(logger, "Prompt", tt.raw_prompt)
439 src.printcolors.print_value(logger, "Default value", tt.default)
440 logger.write("\n", 3)
443 logger.write("= Verification\n", 3)
444 if tsettings.file_subst not in pnames:
446 "file substitution key not defined as a "
447 "parameter: %s" % tsettings.file_subst,
452 reexp = tsettings.delimiter_char.replace("$", "\$") + "{(?P<name>\S[^}]*)"
453 pathlen = len(tmpdir) + 1
454 for root, __, files in os.walk(tmpdir):
456 fpath = os.path.join(root, fic)
457 if not tsettings.check_file_for_substitution(fpath[pathlen:]):
460 with open(fpath, "r") as f:
462 zz = re.findall(reexp, m)
463 zz = list(set(zz)) # reduce
464 zz = filter(lambda l: l not in pnames, zz)
467 "Missing definition in %s: %s"
468 % (src.printcolors.printcLabel(fpath[pathlen:]), ", ".join(zz)),
474 logger.write(src.printcolors.printc("OK"), 3)
476 logger.write(src.printcolors.printc("KO"), 3)
478 logger.write("\n", 3)
481 shutil.rmtree(tmpdir)
487 # Describes the command
490 "The template command creates the sources for a SALOME "
491 "module from a template.\n\nexample\nsat template "
492 "--name my_product_name --template PythonComponent --target /tmp"
496 def run(args, runner, logger):
497 """method that is called when salomeTools is called with template parameter."""
498 (options, args) = parser.parse_args(args)
500 if options.template is None:
501 msg = _("Error: the --%s argument is required\n") % "template"
502 logger.write(src.printcolors.printcError(msg), 1)
503 logger.write("\n", 1)
506 if options.target is None and options.info is None:
507 msg = _("Error: the --%s argument is required\n") % "target"
508 logger.write(src.printcolors.printcError(msg), 1)
509 logger.write("\n", 1)
512 # if "APPLICATION" in runner.cfg:
513 # msg = _("Error: this command does not use a product.")
514 # logger.write(src.printcolors.printcError(msg), 1)
515 # logger.write("\n", 1)
519 return get_template_info(runner.cfg, options.template, logger)
521 if options.name is None:
522 msg = _("Error: the --%s argument is required\n") % "name"
523 logger.write(src.printcolors.printcError(msg), 1)
524 logger.write("\n", 1)
527 if not options.name.replace("_", "").isalnum():
529 "Error: component name must contains only alphanumeric "
530 "characters and no spaces\n"
532 logger.write(src.printcolors.printcError(msg), 1)
533 logger.write("\n", 1)
536 if options.target is None:
537 msg = _("Error: the --%s argument is required\n") % "target"
538 logger.write(src.printcolors.printcError(msg), 1)
539 logger.write("\n", 1)
542 target_dir = os.path.join(options.target, options.name)
543 if os.path.exists(target_dir):
544 msg = _("Error: the target already exists: %s") % target_dir
545 logger.write(src.printcolors.printcError(msg), 1)
546 logger.write("\n", 1)
549 logger.write(_("Create sources from template\n"), 1)
550 src.printcolors.print_value(logger, "destination", target_dir, 2)
551 src.printcolors.print_value(logger, "name", options.name, 2)
552 src.printcolors.print_value(logger, "template", options.template, 2)
553 logger.write("\n", 3, False)
556 if options.param is not None:
558 for elt in options.param.split(","):
559 param_def = elt.strip().split("=")
560 if len(param_def) != 2:
561 msg = _("Error: bad parameter definition")
562 logger.write(src.printcolors.printcError(msg), 1)
563 logger.write("\n", 1)
565 conf_values[param_def[0].strip()] = param_def[1].strip()
567 retcode = prepare_from_template(
568 runner.cfg, options.name, options.template, target_dir, conf_values, logger
573 _("The sources were created in %s")
574 % src.printcolors.printcInfo(target_dir),
578 src.printcolors.printcWarning(
579 _("\nDo not forget to put " "them in your version control system.")
584 logger.write("\n", 3)