Salome HOME
Do not try to produce a launcher if there is no profile
[tools/sat.git] / commands / generate.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
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.
9 #
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.
14 #
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
18
19 import os
20 import sys
21 import shutil
22 import imp
23 import subprocess
24
25 import src
26
27 parser = src.options.Options()
28 parser.add_option('p', 'products', 'list2', 'products',
29                   _("the list of products to generate"))
30 parser.add_option('', 'yacsgen', 'string', 'yacsgen',
31                   _("path to YACSGEN's module_generator package"))
32
33 def generate_component_list(config, product_info, context, logger):
34     res = "?"
35     logger.write("\n", 3)
36     for compo in src.product.get_product_components(product_info):
37         header = "  %s %s " % (src.printcolors.printcLabel(compo),
38                                "." * (20 - len(compo)))
39         res = generate_component(config,
40                                  compo,
41                                  product_info,
42                                  context,
43                                  header,
44                                  logger)
45         if config.USER.output_verbose_level == 3:
46             logger.write("\r%s%s\r%s" % (header, " " * 20, header), 3)
47         logger.write(src.printcolors.printc(res), 3, False)
48         logger.write("\n", 3, False)
49     return res
50
51 def generate_component(config, compo, product_info, context, header, logger):
52     hxxfile = compo + ".hxx"
53     cpplib = "lib" + compo + "CXX.so"
54     cpp_path = product_info.install_dir
55
56     logger.write("%s\n" % header, 4, False)
57     src.printcolors.print_value(logger, "hxxfile", hxxfile, 4)
58     src.printcolors.print_value(logger, "cpplib", cpplib, 4)
59     src.printcolors.print_value(logger, "cpp_path", cpp_path, 4)
60
61     # create a product_info at runtime
62     pp = src.pyconf.Mapping(config)
63     pp.name = compo
64     pp.nb_proc = 1
65     generate_dir = os.path.join(config.APPLICATION.workdir, "GENERATED")
66     install_dir = os.path.join(config.APPLICATION.workdir, "INSTALL")
67     build_dir = os.path.join(config.APPLICATION.workdir, "BUILD")
68     pp.source_dir = os.path.join(generate_dir, compo + "_SRC")
69     pp.install_dir = os.path.join(install_dir, compo)
70     pp.build_dir = os.path.join(build_dir, compo)
71     pp.depend = product_info.depend
72     pp.depend.append(product_info.name, "") # add cpp module
73     pp.opt_depend = product_info.opt_depend
74
75     config.PRODUCTS[compo].default = pp
76
77     builder = src.compilation.Builder(config, logger, pp, check_src=False)
78     builder.header = header
79
80     # generate the component
81     # create GENERETE dir if necessary
82     if not os.path.exists(generate_dir):
83         os.mkdir(generate_dir)
84
85     # delete previous generated directory if it already exists
86     if os.path.exists(pp.source_dir):
87         logger.write("  delete %s\n" % pp.source_dir, 4)
88         shutil.rmtree(pp.source_dir)
89
90     # generate generates in the current directory => change for generate dir
91     curdir = os.curdir
92     os.chdir(generate_dir)
93
94     # inline class to override bootstrap method
95     import module_generator
96     class sat_generator(module_generator.Generator):
97         # old bootstrap for automake (used if salome version <= 7.4)
98         def bootstrap(self, source_dir, log_file):
99             # replace call to default bootstrap() by using subprocess call (cleaner)
100             command = "sh autogen.sh"
101             ier = subprocess.call(command, shell=True, cwd=source_dir,
102                                   stdout=log_file, stderr=subprocess.STDOUT)
103             if ier != 0:
104                 raise src.SatException("bootstrap has ended in error")
105
106     
107     # determine salome version
108     VersionSalome = src.get_salome_version(config)
109     if VersionSalome >= 750 :
110         use_autotools=False
111         builder.log_step('USE CMAKE')
112     else:
113         use_autotools=True
114         builder.log_step('USE AUTOTOOLS')
115
116     result = "GENERATE"
117     builder.log_step('GENERATE')
118
119     prevstdout = sys.stdout
120     prevstderr = sys.stderr
121
122     try:
123         sys.stdout = logger.logTxtFile
124         sys.stderr = logger.logTxtFile
125
126         if src.product.product_is_mpi(product_info):
127             salome_compo = module_generator.HXX2SALOMEParaComponent(hxxfile,
128                                                                     cpplib,
129                                                                     cpp_path)
130         else:
131             salome_compo = module_generator.HXX2SALOMEComponent(hxxfile,
132                                                                 cpplib,
133                                                                 cpp_path)
134
135         if product_info.has_gui == "yes":
136             # get files to build a template GUI
137             gui_files = salome_compo.getGUIfilesTemplate()
138         else:
139             gui_files = None
140
141         mg = module_generator.Module(compo, components=[salome_compo],
142                                      prefix=generate_dir, gui=gui_files)
143         g = sat_generator(mg, context)
144         g.generate()
145
146         if use_autotools:
147             result = "BUID_CONFIGURE"
148             builder.log_step('BUID_CONFIGURE (no bootstrap)')
149             g.bootstrap(pp.source_dir, logger.logTxtFile)
150
151         result = src.OK_STATUS
152     finally:
153         sys.stdout = prevstdout
154         sys.stderr = prevstderr
155
156     # go back to previous directory
157     os.chdir(curdir)
158
159     # do the compilation using the builder object
160     if not builder.prepare(): return "Error in prepare"
161     if use_autotools:
162         if not builder.configure(): return "Error in configure"
163     else:
164         if not builder.cmake(): return "Error in cmake"
165
166     if not builder.make(): return "Error in make"
167     if not builder.install(): return "Error in make install"
168
169     # copy specified logo in generated component install directory
170     # rem : logo is not copied in source dir because this would require
171     #       to modify the generated makefile
172     logo_path = src.product.product_has_logo(product_info)
173     if logo_path:
174         destlogo = os.path.join(pp.install_dir, "share", "salome",
175             "resources", compo.lower(), compo + ".png")
176         src.Path(logo_path).copyfile(destlogo)
177
178     return result
179
180 def build_context(config, logger):
181     environ_info = {}
182     products_list = [ 'KERNEL', 'GUI' ]
183     environ_info['products'] = config.APPLICATION.products
184     ctxenv = src.environment.SalomeEnviron(config,
185                                            src.environment.Environ(dict(
186                                                                    os.environ)),
187                                            True)
188     ctxenv.silent = True
189     ctxenv.set_full_environ(logger, environ_info)
190
191     dicdir = {}
192     for p in products_list:
193         prod_env = p + "_ROOT_DIR"
194         val = os.getenv(prod_env)
195         if os.getenv(prod_env) is None:
196             if p not in config.APPLICATION.products:
197                 warn = _("product %(product)s is not defined. Include it in the"
198                          " application or define $%(env)s.") % \
199                     { "product": p, "env": prod_env}
200                 logger.write(src.printcolors.printcWarning(warn), 1)
201                 logger.write("\n", 3, False)
202                 val = ""
203             val = ctxenv.environ.environ[prod_env]
204         dicdir[p] = val
205
206     # the dictionary requires all keys 
207     # but the generation requires only values for KERNEL and GUI
208     context = {
209         "update": 1,
210         "makeflags": "-j2",
211         "kernel": dicdir["KERNEL"],
212         "gui":    dicdir["GUI"],
213         "yacs":   "",
214         "med":    "",
215         "mesh":   "",
216         "visu":   "",
217         "geom":   "",
218     }
219     return context
220
221 def check_module_generator(directory=None):
222     """Check if module_generator is available.
223     
224     :param directory str: The directory of YACSGEN.
225     :return: The YACSGEN path if the module_generator is available, else None
226     :rtype: str
227     """
228     undo = False
229     if directory is not None and directory not in sys.path:
230         sys.path.insert(0, dir)
231         undo = True
232     
233     res = None
234     try:
235         #import module_generator
236         info = imp.find_module("module_generator")
237         res = info[1]
238     except ImportError:
239         if undo:
240             sys.path.remove(dir)
241         res = None
242
243     return res
244
245 def check_yacsgen(config, directory, logger):
246     """Check if YACSGEN is available.
247     
248     :param config Config: The global configuration.
249     :param directory str: The directory given by option --yacsgen
250     :param logger Logger: The logger instance
251     :return: The path to yacsgen directory
252     :rtype: str
253     """
254     # first check for YACSGEN (command option, then product, then environment)
255     yacsgen_dir = None
256     yacs_src = "?"
257     if directory is not None:
258         yacsgen_dir = directory
259         yacs_src = _("Using YACSGEN from command line")
260     elif 'YACSGEN' in config.APPLICATION.products:
261         yacsgen_info = src.product.get_product_config(config, 'YACSGEN')
262         yacsgen_dir = yacsgen_info.install_dir
263         yacs_src = _("Using YACSGEN from application")
264     elif os.environ.has_key("YACSGEN_ROOT_DIR"):
265         yacsgen_dir = os.getenv("YACSGEN_ROOT_DIR")
266         yacs_src = _("Using YACSGEN from environment")
267
268     if yacsgen_dir is None:
269         return (False, _("The generate command requires YACSGEN."))
270     
271     logger.write("  %s\n" % yacs_src, 2, True)
272     logger.write("  %s\n" % yacsgen_dir, 5, True)
273
274     if not os.path.exists(yacsgen_dir):
275         message = _("YACSGEN directory not found: '%s'") % yacsgen_dir
276         return (False, _(message))
277     
278     # load module_generator
279     c = check_module_generator(yacsgen_dir)
280     if c is not None:
281         return c
282     
283     pv = os.getenv("PYTHON_VERSION")
284     if pv is None:
285         python_info = src.product.get_product_config(config, "Python")
286         pv = '.'.join(python_info.version.split('.')[:2])
287     assert pv is not None, "$PYTHON_VERSION not defined"
288     yacsgen_dir = os.path.join(yacsgen_dir, "lib", "python%s" % pv,
289                                "site-packages")
290     c = check_module_generator(yacsgen_dir)
291     if c is not None:
292         return c
293
294     return (False,
295             _("The python module module_generator was not found in YACSGEN"))
296
297
298 def description():
299     '''method that is called when salomeTools is called with --help option.
300     
301     :return: The text to display for the generate command description.
302     :rtype: str
303     '''
304     return _("The generate command generates SALOME modules from 'pure cpp' "
305              "products.\nWARNING this command NEEDS YACSGEN to run!")
306
307
308 def run(args, runner, logger):
309     '''method that is called when salomeTools is called with generate parameter.
310     '''
311     
312     # Check that the command has been called with an application
313     src.check_config_has_application(runner.cfg)
314     
315     logger.write(_('Generation of SALOME modules for application %s\n') % \
316         src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
317
318     (options, args) = parser.parse_args(args)
319
320     status = src.KO_STATUS
321
322     # verify that YACSGEN is available
323     yacsgen_dir = check_yacsgen(runner.cfg, options.yacsgen, logger)
324     
325     if isinstance(yacsgen_dir, tuple):
326         # The check failed
327         __, error = yacsgen_dir
328         msg = _("Error: %s" % error)
329         logger.write(src.printcolors.printcError(msg), 1)
330         logger.write("\n", 1)
331         return 1
332     
333     # Make the generator module visible by python
334     sys.path.insert(0, yacsgen_dir)
335
336     src.printcolors.print_value(logger, _("YACSGEN dir"), yacsgen_dir, 3)
337     logger.write("\n", 2)
338     products = runner.cfg.APPLICATION.products
339     if options.products:
340         products = options.products
341
342     details = []
343     nbgen = 0
344
345     context = build_context(runner.cfg, logger)
346     for product in products:
347         header = _("Generating %s") % src.printcolors.printcLabel(product)
348         header += " %s " % ("." * (20 - len(product)))
349         logger.write(header, 3)
350         logger.flush()
351
352         if product not in runner.cfg.PRODUCTS:
353             logger.write(_("Unknown product\n"), 3, False)
354             continue
355
356         pi = src.product.get_product_config(runner.cfg, product)
357         if not src.product.product_is_generated(pi):
358             logger.write(_("not a generated product\n"), 3, False)
359             continue
360
361         nbgen += 1
362         try:
363             result = generate_component_list(runner.cfg,
364                                              pi,
365                                              context,
366                                              logger)
367         except Exception as exc:
368             result = str(exc)
369
370         if result != src.OK_STATUS:
371             result = _("ERROR: %s") % result
372             details.append([product, result])
373
374     if len(details) == 0:
375         status = src.OK_STATUS
376     else: #if config.USER.output_level != 3:
377         logger.write("\n", 2, False)
378         logger.write(_("The following modules were not generated correctly:\n"), 2)
379         for d in details:
380             logger.write("  %s: %s\n" % (d[0], d[1]), 2, False)
381     logger.write("\n", 2, False)
382
383     if status == src.OK_STATUS:
384         return 0
385     return len(details)
386