Salome HOME
sat #32302 pip option --build obsolète : integration du patch fourni par Nabil
[tools/sat.git] / commands / launcher.py
index fa65d6d29605e7b6cd189f2a5e6ac11131eee3d9..ea7cc82136738a343d63a2ecde9c1becc34f72f4 100644 (file)
@@ -24,77 +24,141 @@ import subprocess
 import stat
 
 import src
+import src.debug as DBG
 
 parser = src.options.Options()
 
 parser.add_option('n', 'name', 'string', 'name', _('Optional: The name of the'
-                            ' launcher (default is '
-                            'APPLICATION.profile.launcher_name)'))
+                  ' launcher (default is APPLICATION.profile.launcher_name)'))
+parser.add_option('e', 'exe', 'string', 'path_exe', _('Use this option to generate a launcher which sets'
+                  ' the environment and call the executable given as argument'
+                  ' (its relative path to application workdir, or an exe name present in appli PATH)'))
+parser.add_option('p', 'products', 'list2', 'products',
+    _("Optional: Includes only the specified products."))
 parser.add_option('c', 'catalog', 'string', 'catalog',
-    _('Optional: The resources catalog to use'))
+                  _('Optional: The resources catalog to use'))
 parser.add_option('', 'gencat', 'string', 'gencat',
-    _("Optional: Create a resources catalog for the specified machines "
-      "(separated with ',') \n\tNOTICE: this command will ssh to retrieve"
-      " information to each machine in the list"))
+                  _("Optional: Create a resources catalog for the specified machines "
+                  "(separated with ',') \n\tNOTICE: this command will ssh to retrieve"
+                  " information to each machine in the list"))
+parser.add_option('', 'use_mesa', 'boolean', 'use_mesa',
+                  _("Optional: Create a launcher that will use mesa products\n\t"
+                  "It can be usefull whan salome is used on a remote machine through ssh"))
+parser.add_option('', 'no_path_init', 'boolean', 'no_path_init',
+                 _("Optional: Create a launcher that will not reinitilise all path variables\n\t"
+                   "By default only PATH is not reinitialised (its value is inherited from "
+                   "user's environment)\n\tUse no_path_init option to suppress the reinitilisation"
+                   " of every paths (LD_LIBRARY_PATH, PYTHONPATH, ...)"))
+
 
 def generate_launch_file(config,
                          logger,
-                         env_info=None,
-                         pathlauncher=None,
+                         launcher_name,
+                         pathlauncher,
+                         path_exe,
+                         env_info,
                          display=True,
-                         additional_env={}):
+                         additional_env={},
+                         no_path_init=False):
     '''Generates the launcher file.
     
     :param config Config: The global configuration
     :param logger Logger: The logger instance to use for the display 
                           and logging
-    :param env_info str: The list of products to add in the files.
+    :param launcher_name str: The name of the launcher to generate
+    :param path_exe str: The executable to use (either a relative path to 
+                         application workdir, or an exe name in the path)
     :param pathlauncher str: The path to the launcher to generate
-    :param src_root str: The path to the directory where the sources are
     :param display boolean: If False, do not print anything in the terminal
     :param additional_env dict: The dict giving additional 
                                 environment variables
+    :param env_info str: The list of products to add in the files.
     :return: The launcher file path.
     :rtype: str
     '''
-    # Get the application directory and the profile directory
-    out_dir = config.APPLICATION.workdir
-    profile = config.APPLICATION.profile
-    profile_install_dir = get_profile_dir(config)
-    
-    # Compute the default launcher path if it is not provided in pathlauncher
-    # parameter
-    if pathlauncher is None:
-        if platform.system() == "Windows" :
-            filepath = os.path.join( os.path.join( profile_install_dir,
-                                                   'bin',
-                                                   'salome' ),
-                                     profile['launcher_name'] )
-        else:
-            filepath = os.path.join( out_dir,
-                                     profile['launcher_name'] )
-    else:
-        filepath = os.path.join(pathlauncher, profile['launcher_name'])
-
-    # Remove the file if it exists in order to replace it
+    # build the launcher path, delete it if it exists
+    filepath = os.path.join(pathlauncher, launcher_name)
     if os.path.exists(filepath):
         os.remove(filepath)
+    kernel_root_dir=None
+    cmd=None
+    salome_application_name=None
+    app_root_dir=None
 
-    # Add the APPLI variable
-    additional_env['APPLI'] = filepath
+    if path_exe:
+        #case of a launcher based upon an executable
+        
+        if os.path.basename(path_exe) != path_exe:
+            # for a relative (to workdir) path 
+            # build absolute path of exe and check it
+            exepath=os.path.join(config.APPLICATION.workdir, path_exe)
+            if not os.path.exists(exepath):
+                raise src.SatException(_("cannot find executable given : %s" % exepath))
+        else:
+            exepath=path_exe 
+
+        # select the shell for the launcher (bast/bat)
+        # and build the command used to launch the exe
+        if src.architecture.is_windows():
+            shell="bat"
+            cmd="\n\nrem Launch exe with user arguments\n%s " % exepath + "%*"
+        else:
+            shell="bash"
+            cmd='\n\n# Launch exe with user arguments\n%s "$*"' % exepath
 
-    # Get the launcher template
-    withProfile = src.fileEnviron.withProfile.replace( "PROFILE_INSTALL_DIR",
-                                                       profile_install_dir )
-    before, after = withProfile.split(
-                                "# here your local standalone environment\n")
+    else:
+        #case of a salome python2/3 launcher
+        shell="cfgForPy"
+
+        # get KERNEL bin installation path 
+        # (in order for the launcher to get python salomeContext API)
+        kernel_cfg = src.product.get_product_config(config, "KERNEL")
+        if not src.product.check_installation(config, kernel_cfg):
+            raise src.SatException(_("KERNEL is not installed"))
+        kernel_root_dir = kernel_cfg.install_dir
+        # set kernel bin dir (considering fhs property)
+        if src.get_property_in_product_cfg(kernel_cfg, "fhs"):
+            bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin") 
+        else:
+            bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome") 
+
+        # Add two sat variables used by fileEnviron to choose the right launcher header 
+        # and do substitutions
+        additional_env['sat_bin_kernel_install_dir'] = bin_kernel_install_dir
+        if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
+            additional_env['sat_python_version'] = 3
+        else:
+            additional_env['sat_python_version'] = 2
+
+    # check if the application contains an application module
+    l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(),
+                                                    config)
+    for prod_name, prod_info in l_product_info:
+        # look for a salome application
+        if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
+            # if user choose -p option (env_info not None), the set appli name only if product was selected.
+            if env_info == None or ( prod_name in env_info):
+                salome_application_name=prod_info.install_dir
+            continue
+
+    # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it.
+    # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
+    if salome_application_name:
+        app_root_dir=salome_application_name
+    elif kernel_root_dir:
+        app_root_dir=kernel_root_dir
+
+    # Add the APPLI and ABSOLUTE_APPLI_PATH variable
+    additional_env['APPLI'] = filepath
+    if app_root_dir:
+        additional_env['ABSOLUTE_APPLI_PATH'] = app_root_dir
 
     # create an environment file writer
     writer = src.environment.FileEnvWriter(config,
                                            logger,
-                                           out_dir,
-                                           src_root=None,
-                                           env_info=env_info)
+                                           pathlauncher,
+                                           None,
+                                           env_info)
 
     # Display some information
     if display:
@@ -103,14 +167,19 @@ def generate_launch_file(config,
                      src.printcolors.printcLabel(config.VARS.application), 1)
         logger.write("  %s\n" % src.printcolors.printcLabel(filepath), 1)
     
-    # open the file and write into it
-    launch_file = open(filepath, "w")
-    launch_file.write(before)
-    # Write
-    writer.write_cfgForPy_file(launch_file, additional_env=additional_env)
-    launch_file.write(after)
-    launch_file.close()
+    # Write the launcher
+    writer.write_env_file(filepath, 
+                          False,  # for launch
+                          shell,
+                          additional_env=additional_env,
+                          no_path_init=no_path_init)
     
+
+    # ... and append the launch of the exe 
+    if cmd:
+        with open(filepath, "a") as exe_launcher:
+            exe_launcher.write(cmd)
+
     # change the rights in order to make the file executable for everybody
     os.chmod(filepath,
              stat.S_IRUSR |
@@ -122,89 +191,6 @@ def generate_launch_file(config,
              stat.S_IXOTH)
     return filepath
 
-def generate_launch_link(config,
-                         logger,
-                         launcherPath,
-                         pathlauncher=None,
-                         display=True,
-                         packageLauncher=False):
-    '''Generates the launcher link that sources Python 
-       and call the actual launcher.
-    
-    :param config Config: The global configuration
-    :param logger Logger: The logger instance to use for the display 
-                          and logging
-    :param launcherPath str: The path to the launcher to call
-    :param pathlauncher str: The path to the launcher (link) to generate
-    :param display boolean: If False, do not print anything in the terminal
-    :param packageLauncher boolean: if True, use a relative path (for package)
-    :return: The launcher link file path.
-    :rtype: str
-    '''
-    if pathlauncher is None:
-        # Make an executable file that sources python, then launch the launcher
-        # produced by generate_launch_file method
-        sourceLauncher = os.path.join(config.APPLICATION.workdir,
-                                      config.APPLICATION.profile.launcher_name)
-    else:
-        sourceLauncher = os.path.join(pathlauncher,
-                                      config.APPLICATION.profile.launcher_name)
-
-    # Change the extension for the windows case
-    if platform.system() == "Windows" :
-            sourceLauncher += '.bat'
-
-    # display some information
-    if display:
-        logger.write(_("\nGenerating the executable that sources"
-                       " python and runs the launcher :\n") , 1)
-        logger.write("  %s\n" %src.printcolors.printcLabel(sourceLauncher), 1)
-
-    # open the file to write
-    f = open(sourceLauncher, "w")
-
-    # Write the set up of the environment
-    if platform.system() == "Windows" :
-        shell = 'bat'
-    else:
-        shell = 'bash'
-        
-    # Write the Python environment files
-    env = src.environment.SalomeEnviron( config, 
-                        src.fileEnviron.get_file_environ( f, shell, config ) )
-    env.set_a_product( "Python", logger)
-
-    # Write the call to the original launcher
-    f.write( "\n\n")
-    if packageLauncher:
-        cmd = os.path.join('${out_dir_Path}', launcherPath)
-    else:
-        cmd = launcherPath
-
-    if platform.system() == "Windows" :
-        cmd = 'python ' + cmd + ' %*'
-    else:
-        cmd = cmd + ' $*'
-    
-    f.write( cmd )
-    f.write( "\n\n")
-
-    # Write the cleaning of the environment
-    env.finish(True)
-
-    # Close new launcher
-    f.close()
-    os.chmod(sourceLauncher,
-             stat.S_IRUSR |
-             stat.S_IRGRP |
-             stat.S_IROTH |
-             stat.S_IWUSR |
-             stat.S_IWGRP |
-             stat.S_IWOTH |
-             stat.S_IXUSR |
-             stat.S_IXGRP |
-             stat.S_IXOTH)
-    return sourceLauncher
 
 def generate_catalog(machines, config, logger):
     """Generates an xml catalog file from a list of machines.
@@ -230,52 +216,55 @@ def generate_catalog(machines, config, logger):
 
     # Create the catalog path
     catfile = src.get_tmp_filename(config, "CatalogResources.xml")
-    catalog = file(catfile, "w")
-    
-    # Write into it
-    catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
-    for k in machines:
-        logger.write("    ssh %s " % (k + " ").ljust(20, '.'), 4)
-        logger.flush()
-
-        # Verify that the machine is accessible
-        ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
-        p = subprocess.Popen(ssh_cmd, shell=True,
-                stdin=subprocess.PIPE,
-                stdout=subprocess.PIPE,
-                stderr=subprocess.PIPE)
-        p.wait()
-
-        if p.returncode != 0: # The machine is not accessible
-            logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
-            logger.write("    " + 
-                         src.printcolors.printcWarning(p.stderr.read()), 2)
-        else:
-            # The machine is accessible, write the corresponding section on
-            # the xml file
-            logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
-            lines = p.stdout.readlines()
-            freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
-            nb_proc = len(lines) -1
-            memory = lines[-1].split(':')[-1].split()[0].strip()
-            memory = int(memory) / 1000
+    with open(catfile, 'w') as catalog:
+        # Write into it
+        catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
+        for k in machines:
+            if not src.architecture.is_windows(): 
+                logger.write("    ssh %s " % (k + " ").ljust(20, '.'), 4)
+                logger.flush()
+
+                # Verify that the machine is accessible
+                ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
+                p = subprocess.Popen(ssh_cmd, shell=True,
+                        stdin=subprocess.PIPE,
+                        stdout=subprocess.PIPE,
+                        stderr=subprocess.PIPE)
+                p.wait()
+
+                machine_access = (p.returncode == 0) 
+                if not machine_access: # The machine is not accessible
+                    logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
+                    logger.write("    " + 
+                                 src.printcolors.printcWarning(p.stderr.read()), 2)
+                else:
+                    # The machine is accessible, write the corresponding section on
+                    # the xml file
+                    logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
+                    lines = p.stdout.readlines()
+                    freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
+                    nb_proc = len(lines) -1
+                    memory = lines[-1].split(':')[-1].split()[0].strip()
+                    memory = int(memory) / 1000
 
             catalog.write("    <machine\n")
             catalog.write("        protocol=\"ssh\"\n")
             catalog.write("        nbOfNodes=\"1\"\n")
             catalog.write("        mode=\"interactif\"\n")
             catalog.write("        OS=\"LINUX\"\n")
-            catalog.write("        CPUFreqMHz=\"%s\"\n" % freq)
-            catalog.write("        nbOfProcPerNode=\"%s\"\n" % nb_proc)
-            catalog.write("        memInMB=\"%s\"\n" % memory)
+
+            if (not src.architecture.is_windows()) and machine_access :
+                catalog.write("        CPUFreqMHz=\"%s\"\n" % freq)
+                catalog.write("        nbOfProcPerNode=\"%s\"\n" % nb_proc)
+                catalog.write("        memInMB=\"%s\"\n" % memory)
+
             catalog.write("        userName=\"%s\"\n" % user)
             catalog.write("        name=\"%s\"\n" % k)
             catalog.write("        hostname=\"%s\"\n" % k)
             catalog.write("    >\n")
             catalog.write("    </machine>\n")
 
-    catalog.write("</resources>\n")
-    catalog.close()
+        catalog.write("</resources>\n")
     return catfile
 
 def copy_catalog(config, catalog_path):
@@ -289,65 +278,16 @@ def copy_catalog(config, catalog_path):
     # Verify the existence of the file
     if not os.path.exists(catalog_path):
         raise IOError(_("Catalog not found: %s") % catalog_path)
-    # Compute the location where to copy the file
-    profile_dir = get_profile_dir(config)
-    new_catalog_path = os.path.join(profile_dir, "CatalogResources.xml")
+    # Get the application directory and copy catalog inside
+    out_dir = config.APPLICATION.workdir
+    new_catalog_path = os.path.join(out_dir, "CatalogResources.xml")
     # Do the copy
-    shutil.copy(catalog_path, new_catalog_path)
+    if catalog_path != new_catalog_path:
+        shutil.copy(catalog_path, new_catalog_path)
     additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
     return additional_environ
 
-def get_profile_dir(config):
-    """Get the profile directory from the config
-    
-    :param config Config: The global configuration
-    :return: The profile install directory
-    :rtype: Str
-    """
-    profile_name = config.APPLICATION.profile.product
-    profile_info = src.product.get_product_config(config, profile_name)
-    return profile_info.install_dir
-
-def finish_profile_install(config, launcherPath):
-    """Add some symlinks required for SALOME
-    
-    :param config Config: The global configuration
-    :param launcherPath str: the launcher file path
-    """
-    # Create a USERS directory
-    profile_dir = get_profile_dir(config)
-    user_dir = os.path.join(profile_dir, 'USERS')
-    if not os.path.exists(user_dir):
-        os.makedirs(user_dir)
-    # change rights of USERS directory
-    os.chmod(user_dir,
-             stat.S_IRUSR |
-             stat.S_IRGRP |
-             stat.S_IROTH |
-             stat.S_IWUSR |
-             stat.S_IWGRP |
-             stat.S_IWOTH |
-             stat.S_IXUSR |
-             stat.S_IXGRP |
-             stat.S_IXOTH)
 
-    # create a link in root directory to the launcher
-    if platform.system() != "Windows" :
-        link_path = os.path.join(config.APPLICATION.workdir, 'salome')
-        if not os.path.exists(link_path):
-            try:
-                os.symlink(launcherPath, link_path)
-            except OSError:
-                os.remove(link_path)
-                os.symlink(launcherPath, link_path)
-
-        link_path = os.path.join(profile_dir, 'salome')
-        relativeLauncherPath = "../../salome"
-        try:
-            os.symlink(relativeLauncherPath, link_path)
-        except OSError:
-            os.remove(link_path)
-            os.symlink(relativeLauncherPath, link_path)
 
 ##################################################
 
@@ -367,22 +307,26 @@ def run(args, runner, logger):
     # Verify that the command was called with an application
     src.check_config_has_application( runner.cfg )
     
-    # Verify that the APPLICATION section has a profile section
-    src.check_config_has_profile( runner.cfg )
-
-    # Verify that the profile is installed
-    if not src.product.check_installation(
-                                src.product.get_product_config(
-                                    runner.cfg,
-                                    runner.cfg.APPLICATION.profile.product)):
-        msg = _("The profile of the application is not correctly installed.")
-        logger.write(src.printcolors.printcError(msg), 1)
-        return 1
-
-    # Change the name of the file to create 
-    # if the corresponding option was called
+    # Determine the launcher name (from option, profile section or by default "salome")
+    if options.products is None:
+        environ_info = None
+    else:
+        # add products specified by user (only products 
+        # included in the application)
+        environ_info = filter(lambda l:
+                              l in runner.cfg.APPLICATION.products.keys(),
+                              options.products)
     if options.name:
-        runner.cfg.APPLICATION.profile['launcher_name'] = options.name
+        launcher_name = options.name
+    else:
+        launcher_name = src.get_launcher_name(runner.cfg)
+
+    no_path_initialisation=False
+    if options.no_path_init:
+        no_path_initialisation = True
+        
+    # set the launcher path
+    launcher_path = runner.cfg.APPLICATION.workdir
 
     # Copy a catalog if the option is called
     additional_environ = {}
@@ -396,17 +340,22 @@ def run(args, runner, logger):
                                          logger)
         additional_environ = copy_catalog(runner.cfg, catalog_path)
 
-    # Generate the launcher
-    launcherPath = generate_launch_file( runner.cfg,
-                                         logger,
-                                         additional_env = additional_environ )
+    # activate mesa use in the generated launcher
+    if options.use_mesa:
+        src.activate_mesa_property(runner.cfg)
 
-    if platform.system() == "Windows" :
-        # Create the link (bash file that sources python and then call 
-        # the actual launcher) to the launcher
-        generate_launch_link( runner.cfg, logger, launcherPath)
+    # option -e has precedence over section profile
+    if not options.path_exe and src.get_launcher_exe(runner.cfg):
+        options.path_exe=src.get_launcher_exe(runner.cfg)
 
-    # Add some links
-    finish_profile_install(runner.cfg, launcherPath)
+    # Generate the launcher
+    generate_launch_file(runner.cfg,
+                         logger,
+                         launcher_name,
+                         launcher_path,
+                         options.path_exe,
+                         additional_env = additional_environ,
+                         env_info=environ_info,
+                         no_path_init = no_path_initialisation )
 
     return 0