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
28 from src.versionMinorMajorPatch import MinorMajorPatch as MMP
30 parser = src.options.Options()
37 "Optional: The name of the application (default is APPLICATION.virtual_app.name or "
42 "c", "catalog", "string", "catalog", _("Optional: The resources catalog to use")
50 "Optional: The directory where to create the application (default is "
51 "APPLICATION.workdir)"
60 "Optional: Create a resources catalog for the specified machines "
61 "(separated with ',')\n\tNOTICE: this command will ssh to retrieve "
62 "information to each machine in the list"
70 _("Optional: the restricted list of module(s) to include in the " "application"),
78 "Optional: Create a launcher that will use mesa products\n\t"
79 "It can be usefull whan salome is used on a remote machine through ssh"
84 # Creates an alias for runAppli.
85 def make_alias(appli_path, alias_path, force=False):
86 assert len(alias_path) > 0, "Bad name for alias"
87 if os.path.exists(alias_path) and not force:
88 raise src.SatException(_("Cannot create the alias '%s'\n") % alias_path)
89 else: # find relative path
90 os.symlink(appli_path, alias_path)
94 # add the definition of a module to out stream.
95 def add_module_to_appli(out, module, has_gui, module_path, logger, flagline):
96 if not os.path.exists(module_path):
98 logger.write("\n", 3, False)
102 + src.printcolors.printcWarning(
103 _("WARNING: module %s not installed") % module
110 ' <module name="%s" gui="%s" path="%s"/>\n' % (module, has_gui, module_path)
116 # Creates the config file to create an application with the list of modules.
117 def create_config_file(config, modules, env_files, logger):
120 if "SAMPLES" in config.APPLICATION.products:
121 samples = src.product.get_product_config(config, "SAMPLES").source_dir
123 config_file = src.get_tmp_filename(config, "appli_config.xml")
124 f = open(config_file, "w")
126 f.write("<application>\n")
127 for env_file in env_files:
128 if env_file.endswith("cfg"):
129 f.write('<context path="%s"/>\n' % env_file)
131 f.write('<prerequisites path="%s"/>\n' % env_file)
133 f.write('<resources path="CatalogResources.xml"/>\n')
134 f.write("<modules>\n")
138 mm = src.product.get_product_config(config, m)
139 # do not include in virtual application application module!
140 if src.get_property_in_product_cfg(mm, "is_salome_application") == "yes":
142 # do not include products that do not compile
143 if not src.product.product_compiles(mm):
146 if src.product.product_is_smesh_plugin(mm):
149 if "install_dir" in mm and bool(mm.install_dir):
150 if src.product.product_is_cpp(mm):
152 for aa in src.product.get_product_components(mm):
153 install_dir = os.path.join(
154 config.APPLICATION.workdir, config.INTERNAL.config.install_dir
156 mp = os.path.join(install_dir, aa)
157 flagline = add_module_to_appli(f, aa, "yes", mp, logger, flagline)
161 gui = src.get_cfg_param(mm, "has_gui", "yes")
162 flagline = add_module_to_appli(f, m, gui, mp, logger, flagline)
164 f.write("</modules>\n")
165 f.write('<samples path="%s"/>\n' % samples)
166 f.write("</application>\n")
173 # Customizes the application by editing SalomeApp.xml.
174 def customize_app(config, appli_dir, logger):
176 "configure" not in config.APPLICATION.virtual_app
177 or len(config.APPLICATION.virtual_app.configure) == 0
181 # shortcut to get an element (section or parameter) from parent.
182 def get_element(parent, name, strtype):
183 for c in parent.getchildren():
184 if c.attrib["name"] == name:
187 # element not found create it
188 elt = add_simple_node(parent, strtype)
189 elt.attrib["name"] = name
192 # shortcut method to create a node
193 def add_simple_node(parent, node_name, text=None):
194 n = etree.Element(node_name)
197 n.text = text.strip("\n\t").decode("UTF-8")
199 sys.stderr.write("################ %s %s\n" % (node_name, text))
205 app_file = os.path.join(appli_dir, "SalomeApp.xml")
206 tree = etree.parse(app_file)
207 document = tree.getroot()
208 assert document is not None, "document tag not found"
210 logger.write("\n", 4)
211 for section_name in config.APPLICATION.virtual_app.configure:
212 for parameter_name in config.APPLICATION.virtual_app.configure[section_name]:
213 parameter_value = config.APPLICATION.virtual_app.configure[section_name][
217 " configure: %s/%s = %s\n"
218 % (section_name, parameter_name, parameter_value),
221 section = get_element(document, section_name, "section")
222 parameter = get_element(section, parameter_name, "parameter")
223 parameter.attrib["value"] = parameter_value
226 f = open(app_file, "w")
227 f.write("<?xml version='1.0' encoding='utf-8'?>\n")
228 f.write(etree.tostring(document, encoding="utf-8"))
233 # Generates the application with the config_file.
234 def generate_application(config, appli_dir, config_file, logger):
235 target_dir = os.path.dirname(appli_dir)
237 install_KERNEL_dir = src.product.get_product_config(config, "KERNEL").install_dir
238 script = os.path.join(install_KERNEL_dir, "bin", "salome", "appli_gen.py")
239 if not os.path.exists(script):
240 raise src.SatException(_("KERNEL is not installed"))
242 # Add SALOME python in the environment in order to avoid python version
243 # problems at appli_gen.py call
244 if "Python" in config.APPLICATION.products:
245 envi = src.environment.SalomeEnviron(
246 config, src.environment.Environ(dict(os.environ)), True
248 envi.set_a_product("Python", logger)
250 command = "python %s --prefix=%s --config=%s" % (script, appli_dir, config_file)
251 logger.write("\n>" + command + "\n", 5, False)
252 res = subprocess.call(
256 env=envi.environ.environ,
257 stdout=logger.logTxtFile,
258 stderr=subprocess.STDOUT,
262 raise src.SatException(_("Cannot create application, code = %d\n") % res)
269 def write_step(logger, message, level=3, pad=50):
271 "%s %s " % (message, "." * (pad - len(message.decode("UTF-8")))), level
277 # Creates a SALOME application.
278 def create_application(config, appli_dir, catalog, logger, display=True):
280 SALOME_modules = get_SALOME_modules(config)
282 warn = ["KERNEL", "GUI"]
285 if w not in SALOME_modules:
286 msg = _("WARNING: module %s is required to create application\n") % w
287 logger.write(src.printcolors.printcWarning(msg), 2)
289 # generate the launch file
290 retcode = generate_launch_file(config, appli_dir, catalog, logger, SALOME_modules)
293 cmd = src.printcolors.printcLabel("%s/salome" % appli_dir)
296 logger.write("\n", 3, False)
297 logger.write(_("To launch the application, type:\n"), 3, False)
298 logger.write(" %s" % (cmd), 3, False)
299 logger.write("\n", 3, False)
303 def get_SALOME_modules(config):
305 for product in config.APPLICATION.products:
306 product_info = src.product.get_product_config(config, product)
307 if src.product.product_is_salome(
309 ) or src.product.product_is_generated(product_info):
310 l_modules.append(product)
315 # Obsolescent way of creating the application.
316 # This method will use appli_gen to create the application directory.
317 def generate_launch_file(config, appli_dir, catalog, logger, l_SALOME_modules):
320 if len(catalog) > 0 and not os.path.exists(catalog):
321 raise IOError(_("Catalog not found: %s") % catalog)
323 write_step(logger, _("Creating environment files"))
324 status = src.KO_STATUS
326 # build the application (the name depends upon salome version
327 env_file = os.path.join(config.APPLICATION.workdir, "env_launch")
328 VersionSalome = src.get_salome_version(config)
329 if VersionSalome >= MMP([8, 2, 0]):
330 # for salome 8+ we use a salome context file for the virtual app
331 app_shell = ["cfg", "bash"]
332 env_files = [env_file + ".cfg", env_file + ".sh"]
335 env_files = [env_file + ".sh"]
340 # generate only shells the user wants (by default bash, csh, batch)
341 # the environ command will only generate file compatible
342 # with the current system.
343 environ.write_all_source_files(config, logger, shells=app_shell, silent=True)
344 status = src.OK_STATUS
346 logger.write(src.printcolors.printc(status) + "\n", 2, False)
348 write_step(logger, _("Building application"), level=2)
349 cf = create_config_file(config, l_SALOME_modules, env_files, logger)
351 # create the application directory
352 os.makedirs(appli_dir)
354 # generate the application
355 status = src.KO_STATUS
357 retcode = generate_application(config, appli_dir, cf, logger)
358 customize_app(config, appli_dir, logger)
359 status = src.OK_STATUS
361 logger.write(src.printcolors.printc(status) + "\n", 2, False)
363 # copy the catalog if one
365 shutil.copy(catalog, os.path.join(appli_dir, "CatalogResources.xml"))
371 # Generates the catalog from a list of machines.
372 def generate_catalog(machines, config, logger):
373 # remove empty machines
374 machines = map(lambda l: l.strip(), machines)
375 machines = filter(lambda l: len(l) > 0, machines)
377 src.printcolors.print_value(
378 logger, _("Generate Resources Catalog"), ", ".join(machines), 4
380 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
381 user = getpass.getuser()
383 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
384 with open(catfile, "w") as catalog:
385 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
387 if not src.architecture.is_windows():
388 logger.write(" ssh %s " % (k + " ").ljust(20, "."), 4)
391 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
392 p = subprocess.Popen(
395 stdin=subprocess.PIPE,
396 stdout=subprocess.PIPE,
397 stderr=subprocess.PIPE,
401 machine_access = p.returncode == 0
402 if not machine_access:
403 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
405 " " + src.printcolors.printcWarning(p.stderr.read()), 2
408 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
409 lines = p.stdout.readlines()
410 freq = lines[0][:-1].split(":")[-1].split(".")[0].strip()
411 nb_proc = len(lines) - 1
412 memory = lines[-1].split(":")[-1].split()[0].strip()
413 memory = int(memory) / 1000
415 catalog.write(" <machine\n")
416 catalog.write(' protocol="ssh"\n')
417 catalog.write(' nbOfNodes="1"\n')
418 catalog.write(' mode="interactif"\n')
419 catalog.write(' OS="LINUX"\n')
421 if (not src.architecture.is_windows()) and machine_access:
422 catalog.write(' CPUFreqMHz="%s"\n' % freq)
423 catalog.write(' nbOfProcPerNode="%s"\n' % nb_proc)
424 catalog.write(' memInMB="%s"\n' % memory)
426 catalog.write(' userName="%s"\n' % user)
427 catalog.write(' name="%s"\n' % k)
428 catalog.write(' hostname="%s"\n' % k)
429 catalog.write(" >\n")
430 catalog.write(" </machine>\n")
432 catalog.write("</resources>\n")
436 ##################################################
439 # Describes the command
441 """method that is called when salomeTools is called with --help option.
443 :return: The text to display for the application command description.
447 "The application command creates a SALOME application.\n"
448 'WARNING: it works only for SALOME 6. Use the "launcher" '
449 "command for newer versions of SALOME\n\nexample:\nsat application"
456 def run(args, runner, logger):
457 """method that is called when salomeTools is called with application
461 (options, args) = parser.parse_args(args)
464 src.check_config_has_application(runner.cfg)
466 application = src.printcolors.printcLabel(runner.cfg.VARS.application)
467 logger.write(_("Building application for %s\n") % application, 1)
469 # if section APPLICATION.virtual_app does not exists create one
470 if "virtual_app" not in runner.cfg.APPLICATION:
472 "The section APPLICATION.virtual_app is not defined in the product. Use sat launcher in state"
474 logger.write(src.printcolors.printcError(msg), 1)
475 logger.write("\n", 1)
478 # get application dir
479 target_dir = runner.cfg.APPLICATION.workdir
481 target_dir = options.target
483 # set list of modules
485 runner.cfg.APPLICATION.virtual_app["modules"] = options.modules
487 # activate mesa use in the generated application
489 src.activate_mesa_property(runner.cfg)
491 # set name and application_name
493 runner.cfg.APPLICATION.virtual_app["name"] = options.name
494 runner.cfg.APPLICATION.virtual_app["application_name"] = (
495 options.name + "_appdir"
498 application_name = src.get_cfg_param(
499 runner.cfg.APPLICATION.virtual_app,
501 runner.cfg.APPLICATION.virtual_app.name + "_appdir",
503 appli_dir = os.path.join(target_dir, application_name)
505 src.printcolors.print_value(logger, _("Application directory"), appli_dir, 3)
508 catalog, catalog_src = "", ""
510 # use catalog specified in the command line
511 catalog = options.catalog
513 # generate catalog for given list of computers
514 catalog_src = options.gencat
515 catalog = generate_catalog(options.gencat.split(","), runner.cfg, logger)
516 elif "catalog" in runner.cfg.APPLICATION.virtual_app:
517 # use catalog specified in the product
518 if runner.cfg.APPLICATION.virtual_app.catalog.endswith(".xml"):
520 catalog = runner.cfg.APPLICATION.virtual_app.catalog
522 # catalog as a list of computers
523 catalog_src = runner.cfg.APPLICATION.virtual_app.catalog
525 lambda l: len(l.strip()) > 0,
526 runner.cfg.APPLICATION.virtual_app.catalog.split(","),
529 catalog = generate_catalog(
530 runner.cfg.APPLICATION.virtual_app.catalog.split(","),
535 # display which catalog is used
537 catalog = os.path.realpath(catalog)
538 if len(catalog_src) > 0:
539 src.printcolors.print_value(logger, _("Resources Catalog"), catalog_src, 3)
541 src.printcolors.print_value(logger, _("Resources Catalog"), catalog, 3)
543 logger.write("\n", 3, False)
547 # remove previous application
548 if os.path.exists(appli_dir):
549 write_step(logger, _("Removing previous application directory"))
552 shutil.rmtree(appli_dir)
555 logger.write(src.printcolors.printc(rres) + "\n", 3, False)
557 # generate the application
559 try: # try/except/finally not supported in all version of python
560 retcode = create_application(runner.cfg, appli_dir, catalog, logger)
561 except Exception as exc:
562 details.append(str(exc))
565 logger.write("\n", 3, False)