Salome HOME
[bos #32522][EDF] SALOME on Demand. Added extension_remover module.
[modules/kernel.git] / bin / SalomeOnDemandTK / extension_remover.py
1 #!/usr/bin/env python3
2 # -*- coding:utf-8 -*-
3 # Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24
25 #  File   : extension_remover.py
26 #  Author : Konstantin Leontev, Open Cascade
27 #
28 #  @package SalomeOnDemandTK
29 #  @brief Set of utility to unpack SALOME python extensions.
30
31 """Set of utility to remove SALOME python extensions.
32 """
33
34 import os
35 import sys
36 import shutil
37 from traceback import format_exc
38
39 from .extension_utilities import logger, \
40     DFILE_EXT, CFILE_EXT, SALOME_EXTDIR, ENVPYFILE_SUF, \
41     isvalid_dirname, list_dependants, is_empty_dir
42
43
44 def remove_if_empty(top_dir, directory):
45     """
46     Recursively remove empty directories from the given one to the top.
47
48     Args:
49         top_dir - top parent directory that can be removed as well
50         directory - the given directory
51
52     Returns:
53         None.
54     """
55
56     #logger.debug('Check if %s is empty...', directory)
57     if not is_empty_dir(directory):
58         return
59
60     logger.debug('Directory %s is empty. Remove it.', directory)
61     os.rmdir(directory)
62
63     # Don't go up than top root
64     if top_dir == directory:
65         return
66
67     # Remove the parent dir as well
68     parent_dir = os.path.abspath(os.path.join(directory, os.pardir))
69     remove_if_empty(top_dir, parent_dir)
70
71
72 def remove_bylist(root_dir, salomexc):
73     """
74     Remove files and directories listed in the given salomexc file.
75
76     Args:
77         root_dir - a root dir for listed files
78         salomexc - file that contents a list of files to remove.
79
80     Returns:
81         True if all the files were deleted without critical errors.
82     """
83
84     logger.debug('Remove files from %s dir listed in %s...',
85         root_dir, salomexc)
86
87     try:
88         with open(salomexc, 'r', encoding='UTF-8') as file:
89             for line in file:
90                 path_to_remove = os.path.join(root_dir, line.strip())
91                 logger.debug('Remove file %s...', path_to_remove)
92
93                 if os.path.isfile(path_to_remove):
94                     os.remove(path_to_remove)
95
96                     # Remove the parent folder if empty
97                     parent_dir = os.path.dirname(path_to_remove)
98                     remove_if_empty(root_dir, parent_dir)
99
100                 elif os.path.islink(path_to_remove):
101                     os.unlink(path_to_remove)
102
103                     # Remove the parent folder if empty
104                     parent_dir = os.path.dirname(path_to_remove)
105                     remove_if_empty(root_dir, parent_dir)
106
107                 elif os.path.isdir(path_to_remove):
108                     logger.warning('Directories are not expected to be listed in %s file! '
109                         'Remove %s anyway.',
110                         salomexc, path_to_remove)
111                     # Use instead of rmdir here, because dir can be not empty
112                     shutil.rmtree(path_to_remove)
113
114                 else:
115                     logger.error('Unexpected path %s!'
116                         'It is not a file or directory. Skip.',
117                         path_to_remove)
118
119     except OSError:
120         logger.error(format_exc())
121         return False
122
123     return True
124
125 def remove_salomex(install_dir, salomex_name):
126     """
127     Remove a salome extension from SALOME install root.
128
129     Args:
130         salome_root - path to SALOME install root directory.
131         salomex_name - a name of salome extension to remove.
132
133     Returns:
134         None.
135     """
136
137     logger.debug('Starting remove a salome extension %s', salomex_name)
138
139     # Check if provided dirname is valid
140     if not isvalid_dirname(install_dir):
141         return
142
143     # Check if the given extension is installed
144     logger.debug('Check if an extension %s is installed:', salomex_name)
145
146     logger.debug('Try to find %s file...', DFILE_EXT)
147     salomexd = os.path.join(install_dir, salomex_name + '.' + DFILE_EXT)
148     has_salomexd = os.path.isfile(salomexd)
149     if not has_salomexd:
150         logger.debug('Cannot find a description file %s for extension %s! '
151             'Extension has been already removed or %s file was deleted by mistake. '
152             'In the former case we can use %s file to clean up.',
153             salomexd, salomex_name, DFILE_EXT, CFILE_EXT)
154
155     logger.debug('Try to find %s file...', CFILE_EXT)
156     salomexc = os.path.join(install_dir, salomex_name + '.' + CFILE_EXT)
157     if not os.path.isfile(salomexc):
158         logger.debug('Cannot find %s for extension %s! '
159             'Going to exit from extension removing process.',
160             salomexc, salomex_name)
161         return
162
163     # Check if we cannot remove an extension because of dependencies
164     dependants = list_dependants(install_dir, salomex_name)
165     if len(dependants) > 0:
166         logger.error('Cannot remove an extension %s because followed extensions depend on it: %s! '
167             'Going to exit from extension removing process.',
168             salomex_name, dependants)
169         return
170
171     # Try to remove all the files listed in the control file
172     if not remove_bylist(os.path.join(install_dir, SALOME_EXTDIR), salomexc):
173         return
174
175     # Remove control file
176     os.remove(salomexc)
177
178     # Remove env file
179     env_py = os.path.join(install_dir, salomex_name + ENVPYFILE_SUF)
180     if os.path.isfile(env_py):
181         os.remove(env_py)
182     else:
183         logger.error('Cannot find and remove %s file! ', env_py)
184
185     # Remove description file
186     if has_salomexd:
187         os.remove(salomexd)
188
189     logger.debug('An extension %s was removed from %s',
190         salomex_name, install_dir)
191
192
193 if __name__ == '__main__':
194     if len(sys.argv) == 3:
195         arg_1, arg_2 = sys.argv[1:] # pylint: disable=unbalanced-tuple-unpacking
196         remove_salomex(arg_1, arg_2)
197     else:
198         logger.error('You must provide all the arguments!')
199         logger.info(remove_salomex.__doc__)