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
27 import src.debug as DBG
29 parser = src.options.Options()
31 parser.add_option('n', 'name', 'string', 'name', _('Optional: The name of the'
32 ' launcher (default is APPLICATION.profile.launcher_name)'))
33 parser.add_option('e', 'exe', 'string', 'path_exe', _('Use this option to generate a launcher which sets'
34 ' the environment and call the executable given as argument (its relative path)'))
35 parser.add_option('c', 'catalog', 'string', 'catalog',
36 _('Optional: The resources catalog to use'))
37 parser.add_option('', 'gencat', 'string', 'gencat',
38 _("Optional: Create a resources catalog for the specified machines "
39 "(separated with ',') \n\tNOTICE: this command will ssh to retrieve"
40 " information to each machine in the list"))
41 parser.add_option('', 'use_mesa', 'boolean', 'use_mesa',
42 _("Optional: Create a launcher that will use mesa products\n\t"
43 "It can be usefull whan salome is used on a remote machine through ssh"))
44 parser.add_option('', 'no_path_init', 'boolean', 'no_path_init',
45 _("Optional: Create a launcher that will not reinitilise all path variables\n\t"
46 "By default only PATH is not reinitialised (its value is inherited from "
47 "user's environment)\n\tUse no_path_init option to suppress the reinitilisation"
48 " of every paths (LD_LIBRARY_PATH, PYTHONPATH, ...)"))
50 def generate_exe_launch_file(config,
58 '''Generates the launcher file.
60 :param config Config: The global configuration
61 :param logger Logger: The logger instance to use for the display
63 :param launcher_name str: The name of the launcher to generate
64 :param pathlauncher str: The path to the launcher to generate
65 :param path_exe str: The path of the executable to use
66 :param display boolean: If False, do not print anything in the terminal
67 :param additional_env dict: The dict giving additional
69 :return: The launcher file path.
72 # build absolute path of exe and check it
73 exepath=os.path.join(config.APPLICATION.workdir, path_exe)
74 if not os.path.exists(exepath):
75 raise src.SatException(_("cannot find executable given : %s" % exepath))
77 # build the launcher path, delete it if it exists
78 filepath = os.path.join(pathlauncher, launcher_name)
79 if os.path.exists(filepath):
82 # select the shell for the launcher (bast/bat)
83 # and build the command used to launch the exe
84 if src.architecture.is_windows():
86 cmd="\n\nrem Launch exe with user arguments\n%s " % exepath + "%*"
89 cmd="\n\n# Launch exe with user arguments\n%s $*" % exepath
91 # the writer to generate the launcher
92 writer = src.environment.FileEnvWriter(config,
98 # Display some information
100 logger.write(_("Generating exe launcher for %s :\n") %
101 src.printcolors.printcLabel(config.VARS.application), 1)
102 logger.write(" %s\n" % src.printcolors.printcLabel(filepath), 1)
104 # Write the environment in the launcher...
105 writer.write_env_file(filepath,
108 additional_env=additional_env,
109 no_path_init=no_path_init)
111 # ... and append the launch of the exe
112 with open(filepath, "a") as exe_launcher:
113 exe_launcher.write(cmd)
115 # change the rights in order to make the file executable for everybody
128 def generate_launch_file(config,
135 '''Generates the launcher file.
137 :param config Config: The global configuration
138 :param logger Logger: The logger instance to use for the display
140 :param launcher_name str: The name of the launcher to generate
141 :param pathlauncher str: The path to the launcher to generate
142 :param display boolean: If False, do not print anything in the terminal
143 :param additional_env dict: The dict giving additional
144 environment variables
145 :return: The launcher file path.
148 # Compute the default launcher path if it is not provided in pathlauncher
150 filepath = os.path.join(pathlauncher, launcher_name)
152 # Remove the file if it exists in order to replace it
153 if os.path.exists(filepath):
157 # get KERNEL bin installation path
158 # (in order for the launcher to get python salomeContext API)
159 kernel_cfg = src.product.get_product_config(config, "KERNEL")
160 if not src.product.check_installation(config, kernel_cfg):
161 raise src.SatException(_("KERNEL is not installed"))
162 kernel_root_dir = kernel_cfg.install_dir
164 # set kernel bin dir (considering fhs property)
165 if src.get_property_in_product_cfg(kernel_cfg, "fhs"):
166 bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin")
168 bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome")
170 # check if the application contains an application module
171 l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(),
173 salome_application_name="Not defined"
174 for prod_name, prod_info in l_product_info:
175 # look for a salome application
176 if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
177 salome_application_name=prod_info.install_dir
180 # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it.
181 # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
182 if salome_application_name == "Not defined":
183 app_root_dir=kernel_root_dir
185 app_root_dir=salome_application_name
187 # Add two sat variables used by fileEnviron to choose the right launcher header
188 # and do substitutions
189 additional_env['sat_bin_kernel_install_dir'] = bin_kernel_install_dir
190 if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
191 additional_env['sat_python_version'] = 3
193 additional_env['sat_python_version'] = 2
195 # Add the APPLI and ABSOLUTE_APPLI_PATH variable
196 additional_env['APPLI'] = filepath
197 additional_env['ABSOLUTE_APPLI_PATH'] = app_root_dir
199 # create an environment file writer
200 writer = src.environment.FileEnvWriter(config,
206 # Display some information
208 # Write the launcher file
209 logger.write(_("Generating launcher for %s :\n") %
210 src.printcolors.printcLabel(config.VARS.application), 1)
211 logger.write(" %s\n" % src.printcolors.printcLabel(filepath), 1)
214 writer.write_env_file(filepath,
217 additional_env=additional_env,
218 no_path_init=no_path_init)
220 # change the rights in order to make the file executable for everybody
232 def generate_catalog(machines, config, logger):
233 """Generates an xml catalog file from a list of machines.
235 :param machines List: The list of machines to add in the catalog
236 :param config Config: The global configuration
237 :param logger Logger: The logger instance to use for the display
239 :return: The catalog file path.
242 # remove empty machines
243 machines = map(lambda l: l.strip(), machines)
244 machines = filter(lambda l: len(l) > 0, machines)
247 src.printcolors.print_value(logger, _("Generate Resources Catalog"),
248 ", ".join(machines), 4)
250 # The command to execute on each machine in order to get some information
251 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
252 user = getpass.getuser()
254 # Create the catalog path
255 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
256 catalog = file(catfile, "w")
259 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
261 if not src.architecture.is_windows():
262 logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4)
265 # Verify that the machine is accessible
266 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
267 p = subprocess.Popen(ssh_cmd, shell=True,
268 stdin=subprocess.PIPE,
269 stdout=subprocess.PIPE,
270 stderr=subprocess.PIPE)
273 machine_access = (p.returncode == 0)
274 if not machine_access: # The machine is not accessible
275 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
277 src.printcolors.printcWarning(p.stderr.read()), 2)
279 # The machine is accessible, write the corresponding section on
281 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
282 lines = p.stdout.readlines()
283 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
284 nb_proc = len(lines) -1
285 memory = lines[-1].split(':')[-1].split()[0].strip()
286 memory = int(memory) / 1000
288 catalog.write(" <machine\n")
289 catalog.write(" protocol=\"ssh\"\n")
290 catalog.write(" nbOfNodes=\"1\"\n")
291 catalog.write(" mode=\"interactif\"\n")
292 catalog.write(" OS=\"LINUX\"\n")
294 if (not src.architecture.is_windows()) and machine_access :
295 catalog.write(" CPUFreqMHz=\"%s\"\n" % freq)
296 catalog.write(" nbOfProcPerNode=\"%s\"\n" % nb_proc)
297 catalog.write(" memInMB=\"%s\"\n" % memory)
299 catalog.write(" userName=\"%s\"\n" % user)
300 catalog.write(" name=\"%s\"\n" % k)
301 catalog.write(" hostname=\"%s\"\n" % k)
302 catalog.write(" >\n")
303 catalog.write(" </machine>\n")
305 catalog.write("</resources>\n")
309 def copy_catalog(config, catalog_path):
310 """Copy the xml catalog file into the right location
312 :param config Config: The global configuration
313 :param catalog_path str: the catalog file path
314 :return: The environment dictionary corresponding to the file path.
317 # Verify the existence of the file
318 if not os.path.exists(catalog_path):
319 raise IOError(_("Catalog not found: %s") % catalog_path)
320 # Get the application directory and copy catalog inside
321 out_dir = config.APPLICATION.workdir
322 new_catalog_path = os.path.join(out_dir, "CatalogResources.xml")
324 if catalog_path != new_catalog_path:
325 shutil.copy(catalog_path, new_catalog_path)
326 additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
327 return additional_environ
331 ##################################################
334 # Describes the command
336 return _("The launcher command generates a SALOME launcher.\n\nexample:"
337 "\nsat launcher SALOME-master")
341 def run(args, runner, logger):
344 (options, args) = parser.parse_args(args)
346 # Verify that the command was called with an application
347 src.check_config_has_application( runner.cfg )
349 # Determine the launcher name (from option, profile section or by default "salome")
351 launcher_name = options.name
353 launcher_name = src.get_launcher_name(runner.cfg)
355 no_path_initialisation=False
356 if options.no_path_init:
357 no_path_initialisation = True
359 # set the launcher path
360 launcher_path = runner.cfg.APPLICATION.workdir
362 # Copy a catalog if the option is called
363 additional_environ = {}
365 additional_environ = copy_catalog(runner.cfg, options.catalog)
367 # Generate a catalog of resources if the corresponding option was called
369 catalog_path = generate_catalog(options.gencat.split(","),
372 additional_environ = copy_catalog(runner.cfg, catalog_path)
374 # activate mesa use in the generated launcher
376 src.activate_mesa_property(runner.cfg)
378 # Generate the launcher
380 generate_exe_launch_file(runner.cfg,
385 additional_env = additional_environ,
386 no_path_init = no_path_initialisation )
388 launcherPath = generate_launch_file(runner.cfg,
392 additional_env = additional_environ,
393 no_path_init = no_path_initialisation )