From 578d284ea018de5e6fe3193ab94508d864abed8e Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Tue, 13 Feb 2024 12:03:20 +0100 Subject: [PATCH 1/2] make dependencis of extensions be found in implicit environments Co-authored by: Mark Kittisopikul --- base/loading.jl | 52 ++++++++++++++++--- test/loading.jl | 12 +++++ .../Extensions/ImplicitEnv/A/Project.toml | 9 ++++ .../Extensions/ImplicitEnv/A/ext/BExt.jl | 3 ++ .../project/Extensions/ImplicitEnv/A/src/A.jl | 5 ++ .../Extensions/ImplicitEnv/B/Project.toml | 3 ++ .../project/Extensions/ImplicitEnv/B/src/B.jl | 5 ++ 7 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 test/project/Extensions/ImplicitEnv/A/Project.toml create mode 100644 test/project/Extensions/ImplicitEnv/A/ext/BExt.jl create mode 100644 test/project/Extensions/ImplicitEnv/A/src/A.jl create mode 100644 test/project/Extensions/ImplicitEnv/B/Project.toml create mode 100644 test/project/Extensions/ImplicitEnv/B/src/B.jl diff --git a/base/loading.jl b/base/loading.jl index d18d4e72ae809..ea5fb5c25f43d 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -776,12 +776,21 @@ end function entry_point_and_project_file(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}} path = normpath(joinpath(dir, "$name.jl")) isfile_casesensitive(path) && return path, nothing - dir = joinpath(dir, name) - path, project_file = entry_point_and_project_file_inside(dir, name) + dir_name = joinpath(dir, name) + path, project_file = entry_point_and_project_file_inside(dir_name, name) path === nothing || return path, project_file - dir = dir * ".jl" - path, project_file = entry_point_and_project_file_inside(dir, name) + dir_jl = dir_name * ".jl" + path, project_file = entry_point_and_project_file_inside(dir_jl, name) path === nothing || return path, project_file + # `name` could be an extension so we have to check for that: + for pkg in readdir(dir; join=true) + project_file = env_project_file(pkg) + project_file isa String || continue + path = project_file_ext_path(project_file, name) + if path !== nothing + return path, project_file + end + end return nothing, nothing end @@ -796,11 +805,12 @@ end ## explicit project & manifest API ## # find project file root or deps `name => uuid` mapping +# `ext` is the name of the extension if `name` is loaded from one # return `nothing` if `name` is not found -function explicit_project_deps_get(project_file::String, name::String)::Union{Nothing,UUID} +function explicit_project_deps_get(project_file::String, name::String, ext::Union{String,Nothing}=nothing)::Union{Nothing,UUID} d = parsed_toml(project_file) - root_uuid = dummy_uuid(project_file) if get(d, "name", nothing)::Union{String, Nothing} === name + root_uuid = dummy_uuid(project_file) uuid = get(d, "uuid", nothing)::Union{String, Nothing} return uuid === nothing ? root_uuid : UUID(uuid) end @@ -809,6 +819,19 @@ function explicit_project_deps_get(project_file::String, name::String)::Union{No uuid = get(deps, name, nothing)::Union{String, Nothing} uuid === nothing || return UUID(uuid) end + if ext !== nothing + extensions = get(d, "extensions", nothing) + extensions === nothing && return nothing + ext_data = get(extensions, ext, nothing) + ext_data === nothing && return nothing + if (ext_data isa String && name == ext_data) || (ext_data isa Vector{String} && name in ext_data) + weakdeps = get(d, "weakdeps", nothing)::Union{Dict{String, Any}, Nothing} + weakdeps === nothing && return nothing + wuuid = get(weakdeps, name, nothing)::Union{String, Nothing} + wuuid === nothing && return nothing + return UUID(wuuid) + end + end return nothing end @@ -998,9 +1021,22 @@ function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Un project_file = entry_point_and_project_file(dir, where.name)[2] project_file === nothing && return nothing # a project file is mandatory for a package with a uuid proj = project_file_name_uuid(project_file, where.name) - proj == where || return nothing # verify that this is the correct project file + ext = nothing + if proj !== where + # `where` could be an extension in `proj` + d = parsed_toml(project_file) + exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} + if exts !== nothing && where.name in keys(exts) + if where.uuid !== uuid5(proj.uuid, where.name) + return nothing + end + ext = where.name + else + return nothing + end + end # this is the correct project, so stop searching here - pkg_uuid = explicit_project_deps_get(project_file, name) + pkg_uuid = explicit_project_deps_get(project_file, name, ext) return PkgId(pkg_uuid, name) end diff --git a/test/loading.jl b/test/loading.jl index 3740babcd17ed..ae83b0bb4db53 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1131,6 +1131,18 @@ end cmd = `$(Base.julia_cmd()) --startup-file=no -e $sysimg_ext_test_code` cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([proj, "@stdlib"], sep)) run(cmd) + + + # Extensions in implicit environments + old_load_path = copy(LOAD_PATH) + try + empty!(LOAD_PATH) + push!(LOAD_PATH, joinpath(@__DIR__, "project", "Extensions", "ImplicitEnv")) + pkgid_B = Base.PkgId(Base.uuid5(Base.identify_package("A").uuid, "BExt"), "BExt") + @test Base.identify_package(pkgid_B, "B") isa Base.PkgId + finally + copy!(LOAD_PATH, old_load_path) + end finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/ImplicitEnv/A/Project.toml b/test/project/Extensions/ImplicitEnv/A/Project.toml new file mode 100644 index 0000000000000..043272d4bd015 --- /dev/null +++ b/test/project/Extensions/ImplicitEnv/A/Project.toml @@ -0,0 +1,9 @@ +name = "A" +uuid = "299a509a-2181-4868-8714-15151945d902" +version = "0.1.0" + +[weakdeps] +B = "c2c18cb0-3543-497c-ac2a-523c527589e5" + +[extensions] +BExt = "B" diff --git a/test/project/Extensions/ImplicitEnv/A/ext/BExt.jl b/test/project/Extensions/ImplicitEnv/A/ext/BExt.jl new file mode 100644 index 0000000000000..70be6435bcbe8 --- /dev/null +++ b/test/project/Extensions/ImplicitEnv/A/ext/BExt.jl @@ -0,0 +1,3 @@ +module BExt + +end diff --git a/test/project/Extensions/ImplicitEnv/A/src/A.jl b/test/project/Extensions/ImplicitEnv/A/src/A.jl new file mode 100644 index 0000000000000..ab16fa1de96af --- /dev/null +++ b/test/project/Extensions/ImplicitEnv/A/src/A.jl @@ -0,0 +1,5 @@ +module A + +greet() = print("Hello World!") + +end # module A diff --git a/test/project/Extensions/ImplicitEnv/B/Project.toml b/test/project/Extensions/ImplicitEnv/B/Project.toml new file mode 100644 index 0000000000000..d919c27be0467 --- /dev/null +++ b/test/project/Extensions/ImplicitEnv/B/Project.toml @@ -0,0 +1,3 @@ +name = "B" +uuid = "c2c18cb0-3543-497c-ac2a-523c527589e5" +version = "0.1.0" diff --git a/test/project/Extensions/ImplicitEnv/B/src/B.jl b/test/project/Extensions/ImplicitEnv/B/src/B.jl new file mode 100644 index 0000000000000..79b5a1204765f --- /dev/null +++ b/test/project/Extensions/ImplicitEnv/B/src/B.jl @@ -0,0 +1,5 @@ +module B + +greet() = print("Hello World!") + +end # module B From 74568b5d7c839e25f9e107ef112351a3d635bffc Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Wed, 14 Feb 2024 08:26:31 +0100 Subject: [PATCH 2/2] fix case when implicit env has multiple extension with the same name --- base/loading.jl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index ea5fb5c25f43d..7e74548a394f3 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -782,11 +782,17 @@ function entry_point_and_project_file(dir::String, name::String)::Union{Tuple{No dir_jl = dir_name * ".jl" path, project_file = entry_point_and_project_file_inside(dir_jl, name) path === nothing || return path, project_file - # `name` could be an extension so we have to check for that: + return nothing, nothing +end + +# Find the project file for the extension `ext` in the implicit env `dir`` +function implicit_env_project_file_extension(dir::String, ext::PkgId) for pkg in readdir(dir; join=true) project_file = env_project_file(pkg) project_file isa String || continue - path = project_file_ext_path(project_file, name) + proj = project_file_name_uuid(project_file, "") + uuid5(proj.uuid, ext.name) == ext.uuid || continue + path = project_file_ext_path(project_file, ext.name) if path !== nothing return path, project_file end @@ -1019,7 +1025,11 @@ end function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Union{Nothing,PkgId} @assert where.uuid !== nothing project_file = entry_point_and_project_file(dir, where.name)[2] - project_file === nothing && return nothing # a project file is mandatory for a package with a uuid + if project_file === nothing + # `where` could be an extension + project_file = implicit_env_project_file_extension(dir, where)[2] + project_file === nothing && return nothing + end proj = project_file_name_uuid(project_file, where.name) ext = nothing if proj !== where