Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 81 additions & 18 deletions base/precompilation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,50 @@ end
const Config = Pair{Cmd, Base.CacheFlags}
const PkgConfig = Tuple{PkgId,Config}

# name or parent → ext
function full_name(exts::Dict{PkgId, String}, pkg::PkgId)
if haskey(exts, pkg)
return string(exts[pkg], " → ", pkg.name)
else
return pkg.name
end
end

function excluded_circular_deps_explanation(io::IOContext{IO}, exts::Dict{PkgId, String}, circular_deps, cycles)
outer_deps = copy(circular_deps)
cycles_names = ""
for cycle in cycles
filter!(!in(cycle), outer_deps)
cycle_str = ""
for (i, pkg) in enumerate(cycle)
j = max(0, i - 1)
if length(cycle) == 1
line = " ─ "
elseif i == 1
line = " ┌ "
elseif i < length(cycle)
line = " │ " * " " ^j
else
line = " └" * "─" ^j * " "
end
hascolor = get(io, :color, false)::Bool
line = _color_string(line, :light_black, hascolor) * full_name(exts, pkg) * "\n"
cycle_str *= line
end
cycles_names *= cycle_str
end
plural1 = length(cycles) > 1 ? "these cycles" : "this cycle"
plural2 = length(cycles) > 1 ? "cycles" : "cycle"
msg = """Circular dependency detected.
Precompilation will be skipped for dependencies in $plural1:
$cycles_names"""
if !isempty(outer_deps)
msg *= "Precompilation will also be skipped for the following, which depend on the above $plural2:\n"
msg *= join((" " * full_name(exts, pkg) for pkg in outer_deps), "\n")
end
return msg
end

function precompilepkgs(pkgs::Vector{String}=String[];
internal_call::Bool=false,
strict::Bool = false,
Expand Down Expand Up @@ -408,7 +452,7 @@ function _precompilepkgs(pkgs::Vector{String},
pkg_exts_map = Dict{PkgId, Vector{PkgId}}()

function describe_pkg(pkg::PkgId, is_direct_dep::Bool, flags::Cmd, cacheflags::Base.CacheFlags)
name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name
name = full_name(exts, pkg)
name = is_direct_dep ? name : color_string(name, :light_black)
if nconfigs > 1 && !isempty(flags)
config_str = join(flags, " ")
Expand Down Expand Up @@ -547,32 +591,51 @@ function _precompilepkgs(pkgs::Vector{String},


# find and guard against circular deps
circular_deps = Base.PkgId[]
# Three states
# !haskey -> never visited
# true -> cannot be compiled due to a cycle (or not yet determined)
# false -> not depending on a cycle
cycles = Vector{Base.PkgId}[]
# For every scanned package, true if pkg found to be in a cycle
# or depends on packages in a cycle and false otherwise.
could_be_cycle = Dict{Base.PkgId, Bool}()
# temporary stack for the SCC-like algorithm below
stack = Base.PkgId[]
function scan_pkg!(pkg, dmap)
did_visit_dep = true
inpath = get!(could_be_cycle, pkg) do
did_visit_dep = false
return true
end
if did_visit_dep ? inpath : scan_deps!(pkg, dmap)
# Found a cycle. Delete this and all parents
return true
if haskey(could_be_cycle, pkg)
return could_be_cycle[pkg]
else
return scan_deps!(pkg, dmap)
end
return false
end
function scan_deps!(pkg, dmap)
push!(stack, pkg)
cycle = nothing
for dep in dmap[pkg]
scan_pkg!(dep, dmap) && return true
if dep in stack
# Created fresh cycle
cycle′ = stack[findlast(==(dep), stack):end]
if cycle === nothing || length(cycle′) < length(cycle)
cycle = cycle′ # try to report smallest cycle possible
end
elseif scan_pkg!(dep, dmap)
# Reaches an existing cycle
could_be_cycle[pkg] = true
pop!(stack)
return true
end
end
pop!(stack)
if cycle !== nothing
push!(cycles, cycle)
could_be_cycle[pkg] = true
return true
end
could_be_cycle[pkg] = false
return false
end
# set of packages that depend on a cycle (either because they are
# a part of a cycle themselves or because they transitively depend
# on a package in some cycle)
circular_deps = Base.PkgId[]
for pkg in keys(depsmap)
@assert isempty(stack)
if scan_pkg!(pkg, depsmap)
push!(circular_deps, pkg)
for pkg_config in keys(was_processed)
Expand All @@ -582,7 +645,7 @@ function _precompilepkgs(pkgs::Vector{String},
end
end
if !isempty(circular_deps)
@warn """Circular dependency detected. Precompilation will be skipped for:\n $(join(string.(circular_deps), "\n "))"""
@warn excluded_circular_deps_explanation(io, exts, circular_deps, cycles)
end
@debug "precompile: circular dep check done"

Expand Down Expand Up @@ -973,7 +1036,7 @@ function _precompilepkgs(pkgs::Vector{String},
else
join(split(err, "\n"), color_string("\n│ ", Base.warn_color()))
end
name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name
name = full_name(exts, pkg)
print(iostr, color_string("\n┌ ", Base.warn_color()), name, color_string("\n│ ", Base.warn_color()), err, color_string("\n└ ", Base.warn_color()))
end
end
Expand Down