diff options
| author | Renaud Paquay <rpaquay@google.com> | 2016-11-01 11:24:03 -0700 |
|---|---|---|
| committer | David Pursehouse <dpursehouse@collab.net> | 2017-05-29 19:30:34 +0900 |
| commit | d5cec5e752821ca2710101b626b3a3ca07fdb7f8 (patch) | |
| tree | 4ecee491de2d3d57b4d03f526701c8c06a133b17 /platform_utils.py | |
| parent | 2e7029116204cf2d6f516e4514091f0b492bc689 (diff) | |
| download | git-repo-d5cec5e752821ca2710101b626b3a3ca07fdb7f8.tar.gz | |
Add support for creating symbolic links on Windows
Replace all calls to os.symlink with platform_utils.symlink.
The Windows implementation calls into the CreateSymbolicLinkW Win32
API, as os.symlink is not supported.
Separate the Win32 API definitions into a separate module
platform_utils_win32 for clarity.
Change-Id: I0714c598664c2df93383734e609d948692c17ec5
Diffstat (limited to 'platform_utils.py')
| -rw-r--r-- | platform_utils.py | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/platform_utils.py b/platform_utils.py index 1c719b1d..f4dfa0b1 100644 --- a/platform_utils.py +++ b/platform_utils.py | |||
| @@ -167,3 +167,46 @@ class _FileDescriptorStreamsThreads(FileDescriptorStreams): | |||
| 167 | self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, line)) | 167 | self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, line)) |
| 168 | self.fd.close() | 168 | self.fd.close() |
| 169 | self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, None)) | 169 | self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, None)) |
| 170 | |||
| 171 | |||
| 172 | def symlink(source, link_name): | ||
| 173 | """Creates a symbolic link pointing to source named link_name. | ||
| 174 | Note: On Windows, source must exist on disk, as the implementation needs | ||
| 175 | to know whether to create a "File" or a "Directory" symbolic link. | ||
| 176 | """ | ||
| 177 | if isWindows(): | ||
| 178 | import platform_utils_win32 | ||
| 179 | source = _validate_winpath(source) | ||
| 180 | link_name = _validate_winpath(link_name) | ||
| 181 | target = os.path.join(os.path.dirname(link_name), source) | ||
| 182 | if os.path.isdir(target): | ||
| 183 | platform_utils_win32.create_dirsymlink(source, link_name) | ||
| 184 | else: | ||
| 185 | platform_utils_win32.create_filesymlink(source, link_name) | ||
| 186 | else: | ||
| 187 | return os.symlink(source, link_name) | ||
| 188 | |||
| 189 | |||
| 190 | def _validate_winpath(path): | ||
| 191 | path = os.path.normpath(path) | ||
| 192 | if _winpath_is_valid(path): | ||
| 193 | return path | ||
| 194 | raise ValueError("Path \"%s\" must be a relative path or an absolute " | ||
| 195 | "path starting with a drive letter".format(path)) | ||
| 196 | |||
| 197 | |||
| 198 | def _winpath_is_valid(path): | ||
| 199 | """Windows only: returns True if path is relative (e.g. ".\\foo") or is | ||
| 200 | absolute including a drive letter (e.g. "c:\\foo"). Returns False if path | ||
| 201 | is ambiguous (e.g. "x:foo" or "\\foo"). | ||
| 202 | """ | ||
| 203 | assert isWindows() | ||
| 204 | path = os.path.normpath(path) | ||
| 205 | drive, tail = os.path.splitdrive(path) | ||
| 206 | if tail: | ||
| 207 | if not drive: | ||
| 208 | return tail[0] != os.sep # "\\foo" is invalid | ||
| 209 | else: | ||
| 210 | return tail[0] == os.sep # "x:foo" is invalid | ||
| 211 | else: | ||
| 212 | return not drive # "x:" is invalid | ||
