Salome HOME
add an option to clean build after compilation. See http://is149532.intra.cea.fr...
[tools/sat.git] / commands / compile.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  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
21 import src
22
23 # Compatibility python 2/3 for input function
24 # input stays input for python 3 and input = raw_input for python 2
25 try: 
26     input = raw_input
27 except NameError: 
28     pass
29
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     _('Optional: products to configure. This option can be'
34     ' passed several time to configure several products.'))
35 parser.add_option('', 'with_fathers', 'boolean', 'fathers',
36     _("Optional: build all necessary products to the given product (KERNEL is "
37       "build before building GUI)."), False)
38 parser.add_option('', 'with_children', 'boolean', 'children',
39     _("Optional: build all products using the given product (all SMESH plugins"
40       " are build after SMESH)."), False)
41 parser.add_option('', 'clean_all', 'boolean', 'clean_all',
42     _("Optional: clean BUILD dir and INSTALL dir before building product."),
43     False)
44 parser.add_option('', 'clean_install', 'boolean', 'clean_install',
45     _("Optional: clean INSTALL dir before building product."), False)
46 parser.add_option('', 'make_flags', 'string', 'makeflags',
47     _("Optional: add extra options to the 'make' command."))
48 parser.add_option('', 'show', 'boolean', 'no_compile',
49     _("Optional: DO NOT COMPILE just show if products are installed or not."),
50     False)
51 parser.add_option('', 'stop_first_fail', 'boolean', 'stop_first_fail', _(
52                   "Optional: Stops the command at first product compilation"
53                   " fail."), False)
54 parser.add_option('', 'check', 'boolean', 'check', _(
55                   "Optional: execute the unit tests after compilation"), False)
56
57 parser.add_option('', 'clean_build_after', 'boolean', 'clean_build_after', 
58                   _('Optional: remove the build directory after successful compilation'), False)
59
60 def get_products_list(options, cfg, logger):
61     '''method that gives the product list with their informations from 
62        configuration regarding the passed options.
63     
64     :param options Options: The Options instance that stores the commands 
65                             arguments
66     :param cfg Config: The global configuration
67     :param logger Logger: The logger instance to use for the display and 
68                           logging
69     :return: The list of (product name, product_informations).
70     :rtype: List
71     '''
72     # Get the products to be prepared, regarding the options
73     if options.products is None:
74         # No options, get all products sources
75         products = cfg.APPLICATION.products
76     else:
77         # if option --products, check that all products of the command line
78         # are present in the application.
79         products = options.products
80         for p in products:
81             if p not in cfg.APPLICATION.products:
82                 raise src.SatException(_("Product %(product)s "
83                             "not defined in application %(application)s") %
84                         { 'product': p, 'application': cfg.VARS.application} )
85     
86     # Construct the list of tuple containing 
87     # the products name and their definition
88     products_infos = src.product.get_products_infos(products, cfg)
89     
90     products_infos = [pi for pi in products_infos if not(
91                                      src.product.product_is_fixed(pi[1]))]
92     
93     return products_infos
94
95 def get_children(config, p_name_p_info):
96     l_res = []
97     p_name, __ = p_name_p_info
98     # Get all products of the application
99     products = config.APPLICATION.products
100     products_infos = src.product.get_products_infos(products, config)
101     for p_name_potential_child, p_info_potential_child in products_infos:
102         if ("depend" in p_info_potential_child and 
103                 p_name in p_info_potential_child.depend):
104             l_res.append(p_name_potential_child)
105     return l_res
106
107 def get_recursive_children(config, p_name_p_info, without_native_fixed=False):
108     """ Get the recursive list of the product that depend on 
109         the product defined by prod_info
110     
111     :param config Config: The global configuration
112     :param prod_info Config: The specific config of the product
113     :param without_native_fixed boolean: If true, do not include the fixed
114                                          or native products in the result
115     :return: The list of product_informations.
116     :rtype: List
117     """
118     p_name, __ = p_name_p_info
119     # Initialization of the resulting list
120     l_children = []
121     
122     # Get the direct children (not recursive)
123     l_direct_children = get_children(config, p_name_p_info)
124     # Minimal case : no child
125     if l_direct_children == []:
126         return []
127     # Add the children and call the function to get the children of the
128     # children
129     for child_name in l_direct_children:
130         l_children_name = [pn_pi[0] for pn_pi in l_children]
131         if child_name not in l_children_name:
132             if child_name not in config.APPLICATION.products:
133                 msg = _("The product %(child_name)s that is in %(product_nam"
134                         "e)s children is not present in application "
135                         "%(appli_name)s" % {"child_name" : child_name, 
136                                     "product_name" : p_name.name, 
137                                     "appli_name" : config.VARS.application})
138                 raise src.SatException(msg)
139             prod_info_child = src.product.get_product_config(config,
140                                                               child_name)
141             pname_pinfo_child = (prod_info_child.name, prod_info_child)
142             # Do not append the child if it is native or fixed and 
143             # the corresponding parameter is called
144             if without_native_fixed:
145                 if not(src.product.product_is_native(prod_info_child) or 
146                        src.product.product_is_fixed(prod_info_child)):
147                     l_children.append(pname_pinfo_child)
148             else:
149                 l_children.append(pname_pinfo_child)
150             # Get the children of the children
151             l_grand_children = get_recursive_children(config,
152                                 pname_pinfo_child,
153                                 without_native_fixed = without_native_fixed)
154             l_children += l_grand_children
155     return l_children
156
157 def get_recursive_fathers(config, p_name_p_info, without_native_fixed=False):
158     """ Get the recursive list of the dependencies of the product defined by
159         prod_info
160     
161     :param config Config: The global configuration
162     :param prod_info Config: The specific config of the product
163     :param without_native_fixed boolean: If true, do not include the fixed
164                                          or native products in the result
165     :return: The list of product_informations.
166     :rtype: List
167     """
168     p_name, p_info = p_name_p_info
169     # Initialization of the resulting list
170     l_fathers = []
171     # Minimal case : no dependencies
172     if "depend" not in p_info or p_info.depend == []:
173         return []
174     # Add the dependencies and call the function to get the dependencies of the
175     # dependencies
176     for father_name in p_info.depend:
177         l_fathers_name = [pn_pi[0] for pn_pi in l_fathers]
178         if father_name not in l_fathers_name:
179             if father_name not in config.APPLICATION.products:
180                 msg = _("The product %(father_name)s that is in %(product_nam"
181                         "e)s dependencies is not present in application "
182                         "%(appli_name)s" % {"father_name" : father_name, 
183                                     "product_name" : p_name, 
184                                     "appli_name" : config.VARS.application})
185                 raise src.SatException(msg)
186             prod_info_father = src.product.get_product_config(config,
187                                                               father_name)
188             pname_pinfo_father = (prod_info_father.name, prod_info_father)
189             # Do not append the father if it is native or fixed and 
190             # the corresponding parameter is called
191             if without_native_fixed:
192                 if not(src.product.product_is_native(prod_info_father) or 
193                        src.product.product_is_fixed(prod_info_father)):
194                     l_fathers.append(pname_pinfo_father)
195             else:
196                 l_fathers.append(pname_pinfo_father)
197             # Get the dependencies of the dependency
198             l_grand_fathers = get_recursive_fathers(config,
199                                 pname_pinfo_father,
200                                 without_native_fixed = without_native_fixed)
201             for item in l_grand_fathers:
202                 if item not in l_fathers:
203                     l_fathers.append(item)
204     return l_fathers
205
206 def sort_products(config, p_infos):
207     """ Sort the p_infos regarding the dependencies between the products
208     
209     :param config Config: The global configuration
210     :param p_infos list: List of (str, Config) => (product_name, product_info)
211     """
212     l_prod_sorted = src.deepcopy_list(p_infos)
213     for prod in p_infos:
214         l_fathers = get_recursive_fathers(config,
215                                           prod,
216                                           without_native_fixed=True)
217         l_fathers = [father for father in l_fathers if father in p_infos]
218         if l_fathers == []:
219             continue
220         for p_sorted in l_prod_sorted:
221             if p_sorted in l_fathers:
222                 l_fathers.remove(p_sorted)
223             if l_fathers==[]:
224                 l_prod_sorted.remove(prod)
225                 l_prod_sorted.insert(l_prod_sorted.index(p_sorted)+1, prod)
226                 break
227         
228     return l_prod_sorted
229
230 def extend_with_fathers(config, p_infos):
231     p_infos_res = src.deepcopy_list(p_infos)
232     for p_name_p_info in p_infos:
233         fathers = get_recursive_fathers(config,
234                                         p_name_p_info,
235                                         without_native_fixed=True)
236         for p_name_p_info_father in fathers:
237             if p_name_p_info_father not in p_infos_res:
238                 p_infos_res.append(p_name_p_info_father)
239     return p_infos_res
240
241 def extend_with_children(config, p_infos):
242     p_infos_res = src.deepcopy_list(p_infos)
243     for p_name_p_info in p_infos:
244         children = get_recursive_children(config,
245                                         p_name_p_info,
246                                         without_native_fixed=True)
247         for p_name_p_info_child in children:
248             if p_name_p_info_child not in p_infos_res:
249                 p_infos_res.append(p_name_p_info_child)
250     return p_infos_res    
251
252 def check_dependencies(config, p_name_p_info):
253     l_depends_not_installed = []
254     fathers = get_recursive_fathers(config, p_name_p_info, without_native_fixed=True)
255     for p_name_father, p_info_father in fathers:
256         if not(src.product.check_installation(p_info_father)):
257             l_depends_not_installed.append(p_name_father)
258     return l_depends_not_installed
259
260 def log_step(logger, header, step):
261     logger.write("\r%s%s" % (header, " " * 30), 3)
262     logger.write("\r%s%s" % (header, step), 3)
263     logger.write("\n==== %s \n" % src.printcolors.printcInfo(step), 4)
264     logger.flush()
265
266 def log_res_step(logger, res):
267     if res == 0:
268         logger.write("%s \n" % src.printcolors.printcSuccess("OK"), 4)
269         logger.flush()
270     else:
271         logger.write("%s \n" % src.printcolors.printcError("KO"), 4)
272         logger.flush()
273
274 def compile_all_products(sat, config, options, products_infos, logger):
275     '''Execute the proper configuration commands 
276        in each product build directory.
277
278     :param config Config: The global configuration
279     :param products_info list: List of 
280                                  (str, Config) => (product_name, product_info)
281     :param logger Logger: The logger instance to use for the display and logging
282     :return: the number of failing commands.
283     :rtype: int
284     '''
285     res = 0
286     for p_name_info in products_infos:
287         
288         p_name, p_info = p_name_info
289         
290         # Logging
291         len_end_line = 30
292         logger.write("\n", 4, False)
293         logger.write("################ ", 4)
294         header = _("Compilation of %s") % src.printcolors.printcLabel(p_name)
295         header += " %s " % ("." * (len_end_line - len(p_name)))
296         logger.write(header, 3)
297         logger.write("\n", 4, False)
298         logger.flush()
299
300         # Do nothing if the product is not compilable
301         if ("properties" in p_info and "compilation" in p_info.properties and 
302                                             p_info.properties.compilation == "no"):
303             log_step(logger, header, "ignored")
304             logger.write("\n", 3, False)
305             continue
306
307         # Do nothing if the product is native
308         if src.product.product_is_native(p_info):
309             log_step(logger, header, "native")
310             logger.write("\n", 3, False)
311             continue
312
313         # Clean the build and the install directories 
314         # if the corresponding options was called
315         if options.clean_all:
316             log_step(logger, header, "CLEAN BUILD AND INSTALL")
317             sat.clean(config.VARS.application + 
318                       " --products " + p_name + 
319                       " --build --install",
320                       batch=True,
321                       verbose=0,
322                       logger_add_link = logger)
323         
324         # Clean the the install directory 
325         # if the corresponding option was called
326         if options.clean_install and not options.clean_all:
327             log_step(logger, header, "CLEAN INSTALL")
328             sat.clean(config.VARS.application + 
329                       " --products " + p_name + 
330                       " --install",
331                       batch=True,
332                       verbose=0,
333                       logger_add_link = logger)
334         
335         # Recompute the product information to get the right install_dir
336         # (it could change if there is a clean of the install directory)
337         p_info = src.product.get_product_config(config, p_name)
338         
339         # Check if it was already successfully installed
340         if src.product.check_installation(p_info):
341             logger.write(_("Already installed\n"))
342             continue
343         
344         # If the show option was called, do not launch the compilation
345         if options.no_compile:
346             logger.write(_("Not installed\n"))
347             continue
348         
349         # Check if the dependencies are installed
350         l_depends_not_installed = check_dependencies(config, p_name_info)
351         if len(l_depends_not_installed) > 0:
352             log_step(logger, header, "")
353             logger.write(src.printcolors.printcError(
354                     _("ERROR : the following product(s) is(are) mandatory: ")))
355             for prod_name in l_depends_not_installed:
356                 logger.write(src.printcolors.printcError(prod_name + " "))
357             logger.write("\n")
358             continue
359         
360         # Call the function to compile the product
361         res_prod, len_end_line, error_step = compile_product(sat,
362                                                              p_name_info,
363                                                              config,
364                                                              options,
365                                                              logger,
366                                                              header,
367                                                              len_end_line)
368         
369         if res_prod != 0:
370             res += 1
371             
372             if error_step != "CHECK":
373                 # Clean the install directory if there is any
374                 logger.write(_(
375                             "Cleaning the install directory if there is any\n"),
376                              5)
377                 sat.clean(config.VARS.application + 
378                           " --products " + p_name + 
379                           " --install",
380                           batch=True,
381                           verbose=0,
382                           logger_add_link = logger)
383         else:
384             # Clean the build directory if the compilation and tests succeed
385             if options.clean_build_after:
386                 log_step(logger, header, "CLEAN BUILD")
387                 sat.clean(config.VARS.application + 
388                           " --products " + p_name + 
389                           " --build",
390                           batch=True,
391                           verbose=0,
392                           logger_add_link = logger)
393
394         # Log the result
395         if res_prod > 0:
396             logger.write("\r%s%s" % (header, " " * len_end_line), 3)
397             logger.write("\r" + header + src.printcolors.printcError("KO ") + error_step)
398             logger.write("\n==== %(KO)s in compile of %(name)s \n" %
399                 { "name" : p_name , "KO" : src.printcolors.printcInfo("ERROR")}, 4)
400             if error_step == "CHECK":
401                 logger.write(_("\nINSTALL directory = %s" % 
402                            src.printcolors.printcInfo(p_info.install_dir)), 3)
403             logger.flush()
404         else:
405             logger.write("\r%s%s" % (header, " " * len_end_line), 3)
406             logger.write("\r" + header + src.printcolors.printcSuccess("OK"))
407             logger.write(_("\nINSTALL directory = %s" % 
408                            src.printcolors.printcInfo(p_info.install_dir)), 3)
409             logger.write("\n==== %s \n" % src.printcolors.printcInfo("OK"), 4)
410             logger.write("\n==== Compilation of %(name)s %(OK)s \n" %
411                 { "name" : p_name , "OK" : src.printcolors.printcInfo("OK")}, 4)
412             logger.flush()
413         logger.write("\n", 3, False)
414         
415         
416         if res_prod != 0 and options.stop_first_fail:
417             break
418         
419     return res
420
421 def compile_product(sat, p_name_info, config, options, logger, header, len_end):
422     '''Execute the proper configuration command(s) 
423        in the product build directory.
424     
425     :param p_name_info tuple: (str, Config) => (product_name, product_info)
426     :param config Config: The global configuration
427     :param logger Logger: The logger instance to use for the display 
428                           and logging
429     :param header Str: the header to display when logging
430     :param len_end Int: the lenght of the the end of line (used in display)
431     :return: 1 if it fails, else 0.
432     :rtype: int
433     '''
434     
435     p_name, p_info = p_name_info
436           
437     # Get the build procedure from the product configuration.
438     # It can be :
439     # build_sources : autotools -> build_configure, configure, make, make install
440     # build_sources : cmake     -> cmake, make, make install
441     # build_sources : script    -> script executions
442     res = 0
443     if (src.product.product_is_autotools(p_info) or 
444                                           src.product.product_is_cmake(p_info)):
445         res, len_end_line, error_step = compile_product_cmake_autotools(sat,
446                                                                   p_name_info,
447                                                                   config,
448                                                                   options,
449                                                                   logger,
450                                                                   header,
451                                                                   len_end)
452     if src.product.product_has_script(p_info):
453         res, len_end_line, error_step = compile_product_script(sat,
454                                                                   p_name_info,
455                                                                   config,
456                                                                   options,
457                                                                   logger,
458                                                                   header,
459                                                                   len_end)
460
461     # Check that the install directory exists
462     if res==0 and not(os.path.exists(p_info.install_dir)):
463         res = 1
464         error_step = "NO INSTALL DIR"
465         msg = _("Error: despite the fact that all the steps ended successfully,"
466                 " no install directory was found !")
467         logger.write(src.printcolors.printcError(msg), 4)
468         logger.write("\n", 4)
469         return res, len_end_line, error_step
470     
471     # Add the config file corresponding to the dependencies/versions of the 
472     # product that have been successfully compiled
473     if res==0:       
474         logger.write(_("Add the config file in installation directory\n"), 5)
475         add_compile_config_file(p_info, config)
476         
477         if options.check:
478             # Do the unit tests (call the check command)
479             log_step(logger, header, "CHECK")
480             res_check = sat.check(
481                               config.VARS.application + " --products " + p_name,
482                               verbose = 0,
483                               logger_add_link = logger)
484             if res_check != 0:
485                 error_step = "CHECK"
486                 
487             res += res_check
488     
489     return res, len_end_line, error_step
490
491 def compile_product_cmake_autotools(sat,
492                                     p_name_info,
493                                     config,
494                                     options,
495                                     logger,
496                                     header,
497                                     len_end):
498     '''Execute the proper build procedure for autotools or cmake
499        in the product build directory.
500     
501     :param p_name_info tuple: (str, Config) => (product_name, product_info)
502     :param config Config: The global configuration
503     :param logger Logger: The logger instance to use for the display 
504                           and logging
505     :param header Str: the header to display when logging
506     :param len_end Int: the lenght of the the end of line (used in display)
507     :return: 1 if it fails, else 0.
508     :rtype: int
509     '''
510     p_name, p_info = p_name_info
511     
512     # Execute "sat configure", "sat make" and "sat install"
513     res = 0
514     error_step = ""
515     
516     # Logging and sat command call for configure step
517     len_end_line = len_end
518     log_step(logger, header, "CONFIGURE")
519     res_c = sat.configure(config.VARS.application + " --products " + p_name,
520                           verbose = 0,
521                           logger_add_link = logger)
522     log_res_step(logger, res_c)
523     res += res_c
524     
525     if res_c > 0:
526         error_step = "CONFIGURE"
527     else:
528         # Logging and sat command call for make step
529         # Logging take account of the fact that the product has a compilation 
530         # script or not
531         if src.product.product_has_script(p_info):
532             # if the product has a compilation script, 
533             # it is executed during make step
534             scrit_path_display = src.printcolors.printcLabel(
535                                                         p_info.compil_script)
536             log_step(logger, header, "SCRIPT " + scrit_path_display)
537             len_end_line = len(scrit_path_display)
538         else:
539             log_step(logger, header, "MAKE")
540         make_arguments = config.VARS.application + " --products " + p_name
541         # Get the make_flags option if there is any
542         if options.makeflags:
543             make_arguments += " --option -j" + options.makeflags
544         res_m = sat.make(make_arguments,
545                          verbose = 0,
546                          logger_add_link = logger)
547         log_res_step(logger, res_m)
548         res += res_m
549         
550         if res_m > 0:
551             error_step = "MAKE"
552         else: 
553             # Logging and sat command call for make install step
554             log_step(logger, header, "MAKE INSTALL")
555             res_mi = sat.makeinstall(config.VARS.application + 
556                                      " --products " + 
557                                      p_name,
558                                     verbose = 0,
559                                     logger_add_link = logger)
560
561             log_res_step(logger, res_mi)
562             res += res_mi
563             
564             if res_mi > 0:
565                 error_step = "MAKE INSTALL"
566                 
567     return res, len_end_line, error_step 
568
569 def compile_product_script(sat,
570                            p_name_info,
571                            config,
572                            options,
573                            logger,
574                            header,
575                            len_end):
576     '''Execute the script build procedure in the product build directory.
577     
578     :param p_name_info tuple: (str, Config) => (product_name, product_info)
579     :param config Config: The global configuration
580     :param logger Logger: The logger instance to use for the display 
581                           and logging
582     :param header Str: the header to display when logging
583     :param len_end Int: the lenght of the the end of line (used in display)
584     :return: 1 if it fails, else 0.
585     :rtype: int
586     '''
587     p_name, p_info = p_name_info
588     
589     # Execute "sat configure", "sat make" and "sat install"
590     error_step = ""
591     
592     # Logging and sat command call for the script step
593     scrit_path_display = src.printcolors.printcLabel(p_info.compil_script)
594     log_step(logger, header, "SCRIPT " + scrit_path_display)
595     len_end_line = len_end + len(scrit_path_display)
596     res = sat.script(config.VARS.application + " --products " + p_name,
597                      verbose = 0,
598                      logger_add_link = logger)
599     log_res_step(logger, res)
600               
601     return res, len_end_line, error_step 
602
603 def add_compile_config_file(p_info, config):
604     '''Execute the proper configuration command(s) 
605        in the product build directory.
606     
607     :param p_info Config: The specific config of the product
608     :param config Config: The global configuration
609     '''
610     # Create the compile config
611     compile_cfg = src.pyconf.Config()
612     for prod_name in p_info.depend:
613         if prod_name not in compile_cfg:
614             compile_cfg.addMapping(prod_name,
615                                    src.pyconf.Mapping(compile_cfg),
616                                    "")
617         prod_dep_info = src.product.get_product_config(config, prod_name, False)
618         compile_cfg[prod_name] = prod_dep_info.version
619     # Write it in the install directory of the product
620     compile_cfg_path = os.path.join(p_info.install_dir, src.CONFIG_FILENAME)
621     f = open(compile_cfg_path, 'w')
622     compile_cfg.__save__(f)
623     f.close()
624     
625 def description():
626     '''method that is called when salomeTools is called with --help option.
627     
628     :return: The text to display for the compile command description.
629     :rtype: str
630     '''
631     return _("The compile command constructs the products of the application"
632              "\n\nexample:\nsat compile SALOME-master --products KERNEL,GUI,"
633              "MEDCOUPLING --clean_all")
634   
635 def run(args, runner, logger):
636     '''method that is called when salomeTools is called with compile parameter.
637     '''
638     
639     # Parse the options
640     (options, args) = parser.parse_args(args)
641
642     # Warn the user if he invoked the clean_all option 
643     # without --products option
644     if (options.clean_all and 
645         options.products is None and 
646         not runner.options.batch):
647         rep = input(_("You used --clean_all without specifying a product"
648                           " are you sure you want to continue? [Yes/No] "))
649         if rep.upper() != _("YES").upper():
650             return 0
651         
652     # check that the command has been called with an application
653     src.check_config_has_application( runner.cfg )
654
655     # Print some informations
656     logger.write(_('Executing the compile commands in the build '
657                                 'directories of the products of '
658                                 'the application %s\n') % 
659                 src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
660     
661     info = [
662             (_("SOURCE directory"),
663              os.path.join(runner.cfg.APPLICATION.workdir, 'SOURCES')),
664             (_("BUILD directory"),
665              os.path.join(runner.cfg.APPLICATION.workdir, 'BUILD'))
666             ]
667     src.print_info(logger, info)
668
669     # Get the list of products to treat
670     products_infos = get_products_list(options, runner.cfg, logger)
671
672     if options.fathers:
673         # Extend the list with all recursive dependencies of the given products
674         products_infos = extend_with_fathers(runner.cfg, products_infos)
675
676     if options.children:
677         # Extend the list with all products that use the given products
678         products_infos = extend_with_children(runner.cfg, products_infos)
679
680     # Sort the list regarding the dependencies of the products
681     products_infos = sort_products(runner.cfg, products_infos)
682
683     
684     # Call the function that will loop over all the products and execute
685     # the right command(s)
686     res = compile_all_products(runner, runner.cfg, options, products_infos, logger)
687     
688     # Print the final state
689     nb_products = len(products_infos)
690     if res == 0:
691         final_status = "OK"
692     else:
693         final_status = "KO"
694    
695     logger.write(_("\nCompilation: %(status)s (%(valid_result)d/%(nb_products)d)\n") % \
696         { 'status': src.printcolors.printc(final_status), 
697           'valid_result': nb_products - res,
698           'nb_products': nb_products }, 1)    
699     
700     code = res
701     if code != 0:
702         code = 1
703     return code