Skip to content

Commit

Permalink
precompilepkgs: make the circular dep warning clearer and more inform…
Browse files Browse the repository at this point in the history
…ative (JuliaLang#56621)

Was e.g.
```
┌ Warning: Circular dependency detected. Precompilation will be skipped for:
│   Base.PkgId(Base.UUID("eb0c05c4-6780-5852-a67e-5d31d2970b9a"), "ArrayInterfaceTrackerExt")
│   Base.PkgId(Base.UUID("f517fe37-dbe3-4b94-8317-1923a5111588"), "Polyester")
│   Base.PkgId(Base.UUID("0d7ed370-da01-4f52-bd93-41d350b8b718"), "StaticArrayInterface")
│   Base.PkgId(Base.UUID("6a4ca0a5-0e36-4168-a932-d9be78d558f1"), "AcceleratedKernels")
│   Base.PkgId(Base.UUID("244f68ed-b92b-5712-87ae-6c617c41e16a"), "NNlibAMDGPUExt")
│   Base.PkgId(Base.UUID("06b0261c-7a9b-5753-9bdf-fd6840237b4a"), "StaticArrayInterfaceStaticArraysExt")
│   Base.PkgId(Base.UUID("21141c5a-9bdb-4563-92ae-f87d6854732e"), "AMDGPU")
│   Base.PkgId(Base.UUID("9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c"), "Tracker")
└ @ Base.Precompilation precompilation.jl:511
```
Now
![Screenshot 2024-11-21 at 11 20
50 PM](https://github.com/user-attachments/assets/6939d834-90c3-4d87-baa9-cf6a4931ca03)

Thanks to @topolarity figuring out proper cycles tracking.

---------

Co-authored-by: Cody Tapscott <[email protected]>
  • Loading branch information
IanButterworth and topolarity committed Nov 24, 2024
1 parent d0dd391 commit fb24aa3
Showing 1 changed file with 81 additions and 18 deletions.
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

0 comments on commit fb24aa3

Please sign in to comment.