-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
read.jl
233 lines (210 loc) · 7.69 KB
/
read.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# This file is a part of Julia. License is MIT: http:https://julialang.org/license
module Read
import ...LibGit2, ..Cache, ..Reqs, ...Pkg.PkgError, ..Dir
using ..Types
readstrip(path...) = strip(readstring(joinpath(path...)))
url(pkg::AbstractString) = readstrip(Dir.path("METADATA"), pkg, "url")
sha1(pkg::AbstractString, ver::VersionNumber) = readstrip(Dir.path("METADATA"), pkg, "versions", string(ver), "sha1")
function available(names=readdir("METADATA"))
pkgs = Dict{String,Dict{VersionNumber,Available}}()
for pkg in names
isfile("METADATA", pkg, "url") || continue
versdir = joinpath("METADATA", pkg, "versions")
isdir(versdir) || continue
for ver in readdir(versdir)
ismatch(Base.VERSION_REGEX, ver) || continue
isfile(versdir, ver, "sha1") || continue
haskey(pkgs,pkg) || (pkgs[pkg] = Dict{VersionNumber,Available}())
pkgs[pkg][convert(VersionNumber,ver)] = Available(
readchomp(joinpath(versdir,ver,"sha1")),
Reqs.parse(joinpath(versdir,ver,"requires"))
)
end
end
return pkgs
end
available(pkg::AbstractString) = get(available([pkg]),pkg,Dict{VersionNumber,Available}())
function latest(names=readdir("METADATA"))
pkgs = Dict{String,Available}()
for pkg in names
isfile("METADATA", pkg, "url") || continue
versdir = joinpath("METADATA", pkg, "versions")
isdir(versdir) || continue
pkgversions = VersionNumber[]
for ver in readdir(versdir)
ismatch(Base.VERSION_REGEX, ver) || continue
isfile(versdir, ver, "sha1") || continue
push!(pkgversions, convert(VersionNumber,ver))
end
isempty(pkgversions) && continue
ver = string(maximum(pkgversions))
pkgs[pkg] = Available(
readchomp(joinpath(versdir,ver,"sha1")),
Reqs.parse(joinpath(versdir,ver,"requires"))
)
end
return pkgs
end
isinstalled(pkg::AbstractString) =
pkg != "METADATA" && pkg != "REQUIRE" && pkg[1] != '.' && isdir(pkg)
function isfixed(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::Dict=available(pkg))
isinstalled(pkg) || throw(PkgError("$pkg is not an installed package."))
isfile("METADATA", pkg, "url") || return true
ispath(pkg, ".git") || return true
LibGit2.isdirty(prepo) && return true
LibGit2.isattached(prepo) && return true
LibGit2.need_update(prepo)
LibGit2.iszero(LibGit2.revparseid(prepo, "HEAD:REQUIRE")) && isfile(pkg,"REQUIRE") && return true
head = string(LibGit2.head_oid(prepo))
for (ver,info) in avail
head == info.sha1 && return false
end
cache = Cache.path(pkg)
cache_has_head = if isdir(cache)
crepo = LibGit2.GitRepo(cache)
LibGit2.iscommit(head, crepo)
else
false
end
res = true
try
for (ver,info) in avail
if cache_has_head && LibGit2.iscommit(info.sha1, crepo)
if LibGit2.is_ancestor_of(head, info.sha1, crepo)
res = false
break
end
elseif LibGit2.iscommit(info.sha1, prepo)
if LibGit2.is_ancestor_of(head, info.sha1, prepo)
res = false
break
end
else
Base.warn_once("unknown $pkg commit $(info.sha1[1:8]), metadata may be ahead of package cache")
end
end
finally
cache_has_head && LibGit2.finalize(crepo)
end
return res
end
function installed_version(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::Dict=available(pkg))
ispath(pkg,".git") || return typemin(VersionNumber)
# get package repo head hash
local head
try
head = string(LibGit2.head_oid(prepo))
catch ex
# refs/heads/master does not exist
if isa(ex,LibGit2.GitError) &&
ex.code == LibGit2.Error.EUNBORNBRANCH
head = ""
else
rethrow(ex)
end
end
isempty(head) && return typemin(VersionNumber)
vers = collect(keys(filter((ver,info)->info.sha1==head, avail)))
!isempty(vers) && return maximum(vers)
cache = Cache.path(pkg)
cache_has_head = if isdir(cache)
crepo = LibGit2.GitRepo(cache)
LibGit2.iscommit(head, crepo)
else
false
end
ancestors = VersionNumber[]
descendants = VersionNumber[]
try
for (ver,info) in avail
sha1 = info.sha1
base = if cache_has_head && LibGit2.iscommit(sha1, crepo)
LibGit2.merge_base(crepo, head, sha1)
elseif LibGit2.iscommit(sha1, prepo)
LibGit2.merge_base(prepo, head, sha1)
else
Base.warn_once("unknown $pkg commit $(sha1[1:8]), metadata may be ahead of package cache")
continue
end
string(base) == sha1 && push!(ancestors,ver)
string(base) == head && push!(descendants,ver)
end
finally
cache_has_head && LibGit2.finalize(crepo)
end
both = sort!(intersect(ancestors,descendants))
isempty(both) || warn("$pkg: some versions are both ancestors and descendants of head: $both")
if !isempty(descendants)
v = minimum(descendants)
return VersionNumber(v.major, v.minor, v.patch, ("",), ())
elseif !isempty(ancestors)
v = maximum(ancestors)
return VersionNumber(v.major, v.minor, v.patch, (), ("",))
else
return typemin(VersionNumber)
end
end
function requires_path(pkg::AbstractString, avail::Dict=available(pkg))
pkgreq = joinpath(pkg,"REQUIRE")
ispath(pkg,".git") || return pkgreq
repo = LibGit2.GitRepo(pkg)
head = LibGit2.with(LibGit2.GitRepo, pkg) do repo
LibGit2.isdirty(repo, "REQUIRE") && return pkgreq
LibGit2.need_update(repo)
LibGit2.iszero(LibGit2.revparseid(repo, "HEAD:REQUIRE")) && isfile(pkgreq) && return pkgreq
string(LibGit2.head_oid(repo))
end
for (ver,info) in avail
if head == info.sha1
return joinpath("METADATA", pkg, "versions", string(ver), "requires")
end
end
return pkgreq
end
requires_list(pkg::AbstractString, avail::Dict=available(pkg)) =
collect(keys(Reqs.parse(requires_path(pkg,avail))))
requires_dict(pkg::AbstractString, avail::Dict=available(pkg)) =
Reqs.parse(requires_path(pkg,avail))
function installed(avail::Dict=available())
pkgs = Dict{String,Tuple{VersionNumber,Bool}}()
for pkg in readdir()
isinstalled(pkg) || continue
ap = get(avail,pkg,Dict{VersionNumber,Available}())
if ispath(pkg,".git")
LibGit2.with(LibGit2.GitRepo, pkg) do repo
ver = installed_version(pkg, repo, ap)
fixed = isfixed(pkg, repo, ap)
pkgs[pkg] = (ver, fixed)
end
else
pkgs[pkg] = (typemin(VersionNumber), true)
end
end
return pkgs
end
function fixed(avail::Dict=available(), inst::Dict=installed(avail),
julia_version::VersionNumber=VERSION)
pkgs = Dict{String,Fixed}()
for (pkg,(ver,fix)) in inst
fix || continue
ap = get(avail,pkg,Dict{VersionNumber,Available}())
pkgs[pkg] = Fixed(ver,requires_dict(pkg,ap))
end
pkgs["julia"] = Fixed(julia_version)
return pkgs
end
function free(inst::Dict=installed())
pkgs = Dict{String,VersionNumber}()
for (pkg,(ver,fix)) in inst
fix && continue
pkgs[pkg] = ver
end
return pkgs
end
function issue_url(pkg::AbstractString)
ispath(pkg,".git") || return ""
m = match(LibGit2.GITHUB_REGEX, url(pkg))
m === nothing && return ""
return "https://github.com/" * m.captures[1] * "/issues"
end
end # module