Repository: JuliaLang/julia
File: base/file.jl (Julia)

Code before:
        end
        req = Libc.malloc(_sizeof_uv_fs)
        try
            ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
            uv_fs_req_cleanup(req)
            if ret < 0 && !(force && ret == Base.UV_ENOENT)
                uv_error("rm($(repr(path)))", ret)
            end
            nothing
        finally
            Libc.free(req)
        end
    end
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))
    print(io, temp_path) # record the temporary path for Pkg.gc()
    close(io)
    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)
    if ispath(dst)
        if force
            # Check for issue when: (src == dst) or when one is a link to the other
            # https://github.com/JuliaLang/julia/pull/11172#issuecomment-100391076
            if Base.samefile(src, dst)
                abs_src = islink(src) ? abspath(readlink(src)) : abspath(src)
                abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst)
                throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir. ",
                                           "This is not supported.\n  ",
                                           "`src` refers to: $(abs_src)\n  ",
                                           "`dst` refers to: $(abs_dst)\n")))
            end
            rm(dst; recursive=true, force=true)
        else
            throw(ArgumentError(string("'$dst' exists. `force=true` ",
                                       "is required to remove '$dst' before $(txt).")))
        end
    end
end

Review comment (bug):
I suggest using `open() do io` form here, just in case the `print()` throws an error (out of space?) which would then cause us to not call `close(io)` until we eventually GC.  Considering we could end up attempting to delete many files in a row and hit the same error over and over again, it'd be nice to ensure that we're closing `io` immediately so we don't accidentally have many file handles stacking up.

I think it is clever to name the temp path ending in `.dll`, this ensures that future attempts to delete this file (if we fail to record the reference here) will go through the same code path, so it has a hope to be cleaned up in the future.

Code after:
        end
        req = Libc.malloc(_sizeof_uv_fs)
        try
            ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
            uv_fs_req_cleanup(req)
            if ret < 0 && !(force && ret == Base.UV_ENOENT)
                uv_error("rm($(repr(path)))", ret)
            end
            nothing
        finally
            Libc.free(req)
        end
    end
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)
    if ispath(dst)
        if force
            # Check for issue when: (src == dst) or when one is a link to the other
            # https://github.com/JuliaLang/julia/pull/11172#issuecomment-100391076
            if Base.samefile(src, dst)
                abs_src = islink(src) ? abspath(readlink(src)) : abspath(src)
                abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst)
                throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir. ",
                                           "This is not supported.\n  ",
                                           "`src` refers to: $(abs_src)\n  ",
                                           "`dst` refers to: $(abs_dst)\n")))
            end
            rm(dst; recursive=true, force=true)
        else
            throw(ArgumentError(string("'$dst' exists. `force=true` ",
                                       "is required to remove '$dst' before $(txt).")))