3 # Copyright (C) 2010-2012 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
23 # Compatibility python 2/3 for input function
24 # input stays input for python 3 and input = raw_input for python 2
30 # Define all possible option for the compile command : sat compile <options>
31 parser = src.options.Options()
32 parser.add_option('p', 'products', 'list2', 'products',
33 _('products to configure. This option can be'
34 ' passed several time to configure several products.'))
35 parser.add_option('', 'with_fathers', 'boolean', 'fathers',
36 _("build all necessary products to the given product (KERNEL is build before"
37 " building GUI)."), False)
38 parser.add_option('', 'with_children', 'boolean', 'children',
39 _("build all products using the given product (all SMESH plugins are build "
40 "after SMESH)."), False)
41 parser.add_option('', 'clean_all', 'boolean', 'clean_all',
42 _("clean BUILD dir and INSTALL dir before building product."), False)
43 parser.add_option('', 'clean_install', 'boolean', 'clean_install',
44 _("clean INSTALL dir before building product."), False)
45 parser.add_option('', 'make_flags', 'string', 'makeflags',
46 _("add extra options to the 'make' command."))
47 parser.add_option('', 'show', 'boolean', 'no_compile',
48 _("DO NOT COMPILE just show if products are installed or not."), False)
49 parser.add_option('', 'stop_first_fail', 'boolean', 'stop_first_fail', _("Stop"
50 "s the command at first product compilation fail."), False)
52 def get_products_list(options, cfg, logger):
53 '''method that gives the product list with their informations from
54 configuration regarding the passed options.
56 :param options Options: The Options instance that stores the commands
58 :param cfg Config: The global configuration
59 :param logger Logger: The logger instance to use for the display and
61 :return: The list of (product name, product_informations).
64 # Get the products to be prepared, regarding the options
65 if options.products is None:
66 # No options, get all products sources
67 products = cfg.APPLICATION.products
69 # if option --products, check that all products of the command line
70 # are present in the application.
71 products = options.products
73 if p not in cfg.APPLICATION.products:
74 raise src.SatException(_("Product %(product)s "
75 "not defined in application %(application)s") %
76 { 'product': p, 'application': cfg.VARS.application} )
78 # Construct the list of tuple containing
79 # the products name and their definition
80 products_infos = src.product.get_products_infos(products, cfg)
82 products_infos = [pi for pi in products_infos if not(
83 src.product.product_is_fixed(pi[1]))]
87 def get_children(config, p_name_p_info):
89 p_name, __ = p_name_p_info
90 # Get all products of the application
91 products = config.APPLICATION.products
92 products_infos = src.product.get_products_infos(products, config)
93 for p_name_potential_child, p_info_potential_child in products_infos:
94 if ("depend" in p_info_potential_child and
95 p_name in p_info_potential_child.depend):
96 l_res.append(p_name_potential_child)
99 def get_recursive_children(config, p_name_p_info, without_native_fixed=False):
100 """ Get the recursive list of the product that depend on
101 the product defined by prod_info
103 :param config Config: The global configuration
104 :param prod_info Config: The specific config of the product
105 :param without_native_fixed boolean: If true, do not include the fixed
106 or native products in the result
107 :return: The list of product_informations.
110 p_name, __ = p_name_p_info
111 # Initialization of the resulting list
114 # Get the direct children (not recursive)
115 l_direct_children = get_children(config, p_name_p_info)
116 # Minimal case : no child
117 if l_direct_children == []:
119 # Add the children and call the function to get the children of the
121 for child_name in l_direct_children:
122 l_children_name = [pn_pi[0] for pn_pi in l_children]
123 if child_name not in l_children_name:
124 if child_name not in config.APPLICATION.products:
125 msg = _("The product %(child_name)s that is in %(product_nam"
126 "e)s children is not present in application "
127 "%(appli_name)s" % {"child_name" : child_name,
128 "product_name" : p_name.name,
129 "appli_name" : config.VARS.application})
130 raise src.SatException(msg)
131 prod_info_child = src.product.get_product_config(config,
133 pname_pinfo_child = (prod_info_child.name, prod_info_child)
134 # Do not append the child if it is native or fixed and
135 # the corresponding parameter is called
136 if without_native_fixed:
137 if not(src.product.product_is_native(prod_info_child) or
138 src.product.product_is_fixed(prod_info_child)):
139 l_children.append(pname_pinfo_child)
141 l_children.append(pname_pinfo_child)
142 # Get the children of the children
143 l_grand_children = get_recursive_children(config,
145 without_native_fixed = without_native_fixed)
146 l_children += l_grand_children
149 def get_recursive_fathers(config, p_name_p_info, without_native_fixed=False):
150 """ Get the recursive list of the dependencies of the product defined by
153 :param config Config: The global configuration
154 :param prod_info Config: The specific config of the product
155 :param without_native_fixed boolean: If true, do not include the fixed
156 or native products in the result
157 :return: The list of product_informations.
160 p_name, p_info = p_name_p_info
161 # Initialization of the resulting list
163 # Minimal case : no dependencies
164 if "depend" not in p_info or p_info.depend == []:
166 # Add the dependencies and call the function to get the dependencies of the
168 for father_name in p_info.depend:
169 l_fathers_name = [pn_pi[0] for pn_pi in l_fathers]
170 if father_name not in l_fathers_name:
171 if father_name not in config.APPLICATION.products:
172 msg = _("The product %(father_name)s that is in %(product_nam"
173 "e)s dependencies is not present in application "
174 "%(appli_name)s" % {"father_name" : father_name,
175 "product_name" : p_name,
176 "appli_name" : config.VARS.application})
177 raise src.SatException(msg)
178 prod_info_father = src.product.get_product_config(config,
180 pname_pinfo_father = (prod_info_father.name, prod_info_father)
181 # Do not append the father if it is native or fixed and
182 # the corresponding parameter is called
183 if without_native_fixed:
184 if not(src.product.product_is_native(prod_info_father) or
185 src.product.product_is_fixed(prod_info_father)):
186 l_fathers.append(pname_pinfo_father)
188 l_fathers.append(pname_pinfo_father)
189 # Get the dependencies of the dependency
190 l_grand_fathers = get_recursive_fathers(config,
192 without_native_fixed = without_native_fixed)
193 for item in l_grand_fathers:
194 if item not in l_fathers:
195 l_fathers.append(item)
198 def sort_products(config, p_infos):
199 """ Sort the p_infos regarding the dependencies between the products
201 :param config Config: The global configuration
202 :param p_infos list: List of (str, Config) => (product_name, product_info)
204 l_prod_sorted = src.deepcopy_list(p_infos)
206 l_fathers = get_recursive_fathers(config,
208 without_native_fixed=True)
209 l_fathers = [father for father in l_fathers if father in p_infos]
212 for p_sorted in l_prod_sorted:
213 if p_sorted in l_fathers:
214 l_fathers.remove(p_sorted)
216 l_prod_sorted.remove(prod)
217 l_prod_sorted.insert(l_prod_sorted.index(p_sorted)+1, prod)
222 def extend_with_fathers(config, p_infos):
223 p_infos_res = src.deepcopy_list(p_infos)
224 for p_name_p_info in p_infos:
225 fathers = get_recursive_fathers(config,
227 without_native_fixed=True)
228 for p_name_p_info_father in fathers:
229 if p_name_p_info_father not in p_infos_res:
230 p_infos_res.append(p_name_p_info_father)
233 def extend_with_children(config, p_infos):
234 p_infos_res = src.deepcopy_list(p_infos)
235 for p_name_p_info in p_infos:
236 children = get_recursive_children(config,
238 without_native_fixed=True)
239 for p_name_p_info_child in children:
240 if p_name_p_info_child not in p_infos_res:
241 p_infos_res.append(p_name_p_info_child)
244 def check_dependencies(config, p_name_p_info):
245 l_depends_not_installed = []
246 fathers = get_recursive_fathers(config, p_name_p_info, without_native_fixed=True)
247 for p_name_father, p_info_father in fathers:
248 if not(src.product.check_installation(p_info_father)):
249 l_depends_not_installed.append(p_name_father)
250 return l_depends_not_installed
252 def log_step(logger, header, step):
253 logger.write("\r%s%s" % (header, " " * 30), 3)
254 logger.write("\r%s%s" % (header, step), 3)
255 logger.write("\n==== %s \n" % src.printcolors.printcInfo(step), 4)
258 def log_res_step(logger, res):
260 logger.write("%s \n" % src.printcolors.printcSuccess("OK"), 4)
263 logger.write("%s \n" % src.printcolors.printcError("KO"), 4)
266 def compile_all_products(sat, config, options, products_infos, logger):
267 '''Execute the proper configuration commands
268 in each product build directory.
270 :param config Config: The global configuration
271 :param products_info list: List of
272 (str, Config) => (product_name, product_info)
273 :param logger Logger: The logger instance to use for the display and logging
274 :return: the number of failing commands.
278 for p_name_info in products_infos:
280 p_name, p_info = p_name_info
284 logger.write("\n", 4, False)
285 logger.write("################ ", 4)
286 header = _("Compilation of %s") % src.printcolors.printcLabel(p_name)
287 header += " %s " % ("." * (len_end_line - len(p_name)))
288 logger.write(header, 3)
289 logger.write("\n", 4, False)
292 # Do nothing if the product is not compilable
293 if ("properties" in p_info and "compilation" in p_info.properties and
294 p_info.properties.compilation == "no"):
295 log_step(logger, header, "ignored")
296 logger.write("\n", 3, False)
299 # Do nothing if the product is native
300 if src.product.product_is_native(p_info):
301 log_step(logger, header, "native")
302 logger.write("\n", 3, False)
305 # Clean the build and the install directories
306 # if the corresponding options was called
307 if options.clean_all:
308 log_step(logger, header, "CLEAN BUILD AND INSTALL")
309 sat.clean(config.VARS.application +
310 " --products " + p_name +
311 " --build --install",
314 logger_add_link = logger)
316 # Clean the the install directory
317 # if the corresponding option was called
318 if options.clean_install and not options.clean_all:
319 log_step(logger, header, "CLEAN INSTALL")
320 sat.clean(config.VARS.application +
321 " --products " + p_name +
325 logger_add_link = logger)
327 # Check if it was already successfully installed
328 if src.product.check_installation(p_info):
329 logger.write(_("Already installed\n"))
332 # If the show option was called, do not launch the compilation
333 if options.no_compile:
334 logger.write(_("Not installed\n"))
337 # Check if the dependencies are installed
338 l_depends_not_installed = check_dependencies(config, p_name_info)
339 if len(l_depends_not_installed) > 0:
340 log_step(logger, header, "")
341 logger.write(src.printcolors.printcError(
342 _("ERROR : the following product(s) is(are) mandatory: ")))
343 for prod_name in l_depends_not_installed:
344 logger.write(src.printcolors.printcError(prod_name + " "))
348 # Call the function to compile the product
349 res_prod, len_end_line, error_step = compile_product(sat,
358 # Clean the install directory if there is any
359 logger.write(_("Cleaning the install directory if there is any\n"),
361 sat.clean(config.VARS.application +
362 " --products " + p_name +
366 logger_add_link = logger)
371 logger.write("\r%s%s" % (header, " " * len_end_line), 3)
372 logger.write("\r" + header + src.printcolors.printcError("KO ") + error_step)
373 logger.write("\n==== %(KO)s in compile of %(name)s \n" %
374 { "name" : p_name , "KO" : src.printcolors.printcInfo("ERROR")}, 4)
377 logger.write("\r%s%s" % (header, " " * len_end_line), 3)
378 logger.write("\r" + header + src.printcolors.printcSuccess("OK"))
379 logger.write(_("\nINSTALL directory = %s" %
380 src.printcolors.printcInfo(p_info.install_dir)), 3)
381 logger.write("\n==== %s \n" % src.printcolors.printcInfo("OK"), 4)
382 logger.write("\n==== Compilation of %(name)s %(OK)s \n" %
383 { "name" : p_name , "OK" : src.printcolors.printcInfo("OK")}, 4)
385 logger.write("\n", 3, False)
388 if res_prod != 0 and options.stop_first_fail:
393 def compile_product(sat, p_name_info, config, options, logger, header, len_end):
394 '''Execute the proper configuration command(s)
395 in the product build directory.
397 :param p_name_info tuple: (str, Config) => (product_name, product_info)
398 :param config Config: The global configuration
399 :param logger Logger: The logger instance to use for the display
401 :return: 1 if it fails, else 0.
405 p_name, p_info = p_name_info
407 # Execute "sat configure", "sat make" and "sat install"
411 # Logging and sat command call for configure step
412 len_end_line = len_end
413 log_step(logger, header, "CONFIGURE")
414 res_c = sat.configure(config.VARS.application + " --products " + p_name,
416 logger_add_link = logger)
417 log_res_step(logger, res_c)
421 error_step = "CONFIGURE"
423 # Logging and sat command call for make step
424 # Logging take account of the fact that the product has a compilation
426 if src.product.product_has_script(p_info):
427 # if the product has a compilation script,
428 # it is executed during make step
429 scrit_path_display = src.printcolors.printcLabel(
430 p_info.compil_script)
431 log_step(logger, header, "SCRIPT " + scrit_path_display)
432 len_end_line = len(scrit_path_display)
434 log_step(logger, header, "MAKE")
435 make_arguments = config.VARS.application + " --products " + p_name
436 # Get the make_flags option if there is any
437 if options.makeflags:
438 make_arguments += " --option -j" + options.makeflags
439 res_m = sat.make(make_arguments,
441 logger_add_link = logger)
442 log_res_step(logger, res_m)
448 # Logging and sat command call for make install step
449 log_step(logger, header, "MAKE INSTALL")
450 res_mi = sat.makeinstall(config.VARS.application +
454 logger_add_link = logger)
456 log_res_step(logger, res_mi)
460 error_step = "MAKE INSTALL"
462 # Check that the install directory exists
463 if res==0 and not(os.path.exists(p_info.install_dir)):
465 error_step = "NO INSTALL DIR"
466 msg = _("Error: despite the fact that all the steps ended successfully,"
467 " no install directory was found !")
468 logger.write(src.printcolors.printcError(msg), 4)
469 logger.write("\n", 4)
471 # Add the config file corresponding to the dependencies/versions of the
472 # product that have been successfully compiled
474 logger.write(_("Add the config file in installation directory\n"), 5)
475 add_compile_config_file(p_info, config)
477 return res, len_end_line, error_step
479 def add_compile_config_file(p_info, config):
480 '''Execute the proper configuration command(s)
481 in the product build directory.
483 :param p_info Config: The specific config of the product
484 :param config Config: The global configuration
486 # Create the compile config
487 compile_cfg = src.pyconf.Config()
488 for prod_name in p_info.depend:
489 if prod_name not in compile_cfg:
490 compile_cfg.addMapping(prod_name,
491 src.pyconf.Mapping(compile_cfg),
493 prod_dep_info = src.product.get_product_config(config, prod_name, False)
494 compile_cfg[prod_name] = prod_dep_info.version
495 # Write it in the install directory of the product
496 compile_cfg_path = os.path.join(p_info.install_dir, src.CONFIG_FILENAME)
497 f = open(compile_cfg_path, 'w')
498 compile_cfg.__save__(f)
502 '''method that is called when salomeTools is called with --help option.
504 :return: The text to display for the compile command description.
507 return _("The compile command constructs the products of the application")
509 def run(args, runner, logger):
510 '''method that is called when salomeTools is called with compile parameter.
514 (options, args) = parser.parse_args(args)
516 # Warn the user if he invoked the clean_all option
517 # without --products option
518 if (options.clean_all and
519 options.products is None and
520 not runner.options.batch):
521 rep = input(_("You used --clean_all without specifying a product"
522 " are you sure you want to continue? [Yes/No] "))
523 if rep.upper() != _("YES").upper():
526 # check that the command has been called with an application
527 src.check_config_has_application( runner.cfg )
529 # Print some informations
530 logger.write(_('Executing the compile commands in the build '
531 'directories of the products of '
532 'the application %s\n') %
533 src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
536 (_("SOURCE directory"),
537 os.path.join(runner.cfg.APPLICATION.workdir, 'SOURCES')),
538 (_("BUILD directory"),
539 os.path.join(runner.cfg.APPLICATION.workdir, 'BUILD'))
541 src.print_info(logger, info)
543 # Get the list of products to treat
544 products_infos = get_products_list(options, runner.cfg, logger)
547 # Extend the list with all recursive dependencies of the given products
548 products_infos = extend_with_fathers(runner.cfg, products_infos)
551 # Extend the list with all products that use the given products
552 products_infos = extend_with_children(runner.cfg, products_infos)
554 # Sort the list regarding the dependencies of the products
555 products_infos = sort_products(runner.cfg, products_infos)
558 # Call the function that will loop over all the products and execute
559 # the right command(s)
560 res = compile_all_products(runner, runner.cfg, options, products_infos, logger)
562 # Print the final state
563 nb_products = len(products_infos)
569 logger.write(_("\nCompilation: %(status)s (%(valid_result)d/%(nb_products)d)\n") % \
570 { 'status': src.printcolors.printc(final_status),
571 'valid_result': nb_products - res,
572 'nb_products': nb_products }, 1)