@@ -406,7 +406,7 @@ function excluded_circular_deps_explanation(io::IOContext{IO}, ext_to_parent::Di
406406 else
407407 line = " └" * " ─" ^ j * " "
408408 end
409- hascolor = get (io, :color , false ):: Bool
409+ hascolor = get (io, :color , false ):: Bool # XXX : this output does not go to `io` so this is bad to call here
410410 line = _color_string (line, :light_black , hascolor) * full_name (ext_to_parent, pkg) * " \n "
411411 cycle_str *= line
412412 end
@@ -482,14 +482,17 @@ empty (default), precompiles all project dependencies. When specified,
482482precompiles only the given packages and their dependencies (unless
483483`manifest=true`).
484484
485+ !!! note
486+ Errors will only throw when precompiling the top-level dependencies, given that
487+ not all manifest dependencies may be loaded by the top-level dependencies on the given system.
488+ This can be overridden to make errors in all dependencies throw by setting the kwarg `strict` to `true`
489+
485490# Keyword Arguments
486- - `internal_call::Bool`: Indicates this is an automatic/internal precompilation call
487- (e.g., triggered by package loading). When `true`, errors are handled gracefully: in
488- interactive sessions, errors are stored in `Base.MainInclude.err` instead of throwing;
489- in non-interactive sessions, errors are printed but not thrown. Default: `false`.
491+ - `internal_call::Bool`: Indicates this is an automatic precompilation call
492+ from somewhere external (e.g. Pkg). Do not use this parameter.
490493
491494- `strict::Bool`: Controls error reporting scope. When `false` (default), only reports
492- errors for direct project dependencies.
495+ errors for direct project dependencies. Only relevant when `manifest=true`.
493496
494497- `warn_loaded::Bool`: When `true` (default), checks for and warns about packages that are
495498 precompiled but already loaded with a different version. Displays a warning that Julia
@@ -564,7 +567,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
564567 timing:: Bool ,
565568 _from_loading:: Bool ,
566569 configs:: Vector{Config} ,
567- _io :: IOContext{IO} ,
570+ io :: IOContext{IO} ,
568571 fancyprint:: Bool ,
569572 manifest:: Bool ,
570573 ignore_loaded:: Bool )
@@ -602,16 +605,18 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
602605 # suppress precompilation progress messages when precompiling for loading packages, except during interactive sessions
603606 # or when specified by logging heuristics that explicitly require it
604607 # since the complicated IO implemented here can have somewhat disastrous consequences when happening in the background (e.g. #59599)
605- io = _io
608+ logio = io
606609 logcalls = nothing
607- if _from_loading && ! isinteractive ()
608- io = IOContext {IO} (devnull )
609- fancyprint = false
610+ if _from_loading
611+ if ! isinteractive ()
612+ logio = IOContext {IO} (devnull )
613+ fancyprint = false
614+ end
610615 logcalls = isinteractive () ? CoreLogging. Info : CoreLogging. Debug # sync with Base.compilecache
611616 end
612617
613618 nconfigs = length (configs)
614- hascolor = get (io , :color , false ):: Bool
619+ hascolor = get (logio , :color , false ):: Bool
615620 color_string (cstr:: String , col:: Union{Int64, Symbol} ) = _color_string (cstr, col, hascolor)
616621
617622 stale_cache = Dict {StaleCacheKey, Bool} ()
@@ -901,9 +906,10 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
901906 pkg_liveprinted = Ref {Union{Nothing, PkgId}} (nothing )
902907
903908 function monitor_std (pkg_config, pipe; single_requested_pkg= false )
904- pkg, config = pkg_config
909+ local pkg, config = pkg_config
905910 try
906- liveprinting = false
911+ local liveprinting = false
912+ local thistaskwaiting = false
907913 while ! eof (pipe)
908914 local str = readline (pipe, keep= true )
909915 if single_requested_pkg && (liveprinting || ! isempty (str))
@@ -916,15 +922,18 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
916922 end
917923 end
918924 write (get! (IOBuffer, std_outputs, pkg_config), str)
919- if ! in (pkg_config, taskwaiting) && occursin (" waiting for IO to finish" , str)
920- ! fancyprint && @lock print_lock begin
921- println (io, pkg. name, color_string (" Waiting for background task / IO / timer." , Base. warn_color ()))
925+ if thistaskwaiting
926+ if occursin (" Waiting for background task / IO / timer" , str)
927+ thistaskwaiting = true
928+ ! liveprinting && ! fancyprint && @lock print_lock begin
929+ println (io, pkg. name, color_string (str, Base. warn_color ()))
930+ end
931+ push! (taskwaiting, pkg_config)
922932 end
923- push! (taskwaiting, pkg_config)
924- end
925- if ! fancyprint && in (pkg_config, taskwaiting)
926- @lock print_lock begin
927- print (io, str)
933+ else
934+ # XXX : don't just re-enable IO for random packages without printing the context for them first
935+ ! liveprinting && ! fancyprint && @lock print_lock begin
936+ print (io, ansi_cleartoendofline, str)
928937 end
929938 end
930939 end
@@ -940,10 +949,10 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
940949 (isempty (pkg_queue) || interrupted_or_done[]) && return
941950 @lock print_lock begin
942951 if target[] != = nothing
943- printpkgstyle (io , :Precompiling , target[])
952+ printpkgstyle (logio , :Precompiling , target[])
944953 end
945954 if fancyprint
946- print (io , ansi_disablecursor)
955+ print (logio , ansi_disablecursor)
947956 end
948957 end
949958 t = Timer (0 ; interval= 1 / 10 )
@@ -957,7 +966,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
957966 n_print_rows = 0
958967 while ! printloop_should_exit[]
959968 @lock print_lock begin
960- term_size = displaysize (io ):: Tuple{Int, Int}
969+ term_size = displaysize (logio ):: Tuple{Int, Int}
961970 num_deps_show = max (term_size[1 ] - 3 , 2 ) # show at least 2 deps
962971 pkg_queue_show = if ! interrupted_or_done[] && length (pkg_queue) > num_deps_show
963972 last (pkg_queue, num_deps_show)
@@ -974,7 +983,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
974983 # window between print cycles
975984 termwidth = (displaysize (io):: Tuple{Int,Int} )[2 ] - 4
976985 if ! final_loop
977- s = sprint (io -> show_progress (io, bar; termwidth, carriagereturn= false ); context= io )
986+ s = sprint (io -> show_progress (io, bar; termwidth, carriagereturn= false ); context= logio )
978987 print (iostr, Base. _truncate_at_width_or_chars (true , s, termwidth), " \n " )
979988 end
980989 for pkg_config in pkg_queue_show
@@ -1015,11 +1024,11 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
10151024 end
10161025 last_length = length (pkg_queue_show)
10171026 n_print_rows = count (" \n " , str_)
1018- print (io , str_)
1027+ print (logio , str_)
10191028 printloop_should_exit[] = interrupted_or_done[] && final_loop
10201029 final_loop = interrupted_or_done[] # ensures one more loop to tidy last task after finish
10211030 i += 1
1022- printloop_should_exit[] || print (io , ansi_moveup (n_print_rows), ansi_movecol1)
1031+ printloop_should_exit[] || print (logio , ansi_moveup (n_print_rows), ansi_movecol1)
10231032 end
10241033 wait (t)
10251034 end
@@ -1029,7 +1038,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
10291038 # Base.display_error(ErrorException(""), Base.catch_backtrace())
10301039 handle_interrupt (err, true ) || rethrow ()
10311040 finally
1032- fancyprint && print (io , ansi_enablecursor)
1041+ fancyprint && print (logio , ansi_enablecursor)
10331042 end
10341043 end
10351044
@@ -1081,8 +1090,8 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
10811090 end
10821091 if ! circular && is_stale
10831092 Base. acquire (parallel_limiter)
1084- is_project_dep = pkg in project_deps
10851093 is_serial_dep = pkg in serial_deps
1094+ is_project_dep = pkg in project_deps
10861095
10871096 # std monitoring
10881097 std_pipe = Base. link_pipe! (Pipe (); reader_supports_async= true , writer_supports_async= true )
@@ -1092,7 +1101,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
10921101 name = describe_pkg (pkg, is_project_dep, is_serial_dep, flags, cacheflags)
10931102 @lock print_lock begin
10941103 if ! fancyprint && isempty (pkg_queue)
1095- printpkgstyle (io , :Precompiling , something (target[], " packages..." ))
1104+ printpkgstyle (logio , :Precompiling , something (target[], " packages..." ))
10961105 end
10971106 end
10981107 push! (pkg_queue, pkg_config)
@@ -1141,11 +1150,11 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
11411150 if ret isa Exception
11421151 push! (precomperr_deps, pkg_config)
11431152 ! fancyprint && @lock print_lock begin
1144- println (io , _timing_string (t), color_string (" ? " , Base. warn_color ()), name)
1153+ println (logio , _timing_string (t), color_string (" ? " , Base. warn_color ()), name)
11451154 end
11461155 else
11471156 ! fancyprint && @lock print_lock begin
1148- println (io , _timing_string (t), color_string (" ✓ " , loaded ? Base. warn_color () : :green ), name)
1157+ println (logio , _timing_string (t), color_string (" ✓ " , loaded ? Base. warn_color () : :green ), name)
11491158 end
11501159 if ret != = nothing
11511160 was_recompiled[pkg_config] = true
@@ -1161,11 +1170,9 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
11611170 close (std_pipe. in) # close pipe to end the std output monitor
11621171 wait (t_monitor)
11631172 if err isa ErrorException || (err isa ArgumentError && startswith (err. msg, " Invalid header in cache file" ))
1164- errmsg = String (take! (get (IOBuffer, std_outputs, pkg_config)))
1165- delete! (std_outputs, pkg_config) # so it's not shown as warnings, given error report
1166- failed_deps[pkg_config] = (strict || is_project_dep) ? string (sprint (showerror, err), " \n " , strip (errmsg)) : " "
1173+ failed_deps[pkg_config] = sprint (showerror, err)
11671174 ! fancyprint && @lock print_lock begin
1168- println (io , " " ^ 12 , color_string (" ✗ " , Base. error_color ()), name)
1175+ println (logio , " " ^ 12 , color_string (" ✗ " , Base. error_color ()), name)
11691176 end
11701177 else
11711178 rethrow ()
@@ -1213,9 +1220,25 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
12131220 quick_exit = any (t -> ! istaskdone (t) || istaskfailed (t), tasks) || interrupted[] # all should have finished (to avoid memory corruption)
12141221 seconds_elapsed = round (Int, (time_ns () - time_start) / 1e9 )
12151222 ndeps = count (values (was_recompiled))
1216- if ndeps > 0 || ! isempty (failed_deps) || (quick_exit && ! isempty (std_outputs))
1217- str = sprint (context= io) do iostr
1218- if ! quick_exit
1223+ # Determine if any of failures were a requested package
1224+ requested_errs = false
1225+ for ((dep, config), err) in failed_deps
1226+ if dep in requested_pkgids
1227+ requested_errs = true
1228+ break
1229+ end
1230+ end
1231+ # if every requested package succeeded, filter away output from failed packages
1232+ # since it didn't contribute to the overall success and can be regenerated if that package is later required
1233+ if ! strict && requested_errs
1234+ for (pkg_config, err) in failed_deps
1235+ delete! (std_outputs, pkg_config)
1236+ end
1237+ empty! (failed_deps)
1238+ end
1239+ if ndeps > 0 || ! isempty (failed_deps)
1240+ if ! quick_exit
1241+ logstr = sprint (context= logio) do iostr
12191242 if fancyprint # replace the progress bar
12201243 what = isempty (requested_pkgids) ? " packages finished." : " $(join ((p. name for p in requested_pkgids), " , " , " and " )) finished."
12211244 printpkgstyle (iostr, :Precompiling , what)
@@ -1248,10 +1271,17 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
12481271 )
12491272 end
12501273 end
1274+ @lock print_lock begin
1275+ println (logio, logstr)
1276+ end
1277+ end
1278+ end
1279+ if ! isempty (std_outputs)
1280+ str = sprint (context= io) do iostr
12511281 # show any stderr output, even if Pkg.precompile has been interrupted (quick_exit=true), given user may be
1252- # interrupting a hanging precompile job with stderr output. julia#48371
1282+ # interrupting a hanging precompile job with stderr output.
12531283 let std_outputs = Tuple{PkgConfig,SubString{String}}[(pkg_config, strip (String (take! (io)))) for (pkg_config,io) in std_outputs]
1254- filter! (kv -> ! isempty ( last (kv)) , std_outputs)
1284+ filter! (! isempty∘ last, std_outputs)
12551285 if ! isempty (std_outputs)
12561286 local plural1 = length (std_outputs) == 1 ? " y" : " ies"
12571287 local plural2 = length (std_outputs) == 1 ? " " : " s"
@@ -1269,49 +1299,32 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
12691299 end
12701300 end
12711301 end
1272- @lock print_lock begin
1302+ isempty (str) || @lock print_lock begin
12731303 println (io, str)
12741304 end
1275- if interrupted[]
1276- # done cleanup, now ensure caller aborts too
1277- throw (InterruptException ())
1278- end
1279- quick_exit && return Vector{String}[]
1305+ end
1306+ # Done cleanup and sub-process output, now ensure caller aborts too with the right error
1307+ if interrupted[]
1308+ throw (InterruptException ())
1309+ end
1310+ # Fail noisily now with failed_deps if any.
1311+ # Include all messages from compilecache since any might be relevant in the failure.
1312+ if ! isempty (failed_deps)
12801313 err_str = IOBuffer ()
1281- n_direct_errs = 0
1282- for (pkg_config, err) in failed_deps
1283- dep, config = pkg_config
1284- if strict || (dep in project_deps)
1285- print (err_str, " \n " , dep. name, " " )
1286- for cfg in config[1 ]
1287- print (err_str, cfg, " " )
1288- end
1289- print (err_str, " \n\n " , err)
1290- n_direct_errs > 0 && write (err_str, " \n " )
1291- n_direct_errs += 1
1292- end
1314+ for ((dep, config), err) in failed_deps
1315+ write (err_str, " \n " )
1316+ print (err_str, " \n " , dep. name, " " )
1317+ join (err_str, config[1 ], " " )
1318+ print (err_str, " \n " , err)
12931319 end
1294- if position (err_str) > 0
1295- skip (err_str, - 1 )
1296- truncate (err_str, position (err_str))
1297- pluralde = n_direct_errs == 1 ? " y" : " ies"
1298- direct = strict ? " " : " direct "
1299- err_msg = " The following $n_direct_errs $(direct) dependenc$(pluralde) failed to precompile:\n $(String (take! (err_str))) "
1300- if internal_call # aka. decide which untested code path to run that does some unsafe behavior
1301- if isinteractive () # XXX : this test is incorrect
1302- plural1 = length (failed_deps) == 1 ? " y" : " ies"
1303- println (io, " " , color_string (" $(length (failed_deps)) " , Base. error_color ()), " dependenc$(plural1) errored." )
1304- println (io, " For a report of the errors see `julia> err`. To retry use `pkg> precompile`" )
1305- setglobal! (Base. MainInclude, :err , PkgPrecompileError (err_msg)) # XXX : this call is dangerous
1306- else
1307- # auto-precompilation shouldn't throw but if the user can't easily access the
1308- # error messages, just show them
1309- print (io, " \n " , err_msg)
1310- end
1311- else # XXX : crashing is wrong
1312- println (io)
1313- throw (PkgPrecompileError (err_msg))
1314- end
1320+ n_errs = length (failed_deps)
1321+ pluraled = n_errs == 1 ? " " : " s"
1322+ err_msg = " The following $n_errs package$(pluraled) failed to precompile:$(String (take! (err_str))) \n "
1323+ if internal_call
1324+ # Pkg does not implement correct error handling, so this sometimes handles them instead
1325+ print (io, err_msg)
1326+ else
1327+ throw (PkgPrecompileError (err_msg))
13151328 end
13161329 end
13171330 return collect (String, Iterators. flatten ((v for (pkgid, v) in cachepath_cache if pkgid in requested_pkgids)))
0 commit comments