Skip to content

Commit

Permalink
Raise an error when using include_dependency with non-existent file…
Browse files Browse the repository at this point in the history
… or directory (#53286)

Replaces #52105
Fixes #52063

There is a question about whether the `ispath & uperm` check should be
moved inside the `_track_dependencies[]` check (like it was in #52105),
which would make it such that any errors are thrown only during
precompilation.

---------

Co-authored-by: Qian Long <longqian95@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit 71fa11f)
  • Loading branch information
fatteneder authored and KristofferC committed Jul 23, 2024
1 parent 3387373 commit 3f67b31
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 10 deletions.
13 changes: 10 additions & 3 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2003,14 +2003,21 @@ const include_callbacks = Any[]
const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
const _require_dependencies = Any[] # a list of (mod, abspath, fsize, hash, mtime) tuples that are the file dependencies of the module currently being precompiled
const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies
function _include_dependency(mod::Module, _path::AbstractString; track_content=true)
function _include_dependency(mod::Module, _path::AbstractString; track_content=true,
path_may_be_dir=false)
prev = source_path(nothing)
if prev === nothing
path = abspath(_path)
else
path = normpath(joinpath(dirname(prev), _path))
end
if _track_dependencies[]
if !_track_dependencies[]
if !path_may_be_dir && !isfile(path)
throw(SystemError("opening file $(repr(path))", Libc.ENOENT))
elseif path_may_be_dir && !Filesystem.isreadable(path)
throw(SystemError("opening file or folder $(repr(path))", Libc.ENOENT))
end
else
@lock require_lock begin
if track_content
hash = isdir(path) ? _crc32c(join(readdir(path))) : open(_crc32c, path, "r")
Expand Down Expand Up @@ -2040,7 +2047,7 @@ no effect outside of compilation.
Keyword argument `track_content` requires at least Julia 1.11.
"""
function include_dependency(path::AbstractString; track_content::Bool=false)
_include_dependency(Main, path, track_content=track_content)
_include_dependency(Main, path, track_content=track_content, path_may_be_dir=true)
return nothing
end

Expand Down
24 changes: 23 additions & 1 deletion test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,29 @@ end
end

file = joinpath(depot, "dev", "non-existent.jl")
@test_throws SystemError("opening file $(repr(file))") include(file)
@test try
include(file); false
catch e
@test e isa SystemError
@test e.prefix == "opening file $(repr(file))"
true
end
touch(file)
@test include_dependency(file) === nothing
chmod(file, 0x000)

# same for include_dependency: #52063
dir = mktempdir() do dir
@test include_dependency(dir) === nothing
dir
end
@test try
include_dependency(dir); false
catch e
@test e isa SystemError
@test e.prefix == "opening file or folder $(repr(dir))"
true
end
end
end

Expand Down
57 changes: 51 additions & 6 deletions test/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ using Test, Distributed, Random, Logging
using REPL # doc lookup function

Foo_module = :Foo4b3a94a1a081a8cb
foo_incl_dep = :foo4b3a94a1a081a8cb
bar_incl_dep = :bar4b3a94a1a081a8cb
Foo2_module = :F2oo4b3a94a1a081a8cb
FooBase_module = :FooBase4b3a94a1a081a8cb
@eval module ConflictingBindings
Expand Down Expand Up @@ -102,6 +104,8 @@ precompile_test_harness(false) do dir
Foo_file = joinpath(dir, "$Foo_module.jl")
Foo2_file = joinpath(dir, "$Foo2_module.jl")
FooBase_file = joinpath(dir, "$FooBase_module.jl")
foo_file = joinpath(dir, "$foo_incl_dep.jl")
bar_file = joinpath(dir, "$bar_incl_dep.jl")

write(FooBase_file,
"""
Expand Down Expand Up @@ -150,11 +154,11 @@ precompile_test_harness(false) do dir
# test that docs get reconnected
@doc "foo function" foo(x) = x + 1
include_dependency("foo.jl")
include_dependency("foo.jl")
include_dependency("$foo_incl_dep.jl")
include_dependency("$foo_incl_dep.jl")
module Bar
public bar
include_dependency("bar.jl")
include_dependency("$bar_incl_dep.jl")
end
@doc "Bar module" Bar # this needs to define the META dictionary via eval
@eval Bar @doc "bar function" bar(x) = x + 2
Expand Down Expand Up @@ -297,6 +301,8 @@ precompile_test_harness(false) do dir
oid_mat_int = objectid(a_mat_int)
end
""")
# Issue #52063
touch(foo_file); touch(bar_file)
# Issue #12623
@test __precompile__(false) === nothing

Expand Down Expand Up @@ -436,8 +442,7 @@ precompile_test_harness(false) do dir
modules, (deps, _, requires), required_modules, _... = Base.parse_cache_header(cachefile)
discard_module = mod_fl_mt -> mod_fl_mt.filename
@test modules == [ Base.PkgId(Foo) => Base.module_build_id(Foo) % UInt64 ]
# foo.jl and bar.jl are never written to disk, so they are not relocatable
@test map(x -> x.filename, deps) == [ Foo_file, joinpath("@depot", "foo.jl"), joinpath("@depot", "bar.jl") ]
@test map(x -> x.filename, deps) == [ Foo_file, joinpath("@depot", foo_file), joinpath("@depot", bar_file) ]
@test requires == [ Base.PkgId(Foo) => Base.PkgId(string(FooBase_module)),
Base.PkgId(Foo) => Base.PkgId(Foo2),
Base.PkgId(Foo) => Base.PkgId(Test),
Expand All @@ -446,7 +451,7 @@ precompile_test_harness(false) do dir
@test !isempty(srctxt) && srctxt == read(Foo_file, String)
@test_throws ErrorException Base.read_dependency_src(cachefile, "/tmp/nonexistent.txt")
# dependencies declared with `include_dependency` should not be stored
@test_throws ErrorException Base.read_dependency_src(cachefile, joinpath(dir, "foo.jl"))
@test_throws ErrorException Base.read_dependency_src(cachefile, joinpath(dir, foo_file))

modules, deps1 = Base.cache_dependencies(cachefile)
modules_ok = merge(
Expand Down Expand Up @@ -2111,6 +2116,46 @@ precompile_test_harness("Test flags") do load_path
@test !Base.isprecompiled(id, ;flags=current_flags)
end

precompile_test_harness("Issue #52063") do load_path
fname = joinpath(load_path, "i_do_not_exist.jl")
@test try
include_dependency(fname); false
catch e
@test e isa SystemError
@test e.prefix == "opening file or folder $(repr(fname))"
true
end
touch(fname)
@test include_dependency(fname) === nothing
chmod(fname, 0x000)
@test try
include_dependency(fname); false
catch e
@test e isa SystemError
@test e.prefix == "opening file or folder $(repr(fname))"
true
end broken=Sys.iswindows()
dir = mktempdir() do dir
@test include_dependency(dir) === nothing
chmod(dir, 0x000)
@test try
include_dependency(dir); false
catch e
@test e isa SystemError
@test e.prefix == "opening file or folder $(repr(dir))"
true
end broken=Sys.iswindows()
dir
end
@test try
include_dependency(dir); false
catch e
@test e isa SystemError
@test e.prefix == "opening file or folder $(repr(dir))"
true
end
end

empty!(Base.DEPOT_PATH)
append!(Base.DEPOT_PATH, original_depot_path)
empty!(Base.LOAD_PATH)
Expand Down

0 comments on commit 3f67b31

Please sign in to comment.