Salome HOME
0a5efeb1dc8c366fae40097f935b4bab37a4ada7
[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 # Define all possible option for the compile command :  sat compile <options>
24 parser = src.options.Options()
25 parser.add_option('p', 'products', 'list2', 'products',
26     _('products to configure. This option can be'
27     ' passed several time to configure several products.'))
28 parser.add_option('', 'with_fathers', 'boolean', 'fathers',
29     _("build all necessary modules to the given module (KERNEL is build before"
30       " building GUI)."), False)
31 parser.add_option('', 'with_children', 'boolean', 'children',
32     _("build all modules using the given module (all SMESH plugins are build "
33       "after SMESH)."), False)
34 parser.add_option('', 'clean_all', 'boolean', 'clean_all',
35     _("clean BUILD dir and INSTALL dir before building module."), False)
36 parser.add_option('', 'clean_install', 'boolean', 'clean_install',
37     _("clean INSTALL dir before building module."), False)
38 parser.add_option('', 'make_flags', 'string', 'makeflags',
39     _("add extra options to the 'make' command."))
40 parser.add_option('', 'show', 'boolean', 'no_compile',
41     _("DO NOT COMPILE just show if modules are installed or not."), False)
42 parser.add_option('', 'stop_first_fail', 'boolean', 'stop_first_fail', _("Stop"
43                     "s the command at first module compilation fail."), False)
44
45 def get_products_list(options, cfg, logger):
46     '''method that gives the product list with their informations from 
47        configuration regarding the passed options.
48     
49     :param options Options: The Options instance that stores the commands 
50                             arguments
51     :param cfg Config: The global configuration
52     :param logger Logger: The logger instance to use for the display and 
53                           logging
54     :return: The list of (product name, product_informations).
55     :rtype: List
56     '''
57     # Get the products to be prepared, regarding the options
58     if options.products is None:
59         # No options, get all products sources
60         products = cfg.APPLICATION.products
61     else:
62         # if option --products, check that all products of the command line
63         # are present in the application.
64         products = options.products
65         for p in products:
66             if p not in cfg.APPLICATION.products:
67                 raise src.SatException(_("Product %(product)s "
68                             "not defined in application %(application)s") %
69                         { 'product': p, 'application': cfg.VARS.application} )
70     
71     # Construct the list of tuple containing 
72     # the products name and their definition
73     products_infos = src.product.get_products_infos(products, cfg)
74     
75     products_infos = [pi for pi in products_infos if not(
76                                      src.product.product_is_native(pi[1]) or 
77                                      src.product.product_is_fixed(pi[1]))]
78     
79     return products_infos
80
81 def get_recursive_fathers(config, p_name_p_info, without_native_fixed=False):
82     """ Get the recursive list of the dependencies of the product defined by
83         prod_info
84     
85     :param config Config: The global configuration
86     :param prod_info Config: The specific config of the product
87     :param without_native_fixed boolean: If true, do not include the fixed
88                                          or native products in the result
89     :return: The list of product_informations.
90     :rtype: List
91     """
92     p_name, p_info = p_name_p_info
93     # Initialization of the resulting list
94     l_fathers = []
95     # Minimal case : no dependencies
96     if "depend" not in p_info or p_info.depend == []:
97         return []
98     # Add the dependencies and call the function to get the dependencies of the
99     # dependencies
100     for father_name in p_info.depend:
101         l_fathers_name = [pn_pi[0] for pn_pi in l_fathers]
102         if father_name not in l_fathers_name:
103             if father_name not in config.APPLICATION.products:
104                 msg = _("The product %(father_name)s that is in %(product_nam"
105                         "e)s dependencies is not present in application "
106                         "%(appli_name)s" % {"father_name" : father_name, 
107                                     "product_name" : p_name.name, 
108                                     "appli_name" : config.VARS.application})
109                 raise src.SatException(msg)
110             prod_info_father = src.product.get_product_config(config,
111                                                               father_name)
112             pname_pinfo_father = (prod_info_father.name, prod_info_father)
113             # Do not append the father if the it is native or fixed and 
114             # the corresponding parameter is called
115             if without_native_fixed:
116                 if not(src.product.product_is_native(prod_info_father) or 
117                        src.product.product_is_fixed(prod_info_father)):
118                     l_fathers.append(pname_pinfo_father)
119             else:
120                 l_fathers.append(pname_pinfo_father)
121             # Get the dependencies of the dependency
122             l_grand_fathers = get_recursive_fathers(config,
123                                 pname_pinfo_father,
124                                 without_native_fixed = without_native_fixed)
125             l_fathers += l_grand_fathers
126     return l_fathers
127
128 def sort_products(config, p_infos):
129     """ Sort the p_infos regarding the dependencies between the products
130     
131     :param config Config: The global configuration
132     :param p_infos list: List of (str, Config) => (product_name, product_info)
133     """
134     l_prod_sorted = deepcopy_list(p_infos)
135     for prod in p_infos:
136         l_fathers = get_recursive_fathers(config, prod, without_native_fixed=True)
137         l_fathers = [father for father in l_fathers if father in p_infos]
138         if l_fathers == []:
139             continue
140         for p_sorted in l_prod_sorted:
141             if p_sorted in l_fathers:
142                 l_fathers.remove(p_sorted)
143             if l_fathers==[]:
144                 l_prod_sorted.remove(prod)
145                 l_prod_sorted.insert(l_prod_sorted.index(p_sorted)+1, prod)
146                 break
147         
148     return l_prod_sorted
149        
150 def deepcopy_list(input_list):
151     res = []
152     for elem in input_list:
153         res.append(elem)
154     return res
155
156 def log_step(logger, header, step):
157     logger.write("\r%s%s" % (header, " " * 20), 3)
158     logger.write("\r%s%s" % (header, step), 3)
159     logger.write("\n==== %s \n" % src.printcolors.printcInfo(step), 4)
160     logger.flush()
161
162 def log_res_step(logger, res):
163     if res == 0:
164         logger.write("%s \n" % src.printcolors.printcSuccess("OK"), 4)
165         logger.flush()
166     else:
167         logger.write("%s \n" % src.printcolors.printcError("KO"), 4)
168         logger.flush()
169
170 def compile_all_products(sat, config, products_infos, logger):
171     '''Execute the proper configuration commands 
172        in each product build directory.
173
174     :param config Config: The global configuration
175     :param products_info list: List of 
176                                  (str, Config) => (product_name, product_info)
177     :param logger Logger: The logger instance to use for the display and logging
178     :return: the number of failing commands.
179     :rtype: int
180     '''
181     res = 0
182     for p_name_info in products_infos:
183         res_prod = compile_product(sat, p_name_info, config, logger)
184         if res_prod != 0:
185             res += 1 
186     return res
187
188 def compile_product(sat, p_name_info, config, logger):
189     '''Execute the proper configuration command(s) 
190        in the product build directory.
191     
192     :param p_name_info tuple: (str, Config) => (product_name, product_info)
193     :param config Config: The global configuration
194     :param logger Logger: The logger instance to use for the display 
195                           and logging
196     :return: 1 if it fails, else 0.
197     :rtype: int
198     '''
199     
200     p_name, __ = p_name_info
201     
202     # Logging
203     logger.write("\n", 4, False)
204     logger.write("################ ", 4)
205     header = _("Compilation of %s") % src.printcolors.printcLabel(p_name)
206     header += " %s " % ("." * (20 - len(p_name)))
207     logger.write(header, 3)
208     logger.write("\n", 4, False)
209     logger.flush()
210     
211     # Execute "sat configure", "sat make" and "sat install"
212     len_end_line = 20
213     res = 0
214
215     log_step(logger, header, "CONFIGURE")
216     res_c = sat.configure(config.VARS.application + " --products " + p_name, verbose = 0)
217     log_res_step(logger, res_c)
218     res += res_c
219     
220     log_step(logger, header, "MAKE")
221     res_c = sat.make(config.VARS.application + " --products " + p_name, verbose = 0)
222     log_res_step(logger, res_c)
223     res += res_c
224
225     log_step(logger, header, "MAKE INSTALL")
226     res_c = sat.makeinstall(config.VARS.application + " --products " + p_name, verbose = 0)
227     log_res_step(logger, res_c)
228     res += res_c
229     
230     # Log the result
231     if res > 0:
232         logger.write("\r%s%s" % (header, " " * len_end_line), 3)
233         logger.write("\r" + header + src.printcolors.printcError("KO"))
234         logger.write("==== %(KO)s in compile of %(name)s \n" %
235             { "name" : p_name , "KO" : src.printcolors.printcInfo("ERROR")}, 4)
236         logger.flush()
237     else:
238         logger.write("\r%s%s" % (header, " " * len_end_line), 3)
239         logger.write("\r" + header + src.printcolors.printcSuccess("OK"))
240         logger.write("==== %s \n" % src.printcolors.printcInfo("OK"), 4)
241         logger.write("==== Make of %(name)s %(OK)s \n" %
242             { "name" : p_name , "OK" : src.printcolors.printcInfo("OK")}, 4)
243         logger.flush()
244     logger.write("\n", 3, False)
245
246     return res
247
248 def description():
249     '''method that is called when salomeTools is called with --help option.
250     
251     :return: The text to display for the compile command description.
252     :rtype: str
253     '''
254     return _("The compile command construct the products of the application")
255   
256 def run(args, runner, logger):
257     '''method that is called when salomeTools is called with compile parameter.
258     '''
259     
260     # Parse the options
261     (options, args) = parser.parse_args(args)
262
263     # check that the command has been called with an application
264     src.check_config_has_application( runner.cfg )
265
266     # Get the list of products to treat
267     products_infos = get_products_list(options, runner.cfg, logger)
268
269     # Sort the list regarding the dependencies of the products
270     products_infos = sort_products(runner.cfg, products_infos)
271
272     # Print some informations
273     logger.write(_('Executing the compile command in the build '
274                                 'directories of the application %s\n') % 
275                 src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
276     
277     info = [
278             (_("SOURCE directory"),
279              os.path.join(runner.cfg.APPLICATION.workdir, 'SOURCES')),
280             (_("BUILD directory"),
281              os.path.join(runner.cfg.APPLICATION.workdir, 'BUILD'))
282             ]
283     src.print_info(logger, info)
284     
285     # Call the function that will loop over all the products and execute
286     # the right command(s)
287     res = compile_all_products(runner, runner.cfg, products_infos, logger)
288     
289     # Print the final state
290     nb_products = len(products_infos)
291     if res == 0:
292         final_status = "OK"
293     else:
294         final_status = "KO"
295    
296     logger.write(_("\nCompilation: %(status)s (%(valid_result)d/%(nb_products)d)\n") % \
297         { 'status': src.printcolors.printc(final_status), 
298           'valid_result': nb_products - res,
299           'nb_products': nb_products }, 1)    
300     
301     return res