From 0a0a22687144111b2ff7ef3f4d4308a8e9ed5b62 Mon Sep 17 00:00:00 2001 From: Ian Date: Mon, 19 Jun 2023 18:05:44 -0400 Subject: [PATCH] add Base.isprecompiled --- NEWS.md | 1 + base/loading.jl | 53 ++++++++++++++++++++++++++++++++++++++++++++ doc/src/base/base.md | 1 + test/precompile.jl | 5 ++++- 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index d73373d95d26e..5df70cfa69084 100644 --- a/NEWS.md +++ b/NEWS.md @@ -53,6 +53,7 @@ New library functions * `fourthroot(x)` is now defined in `Base.Math` and can be used to compute the fourth root of `x`. It can also be accessed using the unicode character `∜`, which can be typed by `\fourthroot` ([#48899]). * `Libc.memmove`, `Libc.memset`, and `Libc.memcpy` are now defined, whose functionality matches that of their respective C calls. +* `Base.isprecompiled(pkg::PkgId)` to identify whether a package has already been precompiled ([#50218]). New library features -------------------- diff --git a/base/loading.jl b/base/loading.jl index ae0681608844e..1bb944aa8de9b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1364,6 +1364,59 @@ end # End extensions +# should sync with the types of arguments of `stale_cachefile` +const StaleCacheKey = Tuple{Base.PkgId, UInt128, String, String} + +""" + Base.isprecompiled(pkg::PkgId) + +Returns whether a given PkgId within the active project is precompiled. + +!!! compat "Julia 1.10" + This function requires at least Julia 1.10. +""" +function isprecompiled(pkg::PkgId; + stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), + cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), + sourcepath::String=Base.locate_package(pkg) + ) + isnothing(sourcepath) && error("Cannot locate source for $(repr(PkgId))") + for path_to_try in cachepaths + staledeps = stale_cachefile(sourcepath, path_to_try, ignore_loaded = true) + if staledeps === true + continue + end + staledeps, _ = staledeps::Tuple{Vector{Any}, Union{Nothing, String}} + # finish checking staledeps module graph + for i in 1:length(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} + modpaths = find_all_in_cache_path(modkey) + for modpath_to_try in modpaths::Vector{String} + stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try)::StaleCacheKey + if get!(() -> stale_cachefile(stale_cache_key..., ignore_loaded=true) === true, + stale_cache, stale_cache_key) + continue + end + @goto check_next_dep + end + @goto check_next_path + @label check_next_dep + end + try + # update timestamp of precompilation file so that it is the first to be tried by code loading + touch(path_to_try) + catch ex + # file might be read-only and then we fail to update timestamp, which is fine + ex isa IOError || rethrow() + end + return true + @label check_next_path + end + return false +end + # loads a precompile cache file, after checking stale_cachefile tests function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt128) assert_havelock(require_lock) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index e540f50ca2cb0..81abc009f2fbc 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -462,6 +462,7 @@ Base.identify_package Base.locate_package Base.require Base.compilecache +Base.isprecompiled ``` ## Internals diff --git a/test/precompile.jl b/test/precompile.jl index 9cb5a1d52d485..62d862c384040 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -655,7 +655,10 @@ precompile_test_harness("code caching") do dir precompile(getelsize, (Vector{Int32},)) end """) - Base.compilecache(Base.PkgId(string(Cache_module))) + pkgid = Base.PkgId(string(Cache_module)) + @test !Base.isprecompiled(pkgid) + Base.compilecache(pkgid) + @test Base.isprecompiled(pkgid) @eval using $Cache_module M = getfield(@__MODULE__, Cache_module) # Test that this cache file "owns" all the roots