Skip to content

Commit a480e3c

Browse files
authored
Merge pull request #14 from davidanthoff/windows-support
Windows support
2 parents 26d23de + 923149f commit a480e3c

9 files changed

Lines changed: 109 additions & 55 deletions

File tree

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ os:
44
- linux
55
- osx
66
julia:
7-
- 0.5
87
- 0.6
98
- nightly
109
notifications:

REQUIRE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
julia 0.5
1+
julia 0.6
22
Compat 0.17.0

appveyor.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
environment:
22
matrix:
3-
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
4-
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
53
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
64
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
75

@@ -31,4 +29,7 @@ build_script:
3129
- C:\projects\julia\bin\julia -e "Pkg.clone(pwd(), \"FilePaths\"); versioninfo(); Pkg.build(\"FilePaths\")"
3230

3331
test_script:
34-
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"FilePaths\")"
32+
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"FilePaths\", coverage=true)"
33+
34+
after_test:
35+
- C:\projects\julia\bin\julia -e "cd(Pkg.dir(\"FilePaths\")); Pkg.add(\"Coverage\"); using Coverage; Codecov.submit(Codecov.process_folder())"

src/FilePaths.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export
1515
Status,
1616

1717
# Methods
18+
anchor,
1819
cwd,
20+
drive,
1921
home,
2022
parts,
2123
root,
@@ -66,6 +68,7 @@ Base.next(p::AbstractPath, i::Int) = next(String(p), i)
6668
Base.String(path::AbstractPath) = error("`String not implemented")
6769
parts(path::AbstractPath) = error("`parts` not implemented.")
6870
root(path::AbstractPath) = error("`root` not implemented.")
71+
drive(path::AbstractPath) = error("`drive` not implemented.")
6972

7073
Base.convert(::Type{AbstractPath}, x::AbstractString) = Path(x)
7174

src/libc.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function User(name::String)
7676
User(ps)
7777
end
7878

79-
function User(uid::UInt64)
79+
function User(uid::UInt)
8080
ps = @static if is_unix()
8181
ccall((:getpwuid, "libc"), Ptr{Cpasswd}, (UInt64,), uid)
8282
else
@@ -114,7 +114,7 @@ function Group(name::String)
114114
Group(ps)
115115
end
116116

117-
function Group(gid::UInt64)
117+
function Group(gid::UInt)
118118
gr = @static if is_unix()
119119
ccall((:getgrgid, "libc"), Ptr{Cgroup}, (UInt64,), gid)
120120
else

src/path.jl

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ Responsible for creating the appropriate platform specific path
99
"""
1010
Path() = @static is_unix() ? PosixPath() : WindowsPath()
1111
Path(path::AbstractPath) = path
12-
Path(pieces::Tuple) = @static is_unix() ? PosixPath(pieces) : WindowsPath(pieces, "")
12+
Path(pieces::Tuple) = @static is_unix() ? PosixPath(pieces) : WindowsPath(pieces)
1313
Path(str::AbstractString) = @static is_unix() ? PosixPath(str) : WindowsPath(str)
1414

15-
Base.show(io::IO, path::AbstractPath) = print(io, "p\"$(join(parts(path), '/'))\"")
16-
1715
"""
1816
@p_str -> Path
1917
@@ -27,6 +25,8 @@ end
2725
cwd() = Path(pwd())
2826
home() = Path(homedir())
2927

28+
anchor(path::AbstractPath) = drive(path) * root(path)
29+
3030
#=
3131
Path Modifiers
3232
===============================================
@@ -144,7 +144,7 @@ function extension(path::AbstractPath)
144144
end
145145

146146
"""
147-
extension(path::AbstractPath) -> AbstractString
147+
extensions(path::AbstractPath) -> AbstractString
148148
149149
Extracts all extensions from a filename if there any, otherwise it returns an empty string.
150150
@@ -250,14 +250,21 @@ function relative{T<:AbstractPath}(path::T, start::T=T("."))
250250
p = parts(abs(path))
251251
s = parts(abs(start))
252252

253-
p == s && return curdir
253+
p == s && return T(curdir)
254254

255255
i = 0
256256
while i < min(length(p), length(s))
257257
i += 1
258-
if p[i] != s[i]
259-
i -= 1
260-
break
258+
@static if is_windows()
259+
if lowercase(p[i]) != lowercase(s[i])
260+
i -= 1
261+
break
262+
end
263+
else
264+
if p[i] != s[i]
265+
i -= 1
266+
break
267+
end
261268
end
262269
end
263270

src/posix.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ end
2323
Base.String(path::PosixPath) = joinpath(parts(path)...)
2424
parts(path::PosixPath) = path.parts
2525

26+
Base.show(io::IO, path::PosixPath) = print(io, "p\"$(join(parts(path), '/'))\"")
27+
2628
function isabs(path::PosixPath)
2729
if parts(path)[1] == POSIX_PATH_SEPARATOR
2830
return true

src/windows.jl

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,76 @@
11
immutable WindowsPath <: AbstractPath
2-
parts::Tuple
2+
parts::Tuple{Vararg{String}}
33
drive::String
4+
root::String
45
end
56

6-
WindowsPath() = WindowsPath(tuple(), "")
7+
WindowsPath() = WindowsPath(tuple(), "", "")
8+
9+
WindowsPath(parts::Tuple) = WindowsPath(parts, "", "")
710

811
function WindowsPath(str::AbstractString)
912
if isempty(str)
10-
return WindowsPath(tuple("."), "")
13+
return WindowsPath(tuple("."), "", "")
1114
end
1215

13-
drive, path = splitdir(str)
14-
tokenized = split(path, WIN_PATH_SEPARATOR)
15-
16-
if isempty(tokenized[1])
17-
tokenized[1] = WIN_PATH_SEPARATOR
16+
if startswith(str, "\\\\?\\")
17+
error("The \\\\?\\ prefix is currently not supported.")
1818
end
1919

20-
return WindowsPath(tuple(map(String, tokenized)...), drive)
21-
end
20+
str = replace(str, POSIX_PATH_SEPARATOR, WIN_PATH_SEPARATOR)
21+
22+
if startswith(str, "\\\\")
23+
error("UNC paths are currently not supported.")
24+
elseif startswith(str, "\\")
25+
tokenized = split(str, WIN_PATH_SEPARATOR)
26+
27+
return WindowsPath(tuple(WIN_PATH_SEPARATOR, String.(tokenized[2:end])...), "", WIN_PATH_SEPARATOR)
28+
elseif contains(str, ":")
29+
l_drive, l_path = splitdrive(str)
30+
31+
tokenized = split(l_path, WIN_PATH_SEPARATOR)
32+
33+
l_root = isempty(tokenized[1]) ? WIN_PATH_SEPARATOR : ""
34+
35+
if isempty(tokenized[1])
36+
tokenized = tokenized[2:end]
37+
end
38+
39+
if !isempty(l_drive) || !isempty(l_root)
40+
tokenized = tuple(string(l_drive, l_root), tokenized...)
41+
end
2242

43+
return WindowsPath(tuple(String.(tokenized)...), l_drive, l_root)
44+
else
45+
tokenized = split(str, WIN_PATH_SEPARATOR)
2346

24-
# The following should be implemented in the concrete types
25-
==(a::WindowsPath, b::WindowsPath) = parts(a) == parts(b) && drive(a) == drive(b)
47+
return WindowsPath(tuple(String.(tokenized)...), "", "")
48+
end
49+
end
50+
51+
function ==(a::WindowsPath, b::WindowsPath)
52+
return lowercase.(parts(a)) == lowercase.(parts(b)) &&
53+
lowercase(drive(a)) == lowercase(drive(b)) &&
54+
lowercase(root(a)) == lowercase(root(b))
55+
end
2656
Base.String(path::WindowsPath) = joinpath(parts(path)...)
2757
parts(path::WindowsPath) = path.parts
2858
drive(path::WindowsPath) = path.drive
59+
root(path::WindowsPath) = path.root
2960

30-
function isabs(path::WindowsPath)
31-
if parts(path[1]) == WIN_PATH_SEPARATOR && !isempty(drive(path))
32-
return true
61+
function Base.show(io::IO, path::WindowsPath)
62+
print(io, "p\"")
63+
if isabs(path)
64+
print(io, replace(anchor(path), "\\", "/"))
65+
print(io, join(parts(path)[2:end], "/"))
3366
else
34-
return false
67+
print(io, join(parts(path), "/"))
3568
end
69+
print(io, "\"")
3670
end
3771

38-
function root(path::WindowsPath)
39-
if parts(path)[1] == WIN_PATH_SEPARATOR
40-
return WIN_PATH_SEPARATOR
41-
else
42-
return ""
43-
end
72+
function isabs(path::WindowsPath)
73+
return !isempty(drive(path)) || !isempty(root(path))
4474
end
4575

4676
expanduser(path::WindowsPath) = path

test/path.jl

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
cd(abs(parent(Path(@__FILE__)))) do
33
@testset "Simple Path Usage" begin
4-
reg = "../src/FilePaths.jl"
4+
reg = is_windows() ? "..\\src\\FilePaths.jl" : "../src/FilePaths.jl"
55
@test ispath(reg)
66

77
p = Path(reg)
@@ -30,7 +30,17 @@ cd(abs(parent(Path(@__FILE__)))) do
3030
@test !isabs(p)
3131
@test String(norm(p"../src/../src/FilePaths.jl")) == normpath("../src/../src/FilePaths.jl")
3232
@test String(abs(p)) == abspath(String(p))
33-
@test String(relative(p, home())) == relpath(String(p), homedir())
33+
# This works around an issue with Base.relpath: that function does not take
34+
# into account the paths on Windows should be compared case insensitive.
35+
homedir_patched = homedir()
36+
if is_windows()
37+
conv_f = isupper(abspath(String(p))[1]) ? uppercase : lowercase
38+
homedir_patched = conv_f(homedir_patched[1]) * homedir_patched[2:end]
39+
end
40+
@test String(relative(p, home())) == relpath(String(p), homedir_patched)
41+
42+
@test isa(relative(Path(".")), AbstractPath)
43+
@test relative(Path(".")) == Path(".")
3444

3545
s = stat(p)
3646
lstat(p)
@@ -106,22 +116,24 @@ mktmpdir() do d
106116
@test_throws ErrorException chown(p"newfile", "nobody", "nogroup"; recursive=true)
107117
end
108118

109-
chmod(p"newfile", user=(READ+WRITE+EXEC), group=(READ+EXEC), other=READ)
110-
@test String(mode(p"newfile")) == "-rwxr-xr--"
111-
@test isexecutable(p"newfile")
112-
@test iswritable(p"newfile")
113-
@test isreadable(p"newfile")
114-
115-
chmod(p"newfile", "-x")
116-
@test !isexecutable(p"newfile")
117-
118-
@test String(mode(p"newfile")) == "-rw-r--r--"
119-
chmod(p"newfile", "+x")
120-
write(p"newfile", "foobar")
121-
@test read(p"newfile") == "foobar"
122-
chmod(p"newfile", "u=rwx")
123-
124-
chmod(new_path, mode(p"newfile"); recursive=true)
119+
@static if is_unix()
120+
chmod(p"newfile", user=(READ+WRITE+EXEC), group=(READ+EXEC), other=READ)
121+
@test String(mode(p"newfile")) == "-rwxr-xr--"
122+
@test isexecutable(p"newfile")
123+
@test iswritable(p"newfile")
124+
@test isreadable(p"newfile")
125+
126+
chmod(p"newfile", "-x")
127+
@test !isexecutable(p"newfile")
128+
129+
@test String(mode(p"newfile")) == "-rw-r--r--"
130+
chmod(p"newfile", "+x")
131+
write(p"newfile", "foobar")
132+
@test read(p"newfile") == "foobar"
133+
chmod(p"newfile", "u=rwx")
134+
135+
chmod(new_path, mode(p"newfile"); recursive=true)
136+
end
125137
end
126138
end
127139
end

0 commit comments

Comments
 (0)