@@ -256,8 +256,8 @@ struct LoadingCache
256256 env_project_file:: Dict{String, Union{Bool, String}}
257257 project_file_manifest_path:: Dict{String, Union{Nothing, String}}
258258 require_parsed:: Set{String}
259- identified_where:: Dict{Tuple{PkgId, String}, Union{Nothing, Tuple{PkgId , String}}}
260- identified:: Dict{String, Union{Nothing, Tuple{PkgId , String}}}
259+ identified_where:: Dict{Tuple{PkgId, String}, Union{Nothing, Tuple{ApiId , String}}}
260+ identified:: Dict{String, Union{Nothing, Tuple{ApiId , String}}}
261261 located:: Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{String, String}, Nothing}}
262262end
263263const LOADING_CACHE = Ref {Union{LoadingCache, Nothing}} (nothing ) # n.b.: all access to and through this are protected by require_lock
@@ -303,10 +303,10 @@ end
303303# Used by Pkg but not used in loading itself
304304function find_package (arg) # ::Union{Nothing,String}
305305 @lock require_lock begin
306- pkgenv = identify_package_env (arg)
307- pkgenv === nothing && return nothing
308- pkg , env = pkgenv
309- return locate_package (pkg, env)
306+ apienv = identify_package_env (arg)
307+ apienv === nothing && return nothing
308+ api , env = apienv
309+ return locate_package (api . pkg, env)
310310 end
311311end
312312
@@ -338,7 +338,7 @@ function identify_package_env(where::Union{PkgId, Nothing}, name::String)
338338 if where != = nothing
339339 if where . name === name
340340 # Project tries to load itself
341- return (where , nothing )
341+ return (ApiId ( where , nothing ) , nothing )
342342 elseif where . uuid === nothing
343343 # Project without Project.toml - treat as toplevel load
344344 where = nothing
@@ -351,36 +351,36 @@ function identify_package_env(where::Union{PkgId, Nothing}, name::String)
351351 cache_key = where === nothing ? name : (where , name)
352352 if cache != = nothing
353353 env_cache = where === nothing ? cache. identified : cache. identified_where
354- pkg_env = get (env_cache, cache_key, missing )
355- pkg_env === missing || return pkg_env
354+ api_env = get (env_cache, cache_key, missing )
355+ api_env === missing || return api_env
356356 end
357357
358358 # Main part: Search through all environments in the load path to see if we have
359359 # a matching entry.
360- pkg_env = nothing
360+ api_env = nothing
361361 for env in load_path ()
362- pkgid = environment_deps_get (env, where , name)
362+ apiid = environment_deps_get (env, where , name)
363363 # If we didn't find `where` at all, keep looking through the environment stack
364- pkgid === nothing && continue
365- if pkgid . uuid != = nothing || where === nothing
366- pkg_env = pkgid , env
364+ apiid === nothing && continue
365+ if apiid . pkg . uuid != = nothing || where === nothing
366+ api_env = apiid , env
367367 end
368368 # If we don't have pkgid.uuid, still break here - this is a sentinel that indicates
369369 # that we've found `where` but it did not have the required dependency. We terminate the search.
370370 break
371371 end
372- if pkg_env === nothing && where != = nothing && is_stdlib (where )
372+ if api_env === nothing && where != = nothing && is_stdlib (where )
373373 # if not found it could be that manifests are from a different julia version/commit
374374 # where stdlib dependencies have changed, so look up deps based on the stdlib Project.toml
375375 # as a fallback
376- pkg_env = identify_stdlib_project_dep (where , name)
376+ api_env = identify_stdlib_project_dep (where , name)
377377 end
378378
379379 # Cache the result
380380 if cache != = nothing
381- env_cache[cache_key] = pkg_env
381+ env_cache[cache_key] = api_env
382382 end
383- return pkg_env
383+ return api_env
384384end
385385identify_package_env (name:: String ) = identify_package_env (nothing , name)
386386
@@ -427,9 +427,13 @@ julia> using LinearAlgebra
427427julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of LinearAlgebra
428428```
429429"""
430- identify_package (where :: Module , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
431- identify_package (where :: PkgId , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
432- identify_package (name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (name))
430+ identify_package (where :: Module , name:: String ) = (api = identify_api (where , name); return api === nothing ? nothing : api. pkg)
431+ identify_package (where :: PkgId , name:: String ) = (api = identify_api (where , name); return api === nothing ? nothing : api. pkg)
432+ identify_package (name:: String ) = (api = identify_api (name); return api === nothing ? nothing : api. pkg)
433+
434+ identify_api (where :: Module , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
435+ identify_api (where :: PkgId , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
436+ identify_api (name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (name))
433437
434438function locate_package_env (pkg:: PkgId , stopenv:: Union{String, Nothing} = nothing ):: Union{Nothing,Tuple{String,String}}
435439 assert_havelock (require_lock)
@@ -708,14 +712,15 @@ function base_project(project_file)
708712 end
709713end
710714
711- function package_get_here (project_file, name:: String )
715+ function package_get_here (project_file, name:: String ):: Union{Nothing,ApiId}
712716 # if `where` matches the project, use [deps] section as manifest, and stop searching
713717 pkg_uuid = explicit_project_deps_get (project_file, name)
714- pkg_uuid === nothing && return PkgId (name)
715- return PkgId (pkg_uuid, name)
718+ pkg_compat = explicit_project_compat_get (project_file, name)
719+ pkg_uuid === nothing && return ApiId (PkgId (name), pkg_compat)
720+ return ApiId (PkgId (pkg_uuid, name), pkg_compat)
716721end
717722
718- function package_get (project_file, where :: Union{Nothing, PkgId} , name:: String )
723+ function package_get (project_file, where :: Union{Nothing, PkgId} , name:: String ):: Union{Nothing,ApiId}
719724 if where != = nothing
720725 proj = project_file_name_uuid (project_file, where . name)
721726 proj != where && return nothing
726731ext_may_load_weakdep (exts:: String , name:: String ) = exts == name
727732ext_may_load_weakdep (exts:: Vector{String} , name:: String ) = name in exts
728733
729- function package_extension_get (project_file, where :: PkgId , name:: String )
734+ function package_extension_get (project_file, where :: PkgId , name:: String ):: Union{Nothing,ApiId}
730735 d = parsed_toml (project_file)
731736 exts = get (d, " extensions" , nothing ):: Union{Dict{String, Any}, Nothing}
732737 if exts != = nothing
@@ -739,7 +744,8 @@ function package_extension_get(project_file, where::PkgId, name::String)
739744 if weakdeps != = nothing
740745 wuuid = get (weakdeps, name, nothing ):: Union{String, Nothing}
741746 if wuuid != = nothing
742- return PkgId (UUID (wuuid), name)
747+ return ApiId (PkgId (UUID (wuuid), name),
748+ explicit_project_compat_get (project_file, name))
743749 end
744750 end
745751 end
@@ -750,7 +756,7 @@ function package_extension_get(project_file, where::PkgId, name::String)
750756 return nothing
751757end
752758
753- function environment_deps_get (env:: String , where :: Union{Nothing,PkgId} , name:: String ):: Union{Nothing,PkgId }
759+ function environment_deps_get (env:: String , where :: Union{Nothing,PkgId} , name:: String ):: Union{Nothing,ApiId }
754760 @assert where === nothing || where . uuid != = nothing
755761 project_file = env_project_file (env)
756762 implicit_manifest = ! (project_file isa String)
@@ -760,7 +766,8 @@ function environment_deps_get(env::String, where::Union{Nothing,PkgId}, name::St
760766 # Toplevel load with a directory (implicit manifest) - all we look for is the
761767 # existence of the package name in the directory.
762768 pkg = implicit_manifest_pkgid (env, name)
763- return pkg
769+ pkg === nothing && return nothing
770+ return ApiId (pkg, nothing )
764771 end
765772 project_file = implicit_manifest_project (env, where )
766773 project_file === nothing && return nothing
@@ -781,7 +788,7 @@ function environment_deps_get(env::String, where::Union{Nothing,PkgId}, name::St
781788 # uses the same code path. Otherwise this is the active project.
782789 pkg = package_get (project_file, where , name)
783790 if pkg != = nothing
784- if where === nothing && pkg. uuid === nothing
791+ if where === nothing && pkg . pkg. uuid === nothing
785792 # This is a top-level load - even though we didn't find the dependency
786793 # here, we still want to keep looking through the top-level environment stack.
787794 return nothing
@@ -802,7 +809,7 @@ function environment_deps_get(env::String, where::Union{Nothing,PkgId}, name::St
802809 # With an implicit manifest, getting here means that our (implicit) environment
803810 # *has* the package `where`. If we don't find it, it just means that `where` doesn't
804811 # have `name` as a dependency - c.f. the analogous case in `explicit_manifest_deps_get`.
805- return PkgId (name)
812+ return ApiId ( PkgId (name), nothing )
806813 end
807814
808815 # All other cases, dependencies come from the (top-level) manifest
@@ -995,6 +1002,13 @@ function explicit_project_deps_get(project_file::String, name::String)::Union{No
9951002 return nothing
9961003end
9971004
1005+ function explicit_project_compat_get (project_file:: String , name:: String )
1006+ d = parsed_toml (project_file)
1007+ compat = get (d, " compat" , nothing ):: Union{Dict{String, Any}, Nothing}
1008+ compat === nothing && return nothing
1009+ return get (compat, name, nothing )
1010+ end
1011+
9981012function is_v1_format_manifest (raw_manifest:: Dict{String} )
9991013 if haskey (raw_manifest, " manifest_format" )
10001014 mf = raw_manifest[" manifest_format" ]
@@ -1018,24 +1032,31 @@ function get_deps(raw_manifest::Dict)
10181032 end
10191033end
10201034
1021- function dep_stanza_get (stanza:: Dict{String, Any} , name:: String ):: Union{Nothing, PkgId}
1022- for (dep, uuid) in stanza
1023- uuid:: String
1024- if dep === name
1025- return PkgId (UUID (uuid), name)
1035+ function dep_stanza_get (stanza:: Dict{String, Any} , name):: Union{ApiId, Nothing}
1036+ for (dep, uuid_or_obj) in stanza
1037+ if dep == name
1038+ if uuid_or_obj isa String
1039+ # uuid specified, but no API compat
1040+ return ApiId (PkgId (UUID (uuid_or_obj), name), nothing )
1041+ else
1042+ api_compat = get (uuid_or_obj, " compat" , nothing ):: Union{Nothing, String}
1043+ uuid = get (uuid_or_obj, " uuid" , nothing ):: Union{Nothing, String}
1044+ uuid === nothing && return nothing
1045+ return ApiId (PkgId (UUID (uuid), name), api_compat)
1046+ end
10261047 end
10271048 end
10281049 return nothing
10291050end
10301051
1031- function dep_stanza_get (stanza:: Vector{String} , name:: String ):: Union{Nothing, PkgId }
1032- name in stanza && return PkgId (name)
1052+ function dep_stanza_get (stanza:: Vector{String} , name:: String ):: Union{Nothing, ApiId }
1053+ name in stanza && return ApiId ( PkgId (name), nothing )
10331054 return nothing
10341055end
10351056
10361057dep_stanza_get (stanza:: Nothing , name:: String ) = nothing
10371058
1038- function explicit_manifest_deps_get (project_file:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId }
1059+ function explicit_manifest_deps_get (project_file:: String , where :: PkgId , name:: String ):: Union{Nothing,ApiId }
10391060 manifest_file = project_file_manifest_path (project_file)
10401061 manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH
10411062 d = get_deps (parsed_toml (manifest_file))
@@ -1048,7 +1069,7 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St
10481069 # deps is either a list of names (deps = ["DepA", "DepB"]) or
10491070 # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."}
10501071 deps = get (entry, " deps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing}
1051- local dep:: Union{Nothing, PkgId }
1072+ local dep:: Union{Nothing, ApiId }
10521073 if UUID (uuid) === where . uuid
10531074 dep = dep_stanza_get (deps, name)
10541075
@@ -1057,15 +1078,15 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St
10571078 # change to dependency's Project or our Manifest. Return a sentinel here indicating
10581079 # that we know the package, but do not know its UUID. The caller will terminate the
10591080 # search and provide an appropriate error to the user.
1060- dep === nothing && return PkgId (name)
1081+ dep === nothing && return ApiId ( PkgId (name), nothing )
10611082 else
10621083 # Check if we're trying to load into an extension of this package
10631084 extensions = get (entry, " extensions" , nothing )
10641085 if extensions != = nothing
10651086 if haskey (extensions, where . name) && where . uuid == uuid5 (UUID (uuid), where . name)
10661087 if name == dep_name
10671088 # Extension loads its base package
1068- return PkgId (UUID (uuid), name)
1089+ return ApiId ( PkgId (UUID (uuid), name), nothing )
10691090 end
10701091 exts = extensions[where . name]:: Union{String, Vector{String}}
10711092 # Extensions are allowed to load:
@@ -1078,14 +1099,14 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St
10781099 dep === nothing && continue
10791100 @goto have_dep
10801101 end
1081- return PkgId (name)
1102+ return ApiId ( PkgId (name), nothing )
10821103 end
10831104 end
10841105 continue
10851106 end
10861107
10871108 @label have_dep
1088- dep. uuid != = nothing && return dep
1109+ dep. pkg . uuid != = nothing && return dep
10891110
10901111 # We have the dep, but it did not specify a UUID. In this case,
10911112 # it must be that the name is unique in the manifest - so lookup
@@ -1097,7 +1118,7 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St
10971118 entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
10981119 uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
10991120 uuid === nothing && return PkgId (name)
1100- return PkgId (UUID (uuid), name)
1121+ return ApiId ( PkgId (UUID (uuid), name), nothing )
11011122 end
11021123 end
11031124
@@ -1471,6 +1492,7 @@ function run_module_init(mod::Module, i::Int=1)
14711492 end
14721493end
14731494
1495+ run_package_callbacks (modkey:: ApiId ) = run_package_callbacks (modkey. pkg)
14741496function run_package_callbacks (modkey:: PkgId )
14751497 run_extension_callbacks (modkey)
14761498 assert_havelock (require_lock)
@@ -1505,6 +1527,7 @@ const EXT_PRIMED = Dict{PkgId,Vector{PkgId}}() # Extension -> Parent + Triggers
15051527const EXT_DORMITORY = Dict {PkgId,Vector{ExtensionId}} () # Trigger -> Extensions that can be triggered by it
15061528const EXT_DORMITORY_FAILED = ExtensionId[]
15071529
1530+ insert_extension_triggers (api:: ApiId ) = insert_extension_triggers (api. pkg)
15081531function insert_extension_triggers (pkg:: PkgId )
15091532 pkg. uuid === nothing && return
15101533 path_env_loc = locate_package_env (pkg)
@@ -2263,6 +2286,12 @@ function canstart_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool)
22632286 return cond
22642287end
22652288
2289+ function start_loading (modkey:: ApiId , build_id:: UInt128 , stalecheck:: Bool )
2290+ m = start_loading (modkey. pkg, build_id, true )
2291+ m === nothing && return nothing
2292+ return api_for_loaded_module (modkey, m)
2293+ end
2294+
22662295function start_loading (modkey:: PkgId , build_id:: UInt128 , stalecheck:: Bool )
22672296 # handle recursive and concurrent calls to require
22682297 while true
@@ -2278,6 +2307,7 @@ function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool)
22782307 end
22792308end
22802309
2310+ end_loading (apikey:: ApiId , @nospecialize loaded) = end_loading (apikey. pkg, loaded)
22812311function end_loading (modkey:: PkgId , @nospecialize loaded)
22822312 assert_havelock (require_lock)
22832313 loading = pop! (package_locks, modkey)
@@ -2498,7 +2528,7 @@ function __require(into::Module, mod::Symbol)
24982528 end
24992529 uuidkey, env = uuidkey_env
25002530 if _track_dependencies[]
2501- path = binpack (uuidkey)
2531+ path = binpack (uuidkey. pkg )
25022532 push! (_require_dependencies, (into, path, UInt64 (0 ), UInt32 (0 ), 0.0 ))
25032533 end
25042534 return _require_prelocked (uuidkey, env)
@@ -2565,8 +2595,8 @@ function require(uuidkey::PkgId)
25652595 end
25662596 return invoke_in_world (world, __require, uuidkey)
25672597end
2568- __require (uuidkey:: PkgId ) = @lock require_lock _require_prelocked (uuidkey)
2569- function _require_prelocked (uuidkey:: PkgId , env= nothing )
2598+ __require (uuidkey:: Union{ PkgId, ApiId} ) = @lock require_lock _require_prelocked (uuidkey)
2599+ function _require_prelocked (uuidkey:: Union{ PkgId, ApiId} , env= nothing )
25702600 assert_havelock (require_lock)
25712601 m = start_loading (uuidkey, UInt128 (0 ), true )
25722602 if m === nothing
@@ -2825,6 +2855,23 @@ function __require_prelocked(pkg::PkgId, env)
28252855 return loaded
28262856end
28272857
2858+ __require_prelocked (apikey:: ApiId , env) =
2859+ api_for_loaded_module (apikey, __require_prelocked (apikey. pkg, env))
2860+
2861+ function lookup_api (loaded:: Module , api:: ApiId )
2862+ if ! isdefined (loaded, :_get_versioned_api )
2863+ return loaded
2864+ end
2865+ return loaded. _get_versioned_api (api. compat):: Module
2866+ end
2867+
2868+ function api_for_loaded_module (apikey:: ApiId , loaded:: Module )
2869+ if apikey. compat === nothing
2870+ return loaded
2871+ end
2872+ return invokelatest (lookup_api, loaded, apikey)
2873+ end
2874+
28282875# load a serialized file directly, including dependencies (without checking staleness except for immediate conflicts)
28292876# this does not call start_loading / end_loading, so can lead to some odd behaviors
28302877function _require_from_serialized (uuidkey:: PkgId , path:: String , ocachepath:: Union{String, Nothing} , sourcepath:: String )
0 commit comments