Skip to content
3 changes: 2 additions & 1 deletion Compiler/test/special_loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

# Only run when testing Base compiler
if Base.identify_package("Compiler") === nothing
mktempdir() do dir
include(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "test", "tempdepot.jl"))
mkdepottempdir() do dir
withenv("JULIA_DEPOT_PATH" => dir * (Sys.iswindows() ? ";" : ":"), "JULIA_LOAD_PATH" => nothing) do
cd(joinpath(@__DIR__, "CompilerLoadingTest")) do
@test success(pipeline(`$(Base.julia_cmd()[1]) --startup-file=no --project=. compiler_loading_test.jl`; stdout, stderr))
Expand Down
59 changes: 48 additions & 11 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,9 @@ function mkpath(path::AbstractString; mode::Integer = 0o777)
return path
end

# Files that were requested to be deleted but can't be by the current process
# i.e. loaded DLLs on Windows
delayed_delete_dir() = joinpath(tempdir(), "julia_delayed_deletes")
# Files that were requested to be deleted but can't be by the current process,
# i.e. loaded DLLs on Windows, are listed in the directory below
delayed_delete_ref() = joinpath(tempdir(), "julia_delayed_deletes_ref")

"""
rm(path::AbstractString; force::Bool=false, recursive::Bool=false)
Expand Down Expand Up @@ -288,13 +288,7 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false, allo
force && err.code==Base.UV_ENOENT && return
@static if Sys.iswindows()
if allow_delayed_delete && err.code==Base.UV_EACCES && endswith(path, ".dll")
# Loaded DLLs cannot be deleted on Windows, even with posix delete mode
# but they can be moved. So move out to allow the dir to be deleted.
# Pkg.gc() cleans up this dir when possible
dir = mkpath(delayed_delete_dir())
temp_path = tempname(dir, cleanup = false, suffix = string("_", basename(path)))
@debug "Could not delete DLL most likely because it is loaded, moving to tempdir" path temp_path
mv(path, temp_path)
delayed_delete_dll(path)
return
end
end
Expand Down Expand Up @@ -330,6 +324,22 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false, allo
end


# Loaded DLLs cannot be deleted on Windows, even with posix delete mode but they can be renamed.
# delayed_delete_dll(path) does so temporarily, until later cleanup by Pkg.gc().
function delayed_delete_dll(path)
# in-use DLL must be kept on the same drive
temp_path = tempname(abspath(dirname(path)); cleanup=false, suffix=string("_", basename(path)))
@debug "Could not delete DLL most likely because it is loaded, moving to a temporary path" path temp_path
mkpath(delayed_delete_ref())
io = last(mktemp(delayed_delete_ref(); cleanup=false))
try
print(io, temp_path) # record the temporary path for Pkg.gc()
finally
close(io)
end
rename(path, temp_path) # do not call mv which could recursively call rm(path)
end

# The following use Unix command line facilities
function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::AbstractString;
force::Bool=false)
Expand Down Expand Up @@ -678,8 +688,35 @@ end
# deprecated internal function used by some packages
temp_cleanup_purge(; force=false) = force ? temp_cleanup_purge_all() : @lock TEMP_CLEANUP_LOCK temp_cleanup_purge_prelocked(false)

function temp_cleanup_postprocess(cleanup_dirs)
if !isempty(cleanup_dirs)
rmcmd = """
cleanuplist = readlines(stdin) # This loop won't start running until stdin is closed, which is supposed to be sequenced after the process exits
sleep(1) # Wait for the operating system to hopefully be ready, since the OS implementation is probably incorrect, given the history of buggy work-arounds like this that have existed for ages in dotNet and libuv
for path in cleanuplist
try
rm(path, force=true, recursive=true)
catch ex
@warn "Failed to clean up temporary path \$(repr(path))\n\$ex" _group=:file
end
end
"""
cmd = Cmd(Base.cmd_gen(((Base.julia_cmd(),), ("--startup-file=no",), ("-e",), (rmcmd,))); ignorestatus = true, detach = true)
pw = Base.PipeEndpoint()
run(cmd, pw, devnull, stderr; wait=false)
join(pw, cleanup_dirs, "\n")
Base.dup(Base._fd(pw)) # intentionally leak a reference, until the process exits
close(pw)
end
end

function temp_cleanup_atexit()
temp_cleanup_purge_all()
@lock TEMP_CLEANUP_LOCK temp_cleanup_postprocess(keys(TEMP_CLEANUP))
end

function __postinit__()
Base.atexit(temp_cleanup_purge_all)
Base.atexit(temp_cleanup_atexit)
end

const temp_prefix = "jl_"
Expand Down
4 changes: 3 additions & 1 deletion test/compileall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# We make it a separate test target here, so that it can run in parallel
# with the rest of the tests.

include("tempdepot.jl")

function precompile_test_harness(@nospecialize(f))
load_path = mktempdir()
load_path = mkdepottempdir()
try
pushfirst!(LOAD_PATH, load_path)
pushfirst!(DEPOT_PATH, load_path)
Expand Down
9 changes: 3 additions & 6 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const Bottom = Union{}
# For curmod_*
include("testenv.jl")

include("tempdepot.jl")

## tests that `const` field declarations

# sanity tests that our built-in types are marked correctly for const fields
Expand Down Expand Up @@ -8443,7 +8445,7 @@ end

# precompilation
let load_path = mktempdir()
depot_path = mktempdir()
depot_path = mkdepottempdir()
try
pushfirst!(LOAD_PATH, load_path)
pushfirst!(DEPOT_PATH, depot_path)
Expand Down Expand Up @@ -8485,11 +8487,6 @@ let load_path = mktempdir()
filter!((≠)(load_path), LOAD_PATH)
filter!((≠)(depot_path), DEPOT_PATH)
rm(load_path, recursive=true, force=true)
try
rm(depot_path, force=true, recursive=true)
catch err
@show err
end
end
end

Expand Down
51 changes: 19 additions & 32 deletions test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ end
@test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__())

original_depot_path = copy(Base.DEPOT_PATH)
include("tempdepot.jl")
include("precompile_utils.jl")

loaded_files = String[]
Expand Down Expand Up @@ -226,7 +227,6 @@ end
end
end


## functional testing of package identification, location & loading ##

saved_load_path = copy(LOAD_PATH)
Expand All @@ -236,8 +236,9 @@ watcher_counter = Ref(0)
push!(Base.active_project_callbacks, () -> watcher_counter[] += 1)
push!(Base.active_project_callbacks, () -> error("broken"))

const testdefaultdepot = mkdepottempdir()
push!(empty!(LOAD_PATH), joinpath(@__DIR__, "project"))
append!(empty!(DEPOT_PATH), [mktempdir(), joinpath(@__DIR__, "depot")])
append!(empty!(DEPOT_PATH), [testdefaultdepot, joinpath(@__DIR__, "depot")])
@test watcher_counter[] == 0
@test_logs (:error, r"active project callback .* failed") Base.set_active_project(nothing)
@test watcher_counter[] == 1
Expand Down Expand Up @@ -461,7 +462,7 @@ function make_env(flat, root, roots, graph, paths, dummies)
)
end

const depots = [mktempdir() for _ = 1:3]
const depots = [mkdepottempdir() for _ = 1:3]
const envs = Dict{String,Any}()

append!(empty!(DEPOT_PATH), depots)
Expand Down Expand Up @@ -755,13 +756,6 @@ end
for env in keys(envs)
rm(env, force=true, recursive=true)
end
for depot in depots
try
rm(depot, force=true, recursive=true)
catch err
@show err
end
end

append!(empty!(LOAD_PATH), saved_load_path)
append!(empty!(DEPOT_PATH), saved_depot_path)
Expand Down Expand Up @@ -1043,9 +1037,10 @@ end
_pkgversion == pkgversion(parent) || error("unexpected extension \$ext version: \$_pkgversion")
end
"""
depot_path = mktempdir()
try
proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl")
depot_path = mkdepottempdir()
proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl")

begin

function gen_extension_cmd(compile, distr=false)
load_distr = distr ? "using Distributed; addprocs(1)" : ""
Expand Down Expand Up @@ -1155,7 +1150,7 @@ end

# Extension-to-extension dependencies

mktempdir() do depot # Parallel pre-compilation
mkdepottempdir() do depot # Parallel pre-compilation
code = """
Base.disable_parallel_precompile = false
using ExtToExtDependency
Expand All @@ -1171,7 +1166,7 @@ end
)
@test occursin("Hello ext-to-ext!", String(read(cmd)))
end
mktempdir() do depot # Serial pre-compilation
mkdepottempdir() do depot # Serial pre-compilation
code = """
Base.disable_parallel_precompile = true
using ExtToExtDependency
Expand All @@ -1188,7 +1183,7 @@ end
@test occursin("Hello ext-to-ext!", String(read(cmd)))
end

mktempdir() do depot # Parallel pre-compilation
mkdepottempdir() do depot # Parallel pre-compilation
code = """
Base.disable_parallel_precompile = false
using CrossPackageExtToExtDependency
Expand All @@ -1204,7 +1199,7 @@ end
)
@test occursin("Hello x-package ext-to-ext!", String(read(cmd)))
end
mktempdir() do depot # Serial pre-compilation
mkdepottempdir() do depot # Serial pre-compilation
code = """
Base.disable_parallel_precompile = true
using CrossPackageExtToExtDependency
Expand All @@ -1224,7 +1219,7 @@ end
# Extensions for "parent" dependencies
# (i.e. an `ExtAB` where A depends on / loads B, but B provides the extension)

mktempdir() do depot # Parallel pre-compilation
mkdepottempdir() do depot # Parallel pre-compilation
code = """
Base.disable_parallel_precompile = false
using Parent
Expand All @@ -1239,7 +1234,7 @@ end
)
@test occursin("Hello parent!", String(read(cmd)))
end
mktempdir() do depot # Serial pre-compilation
mkdepottempdir() do depot # Serial pre-compilation
code = """
Base.disable_parallel_precompile = true
using Parent
Expand All @@ -1254,13 +1249,6 @@ end
)
@test occursin("Hello parent!", String(read(cmd)))
end

finally
try
rm(depot_path, force=true, recursive=true)
catch err
@show err
end
end
end

Expand Down Expand Up @@ -1364,7 +1352,7 @@ end
end

@testset "relocatable upgrades #51989" begin
mktempdir() do depot
mkdepottempdir() do depot
# realpath is needed because Pkg is used for one of the precompile paths below, and Pkg calls realpath on the
# project path so the cache file slug will be different if the tempdir is given as a symlink
# (which it often is on MacOS) which would break the test.
Expand Down Expand Up @@ -1438,7 +1426,7 @@ end
end

@testset "code coverage disabled during precompilation" begin
mktempdir() do depot
mkdepottempdir() do depot
cov_test_dir = joinpath(@__DIR__, "project", "deps", "CovTest.jl")
cov_cache_dir = joinpath(depot, "compiled", "v$(VERSION.major).$(VERSION.minor)", "CovTest")
function rm_cov_files()
Expand Down Expand Up @@ -1482,7 +1470,7 @@ end
end

@testset "command-line flags" begin
mktempdir() do depot_path mktempdir() do dir
mkdepottempdir() do depot_path mktempdir() do dir
# generate a Parent.jl and Child.jl package, with Parent depending on Child
open(joinpath(dir, "Child.jl"), "w") do io
println(io, """
Expand Down Expand Up @@ -1565,7 +1553,7 @@ end
end

@testset "including non-existent file throws proper error #52462" begin
mktempdir() do depot
mkdepottempdir() do depot
project_path = joinpath(depot, "project")
mkpath(project_path)

Expand Down Expand Up @@ -1707,7 +1695,7 @@ end
end

@testset "require_stdlib loading duplication" begin
depot_path = mktempdir()
depot_path = mkdepottempdir()
oldBase64 = nothing
try
push!(empty!(DEPOT_PATH), depot_path)
Expand All @@ -1731,7 +1719,6 @@ end
finally
oldBase64 === nothing || Base.register_root_module(oldBase64)
copy!(DEPOT_PATH, original_depot_path)
rm(depot_path, force=true, recursive=true)
end
end

Expand Down
10 changes: 3 additions & 7 deletions test/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using Test, Distributed, Random, Logging, Libdl
using REPL # testing the doc lookup function should be outside of the scope of this file, but is currently tested here

include("precompile_utils.jl")
include("tempdepot.jl")

Foo_module = :Foo4b3a94a1a081a8cb
foo_incl_dep = :foo4b3a94a1a081a8cb
Expand Down Expand Up @@ -1524,11 +1525,11 @@ end
test_workers = addprocs(1)
push!(test_workers, myid())
save_cwd = pwd()
temp_path = mktempdir()
temp_path = mkdepottempdir()
try
cd(temp_path)
load_path = mktempdir(temp_path)
load_cache_path = mktempdir(temp_path)
load_cache_path = mkdepottempdir(temp_path)

ModuleA = :Issue19960A
ModuleB = :Issue19960B
Expand Down Expand Up @@ -1576,11 +1577,6 @@ end
end
finally
cd(save_cwd)
try
rm(temp_path, recursive=true)
catch err
@show err
end
pop!(test_workers) # remove myid
rmprocs(test_workers)
end
Expand Down
18 changes: 4 additions & 14 deletions test/precompile_utils.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

include("tempdepot.jl")

function precompile_test_harness(@nospecialize(f), testset::String)
@testset "$testset" precompile_test_harness(f, true)
end
function precompile_test_harness(@nospecialize(f), separate::Bool=true)
load_path = mktempdir()
load_cache_path = separate ? mktempdir() : load_path
load_path = mkdepottempdir()
load_cache_path = separate ? mkdepottempdir() : load_path
try
pushfirst!(LOAD_PATH, load_path)
pushfirst!(DEPOT_PATH, load_cache_path)
f(load_path)
finally
try
rm(load_path, force=true, recursive=true)
catch err
@show err
end
if separate
try
rm(load_cache_path, force=true, recursive=true)
catch err
@show err
end
end
filter!(()(load_path), LOAD_PATH)
separate && filter!(()(load_cache_path), DEPOT_PATH)
end
Expand Down
Loading