351
351
const Config = Pair{Cmd, Base. CacheFlags}
352
352
const PkgConfig = Tuple{PkgId,Config}
353
353
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
+
354
398
function precompilepkgs (pkgs:: Vector{String} = String[];
355
399
internal_call:: Bool = false ,
356
400
strict:: Bool = false ,
@@ -408,7 +452,7 @@ function _precompilepkgs(pkgs::Vector{String},
408
452
pkg_exts_map = Dict {PkgId, Vector{PkgId}} ()
409
453
410
454
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)
412
456
name = is_direct_dep ? name : color_string (name, :light_black )
413
457
if nconfigs > 1 && ! isempty (flags)
414
458
config_str = join (flags, " " )
@@ -547,32 +591,51 @@ function _precompilepkgs(pkgs::Vector{String},
547
591
548
592
549
593
# 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.
555
597
could_be_cycle = Dict {Base.PkgId, Bool} ()
598
+ # temporary stack for the SCC-like algorithm below
599
+ stack = Base. PkgId[]
556
600
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)
565
605
end
566
- return false
567
606
end
568
607
function scan_deps! (pkg, dmap)
608
+ push! (stack, pkg)
609
+ cycle = nothing
569
610
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
571
629
end
572
630
could_be_cycle[pkg] = false
573
631
return false
574
632
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[]
575
637
for pkg in keys (depsmap)
638
+ @assert isempty (stack)
576
639
if scan_pkg! (pkg, depsmap)
577
640
push! (circular_deps, pkg)
578
641
for pkg_config in keys (was_processed)
@@ -582,7 +645,7 @@ function _precompilepkgs(pkgs::Vector{String},
582
645
end
583
646
end
584
647
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)
586
649
end
587
650
@debug " precompile: circular dep check done"
588
651
@@ -973,7 +1036,7 @@ function _precompilepkgs(pkgs::Vector{String},
973
1036
else
974
1037
join (split (err, " \n " ), color_string (" \n │ " , Base. warn_color ()))
975
1038
end
976
- name = haskey (exts, pkg) ? string (exts[pkg], " → " , pkg . name) : pkg . name
1039
+ name = full_name (exts, pkg)
977
1040
print (iostr, color_string (" \n ┌ " , Base. warn_color ()), name, color_string (" \n │ " , Base. warn_color ()), err, color_string (" \n └ " , Base. warn_color ()))
978
1041
end
979
1042
end
0 commit comments