33# Base.require is the implementation for the `import` statement
44const require_lock = ReentrantLock ()
55
6+ mutable struct StaleReasons
7+ invalid_header:: Int
8+ empty_file:: Int
9+ mismatched_flags:: Int
10+ requires_pkgimages:: Int
11+ target_mismatch:: Int
12+ missing_ocachefile:: Int
13+ for_different_id:: Int
14+ for_different_buildid:: Int
15+ loaded_incompatible:: Int
16+ missing_source:: Int
17+ wrong_buildid:: Int
18+ different_source:: Int
19+ uuid_change:: Int
20+ unresolvable_depot:: Int
21+ missing_sourcefile:: Int
22+ include_dependency_mtime_change:: Int
23+ include_dependency_fsize_change:: Int
24+ include_dependency_fhash_change:: Int
25+ invalid_checksum:: Int
26+ ocachefile_invalid_checksum:: Int
27+ prefs_hash_mismatch:: Int
28+ StaleReasons () = new (zeros (21 )... )
29+ end
30+
31+ function show (io:: IO , reasons:: StaleReasons )
32+ first = true
33+ for n in fieldnames (StaleReasons)
34+ f = getfield (reasons, n)
35+ if f > 0
36+ first ? print (io, " $f $n " ) : print (io, " , $f $n " )
37+ first = false
38+ end
39+ end
40+ end
41+
642# Cross-platform case-sensitive path canonicalization
743
844if Sys. isunix () && ! Sys. isapple ()
@@ -1633,11 +1669,11 @@ end
16331669
16341670# returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it
16351671# returns the set of modules restored if the cache load succeeded
1636- @constprop :none function _require_search_from_serialized (pkg:: PkgId , sourcepath:: String , build_id:: UInt128 )
1672+ @constprop :none function _require_search_from_serialized (pkg:: PkgId , sourcepath:: String , build_id:: UInt128 ; reasons = nothing )
16371673 assert_havelock (require_lock)
16381674 paths = find_all_in_cache_path (pkg)
16391675 for path_to_try in paths:: Vector{String}
1640- staledeps = stale_cachefile (pkg, build_id, sourcepath, path_to_try)
1676+ staledeps = stale_cachefile (pkg, build_id, sourcepath, path_to_try; reasons )
16411677 if staledeps === true
16421678 continue
16431679 end
@@ -2126,11 +2162,11 @@ function _require(pkg::PkgId, env=nothing)
21262162 set_pkgorigin_version_path (pkg, path)
21272163
21282164 pkg_precompile_attempted = false # being safe to avoid getting stuck in a Pkg.precompile loop
2129-
2165+ reasons = StaleReasons ()
21302166 # attempt to load the module file via the precompile cache locations
21312167 if JLOptions (). use_compiled_modules != 0
21322168 @label load_from_cache
2133- m = _require_search_from_serialized (pkg, path, UInt128 (0 ))
2169+ m = _require_search_from_serialized (pkg, path, UInt128 (0 ); reasons )
21342170 if m isa Module
21352171 return m
21362172 end
@@ -2166,7 +2202,7 @@ function _require(pkg::PkgId, env=nothing)
21662202 # double-check now that we have lock
21672203 m = _require_search_from_serialized (pkg, path, UInt128 (0 ))
21682204 m isa Module && return m
2169- compilecache (pkg, path)
2205+ compilecache (pkg, path; reasons )
21702206 end
21712207 cachefile_or_module isa Module && return cachefile_or_module:: Module
21722208 cachefile = cachefile_or_module
@@ -2568,17 +2604,17 @@ This can be used to reduce package load times. Cache files are stored in
25682604`DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref)
25692605for important notes.
25702606"""
2571- function compilecache (pkg:: PkgId , internal_stderr:: IO = stderr , internal_stdout:: IO = stdout )
2607+ function compilecache (pkg:: PkgId , internal_stderr:: IO = stderr , internal_stdout:: IO = stdout ; reasons :: Union{StaleReasons,Nothing} = nothing )
25722608 @nospecialize internal_stderr internal_stdout
25732609 path = locate_package (pkg)
25742610 path === nothing && throw (ArgumentError (" $pkg not found during precompilation" ))
2575- return compilecache (pkg, path, internal_stderr, internal_stdout)
2611+ return compilecache (pkg, path, internal_stderr, internal_stdout; reasons )
25762612end
25772613
25782614const MAX_NUM_PRECOMPILE_FILES = Ref (10 )
25792615
25802616function compilecache (pkg:: PkgId , path:: String , internal_stderr:: IO = stderr , internal_stdout:: IO = stdout ,
2581- keep_loaded_modules:: Bool = true )
2617+ keep_loaded_modules:: Bool = true ; reasons :: Union{StaleReasons,Nothing} = nothing )
25822618
25832619 @nospecialize internal_stderr internal_stdout
25842620 # decide where to put the resulting cache file
@@ -2595,7 +2631,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
25952631 end
25962632 # run the expression and cache the result
25972633 verbosity = isinteractive () ? CoreLogging. Info : CoreLogging. Debug
2598- @logmsg verbosity " Precompiling $pkg "
2634+ @logmsg verbosity " Precompiling $pkg (cache misses: $reasons ) "
25992635
26002636 # create a temporary file in `cachepath` directory, write the cache in it,
26012637 # write the checksum, _and then_ atomically move the file to `cachefile`.
@@ -3311,21 +3347,25 @@ function maybe_cachefile_lock(f, pkg::PkgId, srcpath::String; stale_age=compilec
33113347 f ()
33123348 end
33133349end
3350+
33143351# returns true if it "cachefile.ji" is stale relative to "modpath.jl" and build_id for modkey
33153352# otherwise returns the list of dependencies to also check
3316- @constprop :none function stale_cachefile (modpath:: String , cachefile:: String ; ignore_loaded:: Bool = false )
3353+ @constprop :none function stale_cachefile (modpath:: String , cachefile:: String ; ignore_loaded:: Bool = false , reasons = nothing )
33173354 return stale_cachefile (PkgId (" " ), UInt128 (0 ), modpath, cachefile; ignore_loaded)
33183355end
3319- @constprop :none function stale_cachefile (modkey:: PkgId , build_id:: UInt128 , modpath:: String , cachefile:: String ; ignore_loaded:: Bool = false )
3356+ @constprop :none function stale_cachefile (modkey:: PkgId , build_id:: UInt128 , modpath:: String , cachefile:: String ;
3357+ ignore_loaded:: Bool = false , reasons:: Union{StaleReasons,Nothing} = nothing )
33203358 io = open (cachefile, " r" )
33213359 try
33223360 checksum = isvalid_cache_header (io)
33233361 if iszero (checksum)
33243362 @debug " Rejecting cache file $cachefile due to it containing an invalid cache header"
3363+ isnothing (reasons) || (reasons. invalid_header += 1 )
33253364 return true # invalid cache file
33263365 end
33273366 modules, (includes, _, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags = parse_cache_header (io, cachefile)
33283367 if isempty (modules)
3368+ isnothing (reasons) || (reasons. empty_file += 1 )
33293369 return true # ignore empty file
33303370 end
33313371 if ccall (:jl_match_cache_flags , UInt8, (UInt8,), flags) == 0
@@ -3334,6 +3374,7 @@ end
33343374 current session: $(CacheFlags ())
33353375 cache file: $(CacheFlags (flags))
33363376 """
3377+ isnothing (reasons) || (reasons. mismatched_flags += 1 )
33373378 return true
33383379 end
33393380 pkgimage = ! isempty (clone_targets)
@@ -3342,6 +3383,7 @@ end
33423383 if JLOptions (). use_pkgimages == 0
33433384 # presence of clone_targets means native code cache
33443385 @debug " Rejecting cache file $cachefile for $modkey since it would require usage of pkgimage"
3386+ isnothing (reasons) || (reasons. requires_pkgimages += 1 )
33453387 return true
33463388 end
33473389 rejection_reasons = check_clone_targets (clone_targets)
@@ -3350,10 +3392,12 @@ end
33503392 Reasons= rejection_reasons,
33513393 var"Image Targets" = parse_image_targets (clone_targets),
33523394 var"Current Targets" = current_image_targets ())
3395+ isnothing (reasons) || (reasons. target_mismatch += 1 )
33533396 return true
33543397 end
33553398 if ! isfile (ocachefile)
33563399 @debug " Rejecting cache file $cachefile for $modkey since pkgimage $ocachefile was not found"
3400+ isnothing (reasons) || (reasons. missing_ocachefile += 1 )
33573401 return true
33583402 end
33593403 else
@@ -3362,12 +3406,14 @@ end
33623406 id = first (modules)
33633407 if id. first != modkey && modkey != PkgId (" " )
33643408 @debug " Rejecting cache file $cachefile for $modkey since it is for $id instead"
3409+ isnothing (reasons) || (reasons. for_different_id += 1 )
33653410 return true
33663411 end
33673412 if build_id != UInt128 (0 )
33683413 id_build = (UInt128 (checksum) << 64 ) | id. second
33693414 if id_build != build_id
33703415 @debug " Ignoring cache file $cachefile for $modkey ($((UUID (id_build))) ) since it does not provide desired build_id ($((UUID (build_id))) )"
3416+ isnothing (reasons) || (reasons. for_different_buildid += 1 )
33713417 return true
33723418 end
33733419 end
@@ -3389,13 +3435,15 @@ end
33893435 @goto locate_branch
33903436 else
33913437 @debug " Rejecting cache file $cachefile because module $req_key is already loaded and incompatible."
3438+ isnothing (reasons) || (reasons. loaded_incompatible += 1 )
33923439 return true # Won't be able to fulfill dependency
33933440 end
33943441 else
33953442 @label locate_branch
33963443 path = locate_package (req_key)
33973444 if path === nothing
33983445 @debug " Rejecting cache file $cachefile because dependency $req_key not found."
3446+ isnothing (reasons) || (reasons. missing_source += 1 )
33993447 return true # Won't be able to fulfill dependency
34003448 end
34013449 depmods[i] = (path, req_key, req_build_id)
@@ -3415,6 +3463,7 @@ end
34153463 break
34163464 end
34173465 @debug " Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID (build_id))) ) for $req_key (want $(UUID (req_build_id)) )"
3466+ isnothing (reasons) || (reasons. wrong_buildid += 1 )
34183467 return true # cachefile doesn't provide the required version of the dependency
34193468 end
34203469 end
@@ -3423,27 +3472,31 @@ end
34233472 if ! skip_check
34243473 if ! samefile (includes[1 ]. filename, modpath) && ! samefile (fixup_stdlib_path (includes[1 ]. filename), modpath)
34253474 @debug " Rejecting cache file $cachefile because it is for file $(includes[1 ]. filename) not file $modpath "
3475+ isnothing (reasons) || (reasons. different_source += 1 )
34263476 return true # cache file was compiled from a different path
34273477 end
34283478 for (modkey, req_modkey) in requires
34293479 # verify that `require(modkey, name(req_modkey))` ==> `req_modkey`
34303480 pkg = identify_package (modkey, req_modkey. name)
34313481 if pkg != req_modkey
34323482 @debug " Rejecting cache file $cachefile because uuid mapping for $modkey => $req_modkey has changed, expected $modkey => $pkg "
3483+ isnothing (reasons) || (reasons. uuid_change += 1 )
34333484 return true
34343485 end
34353486 end
34363487 for chi in includes
34373488 f, fsize_req, hash_req, ftime_req = chi. filename, chi. fsize, chi. hash, chi. mtime
34383489 if startswith (f, " @depot/" )
34393490 @debug (" Rejecting stale cache file $cachefile because its depot could not be resolved" )
3491+ isnothing (reasons) || (reasons. unresolvable_depot += 1 )
34403492 return true
34413493 end
34423494 if ! ispath (f)
34433495 _f = fixup_stdlib_path (f)
34443496 if isfile (_f) && startswith (_f, Sys. STDLIB)
34453497 continue
34463498 end
3499+ isnothing (reasons) || (reasons. missing_sourcefile += 1 )
34473500 @debug " Rejecting stale cache file $cachefile because file $f does not exist"
34483501 return true
34493502 end
@@ -3458,17 +3511,20 @@ end
34583511 ! ( 0 < (ftime_req - ftime) < 1e-6 ) # PR #45552: Compensate for Windows tar giving mtimes that may be incorrect by up to one microsecond
34593512 if is_stale
34603513 @debug " Rejecting stale cache file $cachefile because mtime of include_dependency $f has changed (mtime $ftime , before $ftime_req )"
3514+ isnothing (reasons) || (reasons. include_dependency_mtime_change += 1 )
34613515 return true
34623516 end
34633517 else
34643518 fsize = filesize (f)
34653519 if fsize != fsize_req
34663520 @debug " Rejecting stale cache file $cachefile because file size of $f has changed (file size $fsize , before $fsize_req )"
3521+ isnothing (reasons) || (reasons. include_dependency_fsize_change += 1 )
34673522 return true
34683523 end
34693524 hash = open (_crc32c, f, " r" )
34703525 if hash != hash_req
34713526 @debug " Rejecting stale cache file $cachefile because hash of $f has changed (hash $hash , before $hash_req )"
3527+ isnothing (reasons) || (reasons. include_dependency_fhash_change += 1 )
34723528 return true
34733529 end
34743530 end
@@ -3477,19 +3533,22 @@ end
34773533
34783534 if ! isvalid_file_crc (io)
34793535 @debug " Rejecting cache file $cachefile because it has an invalid checksum"
3536+ isnothing (reasons) || (reasons. invalid_checksum += 1 )
34803537 return true
34813538 end
34823539
34833540 if pkgimage
34843541 if ! isvalid_pkgimage_crc (io, ocachefile:: String )
34853542 @debug " Rejecting cache file $cachefile because $ocachefile has an invalid checksum"
3543+ isnothing (reasons) || (reasons. ocachefile_invalid_checksum += 1 )
34863544 return true
34873545 end
34883546 end
34893547
34903548 curr_prefs_hash = get_preferences_hash (id. uuid, prefs)
34913549 if prefs_hash != curr_prefs_hash
34923550 @debug " Rejecting cache file $cachefile because preferences hash does not match 0x$(string (prefs_hash, base= 16 )) != 0x$(string (curr_prefs_hash, base= 16 )) "
3551+ isnothing (reasons) || (reasons. prefs_hash_mismatch += 1 )
34933552 return true
34943553 end
34953554
0 commit comments