Salome HOME
add the generate command
authorSerge Rehbinder <serge.rehbinder@cea.fr>
Mon, 12 Sep 2016 09:03:57 +0000 (11:03 +0200)
committerSerge Rehbinder <serge.rehbinder@cea.fr>
Mon, 12 Sep 2016 09:03:57 +0000 (11:03 +0200)
commands/generate.py [new file with mode: 0644]
complete_sat.sh
src/product.py

diff --git a/commands/generate.py b/commands/generate.py
new file mode 100644 (file)
index 0000000..a08a4b3
--- /dev/null
@@ -0,0 +1,384 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+#  Copyright (C) 2010-2013  CEA/DEN
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+import os
+import sys
+import shutil
+import imp
+import subprocess
+
+import src
+
+parser = src.options.Options()
+parser.add_option('p', 'products', 'list2', 'products',
+                  _("the list of products to generate"))
+parser.add_option('', 'yacsgen', 'string', 'yacsgen',
+                  _("path to YACSGEN's module_generator package"))
+
+def generate_component_list(config, product_info, context, logger):
+    res = "?"
+    logger.write("\n", 3)
+    for compo in src.product.get_product_components(product_info):
+        header = "  %s %s " % (src.printcolors.printcLabel(compo),
+                               "." * (20 - len(compo)))
+        res = generate_component(config,
+                                 compo,
+                                 product_info,
+                                 context,
+                                 header,
+                                 logger)
+        if config.USER.output_verbose_level == 3:
+            logger.write("\r%s%s\r%s" % (header, " " * 20, header), 3)
+        logger.write(src.printcolors.printc(res), 3, False)
+        logger.write("\n", 3, False)
+    return res
+
+def generate_component(config, compo, product_info, context, header, logger):
+    hxxfile = compo + ".hxx"
+    cpplib = "lib" + compo + "CXX.so"
+    cpp_path = product_info.install_dir
+
+    logger.write("%s\n" % header, 4, False)
+    src.printcolors.print_value(logger, "hxxfile", hxxfile, 4)
+    src.printcolors.print_value(logger, "cpplib", cpplib, 4)
+    src.printcolors.print_value(logger, "cpp_path", cpp_path, 4)
+
+    # create a product_info at runtime
+    pp = src.pyconf.Mapping(config)
+    pp.name = compo
+    pp.nb_proc = 1
+    generate_dir = os.path.join(config.APPLICATION.workdir, "GENERATED")
+    install_dir = os.path.join(config.APPLICATION.workdir, "INSTALL")
+    build_dir = os.path.join(config.APPLICATION.workdir, "BUILD")
+    pp.source_dir = os.path.join(generate_dir, compo + "_SRC")
+    pp.install_dir = os.path.join(install_dir, compo)
+    pp.build_dir = os.path.join(build_dir, compo)
+    pp.depend = product_info.depend
+    pp.depend.append(product_info.name, "") # add cpp module
+    pp.opt_depend = product_info.opt_depend
+
+    config.PRODUCTS[compo].default = pp
+
+    builder = src.compilation.Builder(config, logger, pp, check_src=False)
+    builder.header = header
+
+    # generate the component
+    # create GENERETE dir if necessary
+    if not os.path.exists(generate_dir):
+        os.mkdir(generate_dir)
+
+    # delete previous generated directory if it already exists
+    if os.path.exists(pp.source_dir):
+        logger.write("  delete %s\n" % pp.source_dir, 4)
+        shutil.rmtree(pp.source_dir)
+
+    # generate generates in the current directory => change for generate dir
+    curdir = os.curdir
+    os.chdir(generate_dir)
+
+    # inline class to override bootstrap method
+    import module_generator
+    class sat_generator(module_generator.Generator):
+        # old bootstrap for automake (used if salome version <= 7.4)
+        def bootstrap(self, source_dir, log_file):
+            # replace call to default bootstrap() by using subprocess call (cleaner)
+            command = "sh autogen.sh"
+            ier = subprocess.call(command, shell=True, cwd=source_dir,
+                                  stdout=log_file, stderr=subprocess.STDOUT)
+            if ier != 0:
+                raise src.SatException("bootstrap has ended in error")
+
+    
+    # determine salome version
+    VersionSalome = src.get_salome_version(config)
+    if VersionSalome >= 750 :
+        use_autotools=False
+        builder.log_step('USE CMAKE')
+    else:
+        use_autotools=True
+        builder.log_step('USE AUTOTOOLS')
+
+    result = "GENERATE"
+    builder.log_step('GENERATE')
+
+    prevstdout = sys.stdout
+    prevstderr = sys.stderr
+
+    try:
+        sys.stdout = logger.logTxtFile
+        sys.stderr = logger.logTxtFile
+
+        if src.product.product_is_mpi(product_info):
+            salome_compo = module_generator.HXX2SALOMEParaComponent(hxxfile,
+                                                                    cpplib,
+                                                                    cpp_path)
+        else:
+            salome_compo = module_generator.HXX2SALOMEComponent(hxxfile,
+                                                                cpplib,
+                                                                cpp_path)
+
+        if product_info.has_gui == "yes":
+            # get files to build a template GUI
+            gui_files = salome_compo.getGUIfilesTemplate()
+        else:
+            gui_files = None
+
+        mg = module_generator.Module(compo, components=[salome_compo],
+                                     prefix=generate_dir, gui=gui_files)
+        g = sat_generator(mg, context)
+        g.generate()
+
+        if use_autotools:
+            result = "BUID_CONFIGURE"
+            builder.log_step('BUID_CONFIGURE (no bootstrap)')
+            g.bootstrap(pp.source_dir, logger.logTxtFile)
+
+        result = src.OK_STATUS
+    finally:
+        sys.stdout = prevstdout
+        sys.stderr = prevstderr
+
+    # go back to previous directory
+    os.chdir(curdir)
+
+    # do the compilation using the builder object
+    if not builder.prepare(): return "Error in prepare"
+    if use_autotools:
+        if not builder.configure(): return "Error in configure"
+    else:
+        if not builder.cmake(): return "Error in cmake"
+
+    if not builder.make(): return "Error in make"
+    if not builder.install(): return "Error in make install"
+
+    # copy specified logo in generated component install directory
+    # rem : logo is not copied in source dir because this would require
+    #       to modify the generated makefile
+    if "logo" in product_info:
+        destlogo = os.path.join(pp.install_dir, "share", "salome",
+            "resources", compo.lower(), compo + ".png")
+        src.Path(product_info.logo).copyfile(destlogo)
+
+    return result
+
+def build_context(config, logger):
+    environ_info = {}
+    products_list = [ 'KERNEL', 'GUI' ]
+    environ_info['products'] = config.APPLICATION.products
+    ctxenv = src.environment.SalomeEnviron(config,
+                                           src.environment.Environ(dict(
+                                                                   os.environ)),
+                                           True)
+    ctxenv.silent = True
+    ctxenv.set_full_environ(logger, environ_info)
+
+    dicdir = {}
+    for p in products_list:
+        prod_env = p + "_ROOT_DIR"
+        val = os.getenv(prod_env)
+        if os.getenv(prod_env) is None:
+            if p not in config.APPLICATION.products:
+                warn = _("product %(product)s is not defined. Include it in the"
+                         " application or define $%(env)s.") % \
+                    { "product": p, "env": prod_env}
+                logger.write(src.printcolors.printcWarning(warn), 1)
+                logger.write("\n", 3, False)
+                val = ""
+            val = ctxenv.environ.environ[prod_env]
+        dicdir[p] = val
+
+    # the dictionary requires all keys 
+    # but the generation requires only values for KERNEL and GUI
+    context = {
+        "update": 1,
+        "makeflags": "-j2",
+        "kernel": dicdir["KERNEL"],
+        "gui":    dicdir["GUI"],
+        "yacs":   "",
+        "med":    "",
+        "mesh":   "",
+        "visu":   "",
+        "geom":   "",
+    }
+    return context
+
+def check_module_generator(directory=None):
+    """Check if module_generator is available.
+    
+    :param directory str: The directory of YACSGEN.
+    :return: The YACSGEN path if the module_generator is available, else None
+    :rtype: str
+    """
+    undo = False
+    if directory is not None and directory not in sys.path:
+        sys.path.insert(0, dir)
+        undo = True
+    
+    res = None
+    try:
+        #import module_generator
+        info = imp.find_module("module_generator")
+        res = info[1]
+    except ImportError:
+        if undo:
+            sys.path.remove(dir)
+        res = None
+
+    return res
+
+def check_yacsgen(config, directory, logger):
+    """Check if YACSGEN is available.
+    
+    :param config Config: The global configuration.
+    :param directory str: The directory given by option --yacsgen
+    :param logger Logger: The logger instance
+    :return: The path to yacsgen directory
+    :rtype: str
+    """
+    # first check for YACSGEN (command option, then product, then environment)
+    yacsgen_dir = None
+    yacs_src = "?"
+    if directory is not None:
+        yacsgen_dir = directory
+        yacs_src = _("Using YACSGEN form command line")
+    elif 'YACSGEN' in config.APPLICATION.products:
+        yacsgen_info = src.product.get_product_config(config, 'YACSGEN')
+        yacsgen_dir = yacsgen_info.install_dir
+        yacs_src = _("Using YACSGEN form application")
+    elif os.environ.has_key("YACSGEN_ROOT_DIR"):
+        yacsgen_dir = os.getenv("YACSGEN_ROOT_DIR")
+        yacs_src = _("Using YACSGEN form environment")
+
+    if yacsgen_dir is None:
+        return (False, _("The generate command requires YACSGEN."))
+    
+    logger.write("  %s\n" % yacs_src, 2, True)
+    logger.write("  %s\n" % yacsgen_dir, 5, True)
+
+    if not os.path.exists(yacsgen_dir):
+        message = _("YACSGEN directory not found: '%s'") % yacsgen_dir
+        return (False, _(message))
+    
+    # load module_generator
+    c = check_module_generator(yacsgen_dir)
+    if c is not None:
+        return c
+    
+    pv = os.getenv("PYTHON_VERSION")
+    if pv is None:
+        pv = '.'.join(config.PRODUCT.prerequis["Python"].split('.')[:2])
+    assert pv is not None, "$PYTHON_VERSION not defined"
+    yacsgen_dir = os.path.join(yacsgen_dir, "lib", "python%s" % pv,
+                               "site-packages")
+    c = check_module_generator(yacsgen_dir)
+    if c is not None:
+        return c
+
+    return (False,
+            _("The python module mosule_generator was not found in YACSGEN"))
+
+
+def description():
+    '''method that is called when salomeTools is called with --help option.
+    
+    :return: The text to display for the generate command description.
+    :rtype: str
+    '''
+    return _("The generate command generates SALOME modules from 'pure cpp' "
+             "products.\nWARNING this command NEEDS YACSGEN to run!")
+
+
+def run(args, runner, logger):
+    '''method that is called when salomeTools is called with generate parameter.
+    '''
+    
+    # Check that the command has been called with an application
+    src.check_config_has_application(runner.cfg)
+    
+    logger.write(_('Generation of SALOME modules for application %s\n') % \
+        src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
+
+    (options, args) = parser.parse_args(args)
+
+    status = src.KO_STATUS
+
+    # verify that YACSGEN is available
+    yacsgen_dir = check_yacsgen(runner.cfg, options.yacsgen, logger)
+    
+    if isinstance(yacsgen_dir, tuple):
+        # The check failed
+        __, error = yacsgen_dir
+        msg = _("Error: %s" % error)
+        logger.write(src.printcolors.printcError(msg), 1)
+        logger.write("\n", 1)
+        return 1
+    
+    # Make the generator module visible by python
+    sys.path.insert(0, yacsgen_dir)
+
+    src.printcolors.print_value(logger, _("YACSGEN dir"), yacsgen_dir, 3)
+    logger.write("\n", 2)
+    products = runner.cfg.APPLICATION.products
+    if options.products:
+        products = options.products
+
+    details = []
+    nbgen = 0
+
+    context = build_context(runner.cfg, logger)
+    for product in products:
+        header = _("Generating %s") % src.printcolors.printcLabel(product)
+        header += " %s " % ("." * (20 - len(product)))
+        logger.write(header, 3)
+        logger.flush()
+
+        if product not in runner.cfg.PRODUCTS:
+            logger.write(_("Unknown product\n"), 3, False)
+            continue
+
+        pi = src.product.get_product_config(runner.cfg, product)
+        if not src.product.product_is_generated(pi):
+            logger.write(_("not a generated product\n"), 3, False)
+            continue
+
+        nbgen += 1
+        try:
+            result = generate_component_list(runner.cfg,
+                                             pi,
+                                             context,
+                                             logger)
+        except Exception as exc:
+            result = str(exc)
+
+        if result != src.OK_STATUS:
+            result = _("ERROR: %s") % result
+            details.append([product, result])
+
+    if len(details) == 0:
+        status = src.OK_STATUS
+    else: #if config.USER.output_level != 3:
+        logger.write("\n", 2, False)
+        logger.write(_("The following modules were not generated correctly:\n"), 2)
+        for d in details:
+            logger.write("  %s: %s\n" % (d[0], d[1]), 2, False)
+    logger.write("\n", 2, False)
+
+    if status == src.OK_STATUS:
+        return 0
+    return len(details)
+
index 3c3ee31763fd180bd5ecfe4ff2bf48ed04f0806c..d849448d7876bdb2863f2feffb5a48c73b0a4234 100755 (executable)
@@ -95,7 +95,7 @@ _salomeTools_complete()
     # first argument => show available commands
     if [[ ${argc} == 1 ]]
     then
-        opts="config log testcommand source patch prepare environ clean configure make makeinstall compile launcher run jobs job shell test package --help"
+        opts="config log testcommand source patch prepare environ clean configure make makeinstall compile launcher run jobs job shell test package generate --help"
         COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
         return 0
     fi
@@ -229,6 +229,11 @@ _salomeTools_complete()
             COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
             return 0
             ;;
+        generate)
+            opts="--products --yacsgen"
+            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            return 0
+            ;;
         *) return 0 ;;
     esac
     
index 6a674de733f354b02b122128260996750dcbf5f1..69505607f5dfb7cd803d6fc70465f4847292dc39 100644 (file)
@@ -575,4 +575,47 @@ def product_has_patches(product_info):
     :return: True if the product has one or more patches
     :rtype: boolean
     '''
-    return "patches" in product_info and len(product_info.patches) > 0
\ No newline at end of file
+    return "patches" in product_info and len(product_info.patches) > 0
+
+def product_is_mpi(product_info):
+    '''Know if a product has openmpi in its dependencies
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :return: True if the product has openmpi inits dependencies
+    :rtype: boolean
+    '''
+    return "openmpi" in product_info.depend
+
+def product_is_generated(product_info):
+    '''Know if a product is generated (YACSGEN)
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :return: True if the product is generated
+    :rtype: boolean
+    '''
+    return ("properties" in product_info and
+            "generated" in product_info.properties and
+            product_info.properties.generated == "yes")
+
+def get_product_components(product_info):
+    '''Get the component list to generate with the product
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :return: The list of names of the components
+    :rtype: List
+    
+    '''
+    if not product_is_generated(product_info):
+        return []
+    
+    compo_list = []
+    if "component_name" in product_info:
+        compo_list = product_info.component_name
+    
+        if isinstance(compo_list, str):
+            compo_list = [ compo_list ]
+
+    return compo_list
\ No newline at end of file