diff options
| -rw-r--r-- | git_config.py | 2 | ||||
| -rw-r--r-- | git_refs.py | 5 | ||||
| -rw-r--r-- | manifest_xml.py | 2 | ||||
| -rw-r--r-- | platform_utils.py | 126 | ||||
| -rwxr-xr-x | project.py | 16 | ||||
| -rw-r--r-- | subcmds/status.py | 3 | ||||
| -rw-r--r-- | subcmds/sync.py | 26 |
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 | ||
| 16 | import os | 16 | import os |
| 17 | from trace import Trace | 17 | from trace import Trace |
| 18 | import platform_utils | ||
| 18 | 19 | ||
| 19 | HEAD = 'HEAD' | 20 | HEAD = 'HEAD' |
| 20 | R_CHANGES = 'refs/changes/' | 21 | R_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 | ||
| 223 | def _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 | |||
| 223 | def rmtree(path): | 243 | def 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 | ||
| 236 | def rename(src, dst): | 259 | def 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 | ||
| 252 | def remove(path): | 278 | def 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 | ||
| 302 | def 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 | |||
| 313 | def _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 | |||
| 339 | def 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 | |||
| 347 | def 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 | |||
| 355 | def 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 | |||
| 269 | def islink(path): | 363 | def 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 | ||
| @@ -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 | |||
| 26 | import os | 26 | import os |
| 27 | 27 | ||
| 28 | from color import Coloring | 28 | from color import Coloring |
| 29 | import platform_utils | ||
| 29 | 30 | ||
| 30 | class Status(PagedCommand): | 31 | class 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) |
