Salome HOME
ajout option d'impression des graphes de dépendance
[tools/sat.git] / commands / install.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 import shutil
21 import re
22 import subprocess
23
24 import src
25 import prepare
26 import src.debug as DBG
27
28 PACKAGE_EXT=".tar.gz" # the extension we use for the packages
29
30 # Define all possible option for patch command :  sat patch <options>
31 parser = src.options.Options()
32 parser.add_option('p', 'products', 'list2', 'products',
33     _('Optional: products from which to get the sources. This option accepts a comma separated list.'))
34
35
36 def get_binary_from_archive(config, product_name, product_info, install_dir, logger):
37     '''The method get the binary of the product from an archive
38     
39     :param config Config: The global configuration
40     :param product_name : The name of the product
41     :param product_info Config: The configuration specific to 
42                                the product to be prepared
43     :param install_dir Path: The Path instance corresponding to the 
44                             directory where to put the sources
45     :param logger Logger: The logger instance to use for the display and logging
46     :return: True if it succeed, else False
47     :rtype: boolean
48     '''
49
50
51     # check archive exists
52
53     # the expected name of the bin archive, as produced by sat package --bin_products
54     archive_name = product_name + '-' + product_info.version + "-" + config.VARS.dist + PACKAGE_EXT
55     # we search this archive in bin directory
56     bin_arch_name = os.path.join("bin",archive_name)
57     # search in the config.PATHS.ARCHIVEPATH
58     arch_path = src.find_file_in_lpath(archive_name, config.PATHS.ARCHIVEPATH, "bin")
59     if not arch_path:
60         # bin archive was not found locally in ARCHIVEPATH
61         # search on ftp site
62         logger.write("\n   The bin archive is not found on local file system, we try ftp\n", 3)
63         ret=src.find_file_in_ftppath(archive_name, config.PATHS.ARCHIVEFTP, 
64                                      config.LOCAL.archive_dir, logger, "bin")
65         
66         if ret:
67             # archive was found on ftp and stored in ret
68             arch_path = ret
69         else:
70             logger.write('%s  ' % src.printcolors.printc(src.OK_STATUS), 3, False) 
71             msg = _("Archive not found in ARCHIVEPATH, nor on ARCHIVEFTP: '%s'") % bin_arch_name
72             logger.write(msg, 3)
73             return 1
74
75     logger.write('arc:%s ... ' % 
76                  src.printcolors.printcInfo(archive_name),
77                  3, 
78                  False)
79     logger.flush()
80     # Call the system function that do the extraction in archive mode
81     retcode, NameExtractedDirectory = src.system.archive_extract(arch_path,
82                                       install_dir.dir(), logger)
83     
84     # Rename the source directory if 
85     # it does not match with product_info.source_dir
86     if (NameExtractedDirectory.replace('/', '') != 
87             os.path.basename(product_info.install_dir)):
88         shutil.move(os.path.join(os.path.dirname(product_info.install_dir), 
89                                  NameExtractedDirectory), 
90                     product_info.install_dir)
91     
92     return retcode
93
94
95
96 def get_all_product_binaries(config, products, logger):
97     '''Get all the product sources.
98     
99     :param config Config: The global configuration
100     :param products List: The list of tuples (product name, product informations)
101     :param logger Logger: The logger instance to be used for the logging
102     :return: the tuple (number of success, dictionary product_name/success_fail)
103     :rtype: (int,dict)
104     '''
105
106     # Initialize the variables that will count the fails and success
107     results = dict()
108     good_result = 0
109
110     # Get the maximum name length in order to format the terminal display
111     max_product_name_len = 1
112     if len(products) > 0:
113         max_product_name_len = max(map(lambda l: len(l), products[0])) + 4
114     
115     # The loop on all the products from which to get the binaries
116     for product_name, product_info in products:
117         # display and log
118         logger.write('%s: ' % src.printcolors.printcLabel(product_name), 3)
119         logger.write(' ' * (max_product_name_len - len(product_name)), 3, False)
120         logger.write("\n", 4, False)
121         #
122         do_install_prod=True
123         # check if there is something to do!
124         if src.product.product_is_fixed(product_info):
125             do_install_prod=False
126             msg = _("INFO : Not doing anything because the products %s is fixed\n") % product_name
127         elif src.product.product_is_native(product_info):
128             do_install_prod=False
129             msg = _("INFO : Not doing anything because the products %s is native\n") % product_name
130         elif src.appli_test_property(config,"pip", "yes") and \
131              src.product.product_test_property(product_info,"pip", "yes"):
132             do_install_prod=False
133             msg = _("INFO : Not doing anything because the products %s is managed by pip\n") % product_name
134         else:
135             install_dir=src.Path(product_info.install_dir) 
136             if install_dir.exists():
137                 do_install_prod=False 
138                 msg = _("INFO : Not doing anything because the install directory already exists:\n    %s\n") % install_dir
139
140         if not do_install_prod:
141             logger.write('%s  ' % src.printcolors.printc(src.OK_STATUS), 3, False) 
142             logger.write(msg, 3)
143             good_result = good_result + 1  
144             # Do not get the binaries and go to next product
145             continue
146
147         # we neeed to install binaries for the product
148         retcode = get_binary_from_archive(config, product_name, product_info, install_dir, logger)
149
150         # Check that the sources are correctly get using the files to be tested
151         # in product information
152         if retcode:
153             pass
154             # CNC TODO check md5sum
155             #check_OK, wrong_path = check_sources(product_info, logger)
156             #if not check_OK:
157             #    # Print the missing file path
158             #    msg = _("The required file %s does not exists. " % wrong_path)
159             #    logger.write(src.printcolors.printcError("\nERROR: ") + msg, 3)
160             #    retcode = False
161 # does post install substitutions
162 #for f in $(grep -RIl -e /volatile/salome/jenkins/workspace/Salome_master_CO7/SALOME-9.7.0-CO7/INSTALL INSTALL); do
163 #     sed -i "
164 #        s?/volatile/salome/jenkins/workspace/Salome_master_CO7/SALOME-9.7.0-CO7/INSTALL?$(pwd)/INSTALL?g
165 #            " $f
166 #done
167
168
169         # show results
170         results[product_name] = retcode
171         if retcode:
172             # The case where it succeed
173             res = src.OK_STATUS
174             good_result = good_result + 1
175         else:
176             # The case where it failed
177             res = src.KO_STATUS
178         
179         # print the result
180         if do_install_prod:
181             logger.write('%s\n' % src.printcolors.printc(res), 3, False)
182
183     return good_result, results
184
185 def check_sources(product_info, logger):
186     '''Check that the sources are correctly get, using the files to be tested
187        in product information
188     
189     :param product_info Config: The configuration specific to 
190                                 the product to be prepared
191     :return: True if the files exists (or no files to test is provided).
192     :rtype: boolean
193     '''
194     # Get the files to test if there is any
195     if ("present_files" in product_info and 
196         "source" in product_info.present_files):
197         l_files_to_be_tested = product_info.present_files.source
198         for file_path in l_files_to_be_tested:
199             # The path to test is the source directory 
200             # of the product joined the file path provided
201             path_to_test = os.path.join(product_info.source_dir, file_path)
202             logger.write(_("\nTesting existence of file: \n"), 5)
203             logger.write(path_to_test, 5)
204             if not os.path.exists(path_to_test):
205                 return False, path_to_test
206             logger.write(src.printcolors.printcSuccess(" OK\n"), 5)
207     return True, ""
208
209 def description():
210     '''method that is called when salomeTools is called with --help option.
211     
212     :return: The text to display for the source command description.
213     :rtype: str
214     '''
215     return _("The install command gets the binaries of the application products "
216              "from local (ARCHIVEPATH) or ftp server.\n\nexample:"
217              "\nsat install SALOME-master --products GEOM,SMESH")
218   
219 def run(args, runner, logger):
220     '''method that is called when salomeTools is called with install parameter.
221     '''
222     DBG.write("install.run()", args)
223     # Parse the options
224     (options, args) = parser.parse_args(args)
225     
226     # check that the command has been called with an application
227     src.check_config_has_application( runner.cfg )
228
229     # Print some informations
230     logger.write(_('Getting binaries of the application %s\n') % 
231                 src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
232     src.printcolors.print_value(logger, 'workdir', 
233                                 runner.cfg.APPLICATION.workdir, 2)
234     logger.write("\n", 2, False)
235        
236
237     # Get the list of all application products, and create its dependency graph
238     all_products_infos = src.product.get_products_infos(runner.cfg.APPLICATION.products,
239                                                         runner.cfg)
240     from compile import get_dependencies_graph,depth_search_graph
241     all_products_graph=get_dependencies_graph(all_products_infos)
242     #logger.write("Dependency graph of all application products : %s\n" % all_products_graph, 6)
243     DBG.write("Dependency graph of all application products : ", all_products_graph)
244
245     products_infos=[]
246     if options.products is None:
247         #implicit selection of all products
248         products_infos = all_products_infos
249     else:
250         # a list of products is specified
251         products_list=options.products
252         # we evaluate the complete list including dependencies (~ to the --with-fathers of sat compile)
253
254         # Extend the list with all recursive dependencies of the given products
255         visited=[]
256         for p_name in products_list:
257             visited=depth_search_graph(all_products_graph, p_name, visited)
258         products_list = visited
259         logger.write("Product we have to compile (as specified by user) : %s\n" % products_list, 5)
260
261         #  Create a dict of all products to facilitate products_infos sorting
262         all_products_dict={}
263         for (pname,pinfo) in all_products_infos:
264             all_products_dict[pname]=(pname,pinfo)
265
266         # build products_infos for the products we have to install
267         for product in products_list:
268             products_infos.append(all_products_dict[product])
269
270
271     
272     # Call to the function that gets all the sources
273     good_result, results = get_all_product_binaries(runner.cfg, 
274                                                     products_infos,
275                                                     logger)
276
277     # Display the results (how much passed, how much failed, etc...)
278     status = src.OK_STATUS
279     details = []
280
281     logger.write("\n", 2, False)
282     if good_result == len(products_infos):
283         res_count = "%d / %d" % (good_result, good_result)
284     else:
285         status = src.KO_STATUS
286         res_count = "%d / %d" % (good_result, len(products_infos))
287
288         for product in results:
289             if results[product] == 0 or results[product] is None:
290                 details.append(product)
291
292     result = len(products_infos) - good_result
293
294     # write results
295     logger.write(_("Getting binaries of the application:"), 1)
296     logger.write(" " + src.printcolors.printc(status), 1, False)
297     logger.write(" (%s)\n" % res_count, 1, False)
298
299     if len(details) > 0:
300         logger.write(_("Following binaries haven't been get:\n"), 2)
301         logger.write(" ".join(details), 2)
302         logger.write("\n", 2, False)
303
304     return result