Skip to content

Commit 9afd069

Browse files
fattenederKristofferC
authored andcommitted
Add Base.isrelocatable(pkg) (#53906)
This PR adds a utility function `isrelocatable(pkg)` that can be used to check if `pkg` is already precompiled and if the associated cachefile is relocatable. The reason to implicitly perform the `isprecompiled` check is that the exact same computation needs to be done to find the right `.ji`. A `pkg` is said to be relocatable if 1. all `include()` paths are relocatable (they start with `@depot`), 2. all `include_dependency()` paths are relocatable (they start with `@depot` and `track_content=true` was used to include them). (cherry picked from commit e9d25ca)
1 parent 6f8e78d commit 9afd069

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

base/loading.jl

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,25 +1643,13 @@ end
16431643
# should sync with the types of arguments of `stale_cachefile`
16441644
const StaleCacheKey = Tuple{Base.PkgId, UInt128, String, String}
16451645

1646-
"""
1647-
Base.isprecompiled(pkg::PkgId; ignore_loaded::Bool=false)
1648-
1649-
Returns whether a given PkgId within the active project is precompiled.
1650-
1651-
By default this check observes the same approach that code loading takes
1652-
with respect to when different versions of dependencies are currently loaded
1653-
to that which is expected. To ignore loaded modules and answer as if in a
1654-
fresh julia session specify `ignore_loaded=true`.
1655-
1656-
!!! compat "Julia 1.10"
1657-
This function requires at least Julia 1.10.
1658-
"""
1659-
function isprecompiled(pkg::PkgId;
1646+
function compilecache_path(pkg::PkgId;
16601647
ignore_loaded::Bool=false,
16611648
stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(),
16621649
cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg),
16631650
sourcepath::Union{String,Nothing}=Base.locate_package(pkg),
16641651
flags::CacheFlags=CacheFlags())
1652+
path = nothing
16651653
isnothing(sourcepath) && error("Cannot locate source for $(repr("text/plain", pkg))")
16661654
for path_to_try in cachepaths
16671655
staledeps = stale_cachefile(sourcepath, path_to_try, ignore_loaded = true, requested_flags=flags)
@@ -1693,10 +1681,64 @@ function isprecompiled(pkg::PkgId;
16931681
# file might be read-only and then we fail to update timestamp, which is fine
16941682
ex isa IOError || rethrow()
16951683
end
1696-
return true
1684+
path = path_to_try
1685+
break
16971686
@label check_next_path
16981687
end
1699-
return false
1688+
return path
1689+
end
1690+
1691+
"""
1692+
Base.isprecompiled(pkg::PkgId; ignore_loaded::Bool=false)
1693+
1694+
Returns whether a given PkgId within the active project is precompiled.
1695+
1696+
By default this check observes the same approach that code loading takes
1697+
with respect to when different versions of dependencies are currently loaded
1698+
to that which is expected. To ignore loaded modules and answer as if in a
1699+
fresh julia session specify `ignore_loaded=true`.
1700+
1701+
!!! compat "Julia 1.10"
1702+
This function requires at least Julia 1.10.
1703+
"""
1704+
function isprecompiled(pkg::PkgId;
1705+
ignore_loaded::Bool=false,
1706+
stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(),
1707+
cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg),
1708+
sourcepath::Union{String,Nothing}=Base.locate_package(pkg),
1709+
flags::CacheFlags=CacheFlags())
1710+
path = compilecache_path(pkg; ignore_loaded, stale_cache, cachepaths, sourcepath, flags)
1711+
return !isnothing(path)
1712+
end
1713+
1714+
"""
1715+
Base.isrelocatable(pkg::PkgId)
1716+
1717+
Returns whether a given PkgId within the active project is precompiled and the
1718+
associated cache is relocatable.
1719+
1720+
!!! compat "Julia 1.11"
1721+
This function requires at least Julia 1.11.
1722+
"""
1723+
function isrelocatable(pkg::PkgId)
1724+
path = compilecache_path(pkg)
1725+
isnothing(path) && return false
1726+
io = open(path, "r")
1727+
try
1728+
iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile."))
1729+
_, (includes, includes_srcfiles, _), _... = _parse_cache_header(io, path)
1730+
for inc in includes
1731+
!startswith(inc.filename, "@depot") && return false
1732+
if inc includes_srcfiles
1733+
# its an include_dependency
1734+
track_content = inc.mtime == -1.0
1735+
track_content || return false
1736+
end
1737+
end
1738+
finally
1739+
close(io)
1740+
end
1741+
return true
17001742
end
17011743

17021744
# search for a precompile cache file to load, after some various checks
@@ -3025,7 +3067,7 @@ function resolve_depot(inc::AbstractString)
30253067
end
30263068

30273069

3028-
function parse_cache_header(f::IO, cachefile::AbstractString)
3070+
function _parse_cache_header(f::IO, cachefile::AbstractString)
30293071
flags = read(f, UInt8)
30303072
modules = Vector{Pair{PkgId, UInt64}}()
30313073
while true
@@ -3109,6 +3151,13 @@ function parse_cache_header(f::IO, cachefile::AbstractString)
31093151

31103152
srcfiles = srctext_files(f, srctextpos, includes)
31113153

3154+
return modules, (includes, srcfiles, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags
3155+
end
3156+
3157+
function parse_cache_header(f::IO, cachefile::AbstractString)
3158+
modules, (includes, srcfiles, requires), required_modules,
3159+
srctextpos, prefs, prefs_hash, clone_targets, flags = _parse_cache_header(f, cachefile)
3160+
31123161
includes_srcfiles = CacheHeaderIncludes[]
31133162
includes_depfiles = CacheHeaderIncludes[]
31143163
for (i, inc) in enumerate(includes)

test/relocatedepot.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ if !test_relocated_depot
7878
cachefiles = Base.find_all_in_cache_path(pkg)
7979
rm.(cachefiles, force=true)
8080
@test Base.isprecompiled(pkg) == false
81+
@test Base.isrelocatable(pkg) == false # because not precompiled
8182
Base.require(pkg)
8283
@test Base.isprecompiled(pkg, ignore_loaded=true) == true
84+
@test Base.isrelocatable(pkg) == true
8385
end
8486
end
8587

@@ -93,10 +95,12 @@ if !test_relocated_depot
9395
rm.(cachefiles, force=true)
9496
rm(joinpath(@__DIR__, pkgname, "src", "foodir"), force=true, recursive=true)
9597
@test Base.isprecompiled(pkg) == false
98+
@test Base.isrelocatable(pkg) == false # because not precompiled
9699
touch(joinpath(@__DIR__, pkgname, "src", "foo.txt"))
97100
mkdir(joinpath(@__DIR__, pkgname, "src", "foodir"))
98101
Base.require(pkg)
99102
@test Base.isprecompiled(pkg, ignore_loaded=true) == true
103+
@test Base.isrelocatable(pkg) == false # because tracked by mtime
100104
end
101105
end
102106

@@ -110,10 +114,12 @@ if !test_relocated_depot
110114
rm.(cachefiles, force=true)
111115
rm(joinpath(@__DIR__, pkgname, "src", "bardir"), force=true, recursive=true)
112116
@test Base.isprecompiled(pkg) == false
117+
@test Base.isrelocatable(pkg) == false # because not precompiled
113118
touch(joinpath(@__DIR__, pkgname, "src", "bar.txt"))
114119
mkdir(joinpath(@__DIR__, pkgname, "src", "bardir"))
115120
Base.require(pkg)
116121
@test Base.isprecompiled(pkg, ignore_loaded=true) == true
122+
@test Base.isrelocatable(pkg) == true
117123
end
118124
end
119125

@@ -202,6 +208,7 @@ else
202208
# stdlib should be already precompiled
203209
pkg = Base.identify_package("DelimitedFiles")
204210
@test Base.isprecompiled(pkg) == true
211+
@test Base.isrelocatable(pkg) == true
205212
end
206213
end
207214

@@ -213,6 +220,7 @@ else
213220
push!(DEPOT_PATH, joinpath(@__DIR__, "relocatedepot", "julia")) # contains cache file
214221
pkg = Base.identify_package(pkgname)
215222
@test Base.isprecompiled(pkg) == true
223+
@test Base.isrelocatable(pkg) == true
216224
end
217225
end
218226

@@ -224,10 +232,13 @@ else
224232
push!(DEPOT_PATH, joinpath(@__DIR__, "relocatedepot", "julia")) # contains cache file
225233
pkg = Base.identify_package(pkgname)
226234
@test Base.isprecompiled(pkg) == false # moving depot changes mtime of include_dependency
235+
@test Base.isrelocatable(pkg) == false # because not precompiled
227236
Base.require(pkg)
228237
@test Base.isprecompiled(pkg) == true
238+
@test Base.isrelocatable(pkg) == false # because tracked by mtime
229239
touch(joinpath(@__DIR__, "relocatedepot", "RelocationTestPkg2", "src", "foodir", "foofoo"))
230240
@test Base.isprecompiled(pkg) == false
241+
@test Base.isrelocatable(pkg) == false # because tracked by mtime
231242
end
232243
end
233244

@@ -239,8 +250,10 @@ else
239250
push!(DEPOT_PATH, joinpath(@__DIR__, "relocatedepot", "julia")) # contains cache file
240251
pkg = Base.identify_package(pkgname)
241252
@test Base.isprecompiled(pkg) == true
253+
@test Base.isrelocatable(pkg) == true
242254
touch(joinpath(@__DIR__, "relocatedepot", "RelocationTestPkg3", "src", "bardir", "barbar"))
243255
@test Base.isprecompiled(pkg) == false
256+
@test Base.isrelocatable(pkg) == false # because not precompiled
244257
end
245258
end
246259

0 commit comments

Comments
 (0)