source: rtems-source-builder/source-builder/sb/path.py @ 4578e0a

5
Last change on this file since 4578e0a was 4578e0a, checked in by Chris Johns <chrisj@…>, on 08/08/17 at 10:16:03

sb: Fix long path support in copy_tree and removeall path support.

The change lets the LM32 target build on Windows which has temporary
install paths greater than the Win32 API max size. The buildroot path
compression is still needed as the GNU assembler does not like paths
that exceed the max Windows limit.

Closes #2992.

  • Property mode set to 100644
File size: 8.9 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#
21# Manage paths locally. The internally the path is in Unix or shell format and
22# we convert to the native format when performing operations at the Python
23# level. This allows macro expansion to work.
24#
25
26from __future__ import print_function
27
28import log
29import os
30import shutil
31import stat
32import string
33
34import error
35
36windows = os.name == 'nt'
37win_maxpath = 254
38
39def host(path):
40    if path is not None:
41        while '//' in path:
42            path = path.replace('//', '/')
43        if windows:
44            if len(path) > 2 and \
45               path[0] == '/' and path[2] == '/' and \
46               (path[1] in string.ascii_lowercase or \
47                path[1] in string.ascii_uppercase):
48                path = '%s:%s' % (path[1], path[2:])
49            path = path.replace('/', '\\')
50            if len(path) > win_maxpath:
51                if path.startswith('\\\\?\\'):
52                    path = path[4:]
53                path = u'\\'.join([u'\\\\?', path])
54    return path
55
56def shell(path):
57    if path is not None:
58        if windows:
59            path = path.encode('ascii', 'ignore')
60            if path.startswith('\\\\?\\'):
61                path = path[4:]
62            if len(path) > 1 and path[1] == ':':
63                path = '/%s%s' % (path[0], path[2:])
64            path = path.replace('\\', '/')
65        while '//' in path:
66            path = path.replace('//', '/')
67    return path
68
69def basename(path):
70    return shell(os.path.basename(path))
71
72def dirname(path):
73    return shell(os.path.dirname(path))
74
75def join(path, *args):
76    path = shell(path)
77    for arg in args:
78        if len(path):
79            path += '/' + shell(arg)
80        else:
81            path = shell(arg)
82    return shell(path)
83
84def abspath(path):
85    return shell(os.path.abspath(host(path)))
86
87def splitext(path):
88    root, ext = os.path.splitext(host(path))
89    return shell(root), ext
90
91def listdir(path):
92    hp = host(path)
93    if not os.path.exists(hp):
94        return []
95    return os.listdir(hp)
96
97def exists(paths):
98    def _exists(p):
99        return os.path.basename(p) in listdir(dirname(p))
100
101    if type(paths) == list:
102        results = []
103        for p in paths:
104            results += [_exists(p)]
105        return results
106    return _exists(paths)
107
108def isdir(path):
109    return os.path.isdir(host(path))
110
111def isfile(path):
112    return os.path.isfile(host(path))
113
114def isabspath(path):
115    return path[0] == '/'
116
117def iswritable(path):
118    return os.access(host(path), os.W_OK)
119
120def ispathwritable(path):
121    path = host(path)
122    while len(path) != 0:
123        if exists(path):
124            return iswritable(path)
125        path = os.path.dirname(path)
126    return False
127
128def mkdir(path):
129    path = host(path)
130    if exists(path):
131        if not isdir(path):
132            raise error.general('path exists and is not a directory: %s' % (path))
133    else:
134        if windows:
135            try:
136                os.makedirs(host(path))
137            except IOError as err:
138                raise error.general('cannot make directory: %s' % (path))
139            except OSError as err:
140                raise error.general('cannot make directory: %s' % (path))
141            except WindowsError as err:
142                raise error.general('cannot make directory: %s' % (path))
143        else:
144            try:
145                os.makedirs(host(path))
146            except IOError as err:
147                raise error.general('cannot make directory: %s' % (path))
148            except OSError as err:
149                raise error.general('cannot make directory: %s' % (path))
150
151def chdir(path):
152    os.chdir(host(path))
153
154def removeall(path):
155    #
156    # Perform the removal of the directory tree manually so we can
157    # make sure on Windows the files are correctly encoded to avoid
158    # the file name size limit. On Windows the os.walk fails once we
159    # get to the max path length on Windows.
160    #
161    def _isdir(path):
162        hpath = host(path)
163        return os.path.isdir(hpath) and not os.path.islink(hpath)
164
165    def _remove_node(path):
166        hpath = host(path)
167        if not os.path.islink(hpath) and not os.access(hpath, os.W_OK):
168            os.chmod(hpath, stat.S_IWUSR)
169        if _isdir(path):
170            os.rmdir(hpath)
171        else:
172            os.unlink(hpath)
173
174    def _remove(path):
175        dirs = []
176        for name in listdir(path):
177            path_ = join(path, name)
178            hname = host(path_)
179            if _isdir(path_):
180                dirs += [name]
181            else:
182                _remove_node(path_)
183        for name in dirs:
184            dir = join(path, name)
185            _remove(dir)
186            _remove_node(dir)
187
188    hpath = host(path)
189
190    if os.path.exists(hpath):
191        _remove(path)
192        _remove_node(path)
193
194def expand(name, paths):
195    l = []
196    for p in paths:
197        l += [join(p, name)]
198    return l
199
200def copy(src, dst):
201    hsrc = host(src)
202    hdst = host(dst)
203    try:
204        shutil.copy(hsrc, hdst)
205    except OSError as why:
206        if windows:
207            if WindowsError is not None and isinstance(why, WindowsError):
208                pass
209        else:
210            raise error.general('copying tree (1): %s -> %s: %s' % (hsrc, hdst, str(why)))
211
212def copy_tree(src, dst):
213    trace = False
214
215    hsrc = host(src)
216    hdst = host(dst)
217
218    if exists(hsrc):
219        names = listdir(hsrc)
220    else:
221        names = []
222
223    if trace:
224        print('path.copy_tree:')
225        print('   src: "%s"' % (src))
226        print('  hsrc: "%s"' % (hsrc))
227        print('   dst: "%s"' % (dst))
228        print('  hdst: "%s"' % (hdst))
229        print(' names: %r' % (names))
230
231    if not os.path.isdir(hdst):
232        if trace:
233            print(' mkdir: %s' % (hdst))
234        try:
235            os.makedirs(hdst)
236        except OSError as why:
237            raise error.general('copying tree: cannot create target directory %s: %s' % \
238                                (hdst, str(why)))
239
240    for name in names:
241        srcname = host(os.path.join(hsrc, name))
242        dstname = host(os.path.join(hdst, name))
243        try:
244            if os.path.islink(srcname):
245                linkto = os.readlink(srcname)
246                if exists(dstname):
247                    if os.path.islink(dstname):
248                        dstlinkto = os.readlink(dstname)
249                        if linkto != dstlinkto:
250                            log.warning('copying tree: link does not match: %s -> %s' % \
251                                            (dstname, dstlinkto))
252                            os.remove(dstname)
253                    else:
254                        log.warning('copying tree: destination is not a link: %s' % \
255                                        (dstname))
256                        os.remove(dstname)
257                else:
258                    os.symlink(linkto, dstname)
259            elif os.path.isdir(srcname):
260                copy_tree(srcname, dstname)
261            else:
262                shutil.copyfile(host(srcname), host(dstname))
263                shutil.copystat(host(srcname), host(dstname))
264        except shutil.Error as err:
265            raise error.general('copying tree (2): %s -> %s: %s' % \
266                                (hsrc, hdst, str(err)))
267        except EnvironmentError as why:
268            raise error.general('copying tree (3): %s -> %s: %s' % \
269                                (srcname, dstname, str(why)))
270    try:
271        shutil.copystat(hsrc, hdst)
272    except OSError as why:
273        if windows:
274            if WindowsError is not None and isinstance(why, WindowsError):
275                pass
276        else:
277            raise error.general('copying tree (4): %s -> %s: %s' % (hsrc, hdst, str(why)))
278
279if __name__ == '__main__':
280    print(host('/a/b/c/d-e-f'))
281    print(host('//a/b//c/d-e-f'))
282    print(shell('/w/x/y/z'))
283    print(basename('/as/sd/df/fg/me.txt'))
284    print(dirname('/as/sd/df/fg/me.txt'))
285    print(join('/d', 'g', '/tyty/fgfg'))
286    windows = True
287    print(host('/a/b/c/d-e-f'))
288    print(host('//a/b//c/d-e-f'))
289    print(shell('/w/x/y/z'))
290    print(shell('w:/x/y/z'))
291    print(basename('x:/sd/df/fg/me.txt'))
292    print(dirname('x:/sd/df/fg/me.txt'))
293    print(join('s:/d/e\\f/g', '/h', '/tyty/zxzx', '\\mm\\nn/p'))
Note: See TracBrowser for help on using the repository browser.