Skip to content

Commit 5ef1884

Browse files
[release-1.11] precompilepkgs: make the circular dep warning clearer and more informative (#56621) (#56677)
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) Co-authored-by: Ian Butterworth <[email protected]>
1 parent 040a6ce commit 5ef1884

File tree

1 file changed

+81
-18
lines changed

1 file changed

+81
-18
lines changed

base/precompilation.jl

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,50 @@ end
351351
const Config = Pair{Cmd, Base.CacheFlags}
352352
const PkgConfig = Tuple{PkgId,Config}
353353

354+
# name or parent → ext
355+
function full_name(exts::Dict{PkgId, String}, pkg::PkgId)
356+
if haskey(exts, pkg)
357+
return string(exts[pkg], "", pkg.name)
358+
else
359+
return pkg.name
360+
end
361+
end
362+
363+
function excluded_circular_deps_explanation(io::IOContext{IO}, exts::Dict{PkgId, String}, circular_deps, cycles)
364+
outer_deps = copy(circular_deps)
365+
cycles_names = ""
366+
for cycle in cycles
367+
filter!(!in(cycle), outer_deps)
368+
cycle_str = ""
369+
for (i, pkg) in enumerate(cycle)
370+
j = max(0, i - 1)
371+
if length(cycle) == 1
372+
line = ""
373+
elseif i == 1
374+
line = ""
375+
elseif i < length(cycle)
376+
line = "" * " " ^j
377+
else
378+
line = "" * "" ^j * " "
379+
end
380+
hascolor = get(io, :color, false)::Bool
381+
line = _color_string(line, :light_black, hascolor) * full_name(exts, pkg) * "\n"
382+
cycle_str *= line
383+
end
384+
cycles_names *= cycle_str
385+
end
386+
plural1 = length(cycles) > 1 ? "these cycles" : "this cycle"
387+
plural2 = length(cycles) > 1 ? "cycles" : "cycle"
388+
msg = """Circular dependency detected.
389+
Precompilation will be skipped for dependencies in $plural1:
390+
$cycles_names"""
391+
if !isempty(outer_deps)
392+
msg *= "Precompilation will also be skipped for the following, which depend on the above $plural2:\n"
393+
msg *= join((" " * full_name(exts, pkg) for pkg in outer_deps), "\n")
394+
end
395+
return msg
396+
end
397+
354398
function precompilepkgs(pkgs::Vector{String}=String[];
355399
internal_call::Bool=false,
356400
strict::Bool = false,
@@ -408,7 +452,7 @@ function _precompilepkgs(pkgs::Vector{String},
408452
pkg_exts_map = Dict{PkgId, Vector{PkgId}}()
409453

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

548592

549593
# find and guard against circular deps
550-
circular_deps = Base.PkgId[]
551-
# Three states
552-
# !haskey -> never visited
553-
# true -> cannot be compiled due to a cycle (or not yet determined)
554-
# false -> not depending on a cycle
594+
cycles = Vector{Base.PkgId}[]
595+
# For every scanned package, true if pkg found to be in a cycle
596+
# or depends on packages in a cycle and false otherwise.
555597
could_be_cycle = Dict{Base.PkgId, Bool}()
598+
# temporary stack for the SCC-like algorithm below
599+
stack = Base.PkgId[]
556600
function scan_pkg!(pkg, dmap)
557-
did_visit_dep = true
558-
inpath = get!(could_be_cycle, pkg) do
559-
did_visit_dep = false
560-
return true
561-
end
562-
if did_visit_dep ? inpath : scan_deps!(pkg, dmap)
563-
# Found a cycle. Delete this and all parents
564-
return true
601+
if haskey(could_be_cycle, pkg)
602+
return could_be_cycle[pkg]
603+
else
604+
return scan_deps!(pkg, dmap)
565605
end
566-
return false
567606
end
568607
function scan_deps!(pkg, dmap)
608+
push!(stack, pkg)
609+
cycle = nothing
569610
for dep in dmap[pkg]
570-
scan_pkg!(dep, dmap) && return true
611+
if dep in stack
612+
# Created fresh cycle
613+
cycle′ = stack[findlast(==(dep), stack):end]
614+
if cycle === nothing || length(cycle′) < length(cycle)
615+
cycle = cycle′ # try to report smallest cycle possible
616+
end
617+
elseif scan_pkg!(dep, dmap)
618+
# Reaches an existing cycle
619+
could_be_cycle[pkg] = true
620+
pop!(stack)
621+
return true
622+
end
623+
end
624+
pop!(stack)
625+
if cycle !== nothing
626+
push!(cycles, cycle)
627+
could_be_cycle[pkg] = true
628+
return true
571629
end
572630
could_be_cycle[pkg] = false
573631
return false
574632
end
633+
# set of packages that depend on a cycle (either because they are
634+
# a part of a cycle themselves or because they transitively depend
635+
# on a package in some cycle)
636+
circular_deps = Base.PkgId[]
575637
for pkg in keys(depsmap)
638+
@assert isempty(stack)
576639
if scan_pkg!(pkg, depsmap)
577640
push!(circular_deps, pkg)
578641
for pkg_config in keys(was_processed)
@@ -582,7 +645,7 @@ function _precompilepkgs(pkgs::Vector{String},
582645
end
583646
end
584647
if !isempty(circular_deps)
585-
@warn """Circular dependency detected. Precompilation will be skipped for:\n $(join(string.(circular_deps), "\n "))"""
648+
@warn excluded_circular_deps_explanation(io, exts, circular_deps, cycles)
586649
end
587650
@debug "precompile: circular dep check done"
588651

@@ -973,7 +1036,7 @@ function _precompilepkgs(pkgs::Vector{String},
9731036
else
9741037
join(split(err, "\n"), color_string("\n", Base.warn_color()))
9751038
end
976-
name = haskey(exts, pkg) ? string(exts[pkg], "", pkg.name) : pkg.name
1039+
name = full_name(exts, pkg)
9771040
print(iostr, color_string("\n", Base.warn_color()), name, color_string("\n", Base.warn_color()), err, color_string("\n", Base.warn_color()))
9781041
end
9791042
end

0 commit comments

Comments
 (0)