summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--git_config.py2
-rw-r--r--git_refs.py5
-rw-r--r--manifest_xml.py2
-rw-r--r--platform_utils.py126
-rwxr-xr-xproject.py16
-rw-r--r--subcmds/status.py3
-rw-r--r--subcmds/sync.py26
7 files changed, 138 insertions, 42 deletions
diff --git a/git_config.py b/git_config.py
index 70b22ce1..aac08855 100644
--- a/git_config.py
+++ b/git_config.py
@@ -503,7 +503,7 @@ def close_ssh():
503 d = ssh_sock(create=False) 503 d = ssh_sock(create=False)
504 if d: 504 if d:
505 try: 505 try:
506 os.rmdir(os.path.dirname(d)) 506 platform_utils.rmdir(os.path.dirname(d))
507 except OSError: 507 except OSError:
508 pass 508 pass
509 509
diff --git a/git_refs.py b/git_refs.py
index 7feaffb1..e0a85d7a 100644
--- a/git_refs.py
+++ b/git_refs.py
@@ -15,6 +15,7 @@
15 15
16import os 16import os
17from trace import Trace 17from trace import Trace
18import platform_utils
18 19
19HEAD = 'HEAD' 20HEAD = 'HEAD'
20R_CHANGES = 'refs/changes/' 21R_CHANGES = 'refs/changes/'
@@ -127,9 +128,9 @@ class GitRefs(object):
127 128
128 def _ReadLoose(self, prefix): 129 def _ReadLoose(self, prefix):
129 base = os.path.join(self._gitdir, prefix) 130 base = os.path.join(self._gitdir, prefix)
130 for name in os.listdir(base): 131 for name in platform_utils.listdir(base):
131 p = os.path.join(base, name) 132 p = os.path.join(base, name)
132 if os.path.isdir(p): 133 if platform_utils.isdir(p):
133 self._mtime[prefix] = os.path.getmtime(base) 134 self._mtime[prefix] = os.path.getmtime(base)
134 self._ReadLoose(prefix + name + '/') 135 self._ReadLoose(prefix + name + '/')
135 elif name.endswith('.lock'): 136 elif name.endswith('.lock'):
diff --git a/manifest_xml.py b/manifest_xml.py
index 81a6a858..f37732cd 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -446,7 +446,7 @@ class XmlManifest(object):
446 446
447 local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)) 447 local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME))
448 try: 448 try:
449 for local_file in sorted(os.listdir(local_dir)): 449 for local_file in sorted(platform_utils.listdir(local_dir)):
450 if local_file.endswith('.xml'): 450 if local_file.endswith('.xml'):
451 local = os.path.join(local_dir, local_file) 451 local = os.path.join(local_dir, local_file)
452 nodes.append(self._ParseManifestXml(local, self.repodir)) 452 nodes.append(self._ParseManifestXml(local, self.repodir))
diff --git a/platform_utils.py b/platform_utils.py
index a3e96531..b2cc2459 100644
--- a/platform_utils.py
+++ b/platform_utils.py
@@ -187,10 +187,10 @@ def symlink(source, link_name):
187 source = _validate_winpath(source) 187 source = _validate_winpath(source)
188 link_name = _validate_winpath(link_name) 188 link_name = _validate_winpath(link_name)
189 target = os.path.join(os.path.dirname(link_name), source) 189 target = os.path.join(os.path.dirname(link_name), source)
190 if os.path.isdir(target): 190 if isdir(target):
191 platform_utils_win32.create_dirsymlink(source, link_name) 191 platform_utils_win32.create_dirsymlink(_makelongpath(source), link_name)
192 else: 192 else:
193 platform_utils_win32.create_filesymlink(source, link_name) 193 platform_utils_win32.create_filesymlink(_makelongpath(source), link_name)
194 else: 194 else:
195 return os.symlink(source, link_name) 195 return os.symlink(source, link_name)
196 196
@@ -220,9 +220,32 @@ def _winpath_is_valid(path):
220 return not drive # "x:" is invalid 220 return not drive # "x:" is invalid
221 221
222 222
223def _makelongpath(path):
224 """Return the input path normalized to support the Windows long path syntax
225 ("\\\\?\\" prefix) if needed, i.e. if the input path is longer than the
226 MAX_PATH limit.
227 """
228 if isWindows():
229 # Note: MAX_PATH is 260, but, for directories, the maximum value is actually 246.
230 if len(path) < 246:
231 return path
232 if path.startswith(u"\\\\?\\"):
233 return path
234 if not os.path.isabs(path):
235 return path
236 # Append prefix and ensure unicode so that the special longpath syntax
237 # is supported by underlying Win32 API calls
238 return u"\\\\?\\" + os.path.normpath(path)
239 else:
240 return path
241
242
223def rmtree(path): 243def rmtree(path):
244 """shutil.rmtree(path) wrapper with support for long paths on Windows.
245
246 Availability: Unix, Windows."""
224 if isWindows(): 247 if isWindows():
225 shutil.rmtree(path, onerror=handle_rmtree_error) 248 shutil.rmtree(_makelongpath(path), onerror=handle_rmtree_error)
226 else: 249 else:
227 shutil.rmtree(path) 250 shutil.rmtree(path)
228 251
@@ -234,15 +257,18 @@ def handle_rmtree_error(function, path, excinfo):
234 257
235 258
236def rename(src, dst): 259def rename(src, dst):
260 """os.rename(src, dst) wrapper with support for long paths on Windows.
261
262 Availability: Unix, Windows."""
237 if isWindows(): 263 if isWindows():
238 # On Windows, rename fails if destination exists, see 264 # On Windows, rename fails if destination exists, see
239 # https://docs.python.org/2/library/os.html#os.rename 265 # https://docs.python.org/2/library/os.html#os.rename
240 try: 266 try:
241 os.rename(src, dst) 267 os.rename(_makelongpath(src), _makelongpath(dst))
242 except OSError as e: 268 except OSError as e:
243 if e.errno == errno.EEXIST: 269 if e.errno == errno.EEXIST:
244 os.remove(dst) 270 os.remove(_makelongpath(dst))
245 os.rename(src, dst) 271 os.rename(_makelongpath(src), _makelongpath(dst))
246 else: 272 else:
247 raise 273 raise
248 else: 274 else:
@@ -250,30 +276,98 @@ def rename(src, dst):
250 276
251 277
252def remove(path): 278def remove(path):
253 """Remove (delete) the file path. This is a replacement for os.remove, but 279 """Remove (delete) the file path. This is a replacement for os.remove that
254 allows deleting read-only files on Windows. 280 allows deleting read-only files on Windows, with support for long paths and
255 """ 281 for deleting directory symbolic links.
282
283 Availability: Unix, Windows."""
256 if isWindows(): 284 if isWindows():
285 longpath = _makelongpath(path)
257 try: 286 try:
258 os.remove(path) 287 os.remove(longpath)
259 except OSError as e: 288 except OSError as e:
260 if e.errno == errno.EACCES: 289 if e.errno == errno.EACCES:
261 os.chmod(path, stat.S_IWRITE) 290 os.chmod(longpath, stat.S_IWRITE)
262 os.remove(path) 291 # Directory symbolic links must be deleted with 'rmdir'.
292 if islink(longpath) and isdir(longpath):
293 os.rmdir(longpath)
294 else:
295 os.remove(longpath)
263 else: 296 else:
264 raise 297 raise
265 else: 298 else:
266 os.remove(path) 299 os.remove(path)
267 300
268 301
302def walk(top, topdown=True, onerror=None, followlinks=False):
303 """os.walk(path) wrapper with support for long paths on Windows.
304
305 Availability: Windows, Unix.
306 """
307 if isWindows():
308 return _walk_windows_impl(top, topdown, onerror, followlinks)
309 else:
310 return os.walk(top, topdown, onerror, followlinks)
311
312
313def _walk_windows_impl(top, topdown, onerror, followlinks):
314 try:
315 names = listdir(top)
316 except error, err:
317 if onerror is not None:
318 onerror(err)
319 return
320
321 dirs, nondirs = [], []
322 for name in names:
323 if isdir(os.path.join(top, name)):
324 dirs.append(name)
325 else:
326 nondirs.append(name)
327
328 if topdown:
329 yield top, dirs, nondirs
330 for name in dirs:
331 new_path = os.path.join(top, name)
332 if followlinks or not islink(new_path):
333 for x in _walk_windows_impl(new_path, topdown, onerror, followlinks):
334 yield x
335 if not topdown:
336 yield top, dirs, nondirs
337
338
339def listdir(path):
340 """os.listdir(path) wrapper with support for long paths on Windows.
341
342 Availability: Windows, Unix.
343 """
344 return os.listdir(_makelongpath(path))
345
346
347def rmdir(path):
348 """os.rmdir(path) wrapper with support for long paths on Windows.
349
350 Availability: Windows, Unix.
351 """
352 os.rmdir(_makelongpath(path))
353
354
355def isdir(path):
356 """os.path.isdir(path) wrapper with support for long paths on Windows.
357
358 Availability: Windows, Unix.
359 """
360 return os.path.isdir(_makelongpath(path))
361
362
269def islink(path): 363def islink(path):
270 """Test whether a path is a symbolic link. 364 """os.path.islink(path) wrapper with support for long paths on Windows.
271 365
272 Availability: Windows, Unix. 366 Availability: Windows, Unix.
273 """ 367 """
274 if isWindows(): 368 if isWindows():
275 import platform_utils_win32 369 import platform_utils_win32
276 return platform_utils_win32.islink(path) 370 return platform_utils_win32.islink(_makelongpath(path))
277 else: 371 else:
278 return os.path.islink(path) 372 return os.path.islink(path)
279 373
@@ -288,7 +382,7 @@ def readlink(path):
288 """ 382 """
289 if isWindows(): 383 if isWindows():
290 import platform_utils_win32 384 import platform_utils_win32
291 return platform_utils_win32.readlink(path) 385 return platform_utils_win32.readlink(_makelongpath(path))
292 else: 386 else:
293 return os.readlink(path) 387 return os.readlink(path)
294 388
diff --git a/project.py b/project.py
index d551351b..ddcffdd6 100755
--- a/project.py
+++ b/project.py
@@ -103,7 +103,7 @@ def _ProjectHooks():
103 if _project_hook_list is None: 103 if _project_hook_list is None:
104 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__))) 104 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
105 d = os.path.join(d, 'hooks') 105 d = os.path.join(d, 'hooks')
106 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)] 106 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
107 return _project_hook_list 107 return _project_hook_list
108 108
109 109
@@ -253,7 +253,7 @@ class _CopyFile(object):
253 platform_utils.remove(dest) 253 platform_utils.remove(dest)
254 else: 254 else:
255 dest_dir = os.path.dirname(dest) 255 dest_dir = os.path.dirname(dest)
256 if not os.path.isdir(dest_dir): 256 if not platform_utils.isdir(dest_dir):
257 os.makedirs(dest_dir) 257 os.makedirs(dest_dir)
258 shutil.copy(src, dest) 258 shutil.copy(src, dest)
259 # make the file read-only 259 # make the file read-only
@@ -282,7 +282,7 @@ class _LinkFile(object):
282 platform_utils.remove(absDest) 282 platform_utils.remove(absDest)
283 else: 283 else:
284 dest_dir = os.path.dirname(absDest) 284 dest_dir = os.path.dirname(absDest)
285 if not os.path.isdir(dest_dir): 285 if not platform_utils.isdir(dest_dir):
286 os.makedirs(dest_dir) 286 os.makedirs(dest_dir)
287 platform_utils.symlink(relSrc, absDest) 287 platform_utils.symlink(relSrc, absDest)
288 except IOError: 288 except IOError:
@@ -302,7 +302,7 @@ class _LinkFile(object):
302 else: 302 else:
303 # Entity doesn't exist assume there is a wild card 303 # Entity doesn't exist assume there is a wild card
304 absDestDir = self.abs_dest 304 absDestDir = self.abs_dest
305 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir): 305 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
306 _error('Link error: src with wildcard, %s must be a directory', 306 _error('Link error: src with wildcard, %s must be a directory',
307 absDestDir) 307 absDestDir)
308 else: 308 else:
@@ -750,7 +750,7 @@ class Project(object):
750 750
751 @property 751 @property
752 def Exists(self): 752 def Exists(self):
753 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir) 753 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
754 754
755 @property 755 @property
756 def CurrentBranch(self): 756 def CurrentBranch(self):
@@ -931,7 +931,7 @@ class Project(object):
931 quiet: If True then only print the project name. Do not print 931 quiet: If True then only print the project name. Do not print
932 the modified files, branch name, etc. 932 the modified files, branch name, etc.
933 """ 933 """
934 if not os.path.isdir(self.worktree): 934 if not platform_utils.isdir(self.worktree):
935 if output_redir is None: 935 if output_redir is None:
936 output_redir = sys.stdout 936 output_redir = sys.stdout
937 print(file=output_redir) 937 print(file=output_redir)
@@ -2510,7 +2510,7 @@ class Project(object):
2510 2510
2511 to_copy = [] 2511 to_copy = []
2512 if copy_all: 2512 if copy_all:
2513 to_copy = os.listdir(gitdir) 2513 to_copy = platform_utils.listdir(gitdir)
2514 2514
2515 dotgit = platform_utils.realpath(dotgit) 2515 dotgit = platform_utils.realpath(dotgit)
2516 for name in set(to_copy).union(to_symlink): 2516 for name in set(to_copy).union(to_symlink):
@@ -2529,7 +2529,7 @@ class Project(object):
2529 platform_utils.symlink( 2529 platform_utils.symlink(
2530 os.path.relpath(src, os.path.dirname(dst)), dst) 2530 os.path.relpath(src, os.path.dirname(dst)), dst)
2531 elif copy_all and not platform_utils.islink(dst): 2531 elif copy_all and not platform_utils.islink(dst):
2532 if os.path.isdir(src): 2532 if platform_utils.isdir(src):
2533 shutil.copytree(src, dst) 2533 shutil.copytree(src, dst)
2534 elif os.path.isfile(src): 2534 elif os.path.isfile(src):
2535 shutil.copy(src, dst) 2535 shutil.copy(src, dst)
diff --git a/subcmds/status.py b/subcmds/status.py
index b47c8736..773f22d4 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -26,6 +26,7 @@ import itertools
26import os 26import os
27 27
28from color import Coloring 28from color import Coloring
29import platform_utils
29 30
30class Status(PagedCommand): 31class Status(PagedCommand):
31 common = True 32 common = True
@@ -115,7 +116,7 @@ the following meanings:
115 """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'""" 116 """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'"""
116 status_header = ' --\t' 117 status_header = ' --\t'
117 for item in dirs: 118 for item in dirs:
118 if not os.path.isdir(item): 119 if not platform_utils.isdir(item):
119 outstring.append(''.join([status_header, item])) 120 outstring.append(''.join([status_header, item]))
120 continue 121 continue
121 if item in proj_dirs: 122 if item in proj_dirs:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 943a0264..f6bd983d 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -474,8 +474,8 @@ later is required to fix a server side protocol bug.
474 # so rmtree works. 474 # so rmtree works.
475 try: 475 try:
476 platform_utils.rmtree(os.path.join(path, '.git')) 476 platform_utils.rmtree(os.path.join(path, '.git'))
477 except OSError: 477 except OSError as e:
478 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr) 478 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
479 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) 479 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
480 print(' remove manually, then run sync again', file=sys.stderr) 480 print(' remove manually, then run sync again', file=sys.stderr)
481 return -1 481 return -1
@@ -484,12 +484,12 @@ later is required to fix a server side protocol bug.
484 # another git project 484 # another git project
485 dirs_to_remove = [] 485 dirs_to_remove = []
486 failed = False 486 failed = False
487 for root, dirs, files in os.walk(path): 487 for root, dirs, files in platform_utils.walk(path):
488 for f in files: 488 for f in files:
489 try: 489 try:
490 platform_utils.remove(os.path.join(root, f)) 490 platform_utils.remove(os.path.join(root, f))
491 except OSError: 491 except OSError as e:
492 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr) 492 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
493 failed = True 493 failed = True
494 dirs[:] = [d for d in dirs 494 dirs[:] = [d for d in dirs
495 if not os.path.lexists(os.path.join(root, d, '.git'))] 495 if not os.path.lexists(os.path.join(root, d, '.git'))]
@@ -499,14 +499,14 @@ later is required to fix a server side protocol bug.
499 if platform_utils.islink(d): 499 if platform_utils.islink(d):
500 try: 500 try:
501 platform_utils.remove(d) 501 platform_utils.remove(d)
502 except OSError: 502 except OSError as e:
503 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) 503 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
504 failed = True 504 failed = True
505 elif len(os.listdir(d)) == 0: 505 elif len(platform_utils.listdir(d)) == 0:
506 try: 506 try:
507 os.rmdir(d) 507 platform_utils.rmdir(d)
508 except OSError: 508 except OSError as e:
509 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) 509 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
510 failed = True 510 failed = True
511 continue 511 continue
512 if failed: 512 if failed:
@@ -517,8 +517,8 @@ later is required to fix a server side protocol bug.
517 # Try deleting parent dirs if they are empty 517 # Try deleting parent dirs if they are empty
518 project_dir = path 518 project_dir = path
519 while project_dir != self.manifest.topdir: 519 while project_dir != self.manifest.topdir:
520 if len(os.listdir(project_dir)) == 0: 520 if len(platform_utils.listdir(project_dir)) == 0:
521 os.rmdir(project_dir) 521 platform_utils.rmdir(project_dir)
522 else: 522 else:
523 break 523 break
524 project_dir = os.path.dirname(project_dir) 524 project_dir = os.path.dirname(project_dir)