Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
763
julia-0.6.2/share/julia/base/pkg/entry.jl
Normal file
763
julia-0.6.2/share/julia/base/pkg/entry.jl
Normal file
@@ -0,0 +1,763 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module Entry
|
||||
|
||||
import Base: thispatch, nextpatch, nextminor, nextmajor, check_new_version
|
||||
import ..Reqs, ..Read, ..Query, ..Resolve, ..Cache, ..Write, ..Dir
|
||||
import ...LibGit2
|
||||
importall ...LibGit2
|
||||
import ...Pkg.PkgError
|
||||
using ..Types
|
||||
|
||||
macro recover(ex)
|
||||
quote
|
||||
try $(esc(ex))
|
||||
catch err
|
||||
show(err)
|
||||
print('\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function edit(f::Function, pkg::AbstractString, args...)
|
||||
r = Reqs.read("REQUIRE")
|
||||
reqs = Reqs.parse(r)
|
||||
avail = Read.available()
|
||||
!haskey(avail,pkg) && !haskey(reqs,pkg) && return false
|
||||
rʹ = f(r,pkg,args...)
|
||||
rʹ == r && return false
|
||||
reqsʹ = Reqs.parse(rʹ)
|
||||
reqsʹ != reqs && resolve(reqsʹ,avail)
|
||||
Reqs.write("REQUIRE",rʹ)
|
||||
info("Package database updated")
|
||||
return true
|
||||
end
|
||||
|
||||
function edit()
|
||||
editor = get(ENV,"VISUAL",get(ENV,"EDITOR",nothing))
|
||||
editor !== nothing ||
|
||||
throw(PkgError("set the EDITOR environment variable to an edit command"))
|
||||
editor = Base.shell_split(editor)
|
||||
reqs = Reqs.parse("REQUIRE")
|
||||
run(`$editor REQUIRE`)
|
||||
reqsʹ = Reqs.parse("REQUIRE")
|
||||
reqs == reqsʹ && return info("Nothing to be done")
|
||||
info("Computing changes...")
|
||||
resolve(reqsʹ)
|
||||
end
|
||||
|
||||
function add(pkg::AbstractString, vers::VersionSet)
|
||||
outdated = :maybe
|
||||
@sync begin
|
||||
@async if !edit(Reqs.add,pkg,vers)
|
||||
ispath(pkg) || throw(PkgError("unknown package $pkg"))
|
||||
info("Package $pkg is already installed")
|
||||
end
|
||||
branch = Dir.getmetabranch()
|
||||
outdated = with(GitRepo, "METADATA") do repo
|
||||
if LibGit2.branch(repo) == branch
|
||||
if LibGit2.isdiff(repo, "origin/$branch")
|
||||
outdated = :yes
|
||||
else
|
||||
try
|
||||
LibGit2.fetch(repo)
|
||||
outdated = LibGit2.isdiff(repo, "origin/$branch") ? (:yes) : (:no)
|
||||
end
|
||||
end
|
||||
else
|
||||
:no # user is doing something funky with METADATA
|
||||
end
|
||||
end
|
||||
end
|
||||
if outdated != :no
|
||||
is = outdated == :yes ? "is" : "might be"
|
||||
info("METADATA $is out-of-date — you may not have the latest version of $pkg")
|
||||
info("Use `Pkg.update()` to get the latest versions of your packages")
|
||||
end
|
||||
end
|
||||
add(pkg::AbstractString, vers::VersionNumber...) = add(pkg,VersionSet(vers...))
|
||||
|
||||
function rm(pkg::AbstractString)
|
||||
edit(Reqs.rm,pkg) && return
|
||||
ispath(pkg) || return info("Package $pkg is not installed")
|
||||
info("Removing $pkg (unregistered)")
|
||||
Write.remove(pkg)
|
||||
end
|
||||
|
||||
function available()
|
||||
all_avail = Read.available()
|
||||
avail = AbstractString[]
|
||||
for (pkg, vers) in all_avail
|
||||
any(x->Types.satisfies("julia", VERSION, x[2].requires), vers) && push!(avail, pkg)
|
||||
end
|
||||
sort!(avail, by=lowercase)
|
||||
end
|
||||
|
||||
function available(pkg::AbstractString)
|
||||
avail = Read.available(pkg)
|
||||
if !isempty(avail) || Read.isinstalled(pkg)
|
||||
return sort!(collect(keys(avail)))
|
||||
end
|
||||
throw(PkgError("$pkg is not a package (not registered or installed)"))
|
||||
end
|
||||
|
||||
function installed()
|
||||
pkgs = Dict{String,VersionNumber}()
|
||||
for (pkg,(ver,fix)) in Read.installed()
|
||||
pkgs[pkg] = ver
|
||||
end
|
||||
return pkgs
|
||||
end
|
||||
|
||||
function installed(pkg::AbstractString)
|
||||
avail = Read.available(pkg)
|
||||
if Read.isinstalled(pkg)
|
||||
res = typemin(VersionNumber)
|
||||
if ispath(joinpath(pkg,".git"))
|
||||
LibGit2.with(GitRepo, pkg) do repo
|
||||
res = Read.installed_version(pkg, repo, avail)
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
isempty(avail) && throw(PkgError("$pkg is not a package (not registered or installed)"))
|
||||
return nothing # registered but not installed
|
||||
end
|
||||
|
||||
function status(io::IO; pkgname::AbstractString = "")
|
||||
showpkg(pkg) = isempty(pkgname) ? true : (pkg == pkgname)
|
||||
reqs = Reqs.parse("REQUIRE")
|
||||
instd = Read.installed()
|
||||
required = sort!(collect(keys(reqs)))
|
||||
if !isempty(required)
|
||||
showpkg("") && println(io, "$(length(required)) required packages:")
|
||||
for pkg in required
|
||||
if !haskey(instd, pkg)
|
||||
showpkg(pkg) && status(io,pkg,"not found")
|
||||
else
|
||||
ver,fix = pop!(instd,pkg)
|
||||
showpkg(pkg) && status(io,pkg,ver,fix)
|
||||
end
|
||||
end
|
||||
end
|
||||
additional = sort!(collect(keys(instd)))
|
||||
if !isempty(additional)
|
||||
showpkg("") && println(io, "$(length(additional)) additional packages:")
|
||||
for pkg in additional
|
||||
ver,fix = instd[pkg]
|
||||
showpkg(pkg) && status(io,pkg,ver,fix)
|
||||
end
|
||||
end
|
||||
if isempty(required) && isempty(additional)
|
||||
println(io, "No packages installed")
|
||||
end
|
||||
end
|
||||
|
||||
status(io::IO, pkg::AbstractString) = status(io, pkgname = pkg)
|
||||
|
||||
function status(io::IO, pkg::AbstractString, ver::VersionNumber, fix::Bool)
|
||||
@printf io " - %-29s " pkg
|
||||
fix || return println(io,ver)
|
||||
@printf io "%-19s" ver
|
||||
if ispath(pkg,".git")
|
||||
prepo = GitRepo(pkg)
|
||||
try
|
||||
with(LibGit2.head(prepo)) do phead
|
||||
if LibGit2.isattached(prepo)
|
||||
print(io, LibGit2.shortname(phead))
|
||||
else
|
||||
print(io, string(LibGit2.GitHash(phead))[1:8])
|
||||
end
|
||||
end
|
||||
attrs = AbstractString[]
|
||||
isfile("METADATA",pkg,"url") || push!(attrs,"unregistered")
|
||||
LibGit2.isdirty(prepo) && push!(attrs,"dirty")
|
||||
isempty(attrs) || print(io, " (",join(attrs,", "),")")
|
||||
catch err
|
||||
print_with_color(Base.error_color(), io, " broken-repo (unregistered)")
|
||||
finally
|
||||
close(prepo)
|
||||
end
|
||||
else
|
||||
print_with_color(Base.warn_color(), io, "non-repo (unregistered)")
|
||||
end
|
||||
println(io)
|
||||
end
|
||||
|
||||
function status(io::IO, pkg::AbstractString, msg::AbstractString)
|
||||
@printf io " - %-29s %-19s\n" pkg msg
|
||||
end
|
||||
|
||||
function clone(url::AbstractString, pkg::AbstractString)
|
||||
info("Cloning $pkg from $url")
|
||||
ispath(pkg) && throw(PkgError("$pkg already exists"))
|
||||
try
|
||||
LibGit2.with(LibGit2.clone(url, pkg)) do repo
|
||||
LibGit2.set_remote_url(repo, url)
|
||||
end
|
||||
catch err
|
||||
isdir(pkg) && Base.rm(pkg, recursive=true)
|
||||
rethrow(err)
|
||||
end
|
||||
info("Computing changes...")
|
||||
if !edit(Reqs.add, pkg)
|
||||
isempty(Reqs.parse("$pkg/REQUIRE")) && return
|
||||
resolve()
|
||||
end
|
||||
end
|
||||
|
||||
function url_and_pkg(url_or_pkg::AbstractString)
|
||||
if !(':' in url_or_pkg)
|
||||
# no colon, could be a package name
|
||||
url_file = joinpath("METADATA", url_or_pkg, "url")
|
||||
isfile(url_file) && return readchomp(url_file), url_or_pkg
|
||||
end
|
||||
# try to parse as URL or local path
|
||||
m = match(r"(?:^|[/\\])(\w+?)(?:\.jl)?(?:\.git)?$", url_or_pkg)
|
||||
m === nothing && throw(PkgError("can't determine package name from URL: $url_or_pkg"))
|
||||
return url_or_pkg, m.captures[1]
|
||||
end
|
||||
|
||||
clone(url_or_pkg::AbstractString) = clone(url_and_pkg(url_or_pkg)...)
|
||||
|
||||
function checkout(pkg::AbstractString, branch::AbstractString, do_merge::Bool, do_pull::Bool)
|
||||
ispath(pkg,".git") || throw(PkgError("$pkg is not a git repo"))
|
||||
info("Checking out $pkg $branch...")
|
||||
with(GitRepo, pkg) do r
|
||||
LibGit2.transact(r) do repo
|
||||
LibGit2.isdirty(repo) && throw(PkgError("$pkg is dirty, bailing"))
|
||||
LibGit2.branch!(repo, branch, track=LibGit2.Consts.REMOTE_ORIGIN)
|
||||
do_merge && LibGit2.merge!(repo, fastforward=true) # merge changes
|
||||
if do_pull
|
||||
info("Pulling $pkg latest $branch...")
|
||||
LibGit2.fetch(repo)
|
||||
LibGit2.merge!(repo, fastforward=true)
|
||||
end
|
||||
resolve()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function free(pkg::AbstractString)
|
||||
ispath(pkg,".git") || throw(PkgError("$pkg is not a git repo"))
|
||||
Read.isinstalled(pkg) || throw(PkgError("$pkg cannot be freed – not an installed package"))
|
||||
avail = Read.available(pkg)
|
||||
isempty(avail) && throw(PkgError("$pkg cannot be freed – not a registered package"))
|
||||
with(GitRepo, pkg) do repo
|
||||
LibGit2.isdirty(repo) && throw(PkgError("$pkg cannot be freed – repo is dirty"))
|
||||
info("Freeing $pkg")
|
||||
vers = sort!(collect(keys(avail)), rev=true)
|
||||
while true
|
||||
for ver in vers
|
||||
sha1 = avail[ver].sha1
|
||||
LibGit2.iscommit(sha1, repo) || continue
|
||||
return LibGit2.transact(repo) do r
|
||||
LibGit2.isdirty(repo) && throw(PkgError("$pkg is dirty, bailing"))
|
||||
LibGit2.checkout!(repo, sha1)
|
||||
resolve()
|
||||
end
|
||||
end
|
||||
isempty(Cache.prefetch(pkg, Read.url(pkg), [a.sha1 for (v,a)=avail])) && continue
|
||||
throw(PkgError("can't find any registered versions of $pkg to checkout"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function free(pkgs)
|
||||
try
|
||||
for pkg in pkgs
|
||||
ispath(pkg,".git") || throw(PkgError("$pkg is not a git repo"))
|
||||
Read.isinstalled(pkg) || throw(PkgError("$pkg cannot be freed – not an installed package"))
|
||||
avail = Read.available(pkg)
|
||||
isempty(avail) && throw(PkgError("$pkg cannot be freed – not a registered package"))
|
||||
with(GitRepo, pkg) do repo
|
||||
LibGit2.isdirty(repo) && throw(PkgError("$pkg cannot be freed – repo is dirty"))
|
||||
info("Freeing $pkg")
|
||||
vers = sort!(collect(keys(avail)), rev=true)
|
||||
for ver in vers
|
||||
sha1 = avail[ver].sha1
|
||||
LibGit2.iscommit(sha1, repo) || continue
|
||||
LibGit2.checkout!(repo, sha1)
|
||||
break
|
||||
end
|
||||
end
|
||||
isempty(Cache.prefetch(pkg, Read.url(pkg), [a.sha1 for (v,a)=avail])) && continue
|
||||
throw(PkgError("Can't find any registered versions of $pkg to checkout"))
|
||||
end
|
||||
finally
|
||||
resolve()
|
||||
end
|
||||
end
|
||||
|
||||
function pin(pkg::AbstractString, head::AbstractString)
|
||||
ispath(pkg,".git") || throw(PkgError("$pkg is not a git repo"))
|
||||
should_resolve = true
|
||||
with(GitRepo, pkg) do repo
|
||||
id = if isempty(head) # get HEAD commit
|
||||
# no need to resolve, branch will be from HEAD
|
||||
should_resolve = false
|
||||
LibGit2.head_oid(repo)
|
||||
else
|
||||
LibGit2.revparseid(repo, head)
|
||||
end
|
||||
commit = LibGit2.GitCommit(repo, id)
|
||||
try
|
||||
# note: changing the following naming scheme requires a corresponding change in Read.ispinned()
|
||||
branch = "pinned.$(string(id)[1:8]).tmp"
|
||||
if LibGit2.isattached(repo) && LibGit2.branch(repo) == branch
|
||||
info("Package $pkg is already pinned" * (isempty(head) ? "" : " to the selected commit"))
|
||||
should_resolve = false
|
||||
return
|
||||
end
|
||||
ref = LibGit2.lookup_branch(repo, branch)
|
||||
try
|
||||
if !isnull(ref)
|
||||
if LibGit2.revparseid(repo, branch) != id
|
||||
throw(PkgError("Package $pkg: existing branch $branch has " *
|
||||
"been edited and doesn't correspond to its original commit"))
|
||||
end
|
||||
info("Package $pkg: checking out existing branch $branch")
|
||||
else
|
||||
info("Creating $pkg branch $branch")
|
||||
ref = Nullable(LibGit2.create_branch(repo, branch, commit))
|
||||
end
|
||||
|
||||
# checkout selected branch
|
||||
with(LibGit2.peel(LibGit2.GitTree, get(ref))) do btree
|
||||
LibGit2.checkout_tree(repo, btree)
|
||||
end
|
||||
# switch head to the branch
|
||||
LibGit2.head!(repo, get(ref))
|
||||
finally
|
||||
close(get(ref))
|
||||
end
|
||||
finally
|
||||
close(commit)
|
||||
end
|
||||
end
|
||||
should_resolve && resolve()
|
||||
nothing
|
||||
end
|
||||
pin(pkg::AbstractString) = pin(pkg, "")
|
||||
|
||||
function pin(pkg::AbstractString, ver::VersionNumber)
|
||||
ispath(pkg,".git") || throw(PkgError("$pkg is not a git repo"))
|
||||
Read.isinstalled(pkg) || throw(PkgError("$pkg cannot be pinned – not an installed package"))
|
||||
avail = Read.available(pkg)
|
||||
isempty(avail) && throw(PkgError("$pkg cannot be pinned – not a registered package"))
|
||||
haskey(avail,ver) || throw(PkgError("$pkg – $ver is not a registered version"))
|
||||
pin(pkg, avail[ver].sha1)
|
||||
end
|
||||
|
||||
function update(branch::AbstractString, upkgs::Set{String})
|
||||
info("Updating METADATA...")
|
||||
with(GitRepo, "METADATA") do repo
|
||||
try
|
||||
with(LibGit2.head(repo)) do h
|
||||
if LibGit2.branch(h) != branch
|
||||
if LibGit2.isdirty(repo)
|
||||
throw(PkgError("METADATA is dirty and not on $branch, bailing"))
|
||||
end
|
||||
if !LibGit2.isattached(repo)
|
||||
throw(PkgError("METADATA is detached not on $branch, bailing"))
|
||||
end
|
||||
LibGit2.fetch(repo)
|
||||
LibGit2.checkout_head(repo)
|
||||
LibGit2.branch!(repo, branch, track="refs/remotes/origin/$branch")
|
||||
LibGit2.merge!(repo)
|
||||
end
|
||||
end
|
||||
|
||||
LibGit2.fetch(repo)
|
||||
ff_succeeded = LibGit2.merge!(repo, fastforward=true)
|
||||
if !ff_succeeded
|
||||
LibGit2.rebase!(repo, "origin/$branch")
|
||||
end
|
||||
catch err
|
||||
cex = CapturedException(err, catch_backtrace())
|
||||
throw(PkgError("METADATA cannot be updated. Resolve problems manually in " *
|
||||
Pkg.dir("METADATA") * ".", cex))
|
||||
end
|
||||
end
|
||||
deferred_errors = CompositeException()
|
||||
avail = Read.available()
|
||||
# this has to happen before computing free/fixed
|
||||
for pkg in filter(Read.isinstalled, collect(keys(avail)))
|
||||
try
|
||||
Cache.prefetch(pkg, Read.url(pkg), [a.sha1 for (v,a)=avail[pkg]])
|
||||
catch err
|
||||
cex = CapturedException(err, catch_backtrace())
|
||||
push!(deferred_errors, PkgError("Package $pkg: unable to update cache.", cex))
|
||||
end
|
||||
end
|
||||
instd = Read.installed(avail)
|
||||
reqs = Reqs.parse("REQUIRE")
|
||||
if !isempty(upkgs)
|
||||
for (pkg, (v,f)) in instd
|
||||
satisfies(pkg, v, reqs) || throw(PkgError("Package $pkg: current " *
|
||||
"package status does not satisfy the requirements, cannot do " *
|
||||
"a partial update; use `Pkg.update()`"))
|
||||
end
|
||||
end
|
||||
dont_update = Query.partial_update_mask(instd, avail, upkgs)
|
||||
free = Read.free(instd,dont_update)
|
||||
for (pkg,ver) in free
|
||||
try
|
||||
Cache.prefetch(pkg, Read.url(pkg), [a.sha1 for (v,a)=avail[pkg]])
|
||||
catch err
|
||||
cex = CapturedException(err, catch_backtrace())
|
||||
push!(deferred_errors, PkgError("Package $pkg: unable to update cache.", cex))
|
||||
end
|
||||
end
|
||||
fixed = Read.fixed(avail,instd,dont_update)
|
||||
creds = LibGit2.CachedCredentials()
|
||||
try
|
||||
stopupdate = false
|
||||
for (pkg,ver) in fixed
|
||||
ispath(pkg,".git") || continue
|
||||
pkg in dont_update && continue
|
||||
with(GitRepo, pkg) do repo
|
||||
if LibGit2.isattached(repo)
|
||||
if LibGit2.isdirty(repo)
|
||||
warn("Package $pkg: skipping update (dirty)...")
|
||||
elseif Read.ispinned(repo)
|
||||
info("Package $pkg: skipping update (pinned)...")
|
||||
else
|
||||
prev_sha = string(LibGit2.head_oid(repo))
|
||||
success = true
|
||||
try
|
||||
LibGit2.fetch(repo, payload = Nullable(creds))
|
||||
LibGit2.reset!(creds)
|
||||
LibGit2.merge!(repo, fastforward=true)
|
||||
catch err
|
||||
cex = CapturedException(err, catch_backtrace())
|
||||
push!(deferred_errors, PkgError("Package $pkg cannot be updated.", cex))
|
||||
success = false
|
||||
stopupdate = isa(err, InterruptException)
|
||||
end
|
||||
if success
|
||||
post_sha = string(LibGit2.head_oid(repo))
|
||||
branch = LibGit2.branch(repo)
|
||||
info("Updating $pkg $branch...",
|
||||
prev_sha != post_sha ? " $(prev_sha[1:8]) → $(post_sha[1:8])" : "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
stopupdate && break
|
||||
if haskey(avail,pkg)
|
||||
try
|
||||
Cache.prefetch(pkg, Read.url(pkg), [a.sha1 for (v,a)=avail[pkg]])
|
||||
catch err
|
||||
cex = CapturedException(err, catch_backtrace())
|
||||
push!(deferred_errors, PkgError("Package $pkg: unable to update cache.", cex))
|
||||
end
|
||||
end
|
||||
end
|
||||
finally
|
||||
Base.securezero!(creds)
|
||||
end
|
||||
info("Computing changes...")
|
||||
resolve(reqs, avail, instd, fixed, free, upkgs)
|
||||
# Don't use instd here since it may have changed
|
||||
updatehook(sort!(collect(keys(installed()))))
|
||||
|
||||
# Print deferred errors
|
||||
length(deferred_errors) > 0 && throw(PkgError("Update finished with errors.", deferred_errors))
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
function resolve(
|
||||
reqs :: Dict = Reqs.parse("REQUIRE"),
|
||||
avail :: Dict = Read.available(),
|
||||
instd :: Dict = Read.installed(avail),
|
||||
fixed :: Dict = Read.fixed(avail, instd),
|
||||
have :: Dict = Read.free(instd),
|
||||
upkgs :: Set{String} = Set{String}()
|
||||
)
|
||||
orig_reqs = reqs
|
||||
reqs, bktrc = Query.requirements(reqs, fixed, avail)
|
||||
deps, conflicts = Query.dependencies(avail, fixed)
|
||||
|
||||
for pkg in keys(reqs)
|
||||
if !haskey(deps,pkg)
|
||||
if "julia" in conflicts[pkg]
|
||||
throw(PkgError("$pkg can't be installed because it has no versions that support $VERSION " *
|
||||
"of julia. You may need to update METADATA by running `Pkg.update()`"))
|
||||
else
|
||||
sconflicts = join(conflicts[pkg], ", ", " and ")
|
||||
throw(PkgError("$pkg's requirements can't be satisfied because " *
|
||||
"of the following fixed packages: $sconflicts"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Query.check_requirements(reqs, deps, fixed)
|
||||
|
||||
deps = Query.prune_dependencies(reqs, deps, bktrc)
|
||||
want = Resolve.resolve(reqs, deps)
|
||||
|
||||
if !isempty(upkgs)
|
||||
orig_deps, _ = Query.dependencies(avail)
|
||||
Query.check_partial_updates(orig_reqs, orig_deps, want, fixed, upkgs)
|
||||
end
|
||||
|
||||
# compare what is installed with what should be
|
||||
changes = Query.diff(have, want, avail, fixed)
|
||||
isempty(changes) && return info("No packages to install, update or remove")
|
||||
|
||||
# prefetch phase isolates network activity, nothing to roll back
|
||||
missing = []
|
||||
for (pkg,(ver1,ver2)) in changes
|
||||
vers = String[]
|
||||
ver1 !== nothing && push!(vers,LibGit2.head(pkg))
|
||||
ver2 !== nothing && push!(vers,Read.sha1(pkg,ver2))
|
||||
append!(missing,
|
||||
map(sha1->(pkg,(ver1,ver2),sha1),
|
||||
Cache.prefetch(pkg, Read.url(pkg), vers)))
|
||||
end
|
||||
if !isempty(missing)
|
||||
msg = "Missing package versions (possible metadata misconfiguration):"
|
||||
for (pkg,ver,sha1) in missing
|
||||
msg *= " $pkg v$ver [$sha1[1:10]]\n"
|
||||
end
|
||||
throw(PkgError(msg))
|
||||
end
|
||||
|
||||
# try applying changes, roll back everything if anything fails
|
||||
changed = []
|
||||
imported = String[]
|
||||
try
|
||||
for (pkg,(ver1,ver2)) in changes
|
||||
if ver1 === nothing
|
||||
info("Installing $pkg v$ver2")
|
||||
Write.install(pkg, Read.sha1(pkg,ver2))
|
||||
elseif ver2 === nothing
|
||||
info("Removing $pkg v$ver1")
|
||||
Write.remove(pkg)
|
||||
else
|
||||
up = ver1 <= ver2 ? "Up" : "Down"
|
||||
info("$(up)grading $pkg: v$ver1 => v$ver2")
|
||||
Write.update(pkg, Read.sha1(pkg,ver2))
|
||||
pkgsym = Symbol(pkg)
|
||||
if Base.isbindingresolved(Main, pkgsym) && isa(getfield(Main, pkgsym), Module)
|
||||
push!(imported, "- $pkg")
|
||||
end
|
||||
end
|
||||
push!(changed,(pkg,(ver1,ver2)))
|
||||
end
|
||||
catch err
|
||||
for (pkg,(ver1,ver2)) in reverse!(changed)
|
||||
if ver1 === nothing
|
||||
info("Rolling back install of $pkg")
|
||||
@recover Write.remove(pkg)
|
||||
elseif ver2 === nothing
|
||||
info("Rolling back deleted $pkg to v$ver1")
|
||||
@recover Write.install(pkg, Read.sha1(pkg,ver1))
|
||||
else
|
||||
info("Rolling back $pkg from v$ver2 to v$ver1")
|
||||
@recover Write.update(pkg, Read.sha1(pkg,ver1))
|
||||
end
|
||||
end
|
||||
rethrow(err)
|
||||
end
|
||||
if !isempty(imported)
|
||||
warn(join(["The following packages have been updated but were already imported:",
|
||||
imported..., "Restart Julia to use the updated versions."], "\n"))
|
||||
end
|
||||
# re/build all updated/installed packages
|
||||
build(map(x->x[1], filter(x -> x[2][2] !== nothing, changes)))
|
||||
end
|
||||
|
||||
function warnbanner(msg...; label="[ WARNING ]", prefix="")
|
||||
cols = Base.displaysize(STDERR)[2]
|
||||
warn(prefix="", Base.cpad(label,cols,"="))
|
||||
println(STDERR)
|
||||
warn(prefix=prefix, msg...)
|
||||
println(STDERR)
|
||||
warn(prefix="", "="^cols)
|
||||
end
|
||||
|
||||
function build(pkg::AbstractString, build_file::AbstractString, errfile::AbstractString)
|
||||
# To isolate the build from the running Julia process, we execute each build.jl file in
|
||||
# a separate process. Errors are serialized to errfile for later reporting.
|
||||
# TODO: serialize the same way the load cache does, not with strings
|
||||
LOAD_PATH = filter(x -> x isa AbstractString, Base.LOAD_PATH)
|
||||
code = """
|
||||
empty!(Base.LOAD_PATH)
|
||||
append!(Base.LOAD_PATH, $(repr(LOAD_PATH)))
|
||||
empty!(Base.LOAD_CACHE_PATH)
|
||||
append!(Base.LOAD_CACHE_PATH, $(repr(Base.LOAD_CACHE_PATH)))
|
||||
empty!(Base.DL_LOAD_PATH)
|
||||
append!(Base.DL_LOAD_PATH, $(repr(Base.DL_LOAD_PATH)))
|
||||
open("$(escape_string(errfile))", "a") do f
|
||||
pkg, build_file = "$pkg", "$(escape_string(build_file))"
|
||||
try
|
||||
info("Building \$pkg")
|
||||
cd(dirname(build_file)) do
|
||||
evalfile(build_file)
|
||||
end
|
||||
catch err
|
||||
Base.Pkg.Entry.warnbanner(err, label="[ ERROR: \$pkg ]")
|
||||
serialize(f, pkg)
|
||||
serialize(f, err)
|
||||
end
|
||||
end
|
||||
"""
|
||||
cmd = ```
|
||||
$(Base.julia_cmd()) -O0
|
||||
--compilecache=$(Bool(Base.JLOptions().use_compilecache) ? "yes" : "no")
|
||||
--history-file=no
|
||||
--color=$(Base.have_color ? "yes" : "no")
|
||||
--eval $code
|
||||
```
|
||||
|
||||
success(pipeline(cmd, stdout=STDOUT, stderr=STDERR))
|
||||
end
|
||||
|
||||
function build!(pkgs::Vector, seen::Set, errfile::AbstractString)
|
||||
for pkg in pkgs
|
||||
pkg == "julia" && continue
|
||||
pkg in seen ? continue : push!(seen,pkg)
|
||||
Read.isinstalled(pkg) || throw(PkgError("$pkg is not an installed package"))
|
||||
build!(Read.requires_list(pkg), seen, errfile)
|
||||
path = abspath(pkg,"deps","build.jl")
|
||||
isfile(path) || continue
|
||||
build(pkg, path, errfile) || error("Build process failed.")
|
||||
end
|
||||
end
|
||||
|
||||
function build!(pkgs::Vector, errs::Dict, seen::Set=Set())
|
||||
errfile = tempname()
|
||||
touch(errfile) # create empty file
|
||||
try
|
||||
build!(pkgs, seen, errfile)
|
||||
open(errfile, "r") do f
|
||||
while !eof(f)
|
||||
pkg = deserialize(f)
|
||||
err = deserialize(f)
|
||||
errs[pkg] = err
|
||||
end
|
||||
end
|
||||
finally
|
||||
isfile(errfile) && Base.rm(errfile)
|
||||
end
|
||||
end
|
||||
|
||||
function build(pkgs::Vector)
|
||||
errs = Dict()
|
||||
build!(pkgs,errs)
|
||||
isempty(errs) && return
|
||||
println(STDERR)
|
||||
warnbanner(label="[ BUILD ERRORS ]", """
|
||||
WARNING: $(join(keys(errs),", "," and ")) had build errors.
|
||||
|
||||
- packages with build errors remain installed in $(pwd())
|
||||
- build the package(s) and all dependencies with `Pkg.build("$(join(keys(errs),"\", \""))")`
|
||||
- build a single package by running its `deps/build.jl` script
|
||||
""")
|
||||
end
|
||||
build() = build(sort!(collect(keys(installed()))))
|
||||
|
||||
function updatehook!(pkgs::Vector, errs::Dict, seen::Set=Set())
|
||||
for pkg in pkgs
|
||||
pkg in seen && continue
|
||||
updatehook!(Read.requires_list(pkg),errs,push!(seen,pkg))
|
||||
path = abspath(pkg,"deps","update.jl")
|
||||
isfile(path) || continue
|
||||
info("Running update script for $pkg")
|
||||
cd(dirname(path)) do
|
||||
try evalfile(path)
|
||||
catch err
|
||||
warnbanner(err, label="[ ERROR: $pkg ]")
|
||||
errs[pkg] = err
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function updatehook(pkgs::Vector)
|
||||
errs = Dict()
|
||||
updatehook!(pkgs,errs)
|
||||
isempty(errs) && return
|
||||
println(STDERR)
|
||||
warnbanner(label="[ UPDATE ERRORS ]", """
|
||||
WARNING: $(join(keys(errs),", "," and ")) had update errors.
|
||||
|
||||
- Unrelated packages are unaffected
|
||||
- To retry, run Pkg.update() again
|
||||
""")
|
||||
end
|
||||
|
||||
function test!(pkg::AbstractString,
|
||||
errs::Vector{AbstractString},
|
||||
nopkgs::Vector{AbstractString},
|
||||
notests::Vector{AbstractString}; coverage::Bool=false)
|
||||
reqs_path = abspath(pkg,"test","REQUIRE")
|
||||
if isfile(reqs_path)
|
||||
tests_require = Reqs.parse(reqs_path)
|
||||
if (!isempty(tests_require))
|
||||
info("Computing test dependencies for $pkg...")
|
||||
resolve(merge(Reqs.parse("REQUIRE"), tests_require))
|
||||
end
|
||||
end
|
||||
test_path = abspath(pkg,"test","runtests.jl")
|
||||
if !isdir(pkg)
|
||||
push!(nopkgs, pkg)
|
||||
elseif !isfile(test_path)
|
||||
push!(notests, pkg)
|
||||
else
|
||||
info("Testing $pkg")
|
||||
cd(dirname(test_path)) do
|
||||
try
|
||||
color = Base.have_color? "--color=yes" : "--color=no"
|
||||
codecov = coverage? ["--code-coverage=user"] : ["--code-coverage=none"]
|
||||
compilecache = "--compilecache=" * (Bool(Base.JLOptions().use_compilecache) ? "yes" : "no")
|
||||
julia_exe = Base.julia_cmd()
|
||||
run(`$julia_exe --check-bounds=yes $codecov $color $compilecache $test_path`)
|
||||
info("$pkg tests passed")
|
||||
catch err
|
||||
warnbanner(err, label="[ ERROR: $pkg ]")
|
||||
push!(errs,pkg)
|
||||
end
|
||||
end
|
||||
end
|
||||
isfile(reqs_path) && resolve()
|
||||
end
|
||||
|
||||
mutable struct PkgTestError <: Exception
|
||||
msg::String
|
||||
end
|
||||
|
||||
function Base.showerror(io::IO, ex::PkgTestError, bt; backtrace=true)
|
||||
print_with_color(Base.error_color(), io, ex.msg)
|
||||
end
|
||||
|
||||
function test(pkgs::Vector{AbstractString}; coverage::Bool=false)
|
||||
errs = AbstractString[]
|
||||
nopkgs = AbstractString[]
|
||||
notests = AbstractString[]
|
||||
for pkg in pkgs
|
||||
test!(pkg,errs,nopkgs,notests; coverage=coverage)
|
||||
end
|
||||
if !all(isempty, (errs, nopkgs, notests))
|
||||
messages = AbstractString[]
|
||||
if !isempty(errs)
|
||||
push!(messages, "$(join(errs,", "," and ")) had test errors")
|
||||
end
|
||||
if !isempty(nopkgs)
|
||||
msg = length(nopkgs) > 1 ? " are not installed packages" :
|
||||
" is not an installed package"
|
||||
push!(messages, string(join(nopkgs,", ", " and "), msg))
|
||||
end
|
||||
if !isempty(notests)
|
||||
push!(messages, "$(join(notests,", "," and ")) did not provide a test/runtests.jl file")
|
||||
end
|
||||
throw(PkgTestError(join(messages, "and")))
|
||||
end
|
||||
end
|
||||
|
||||
test(;coverage::Bool=false) = test(sort!(AbstractString[keys(installed())...]); coverage=coverage)
|
||||
|
||||
end # module
|
||||
Reference in New Issue
Block a user