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()
31 parser.add_option('n', 'name', 'string', 'name',
32 _('Optional: The name of the application (default is APPLICATION.virtual_app.name or '
34 parser.add_option('c', 'catalog', 'string', 'catalog',
35 _('Optional: The resources catalog to use'))
36 parser.add_option('t', 'target', 'string', 'target',
37 _('Optional: The directory where to create the application (default is '
38 'APPLICATION.workdir)'))
39 parser.add_option('', 'gencat', 'string', 'gencat',
40 _("Optional: Create a resources catalog for the specified machines "
41 "(separated with ',')\n\tNOTICE: this command will ssh to retrieve "
42 "information to each machine in the list"))
43 parser.add_option('m', 'module', 'list2', 'modules',
44 _("Optional: the restricted list of module(s) to include in the "
46 parser.add_option('', 'use_mesa', 'boolean', 'use_mesa',
47 _("Optional: Create a launcher that will use mesa products\n\t"
48 "It can be usefull whan salome is used on a remote machine through ssh"))
51 # Creates an alias for runAppli.
52 def make_alias(appli_path, alias_path, force=False):
53 assert len(alias_path) > 0, "Bad name for alias"
54 if os.path.exists(alias_path) and not force:
55 raise src.SatException(_("Cannot create the alias '%s'\n") % alias_path)
56 else: # find relative path
57 os.symlink(appli_path, alias_path)
60 # add the definition of a module to out stream.
61 def add_module_to_appli(out, module, has_gui, module_path, logger, flagline):
62 if not os.path.exists(module_path):
64 logger.write("\n", 3, False)
66 logger.write(" " + src.printcolors.printcWarning(_(
67 "WARNING: module %s not installed") % module) + "\n", 3)
69 out.write(' <module name="%s" gui="%s" path="%s"/>\n' % (module,
75 # Creates the config file to create an application with the list of modules.
76 def create_config_file(config, modules, env_files, logger):
79 if 'SAMPLES' in config.APPLICATION.products:
80 samples = src.product.get_product_config(config, 'SAMPLES').source_dir
82 config_file = src.get_tmp_filename(config, "appli_config.xml")
83 f = open(config_file, "w")
85 f.write('<application>\n')
86 for env_file in env_files:
87 if env_file.endswith("cfg"):
88 f.write('<context path="%s"/>\n' % env_file)
90 f.write('<prerequisites path="%s"/>\n' % env_file)
92 f.write('<resources path="CatalogResources.xml"/>\n')
93 f.write('<modules>\n')
97 mm = src.product.get_product_config(config, m)
98 # do not include in virtual application application module!
99 if src.get_property_in_product_cfg(mm, "is_salome_application") == "yes":
101 # do not include products that do not compile
102 if not src.product.product_compiles(mm):
105 if src.product.product_is_smesh_plugin(mm):
108 if 'install_dir' in mm and bool(mm.install_dir):
109 if src.product.product_is_cpp(mm):
111 for aa in src.product.get_product_components(mm):
112 install_dir=os.path.join(config.APPLICATION.workdir,
113 config.INTERNAL.config.install_dir)
114 mp = os.path.join(install_dir, aa)
115 flagline = add_module_to_appli(f,
124 gui = src.get_cfg_param(mm, "has_gui", "yes")
125 flagline = add_module_to_appli(f, m, gui, mp, logger, flagline)
127 f.write('</modules>\n')
128 f.write('<samples path="%s"/>\n' % samples)
129 f.write('</application>\n')
135 # Customizes the application by editing SalomeApp.xml.
136 def customize_app(config, appli_dir, logger):
137 if 'configure' not in config.APPLICATION.virtual_app \
138 or len(config.APPLICATION.virtual_app.configure) == 0:
141 # shortcut to get an element (section or parameter) from parent.
142 def get_element(parent, name, strtype):
143 for c in parent.getchildren():
144 if c.attrib['name'] == name:
147 # element not found create it
148 elt = add_simple_node(parent, strtype)
149 elt.attrib['name'] = name
152 # shortcut method to create a node
153 def add_simple_node(parent, node_name, text=None):
154 n = etree.Element(node_name)
157 n.text = text.strip("\n\t").decode("UTF-8")
159 sys.stderr.write("################ %s %s\n" % (node_name, text))
165 app_file = os.path.join(appli_dir, "SalomeApp.xml")
166 tree = etree.parse(app_file)
167 document = tree.getroot()
168 assert document is not None, "document tag not found"
170 logger.write("\n", 4)
171 for section_name in config.APPLICATION.virtual_app.configure:
172 for parameter_name in config.APPLICATION.virtual_app.configure[section_name]:
173 parameter_value = config.APPLICATION.virtual_app.configure[section_name][parameter_name]
174 logger.write(" configure: %s/%s = %s\n" % (section_name,
177 section = get_element(document, section_name, "section")
178 parameter = get_element(section, parameter_name, "parameter")
179 parameter.attrib['value'] = parameter_value
182 f = open(app_file, "w")
183 f.write("<?xml version='1.0' encoding='utf-8'?>\n")
184 f.write(etree.tostring(document, encoding='utf-8'))
188 # Generates the application with the config_file.
189 def generate_application(config, appli_dir, config_file, logger):
190 target_dir = os.path.dirname(appli_dir)
192 install_KERNEL_dir = src.product.get_product_config(config,
193 'KERNEL').install_dir
194 script = os.path.join(install_KERNEL_dir, "bin", "salome", "appli_gen.py")
195 if not os.path.exists(script):
196 raise src.SatException(_("KERNEL is not installed"))
198 # Add SALOME python in the environment in order to avoid python version
199 # problems at appli_gen.py call
200 if 'Python' in config.APPLICATION.products:
201 envi = src.environment.SalomeEnviron(config,
202 src.environment.Environ(
205 envi.set_a_product('Python', logger)
207 command = "python %s --prefix=%s --config=%s" % (script,
210 logger.write("\n>" + command + "\n", 5, False)
211 res = subprocess.call(command,
214 env=envi.environ.environ,
215 stdout=logger.logTxtFile,
216 stderr=subprocess.STDOUT)
219 raise src.SatException(_("Cannot create application, code = %d\n") % res)
225 def write_step(logger, message, level=3, pad=50):
226 logger.write("%s %s " % (message, '.' * (pad - len(message.decode("UTF-8")))), level)
230 # Creates a SALOME application.
231 def create_application(config, appli_dir, catalog, logger, display=True):
233 SALOME_modules = get_SALOME_modules(config)
235 warn = ['KERNEL', 'GUI']
238 if w not in SALOME_modules:
239 msg = _("WARNING: module %s is required to create application\n") % w
240 logger.write(src.printcolors.printcWarning(msg), 2)
242 # generate the launch file
243 retcode = generate_launch_file(config,
250 cmd = src.printcolors.printcLabel("%s/salome" % appli_dir)
253 logger.write("\n", 3, False)
254 logger.write(_("To launch the application, type:\n"), 3, False)
255 logger.write(" %s" % (cmd), 3, False)
256 logger.write("\n", 3, False)
259 def get_SALOME_modules(config):
261 for product in config.APPLICATION.products:
262 product_info = src.product.get_product_config(config, product)
263 if (src.product.product_is_salome(product_info) or
264 src.product.product_is_generated(product_info)):
265 l_modules.append(product)
269 # Obsolescent way of creating the application.
270 # This method will use appli_gen to create the application directory.
271 def generate_launch_file(config, appli_dir, catalog, logger, l_SALOME_modules):
274 if len(catalog) > 0 and not os.path.exists(catalog):
275 raise IOError(_("Catalog not found: %s") % catalog)
277 write_step(logger, _("Creating environment files"))
278 status = src.KO_STATUS
280 # build the application (the name depends upon salome version
281 env_file = os.path.join(config.APPLICATION.workdir, "env_launch")
282 VersionSalome = src.get_salome_version(config)
283 if VersionSalome>=MMP([8,2,0]):
284 # for salome 8+ we use a salome context file for the virtual app
285 app_shell=["cfg", "bash"]
286 env_files=[env_file+".cfg", env_file+".sh"]
289 env_files=[env_file+".sh"]
293 # generate only shells the user wants (by default bash, csh, batch)
294 # the environ command will only generate file compatible
295 # with the current system.
296 environ.write_all_source_files(config,
300 status = src.OK_STATUS
302 logger.write(src.printcolors.printc(status) + "\n", 2, False)
305 write_step(logger, _("Building application"), level=2)
306 cf = create_config_file(config, l_SALOME_modules, env_files, logger)
308 # create the application directory
309 os.makedirs(appli_dir)
311 # generate the application
312 status = src.KO_STATUS
314 retcode = generate_application(config, appli_dir, cf, logger)
315 customize_app(config, appli_dir, logger)
316 status = src.OK_STATUS
318 logger.write(src.printcolors.printc(status) + "\n", 2, False)
320 # copy the catalog if one
322 shutil.copy(catalog, os.path.join(appli_dir, "CatalogResources.xml"))
329 # Generates the catalog from a list of machines.
330 def generate_catalog(machines, config, logger):
331 # remove empty machines
332 machines = map(lambda l: l.strip(), machines)
333 machines = filter(lambda l: len(l) > 0, machines)
335 src.printcolors.print_value(logger,
336 _("Generate Resources Catalog"),
339 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
340 user = getpass.getuser()
342 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
343 with open(catfile, 'w') as catalog:
344 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
346 if not src.architecture.is_windows():
347 logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4)
350 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
351 p = subprocess.Popen(ssh_cmd, shell=True,
352 stdin=subprocess.PIPE,
353 stdout=subprocess.PIPE,
354 stderr=subprocess.PIPE)
357 machine_access = (p.returncode == 0)
358 if not machine_access:
359 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
360 logger.write(" " + src.printcolors.printcWarning(p.stderr.read()),
363 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
364 lines = p.stdout.readlines()
365 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
366 nb_proc = len(lines) -1
367 memory = lines[-1].split(':')[-1].split()[0].strip()
368 memory = int(memory) / 1000
370 catalog.write(" <machine\n")
371 catalog.write(" protocol=\"ssh\"\n")
372 catalog.write(" nbOfNodes=\"1\"\n")
373 catalog.write(" mode=\"interactif\"\n")
374 catalog.write(" OS=\"LINUX\"\n")
376 if (not src.architecture.is_windows()) and machine_access:
377 catalog.write(" CPUFreqMHz=\"%s\"\n" % freq)
378 catalog.write(" nbOfProcPerNode=\"%s\"\n" % nb_proc)
379 catalog.write(" memInMB=\"%s\"\n" % memory)
381 catalog.write(" userName=\"%s\"\n" % user)
382 catalog.write(" name=\"%s\"\n" % k)
383 catalog.write(" hostname=\"%s\"\n" % k)
384 catalog.write(" >\n")
385 catalog.write(" </machine>\n")
387 catalog.write("</resources>\n")
390 ##################################################
393 # Describes the command
395 '''method that is called when salomeTools is called with --help option.
397 :return: The text to display for the application command description.
400 return _("The application command creates a SALOME application.\n"
401 "WARNING: it works only for SALOME 6. Use the \"launcher\" "
402 "command for newer versions of SALOME\n\nexample:\nsat application"
407 def run(args, runner, logger):
408 '''method that is called when salomeTools is called with application
412 (options, args) = parser.parse_args(args)
415 src.check_config_has_application( runner.cfg )
417 application = src.printcolors.printcLabel(runner.cfg.VARS.application)
418 logger.write(_("Building application for %s\n") % application, 1)
420 # if section APPLICATION.virtual_app does not exists create one
421 if "virtual_app" not in runner.cfg.APPLICATION:
422 msg = _("The section APPLICATION.virtual_app is not defined in the product. Use sat launcher in state")
423 logger.write(src.printcolors.printcError(msg), 1)
424 logger.write("\n", 1)
427 # get application dir
428 target_dir = runner.cfg.APPLICATION.workdir
430 target_dir = options.target
432 # set list of modules
434 runner.cfg.APPLICATION.virtual_app['modules'] = options.modules
436 # activate mesa use in the generated application
438 src.activate_mesa_property(runner.cfg)
440 # set name and application_name
442 runner.cfg.APPLICATION.virtual_app['name'] = options.name
443 runner.cfg.APPLICATION.virtual_app['application_name'] = options.name + "_appdir"
445 application_name = src.get_cfg_param(runner.cfg.APPLICATION.virtual_app,
447 runner.cfg.APPLICATION.virtual_app.name + "_appdir")
448 appli_dir = os.path.join(target_dir, application_name)
450 src.printcolors.print_value(logger,
451 _("Application directory"),
456 catalog, catalog_src = "", ""
458 # use catalog specified in the command line
459 catalog = options.catalog
461 # generate catalog for given list of computers
462 catalog_src = options.gencat
463 catalog = generate_catalog(options.gencat.split(","),
465 elif 'catalog' in runner.cfg.APPLICATION.virtual_app:
466 # use catalog specified in the product
467 if runner.cfg.APPLICATION.virtual_app.catalog.endswith(".xml"):
469 catalog = runner.cfg.APPLICATION.virtual_app.catalog
471 # catalog as a list of computers
472 catalog_src = runner.cfg.APPLICATION.virtual_app.catalog
473 mlist = filter(lambda l: len(l.strip()) > 0,
474 runner.cfg.APPLICATION.virtual_app.catalog.split(","))
476 catalog = generate_catalog(runner.cfg.APPLICATION.virtual_app.catalog.split(","),
479 # display which catalog is used
481 catalog = os.path.realpath(catalog)
482 if len(catalog_src) > 0:
483 src.printcolors.print_value(logger,
484 _("Resources Catalog"),
488 src.printcolors.print_value(logger,
489 _("Resources Catalog"),
493 logger.write("\n", 3, False)
497 # remove previous application
498 if os.path.exists(appli_dir):
499 write_step(logger, _("Removing previous application directory"))
502 shutil.rmtree(appli_dir)
505 logger.write(src.printcolors.printc(rres) + "\n", 3, False)
507 # generate the application
509 try: # try/except/finally not supported in all version of python
510 retcode = create_application(runner.cfg, appli_dir, catalog, logger)
511 except Exception as exc:
512 details.append(str(exc))
515 logger.write("\n", 3, False)