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
26 from src import ElementTree as etree
29 parser = src.options.Options()
30 parser.add_option('n', 'name', 'string', 'name',
31 _('The name of the application (default is APPLI.name or runAppli)'))
32 parser.add_option('c', 'catalog', 'string', 'catalog',
33 _('The resources catalog to use'))
34 parser.add_option('t', 'target', 'string', 'target',
35 _('The directory where to create the application (default is PRODUCT.out_dir)'))
36 parser.add_option('', 'gencat', 'string', 'gencat',
37 _("""Create a resources catalog for the specified machines (separated with ',')
38 \tNOTICE: this command will ssh to retrieve information to each machine in the list"""))
39 parser.add_option('m', 'module', 'list2', 'modules',
40 _("module(s) to include in the application"))
43 # Creates an alias for runAppli.
44 def make_alias(appli_path, alias_path, force=False):
45 assert len(alias_path) > 0, "Bad name for alias"
46 if os.path.exists(alias_path) and not force:
47 raise src.SatException(_("Cannot create the alias '%s'\n") % alias_path)
48 else: # find relative path
49 os.symlink(appli_path, alias_path)
52 # add the definition of a module to out stream.
53 def add_module_to_appli(out, module, has_gui, module_path, logger, flagline):
54 if not os.path.exists(module_path):
56 logger.write("\n", 3, False)
58 logger.write(" " + src.printcolors.printcWarning(_(
59 "WARNING: module %s not installed") % module) + "\n", 3)
61 out.write(' <module name="%s" gui="%s" path="%s"/>\n' % (module,
67 # Creates the config file to create an application with the list of modules.
68 def create_config_file(config, modules, env_file, logger):
70 modules = config.APPLICATION.products
73 if 'SAMPLES' in config.APPLICATION.products:
74 samples = src.product.get_product_config(config, 'SAMPLES').source_dir
76 config_file = src.get_tmp_filename(config, "appli_config.xml")
77 f = open(config_file, "w")
79 f.write('<application>\n')
80 f.write('<prerequisites path="%s"/>\n' % env_file)
81 f.write('<resources path="CatalogResources.xml"/>\n')
82 f.write('<modules>\n')
86 mm = src.product.get_product_config(config, m)
87 if src.product.module_is_smesh_plugin(mm):
90 if 'install_dir' in mm and bool(mm.install_dir) :
91 if src.product.module_is_cpp(mm):
93 for aa in src.product.get_product_components(mm):
94 install_dir = os.path.join(config.APPLICATION.workdir,
96 mp = os.path.join(install_dir, aa)
97 flagline = add_module_to_appli(f,
106 gui = src.get_cfg_param(mm, "has_gui", "yes")
107 flagline = add_module_to_appli(f, m, gui, mp, logger, flagline)
109 f.write('</modules>\n')
110 f.write('<samples path="%s"/>\n' % samples)
111 f.write('</application>\n')
117 # Customizes the application by editing SalomeApp.xml.
118 def customize_app(config, appli_dir, logger):
119 if 'configure' not in config.APPLI \
120 or len(config.APPLI.configure) == 0:
123 # shortcut to get an element (section or parameter) from parent.
124 def get_element(parent, name, strtype):
125 for c in parent.getchildren():
126 if c.attrib['name'] == name:
129 # element not found create it
130 elt = add_simple_node(parent, strtype)
131 elt.attrib['name'] = name
134 # shortcut method to create a node
135 def add_simple_node(parent, node_name, text=None):
136 n = etree.Element(node_name)
139 n.text = text.strip("\n\t").decode("UTF-8")
141 sys.stderr.write("################ %s %s\n" % (node_name, text))
147 app_file = os.path.join(appli_dir, "SalomeApp.xml")
148 tree = etree.parse(app_file)
149 document = tree.getroot()
150 assert document is not None, "document tag not found"
152 logger.write("\n", 4)
153 for section_name in config.APPLI.configure:
154 for parameter_name in config.APPLI.configure[section_name]:
155 parameter_value = config.APPLI.configure[section_name][parameter_name]
156 logger.write(" configure: %s/%s = %s\n" % (section_name,
159 section = get_element(document, section_name, "section")
160 parameter = get_element(section, parameter_name, "parameter")
161 parameter.attrib['value'] = parameter_value
164 f = open(app_file, "w")
165 f.write("<?xml version='1.0' encoding='utf-8'?>\n")
166 f.write(etree.tostring(document, encoding='utf-8'))
170 # Generates the application with the config_file.
171 def generate_application(config, appli_dir, config_file, logger):
172 target_dir = os.path.dirname(appli_dir)
174 install_KERNEL_dir = src.product.get_product_config(config,
175 'KERNEL').install_dir
176 script = os.path.join(install_KERNEL_dir, "bin", "salome", "appli_gen.py")
177 if not os.path.exists(script):
178 raise src.SatException(_("KERNEL is not installed"))
180 # Add SALOME python in the environment in order to avoid python version
181 # problems at appli_gen.py call
182 if 'Python' in config.APPLICATIONS.products:
183 envi = src.environment.SalomeEnviron(config,
184 src.environment.Environ(
187 envi.set_a_product('Python', logger)
189 command = "python %s --prefix=%s --config=%s" % (script,
192 logger.write("\n>" + command + "\n", 5, False)
193 res = subprocess.call(command,
196 env=envi.environ.environ,
197 stdout=logger.log_file,
198 stderr=subprocess.STDOUT)
201 raise src.SatException(_("Cannot create application, code = %d\n") % res)
207 def write_step(logger, message, level=3, pad=50):
208 logger.write("%s %s " % (message, '.' * (pad - len(message.decode("UTF-8")))), level)
212 # Creates a SALOME application.
213 def create_application(config, appli_dir, catalog, logger, display=True):
215 # check modules to add to installation
217 if 'modules' in config.APPLI:
218 modules = config.APPLI.modules
219 # add prerequisites for the module and its dependencies
221 for module in modules:
222 prelist = config.TOOLS.common.module_info[module].pre_depend
223 for prereq in prelist:
224 # add prerequisites define in module_info AND product.prerequis
225 if prereq in config.PRODUCT.prerequis and not prerequis.has_key(prereq):
226 prerequis[prereq] = config.PRODUCT.prerequis[prereq]
228 if len(modules) == 0:
229 modules = src.get_cfg_param(config.PRODUCT, "all_modules", config.PRODUCT.modules)
230 prerequis = config.PRODUCT.prerequis
232 env_info = { 'modules': modules, 'prerequis': prerequis }
234 cmd_old, cmd_new = "", ""
236 warn = ['KERNEL', 'GUI']
239 if w not in env_info['modules']:
240 msg = _("WARNING: module %s is required to create application\n") % w
241 logger.write(src.printcolors.printcWarning(msg), 2)
243 # old way for application
244 retcode = generate_launch_file_old(config, appli_dir, catalog, logger, env_info=env_info)
246 cmd_old = src.printcolors.printcLabel("%s/runAppli" % appli_dir)
248 # new way for application
249 VersionSalome = src.get_salome_version(config)
251 if VersionSalome >= 751 :
252 filename = generate_launch_file(config, appli_dir, catalog, logger, env_info)
253 cmd_new = src.printcolors.printcLabel(filename)
256 logger.write("\n", 3, False)
257 logger.write(_("To launch the application, type:\n"), 3, False)
259 logger.write(" %s" % (cmd_old), 3, False)
260 logger.write("\n", 3, False)
262 logger.write(" %s (%s)" % (cmd_new, _("new command")), 3, False)
263 logger.write("\n", 3, False)
267 # Obsolescent way of creating the application.
268 # This method will use appli_gen to create the application directory.
269 def generate_launch_file_old(config, appli_dir, catalog, logger, env_info=None):
272 if len(catalog) > 0 and not os.path.exists(catalog):
273 raise IOError(_("Catalog not found: %s") % catalog)
275 write_step(logger, _("Creating environment files"))
276 status = src.KO_STATUS
279 # generate only shells the user wants (by default bash, csh, batch)
280 # the environ command will only generate file compatible with the current system.
281 shells = src.get_cfg_param(config.TOOLS.environ,
284 environ.write_all_source_files(config,
288 status = src.OK_STATUS
290 logger.write(src.printcolors.printc(status) + "\n", 2, False)
292 # build the application
293 env_file = os.path.join(config.PRODUCT.out_dir, "env_launch.sh")
294 write_step(logger, _("Building application"), level=2)
295 cf = create_config_file(config, env_info['modules'], env_file, logger)
297 # create the application directory
298 os.makedirs(appli_dir)
300 # generate the application
301 status = src.KO_STATUS
303 retcode = generate_application(config, appli_dir, cf, logger)
304 customize_app(config, appli_dir, logger)
305 status = src.OK_STATUS
307 logger.write(src.printcolors.printc(status) + "\n", 2, False)
309 # copy the catalog if one
311 shutil.copy(catalog, os.path.join(appli_dir, "CatalogResources.xml"))
317 # Generates a launcher that sources Salome's python and calls original launcher
318 def generate_sourcing_launcher(config, appli_dir, logger) :
320 # Rename original launcher
321 launcher_name = os.path.join( appli_dir,
324 config.APPLI.launch_alias_name )
325 original_launcher = launcher_name + "-original"
326 os.rename( launcher_name, original_launcher )
329 f = open(launcher_name, "w")
331 # Write the set up of the environment
332 env = src.environment.SalomeEnviron( config,
333 src.fileEnviron.get_file_environ(
338 env.set_a_product( "Python", logger)
340 # Write the call to the original launcher
342 f.write( "# This is the call to the original launcher\n")
343 f.write( original_launcher + " $*" )
346 # Write the cleaning of the environment
351 os.chmod(launcher_name, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
354 # New method to create an application
355 def generate_launch_file(config,
361 out_dir = config.PRODUCT.out_dir
363 basefilename = config.APPLI.name
364 if "launch_alias_name" in config.APPLI :
365 basefilename = config.APPLI.launch_alias_name
366 filename = os.path.join(appli_dir, "bin/salome", basefilename)
367 if os.path.exists(filename): os.remove(filename)
368 before, after = src.fileEnviron.withProfile.split(
369 "# here your local standalone environment\n")
371 # create an environment file writer
372 writer = src.environment.FileEnvWriter(config,
378 #writer.silent = False
380 # create the command file
381 launch_file = open(filename, "w")
382 launch_file.write(before)
383 writer.write_cfgForPy_file(launch_file)
384 launch_file.write(after)
386 os.chmod(filename, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
388 # If native python < 2.6, write a launcher that sources Salome's
389 # python before calling the original launcher
390 if config.VARS.python < "2.6" :
391 generate_sourcing_launcher(config, appli_dir, logger)
397 # Generates the catalog from a list of machines.
398 def generate_catalog(machines, config, logger):
399 # remove empty machines
400 machines = map(lambda l: l.strip(), machines)
401 machines = filter(lambda l: len(l) > 0, machines)
403 src.printcolors.print_value(logger,
404 _("Generate Resources Catalog"),
407 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
408 user = getpass.getuser()
410 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
411 catalog = file(catfile, "w")
412 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
414 logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4)
417 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
418 p = subprocess.Popen(ssh_cmd, shell=True,
419 stdin=subprocess.PIPE,
420 stdout=subprocess.PIPE,
421 stderr=subprocess.PIPE)
424 if p.returncode != 0:
425 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
426 logger.write(" " + src.printcolors.printcWarning(p.stderr.read()),
429 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
430 lines = p.stdout.readlines()
431 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
432 nb_proc = len(lines) -1
433 memory = lines[-1].split(':')[-1].split()[0].strip()
434 memory = int(memory) / 1000
436 catalog.write(" <machine\n")
437 catalog.write(" protocol=\"ssh\"\n")
438 catalog.write(" nbOfNodes=\"1\"\n")
439 catalog.write(" mode=\"interactif\"\n")
440 catalog.write(" OS=\"LINUX\"\n")
441 catalog.write(" CPUFreqMHz=\"%s\"\n" % freq)
442 catalog.write(" nbOfProcPerNode=\"%s\"\n" % nb_proc)
443 catalog.write(" memInMB=\"%s\"\n" % memory)
444 catalog.write(" userName=\"%s\"\n" % user)
445 catalog.write(" name=\"%s\"\n" % k)
446 catalog.write(" hostname=\"%s\"\n" % k)
447 catalog.write(" >\n")
448 catalog.write(" </machine>\n")
450 catalog.write("</resources>\n")
454 ##################################################
457 # Describes the command
459 '''method that is called when salomeTools is called with --help option.
461 :return: The text to display for the application command description.
464 return _("""The application command creates a SALOME application.\n"""
465 """WARNING: it works only for SALOME 6. Use the "launcher" """
466 """command for newer versions of SALOME""")
470 def run(args, runner, logger):
471 '''method that is called when salomeTools is called with application
475 (options, args) = parser.parse_args(args)
478 src.check_config_has_application( runner.cfg )
480 application = src.printcolors.printcLabel(runner.cfg.VARS.application)
481 logger.write(_("Building application for %s\n") % application, 1)
483 # if section APPLI does not exists create one
484 if "APPLI" not in runner.cfg:
485 msg = _("The section APPLI is not defined in the product.")
486 logger.write(src.printcolors.printcError(msg), 1)
489 # get application dir
490 target_dir = runner.cfg.APPLICATION.workdir
492 target_dir = options.target
494 # set list of modules
496 runner.cfg.APPLI['modules'] = options.modules
498 # set name and application_name
500 runner.cfg.APPLI['name'] = options.name
501 runner.cfg.APPLI['application_name'] = options.name + "_appdir"
503 application_name = src.get_cfg_param(runner.cfg.APPLI,
505 runner.cfg.APPLI.name + "_appdir")
506 appli_dir = os.path.join(target_dir, application_name)
508 src.printcolors.print_value(logger,
509 _("Application directory"),
514 catalog, catalog_src = "", ""
516 # use catalog specified in the command line
517 catalog = options.catalog
519 # generate catalog for given list of computers
520 catalog_src = options.gencat
521 catalog = generate_catalog(options.gencat.split(","),
523 elif 'catalog' in runner.cfg.APPLI:
524 # use catalog specified in the product
525 if runner.cfg.APPLI.catalog.endswith(".xml"):
527 catalog = runner.cfg.APPLI.catalog
529 # catalog as a list of computers
530 catalog_src = runner.cfg.APPLI.catalog
531 mlist = filter(lambda l: len(l.strip()) > 0,
532 runner.cfg.APPLI.catalog.split(","))
534 catalog = generate_catalog(runner.cfg.APPLI.catalog.split(","),
537 # display which catalog is used
539 catalog = os.path.realpath(catalog)
540 if len(catalog_src) > 0:
541 src.printcolors.print_value(logger,
542 _("Resources Catalog"),
546 src.printcolors.print_value(logger,
547 _("Resources Catalog"),
551 logger.write("\n", 3, False)
555 # remove previous application
556 if os.path.exists(appli_dir):
557 write_step(logger, _("Removing previous application directory"))
560 shutil.rmtree(appli_dir)
563 logger.write(src.printcolors.printc(rres) + "\n", 3, False)
565 # generate the application
567 try: # try/except/finally not supported in all version of python
568 retcode = create_application(runner.cfg, appli_dir, catalog, logger)
569 except Exception as exc:
570 details.append(str(exc))
573 logger.write("\n", 3, False)