@@ -348,11 +348,14 @@ function identify_package_env(where::PkgId, name::String)
348348 else
349349 for env in load_path ()
350350 pkgid = manifest_deps_get (env, where , name)
351- pkgid === nothing && continue # not found--keep looking
351+ # If we didn't find `where` at all, keep looking through the environment stack
352+ pkgid === nothing && continue
352353 if pkgid. uuid != = nothing
353- pkg_env = pkgid, env # found in explicit environment--use it
354+ pkg_env = pkgid, env
354355 end
355- break # found in implicit environment--return "not found"
356+ # If we don't have pkgid.uuid, still break here - this is a sentinel that indicates
357+ # that we've found `where` but it did not have the required dependency. We terminate the search.
358+ break
356359 end
357360 if pkg_env === nothing && is_stdlib (where )
358361 # if not found it could be that manifests are from a different julia version/commit
@@ -723,51 +726,77 @@ function project_deps_get(env::String, name::String)::Union{Nothing,PkgId}
723726 return nothing
724727end
725728
729+ function package_get_here (project_file, name:: String )
730+ # if `where` matches the project, use [deps] section as manifest, and stop searching
731+ pkg_uuid = explicit_project_deps_get (project_file, name)
732+ pkg_uuid === nothing && return PkgId (name)
733+ return PkgId (pkg_uuid, name)
734+ end
735+
726736function package_get (project_file, where :: PkgId , name:: String )
727737 proj = project_file_name_uuid (project_file, where . name)
728- if proj == where
729- # if `where` matches the project, use [deps] section as manifest, and stop searching
730- pkg_uuid = explicit_project_deps_get (project_file, name)
731- return PkgId (pkg_uuid, name)
732- end
733- return nothing
738+ proj != where && return nothing
739+ return package_get_here (project_file, name)
734740end
735741
736- function manifest_deps_get (env:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
737- uuid = where . uuid
738- @assert uuid != = nothing
739- project_file = env_project_file (env)
740- if project_file isa String
741- pkg = package_get (project_file, where , name)
742- pkg === nothing || return pkg
743- d = parsed_toml (project_file)
744- exts = get (d, " extensions" , nothing ):: Union{Dict{String, Any}, Nothing}
745- if exts != = nothing
746- proj = project_file_name_uuid (project_file, where . name)
747- # Check if `where` is an extension of the project
748- if where . name in keys (exts) && where . uuid == uuid5 (proj. uuid:: UUID , where . name)
749- # Extensions can load weak deps...
742+ ext_may_load_weakdep (exts:: String , name:: String ) = exts == name
743+ ext_may_load_weakdep (exts:: Vector{String} , name:: String ) = name in exts
744+
745+ function package_extension_get (project_file, where :: PkgId , name:: String )
746+ d = parsed_toml (project_file)
747+ exts = get (d, " extensions" , nothing ):: Union{Dict{String, Any}, Nothing}
748+ if exts != = nothing
749+ proj = project_file_name_uuid (project_file, where . name)
750+ # Check if `where` is an extension of the project
751+ if where . name in keys (exts) && where . uuid == uuid5 (proj. uuid:: UUID , where . name)
752+ # Extensions can load weak deps if they are an extension trigger
753+ if ext_may_load_weakdep (exts[where . name]:: Union{String, Vector{String}} , name)
750754 weakdeps = get (d, " weakdeps" , nothing ):: Union{Dict{String, Any}, Nothing}
751755 if weakdeps != = nothing
752756 wuuid = get (weakdeps, name, nothing ):: Union{String, Nothing}
753757 if wuuid != = nothing
754758 return PkgId (UUID (wuuid), name)
755759 end
756760 end
757- # ... and they can load same deps as the project itself
758- mby_uuid = explicit_project_deps_get (project_file, name)
759- mby_uuid === nothing || return PkgId (mby_uuid, name)
760761 end
762+ # ... and they can load same deps as the project itself
763+ return package_get_here (project_file, name)
761764 end
762- # look for manifest file and `where` stanza
763- return explicit_manifest_deps_get (project_file, where , name)
764- elseif project_file
765- # if env names a directory, search it
766- return implicit_manifest_deps_get (env, where , name)
767765 end
768766 return nothing
769767end
770768
769+ function manifest_deps_get (env:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
770+ @assert where . uuid != = nothing
771+ project_file = env_project_file (env)
772+ implicit_manifest = ! (project_file isa String)
773+ if implicit_manifest
774+ project_file || return nothing
775+ project_file = implicit_manifest_project (env, where )
776+ project_file === nothing && return nothing
777+ end
778+
779+ # 1. Are we loading into the top-level project itself? dependencies come from [deps]
780+ # N.B.: Here "top-level" includes package loaded from an implicit manifest, which
781+ # uses the same code path.
782+ pkg = package_get (project_file, where , name)
783+ pkg === nothing || return pkg
784+
785+ # 2. Are we an extension of the top-level project? dependencies come from [weakdeps] and [deps]
786+ pkg = package_extension_get (project_file, where , name)
787+ pkg === nothing || return pkg
788+
789+ if implicit_manifest
790+ # With an implicit manifest, getting here means that our (implicit) environment
791+ # *has* the package `where`. If we don't find it, it just means that `where` doesn't
792+ # have `name` as a dependency - c.f. the analogous case in `explicit_manifest_deps_get`.
793+ return PkgId (name)
794+ end
795+
796+ # All other cases, dependencies come from the (top-level) manifest
797+ return explicit_manifest_deps_get (project_file, where , name)
798+ end
799+
771800function manifest_uuid_path (env:: String , pkg:: PkgId ):: Union{Nothing,String,Missing}
772801 project_file = env_project_file (env)
773802 if project_file isa String
938967# find project file root or deps `name => uuid` mapping
939968# `ext` is the name of the extension if `name` is loaded from one
940969# return `nothing` if `name` is not found
941- function explicit_project_deps_get (project_file:: String , name:: String , ext :: Union{String,Nothing} = nothing ):: Union{Nothing,UUID}
970+ function explicit_project_deps_get (project_file:: String , name:: String ):: Union{Nothing,UUID}
942971 d = parsed_toml (project_file)
943972 if get (d, " name" , nothing ):: Union{String, Nothing} === name
944973 root_uuid = dummy_uuid (project_file)
@@ -950,19 +979,6 @@ function explicit_project_deps_get(project_file::String, name::String, ext::Unio
950979 uuid = get (deps, name, nothing ):: Union{String, Nothing}
951980 uuid === nothing || return UUID (uuid)
952981 end
953- if ext != = nothing
954- extensions = get (d, " extensions" , nothing )
955- extensions === nothing && return nothing
956- ext_data = get (extensions, ext, nothing )
957- ext_data === nothing && return nothing
958- if (ext_data isa String && name == ext_data) || (ext_data isa Vector{String} && name in ext_data)
959- weakdeps = get (d, " weakdeps" , nothing ):: Union{Dict{String, Any}, Nothing}
960- weakdeps === nothing && return nothing
961- wuuid = get (weakdeps, name, nothing ):: Union{String, Nothing}
962- wuuid === nothing && return nothing
963- return UUID (wuuid)
964- end
965- end
966982 return nothing
967983end
968984
@@ -989,14 +1005,27 @@ function get_deps(raw_manifest::Dict)
9891005 end
9901006end
9911007
992- # find `where` stanza and return the PkgId for `name`
993- # return `nothing` if it did not find `where` (indicating caller should continue searching)
1008+ function dep_stanza_get (stanza:: Dict{String, Any} , name:: String ):: Union{Nothing, PkgId}
1009+ for (dep, uuid) in stanza
1010+ uuid:: String
1011+ if dep === name
1012+ return PkgId (UUID (uuid), name)
1013+ end
1014+ end
1015+ return nothing
1016+ end
1017+
1018+ function dep_stanza_get (stanza:: Vector{String} , name:: String ):: Union{Nothing, PkgId}
1019+ name in stanza && return PkgId (name)
1020+ return nothing
1021+ end
1022+
1023+ dep_stanza_get (stanza:: Nothing , name:: String ) = nothing
1024+
9941025function explicit_manifest_deps_get (project_file:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
9951026 manifest_file = project_file_manifest_path (project_file)
9961027 manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH
9971028 d = get_deps (parsed_toml (manifest_file))
998- found_where = false
999- found_name = false
10001029 for (dep_name, entries) in d
10011030 entries:: Vector{Any}
10021031 for entry in entries
@@ -1006,67 +1035,62 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St
10061035 # deps is either a list of names (deps = ["DepA", "DepB"]) or
10071036 # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."}
10081037 deps = get (entry, " deps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing}
1038+ local dep:: Union{Nothing, PkgId}
10091039 if UUID (uuid) === where . uuid
1010- found_where = true
1011- if deps isa Vector{String}
1012- found_name = name in deps
1013- found_name && @goto done
1014- elseif deps isa Dict{String, Any}
1015- deps = deps:: Dict{String, Any}
1016- for (dep, uuid) in deps
1017- uuid:: String
1018- if dep === name
1019- return PkgId (UUID (uuid), name)
1020- end
1021- end
1022- end
1023- else # Check for extensions
1040+ dep = dep_stanza_get (deps, name)
1041+
1042+ # We found `where` in this environment, but it did not have a deps entry for
1043+ # `name`. This is likely because the dependency was modified without a corresponding
1044+ # change to dependency's Project or our Manifest. Return a sentinel here indicating
1045+ # that we know the package, but do not know its UUID. The caller will terminate the
1046+ # search and provide an appropriate error to the user.
1047+ dep === nothing && return PkgId (name)
1048+ else
1049+ # Check if we're trying to load into an extension of this package
10241050 extensions = get (entry, " extensions" , nothing )
10251051 if extensions != = nothing
10261052 if haskey (extensions, where . name) && where . uuid == uuid5 (UUID (uuid), where . name)
1027- found_where = true
10281053 if name == dep_name
1054+ # Extension loads its base package
10291055 return PkgId (UUID (uuid), name)
10301056 end
10311057 exts = extensions[where . name]:: Union{String, Vector{String}}
1032- weakdeps = get (entry, " weakdeps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing}
1033- if (exts isa String && name == exts) || (exts isa Vector{String} && name in exts)
1034- for deps′ in [weakdeps, deps]
1035- if deps′ != = nothing
1036- if deps′ isa Vector{String}
1037- found_name = name in deps′
1038- found_name && @goto done
1039- elseif deps′ isa Dict{String, Any}
1040- deps′ = deps′:: Dict{String, Any}
1041- for (dep, uuid) in deps′
1042- uuid:: String
1043- if dep === name
1044- return PkgId (UUID (uuid), name)
1045- end
1046- end
1047- end
1048- end
1049- end
1050- end
1051- # `name` is not an ext, do standard lookup as if this was the parent
1052- return identify_package (PkgId (UUID (uuid), dep_name), name)
1058+ # Extensions are allowed to load:
1059+ # 1. Any ordinary dep of the parent package
1060+ # 2. Any weakdep of the parent package declared as an extension trigger
1061+ for deps′ in (ext_may_load_weakdep (exts, name) ?
1062+ (get (entry, " weakdeps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing} , deps) :
1063+ (deps,))
1064+ dep = dep_stanza_get (deps′, name)
1065+ dep === nothing && continue
1066+ @goto have_dep
1067+ end
1068+ return PkgId (name)
10531069 end
10541070 end
1071+ continue
10551072 end
1073+
1074+ @label have_dep
1075+ dep. uuid != = nothing && return dep
1076+
1077+ # We have the dep, but it did not specify a UUID. In this case,
1078+ # it must be that the name is unique in the manifest - so lookup
1079+ # the UUID at the lop level by name
1080+ name_deps = get (d, name, nothing ):: Union{Nothing, Vector{Any}}
1081+ if name_deps === nothing || length (name_deps) != 1
1082+ error (" expected a single entry for $(repr (name)) in $(repr (project_file)) " )
1083+ end
1084+ entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
1085+ uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
1086+ uuid === nothing && return PkgId (name)
1087+ return PkgId (UUID (uuid), name)
10561088 end
10571089 end
1058- @label done
1059- found_where || return nothing
1060- found_name || return PkgId (name)
1061- # Only reach here if deps was not a dict which mean we have a unique name for the dep
1062- name_deps = get (d, name, nothing ):: Union{Nothing, Vector{Any}}
1063- if name_deps === nothing || length (name_deps) != 1
1064- error (" expected a single entry for $(repr (name)) in $(repr (project_file)) " )
1065- end
1066- entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
1067- uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
1068- uuid === nothing && return nothing
1069- return PkgId (UUID (uuid), name)
1090+
1091+ # We did not find `where` in this environment, either as a package or as an extension.
1092+ # The caller should continue searching the environment stack.
1093+ return nothing
10701094end
10711095
10721096# find `uuid` stanza, return the corresponding path
@@ -1149,35 +1173,16 @@ function implicit_project_deps_get(dir::String, name::String)::Union{Nothing,Pkg
11491173 return proj
11501174end
11511175
1152- # look for an entry-point for `name`, check that UUID matches
1153- # if there's a project file, look up `name` in its deps and return that
1154- # otherwise return `nothing` to indicate the caller should keep searching
1155- function implicit_manifest_deps_get (dir:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
1156- @assert where . uuid != = nothing
1157- project_file = entry_point_and_project_file (dir, where . name)[2 ]
1176+ function implicit_manifest_project (dir, pkg:: PkgId ):: Union{Nothing, String}
1177+ @assert pkg. uuid != = nothing
1178+ project_file = entry_point_and_project_file (dir, pkg. name)[2 ]
11581179 if project_file === nothing
11591180 # `where` could be an extension
1160- project_file = implicit_env_project_file_extension (dir, where )[2 ]
1161- project_file === nothing && return nothing
1162- end
1163- proj = project_file_name_uuid (project_file, where . name)
1164- ext = nothing
1165- if proj != = where
1166- # `where` could be an extension in `proj`
1167- d = parsed_toml (project_file)
1168- exts = get (d, " extensions" , nothing ):: Union{Dict{String, Any}, Nothing}
1169- if exts != = nothing && where . name in keys (exts)
1170- if where . uuid != = uuid5 (proj. uuid, where . name)
1171- return nothing
1172- end
1173- ext = where . name
1174- else
1175- return nothing
1176- end
1181+ return implicit_env_project_file_extension (dir, pkg)[2 ]
11771182 end
1178- # this is the correct project, so stop searching here
1179- pkg_uuid = explicit_project_deps_get (project_file, name, ext)
1180- return PkgId (pkg_uuid, name)
1183+ proj = project_file_name_uuid (project_file, pkg . name)
1184+ proj == pkg || return nothing
1185+ return project_file
11811186end
11821187
11831188# look for an entry-point for `pkg` and return its path if UUID matches
0 commit comments