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.versionMinorMajorPatch import MinorMajorPatch as MMP
27 import src.debug as DBG
29 parser = src.options.Options()
35 _("Optional: the list of products to generate"),
42 _("Optional: path to YACSGEN's module_generator package"),
46 def generate_component_list(config, product_name, product_info, context, logger):
49 for compo in src.product.get_product_components(product_info):
50 header = " %s %s " % (
51 src.printcolors.printcLabel(compo),
52 "." * (20 - len(compo)),
54 res = generate_component(
55 config, compo, product_name, product_info, context, header, logger
57 if config.USER.output_verbose_level == 3:
58 logger.write("\r%s%s\r%s" % (header, " " * 20, header), 3)
59 logger.write(src.printcolors.printc(res), 3, False)
60 logger.write("\n", 3, False)
64 def generate_component(
65 config, compo, product_name, product_info, context, header, logger
67 # get from config include file name and librairy name, or take default value
68 if "hxxfile" in product_info:
69 hxxfile = product_info.hxxfile
71 hxxfile = compo + ".hxx"
72 if "cpplib" in product_info:
73 cpplib = product_info.cpplib
75 cpplib = "lib" + compo + "CXX.so"
76 cpp_path = product_info.install_dir
78 logger.write("%s\n" % header, 4, False)
79 src.printcolors.print_value(logger, "hxxfile", hxxfile, 4)
80 src.printcolors.print_value(logger, "cpplib", cpplib, 4)
81 src.printcolors.print_value(logger, "cpp_path", cpp_path, 4)
83 # create a product_info at runtime
84 compo_info = src.pyconf.Mapping(config)
85 compo_info.name = compo
86 compo_info.nb_proc = 1
87 generate_dir = os.path.join(config.APPLICATION.workdir, "GENERATED")
88 install_dir = os.path.join(config.APPLICATION.workdir, "INSTALL")
89 build_dir = os.path.join(config.APPLICATION.workdir, "BUILD")
90 compo_info.source_dir = os.path.join(generate_dir, compo + "_SRC")
91 compo_info.install_dir = os.path.join(install_dir, compo)
92 compo_info.build_dir = os.path.join(build_dir, compo)
93 compo_info.depend = product_info.depend
94 compo_info.depend.append(product_info.name, "") # add cpp module
95 compo_info.opt_depend = product_info.opt_depend
97 config.PRODUCTS.addMapping(compo, src.pyconf.Mapping(config), "")
98 config.PRODUCTS[compo].default = compo_info
100 builder = src.compilation.Builder(
101 config, logger, product_name, compo_info, check_src=False
103 builder.header = header
105 # generate the component
106 # create GENERATE dir if necessary
107 if not os.path.exists(generate_dir):
108 os.mkdir(generate_dir)
110 # delete previous generated directory if it already exists
111 if os.path.exists(compo_info.source_dir):
112 logger.write(" delete %s\n" % compo_info.source_dir, 4)
113 shutil.rmtree(compo_info.source_dir)
115 # generate generates in the current directory => change for generate dir
117 os.chdir(generate_dir)
119 # inline class to override bootstrap method
120 import module_generator
122 class sat_generator(module_generator.Generator):
123 # old bootstrap for automake (used if salome version <= 7.4)
124 def bootstrap(self, source_dir, log_file):
125 # replace call to default bootstrap() by using subprocess call (cleaner)
126 command = "sh autogen.sh"
127 ier = subprocess.call(
132 stderr=subprocess.STDOUT,
135 raise src.SatException("bootstrap has ended in error")
137 # determine salome version
138 VersionSalome = src.get_salome_version(config)
139 if VersionSalome >= MMP([7, 5, 0]):
140 use_autotools = False
141 builder.log("USE CMAKE", 3)
144 builder.log("USE AUTOTOOLS", 3)
147 builder.log("GENERATE", 3)
149 prevstdout = sys.stdout
150 prevstderr = sys.stderr
153 sys.stdout = logger.logTxtFile
154 sys.stderr = logger.logTxtFile
156 if src.product.product_is_mpi(product_info):
157 salome_compo = module_generator.HXX2SALOMEParaComponent(
158 hxxfile, cpplib, cpp_path
161 salome_compo = module_generator.HXX2SALOMEComponent(
162 hxxfile, cpplib, cpp_path
165 if src.product.product_has_salome_gui(product_info):
166 # get files to build a template GUI
167 try: # try new yacsgen api
168 gui_files = salome_compo.getGUIfilesTemplate(compo)
169 except: # use old yacsgen api
170 gui_files = salome_compo.getGUIfilesTemplate()
174 mg = module_generator.Module(
175 compo, components=[salome_compo], prefix=generate_dir, gui=gui_files
177 g = sat_generator(mg, context)
181 result = "BUID_CONFIGURE"
182 builder.log("BUID_CONFIGURE (no bootstrap)", 3)
183 g.bootstrap(compo_info.source_dir, logger.logTxtFile)
185 result = src.OK_STATUS
187 sys.stdout = prevstdout
188 sys.stderr = prevstderr
190 # go back to previous directory
193 # do the compilation using the builder object
194 if builder.prepare() != 0:
195 return "Error in prepare"
197 if builder.configure() != 0:
198 return "Error in configure"
200 if builder.cmake() != 0:
201 return "Error in cmake"
203 if builder.make(config.VARS.nb_proc, "") != 0:
204 return "Error in make"
205 if builder.install() != 0:
206 return "Error in make install"
208 # copy specified logo in generated component install directory
209 # rem : logo is not copied in source dir because this would require
210 # to modify the generated makefile
211 logo_path = src.product.product_has_logo(product_info)
213 destlogo = os.path.join(
214 compo_info.install_dir,
221 src.Path(logo_path).copyfile(destlogo)
226 def build_context(config, logger):
227 products_list = ["KERNEL", "GUI"]
228 ctxenv = src.environment.SalomeEnviron(
229 config, src.environment.Environ(dict(os.environ)), True
232 ctxenv.set_full_environ(logger, config.APPLICATION.products.keys())
235 for p in products_list:
236 prod_env = p + "_ROOT_DIR"
237 val = os.getenv(prod_env)
238 if os.getenv(prod_env) is None:
239 if p not in config.APPLICATION.products:
241 "product %(product)s is not defined. Include it in the"
242 " application or define $%(env)s."
243 ) % {"product": p, "env": prod_env}
244 logger.write(src.printcolors.printcWarning(warn), 1)
245 logger.write("\n", 3, False)
247 val = ctxenv.environ.environ[prod_env]
250 # the dictionary requires all keys
251 # but the generation requires only values for KERNEL and GUI
255 "kernel": dicdir["KERNEL"],
256 "gui": dicdir["GUI"],
266 def check_module_generator(directory=None):
267 """Check if module_generator is available.
269 :param directory str: The directory of YACSGEN.
270 :return: The YACSGEN path if the module_generator is available, else None
274 if directory is not None and directory not in sys.path:
275 sys.path.insert(0, directory)
280 # import module_generator
281 info = imp.find_module("module_generator")
285 sys.path.remove(directory)
291 def check_yacsgen(config, directory, logger):
292 """Check if YACSGEN is available.
294 :param config Config: The global configuration.
295 :param directory str: The directory given by option --yacsgen
296 :param logger Logger: The logger instance
297 :return: The path to yacsgen directory
300 # first check for YACSGEN (command option, then product, then environment)
303 if directory is not None:
304 yacsgen_dir = directory
305 yacs_src = _("Using YACSGEN from command line")
306 elif "YACSGEN" in config.APPLICATION.products:
307 yacsgen_info = src.product.get_product_config(config, "YACSGEN")
308 yacsgen_dir = yacsgen_info.install_dir
309 yacs_src = _("Using YACSGEN from application")
310 elif "YACSGEN_ROOT_DIR" in os.environ:
311 yacsgen_dir = os.getenv("YACSGEN_ROOT_DIR")
312 yacs_src = _("Using YACSGEN from environment")
314 if yacsgen_dir is None:
315 return (False, _("The generate command requires YACSGEN."))
317 logger.write(" %s\n" % yacs_src, 2, True)
318 logger.write(" %s\n" % yacsgen_dir, 5, True)
320 if not os.path.exists(yacsgen_dir):
321 message = _("YACSGEN directory not found: '%s'") % yacsgen_dir
322 return (False, _(message))
324 # load module_generator
325 c = check_module_generator(yacsgen_dir)
329 pv = os.getenv("PYTHON_VERSION")
331 python_info = src.product.get_product_config(config, "Python")
332 pv = ".".join(python_info.version.split(".")[:2])
333 assert pv is not None, "$PYTHON_VERSION not defined"
334 yacsgen_dir = os.path.join(yacsgen_dir, "lib", "python%s" % pv, "site-packages")
335 c = check_module_generator(yacsgen_dir)
339 return (False, _("The python module module_generator was not found in YACSGEN"))
343 """method that is called when salomeTools is called with --help option.
345 :return: The text to display for the generate command description.
349 "The generate command generates SALOME modules from 'pure cpp' "
350 "products.\nWARNING this command NEEDS YACSGEN to run!\n\nexample:"
351 "\nsat generate SALOME-master --products FLICACPP"
355 def run(args, runner, logger):
356 """method that is called when salomeTools is called with generate parameter."""
358 # Check that the command has been called with an application
359 src.check_config_has_application(runner.cfg)
362 _("Generation of SALOME modules for application %s\n")
363 % src.printcolors.printcLabel(runner.cfg.VARS.application),
367 (options, args) = parser.parse_args(args)
369 status = src.KO_STATUS
371 # verify that YACSGEN is available
372 yacsgen_dir = check_yacsgen(runner.cfg, options.yacsgen, logger)
374 if isinstance(yacsgen_dir, tuple):
376 __, error = yacsgen_dir
377 msg = _("Error: %s" % error)
378 logger.write(src.printcolors.printcError(msg), 1)
379 logger.write("\n", 1)
382 # Make the generator module visible by python
383 sys.path.insert(0, yacsgen_dir)
385 src.printcolors.print_value(logger, _("YACSGEN dir"), yacsgen_dir, 3)
386 logger.write("\n", 2)
387 products = runner.cfg.APPLICATION.products
389 products = options.products
394 context = build_context(runner.cfg, logger)
395 for product in products:
396 header = _("Generating %s") % src.printcolors.printcLabel(product)
397 header += " %s " % ("." * (20 - len(product)))
398 logger.write(header, 3)
401 if product not in runner.cfg.PRODUCTS:
402 logger.write(_("Unknown product\n"), 3, False)
405 pi = src.product.get_product_config(runner.cfg, product)
406 if not src.product.product_is_generated(pi):
407 logger.write(_("not a generated product\n"), 3, False)
410 logger.write(_("\nCleaning generated directories\n"), 3, False)
411 # clean source, build and install directories of the generated product
412 # no verbosity to avoid warning at the first generation, for which dirs don't exist
414 runner.cfg.VARS.application + " --products " + pi.name + " --generated",
417 logger_add_link=logger,
421 result = generate_component_list(runner.cfg, product, pi, context, logger)
422 except Exception as exc:
425 if result != src.OK_STATUS:
426 result = _("ERROR: %s") % result
427 details.append([product, result])
429 if len(details) == 0:
430 status = src.OK_STATUS
431 else: # if config.USER.output_level != 3:
432 logger.write("\n", 2, False)
433 logger.write(_("The following modules were not generated correctly:\n"), 2)
435 logger.write(" %s: %s\n" % (d[0], d[1]), 2, False)
436 logger.write("\n", 2, False)
438 if status == src.OK_STATUS: