diff --git a/NEWS.md b/NEWS.md index 1a0cd19825320c..d37695279e952c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,11 @@ Julia v1.10 Release Notes New language features --------------------- - +New feature for packages that allows loading a piece of code based on other +packages being loaded in the Julia session. +This has similar applications as the Requires.jl package but also +supports precompilation and setting compatibility. +Look in the documentation for Pkg.jl for "glue packages" for more information. Language changes ---------------- diff --git a/base/loading.jl b/base/loading.jl index a5df7c24408ae2..834608efc422de 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -563,8 +563,11 @@ function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothi pkg_uuid = explicit_project_deps_get(project_file, name) return PkgId(pkg_uuid, name) end + # Check for this being a dependency to a glue module + glue_dep = project_file_gluedeps_get(project_file, where, name) + glue_dep === nothing || return glue_dep # look for manifest file and `where` stanza - return explicit_manifest_deps_get(project_file, uuid, name) + return explicit_manifest_deps_get(project_file, where, name) elseif project_file # if env names a directory, search it return implicit_manifest_deps_get(env, where, name) @@ -578,8 +581,12 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi proj = project_file_name_uuid(project_file, pkg.name) if proj == pkg # if `pkg` matches the project, return the project itself - return project_file_path(project_file, pkg.name) + return project_file_path(project_file) end + # Only used when the package is loading the glue pkg itself + # which is currently not supported + # mby_glue = project_file_glue_path(project_file, pkg.name) + # mby_glue === nothing || return mby_glue # look for manifest file and `where` stanza return explicit_manifest_uuid_path(project_file, pkg) elseif project_file @@ -598,7 +605,41 @@ function project_file_name_uuid(project_file::String, name::String)::PkgId return PkgId(uuid, name) end -function project_file_path(project_file::String, name::String) +function project_file_gluedeps_get(project_file::String, where::PkgId, name::String) + # Check for glue... + d = parsed_toml(project_file) + glue = get(d, "gluepkgs", nothing)::Union{Dict{String, Any}, Nothing} + project_id = project_file_name_uuid(project_file, "") + if glue !== nothing && where.uuid == uuid5(project_id.uuid, where.name) + gluedeps = get(glue, where.name, nothing)::Union{Nothing, String, Vector{String}} + if gluedeps !== nothing + if gluedeps isa String && name == gluedeps || + gluedeps isa Vector{String} && name in gluedeps + gluedepses = get(d, "gluedeps", nothing)::Union{Dict{String, Any}, Nothing} + return PkgId(UUID(gluedepses[name]::String), name) + end + name == project_id.name && return project_id + end + end + return nothing +end + +function project_file_glue_path(project_file::String, name::String) + d = parsed_toml(project_file) + p = project_file_path(project_file) + glue = get(d, "gluepkgs", nothing)::Union{Dict{String, Any}, Nothing} + if glue !== nothing + if name in keys(glue) + gluefile = joinpath(p, "glue", name * ".jl") + isfile(gluefile) && return gluefile + gluefiledir = joinpath(p, "glue", name, name * ".jl") + isfile(gluefiledir) && return gluefiledir + end + end + return nothing +end + +function project_file_path(project_file::String) d = parsed_toml(project_file) joinpath(dirname(project_file), get(d, "path", "")::String) end @@ -679,15 +720,26 @@ end function explicit_project_deps_get(project_file::String, name::String)::Union{Nothing,UUID} d = parsed_toml(project_file) root_uuid = dummy_uuid(project_file) + mby_uuid_project = get(d, "uuid", nothing)::Union{String, Nothing} + uuid_project = mby_uuid_project === nothing ? root_uuid : UUID(mby_uuid_project) if get(d, "name", nothing)::Union{String, Nothing} === name - uuid = get(d, "uuid", nothing)::Union{String, Nothing} - return uuid === nothing ? root_uuid : UUID(uuid) + return uuid_project end deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing} if deps !== nothing uuid = get(deps, name, nothing)::Union{String, Nothing} uuid === nothing || return UUID(uuid) end + + #= + # Unclear if this is needed + glue = get(d, "gluepkgs", nothing)::Union{Dict{String, Any}, Nothing} + if glue !== nothing + if name in keys(glue) + return uuid5(uuid_project, name) + end + end + =# return nothing end @@ -716,9 +768,10 @@ end # find `where` stanza and return the PkgId for `name` # return `nothing` if it did not find `where` (indicating caller should continue searching) -function explicit_manifest_deps_get(project_file::String, where::UUID, name::String)::Union{Nothing,PkgId} +function explicit_manifest_deps_get(project_file::String, where::PkgId, name::String)::Union{Nothing,PkgId} manifest_file = project_file_manifest_path(project_file) manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH + d = get_deps(parsed_toml(manifest_file)) found_where = false found_name = false @@ -728,16 +781,21 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str entry = entry::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{String, Nothing} uuid === nothing && continue - if UUID(uuid) === where + if UUID(uuid) === where.uuid found_where = true + gluepkgs = get(entry, "gluepkgs", nothing)::Union{Nothing, Dict{String, Any}} + if gluepkgs !== nothing + if name in keys(gluepkgs) + return PkgId(uuid5(where.uuid, name), name) + end + end # deps is either a list of names (deps = ["DepA", "DepB"]) or # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."} deps = get(entry, "deps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} - deps === nothing && continue if deps isa Vector{String} found_name = name in deps break - else + elseif deps isa Dict{String, Any} deps = deps::Dict{String, Any} for (dep, uuid) in deps uuid::String @@ -746,6 +804,35 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str end end end + else # Check for glue modules + gluepkgs = get(entry, "gluepkgs", nothing) + if gluepkgs !== nothing + if where.name in keys(gluepkgs) && where.uuid == uuid5(UUID(uuid), where.name) + found_where = true + if name == dep_name + return PkgId(UUID(uuid), name) + end + glue_entry = gluepkgs[where.name] + if glue_entry isa String && name == glue_entry || + glue_entry isa Vector{String} && name in glue_entry + gluedeps = get(entry, "gluedeps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} + if gluedeps !== nothing + if gluedeps isa Vector{String} + found_name = name in gluedeps + break + elseif gluedeps isa Dict{String, Any} + gluedeps = gluedeps::Dict{String, Any} + for (dep, uuid) in gluedeps + uuid::String + if dep === name + return PkgId(UUID(uuid), name) + end + end + end + end + end + end + end end end end @@ -769,15 +856,33 @@ function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{No d = get_deps(parsed_toml(manifest_file)) entries = get(d, pkg.name, nothing)::Union{Nothing, Vector{Any}} - entries === nothing && return nothing # TODO: allow name to mismatch? - for entry in entries - entry = entry::Dict{String, Any} - uuid = get(entry, "uuid", nothing)::Union{Nothing, String} - uuid === nothing && continue - if UUID(uuid) === pkg.uuid - return explicit_manifest_entry_path(manifest_file, pkg, entry) + if entries !== nothing + for entry in entries + entry = entry::Dict{String, Any} + uuid = get(entry, "uuid", nothing)::Union{Nothing, String} + uuid === nothing && continue + if UUID(uuid) === pkg.uuid + return explicit_manifest_entry_path(manifest_file, pkg, entry) + end end end + + # Glue + for (name, entries::Vector{Any}) in d + for entry in entries + uuid = get(entry, "uuid", nothing)::Union{Nothing, String} + gluedeps = get(entry, "gluepkgs", nothing)::Union{Nothing, Dict{String, Any}} + if gluedeps !== nothing && pkg.name in keys(gluedeps) && uuid !== nothing && uuid5(UUID(uuid), pkg.name) == pkg.uuid + p = normpath(dirname(locate_package(PkgId(UUID(uuid), name))), "..") + gluefile = joinpath(p, "glue", pkg.name * ".jl") + isfile(gluefile) && return gluefile + gluefiledir = joinpath(p, "glue", pkg.name, pkg.name * ".jl") + isfile(gluefiledir) && return gluefiledir + return nothing + end + end + end + return nothing end @@ -958,6 +1063,7 @@ end function run_package_callbacks(modkey::PkgId) assert_havelock(require_lock) unlock(require_lock) + run_glue_callbacks(modkey) try for callback in package_callbacks invokelatest(callback, modkey) @@ -972,6 +1078,145 @@ function run_package_callbacks(modkey::PkgId) nothing end + +######## +# Glue # +######## + +mutable struct GlueId + const id::PkgId # Could be symbol? + const parentid::PkgId + const triggers::Vector{PkgId} # What packages have to be loaded for the glue module to get loaded + triggered::Bool +end + +const GLUE_PKG_DORMITORY = GlueId[] + +function insert_glue_triggers(pkg::PkgId) + pkg.uuid === nothing && return + for env in load_path() + insert_glue_triggers(env, pkg) + break # For now, only insert triggers for packages in the first load_path. + end +end + +function insert_glue_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} + project_file = env_project_file(env) + if project_file isa String + proj = project_file_name_uuid(project_file, pkg.name) + if proj == pkg + insert_glue_triggers_project(project_file, pkg) + else + return insert_glue_triggers_manifest(project_file, pkg) + end + end + return nothing +end + +function insert_glue_triggers_project(project_file::String, parent::PkgId) + d = parsed_toml(project_file) + gluedeps = get(d, "gluedeps", nothing)::Union{Nothing, Dict{String, Any}} + gluepkgs = get(d, "gluepkgs", nothing)::Union{Nothing, Dict{String, Any}} + gluepkgs === nothing && return + gluedeps === nothing && return + _insert_glue_triggers(parent, gluepkgs, gluedeps) +end + +function insert_glue_triggers_manifest(project_file::String, parent::PkgId) + manifest_file = project_file_manifest_path(project_file) + manifest_file === nothing && return + d = get_deps(parsed_toml(manifest_file)) + for (dep_name, entries) in d + entries::Vector{Any} + for entry in entries + entry = entry::Dict{String, Any} + uuid = get(entry, "uuid", nothing)::Union{String, Nothing} + uuid === nothing && continue + if UUID(uuid) === parent.uuid + gluedeps = get(entry, "gluedeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}} + gluepkgs = get(entry, "gluepkgs", nothing)::Union{Nothing, Dict{String, Any}} + gluepkgs === nothing && return + gluedeps === nothing && return + if gluedeps isa Dict{String, Any} + return _insert_glue_triggers(parent, gluepkgs, gluedeps) + end + + d_gluedeps = Dict{String, String}() + for (dep_name, entries) in d + dep_name in gluedeps || continue + entries::Vector{Any} + if length(entries) != 1 + error("expected a single entry for $(repr(name)) in $(repr(project_file))") + end + entry = first(entries)::Dict{String, Any} + uuid = get(entry, "uuid", nothing)::Union{String, Nothing} + d_gluedeps[dep_name] = uuid + end + @assert length(d_gluedeps) == length(gluedeps) + return _insert_glue_triggers(parent, gluepkgs, d_gluedeps) + end + end + end + return +end + +function _insert_glue_triggers(parent::PkgId, gluepkgs::Dict{String, <:Any}, gluedeps::Dict{String, <:Any}) + for (glue_entry::String, triggers::Union{String, Vector{String}}) in gluepkgs + triggers isa String && (triggers = [triggers]) + triggers_id = PkgId[] + id = PkgId(uuid5(parent.uuid, glue_entry), glue_entry) + for trigger in triggers + # TODO: Better error message if this lookup fails? + uuid_trigger = UUID(gluedeps[trigger]::String) + push!(triggers_id, PkgId(uuid_trigger, trigger)) + end + gid = GlueId(id, parent, triggers_id, false) + push!(GLUE_PKG_DORMITORY, gid) + end +end + +function run_glue_callbacks(pkg::PkgId) + try + for glueid in GLUE_PKG_DORMITORY + glueid.triggered && continue + if all(in(keys(Base.loaded_modules)), glueid.triggers) + gluepkg_not_allowed_load = nothing + glueid.triggered = true + # It is possible that some of the triggers were loaded in an environment + # below the one of the parent. This will cause a load failure when the + # glue pkg tries to load the triggers. Therefore, check this first + # before loading the glue pkg. + for trigger in glueid.triggers + pkgenv = Base.identify_package_env(glueid.id, trigger.name) + if pkgenv === nothing + gluepkg_not_allowed_load = trigger + break + else + pkg, env = pkgenv + path = Base.locate_package(pkg, env) + if path === nothing + gluepkg_not_allowed_load = trigger + break + end + end + end + if gluepkg_not_allowed_load !== nothing + @debug "Glue package $(glueid.id.name) of $(glueid.parentid.name) not loaded due to \ + $(gluepkg_not_allowed_load.name) loaded in environment lower in load path" + else + require(glueid.id) + @debug "Glue package $(glueid.id.name) of $(glueid.parentid.name) loaded" + end + end + end + catch + # Try to continue loading if loading a glue package errors + errs = current_exceptions() + @error "Error during loading of glue code" exception=errs + end + nothing +end + # loads a precompile cache file, after checking stale_cachefile tests function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64) assert_havelock(require_lock) @@ -995,6 +1240,7 @@ function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64) notify(loading, loaded, all=true) end if loaded isa Module + insert_glue_triggers(modkey) run_package_callbacks(modkey) end end @@ -1035,6 +1281,7 @@ function _tryrequire_from_serialized(modkey::PkgId, path::String, sourcepath::St notify(loading, loaded, all=true) end if loaded isa Module + insert_glue_triggers(modkey) run_package_callbacks(modkey) end end @@ -1239,7 +1486,7 @@ function require(into::Module, mod::Symbol) LOADING_CACHE[] = LoadingCache() try uuidkey_env = identify_package_env(into, String(mod)) - # Core.println("require($(PkgId(into)), $mod) -> $uuidkey from env \"$env\"") + # Core.println("require($(PkgId(into)), $mod) -> $uuidkey_env") if uuidkey_env === nothing where = PkgId(into) if where.uuid === nothing @@ -1279,14 +1526,6 @@ function require(into::Module, mod::Symbol) end end -mutable struct PkgOrigin - path::Union{String,Nothing} - cachepath::Union{String,Nothing} - version::Union{VersionNumber,Nothing} -end -PkgOrigin() = PkgOrigin(nothing, nothing, nothing) -const pkgorigins = Dict{PkgId,PkgOrigin}() - require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey) function _require_prelocked(uuidkey::PkgId, env=nothing) @@ -1297,6 +1536,7 @@ function _require_prelocked(uuidkey::PkgId, env=nothing) error("package `$(uuidkey.name)` did not define the expected \ module `$(uuidkey.name)`, check for typos in package module name") end + insert_glue_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) else @@ -1305,6 +1545,14 @@ function _require_prelocked(uuidkey::PkgId, env=nothing) return newm end +mutable struct PkgOrigin + path::Union{String,Nothing} + cachepath::Union{String,Nothing} + version::Union{VersionNumber,Nothing} +end +PkgOrigin() = PkgOrigin(nothing, nothing, nothing) +const pkgorigins = Dict{PkgId,PkgOrigin}() + const loaded_modules = Dict{PkgId,Module}() const loaded_modules_order = Vector{Module}() const module_keys = IdDict{Module,PkgId}() # the reverse @@ -1479,6 +1727,7 @@ function _require_from_serialized(uuidkey::PkgId, path::String) set_pkgorigin_version_path(uuidkey, nothing) newm = _tryrequire_from_serialized(uuidkey, path) newm isa Module || throw(newm) + insert_glue_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) return newm @@ -1711,6 +1960,7 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, concrete_d "w", stdout) # write data over stdin to avoid the (unlikely) case of exceeding max command line size write(io.in, """ + empty!(Base.GLUE_PKG_DORMITORY) # If we have a custom sysimage with `GLUE_PKG_DORMITORY` prepopulated Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) """) diff --git a/doc/src/manual/code-loading.md b/doc/src/manual/code-loading.md index d6f359f83d5cb4..693742313bc4fd 100644 --- a/doc/src/manual/code-loading.md +++ b/doc/src/manual/code-loading.md @@ -348,7 +348,46 @@ The subscripted `rootsᵢ`, `graphᵢ` and `pathsᵢ` variables correspond to th 2. Packages in non-primary environments can end up using incompatible versions of their dependencies even if their own environments are entirely compatible. This can happen when one of their dependencies is shadowed by a version in an earlier environment in the stack (either by graph or path, or both). Since the primary environment is typically the environment of a project you're working on, while environments later in the stack contain additional tools, this is the right trade-off: it's better to break your development tools but keep the project working. When such incompatibilities occur, you'll typically want to upgrade your dev tools to versions that are compatible with the main project. +### "Glue" packages and dependencies +A "glue package" is a module that is automatically loaded when a specified set of other packages (called "glue dependencies") are loaded in the current Julia session. +These are defined by adding the following two sections to a package's `Project.toml` file: + +```toml +name = "MyPackage" + +[gluedeps] +GlueDep = "c9a23..." # uuid +OtherGlueDep = "862e..." # uuid + +[gluepkgs] +GlueFoo = "GlueDep" +GlueBar = ["GlueDep", "OtherGlueDep"] +... +``` + +The keys under `gluepkgs` are the name of the glue packages. +They are loaded when all the packages on the right hand side (the glue dependencies) of the glue package are loaded. +If a glue package only has one glue dependency the lit of glue dependencies can be written as just a string for breviety. +The location for the entry point of the glue package is either in `glue/GlueFoo.jl` or `glue/GlueFoo/GlueFoo.jl` for +glue package `GlueFoo`. +The glue package can be viewed as a somewhat normal package that has the glue dependencies and the main package as dependencies. +The content of a glue package is often structured as: + +``` +module GlueFoo + +# Load main package and glue dependencies +using MyPackage, GlueDep + +# Extend functionality in main package with types from the glue dependencies +MyPackage.func(x::GlueDep.SomeStruct) = ... + +end +``` + +When a package with glue packages is added to an environment, the `gluedeps` and `gluepkgs` sections +are stored in the manifest file in the section for that package. ### Package/Environment Preferences Preferences are dictionaries of metadata that influence package behavior within an environment. diff --git a/test/loading.jl b/test/loading.jl index d057f0b3c37024..f74411c6a12281 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -991,5 +991,34 @@ end end end + +@testset "GluePkgs" begin + old_depot_path = copy(DEPOT_PATH) + try + tmp = mktempdir() + push!(empty!(DEPOT_PATH), joinpath(tmp, "depot")) + + proj = joinpath(@__DIR__, "project", "GluePkgs", "HasDepWithGluePkgs.jl") + for i in 1:2 # Once when requiring precomilation, once where it is already precompiled + cmd = `$(Base.julia_cmd()) --project=$proj --startup-file=no -e ' + begin + using HasGluePkgs + HasGluePkgs.glue_loaded && error() + using HasDepWithGluePkgs + HasGluePkgs.glue_loaded || error() + HasGluePkgs.glue_folder_loaded && error() + HasDepWithGluePkgs.do_something() || error() + using GlueDep2 + HasGluePkgs.glue_folder_loaded || error() + end + '` + @test success(cmd) + end + finally + copy!(DEPOT_PATH, old_depot_path) + end +end + + empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) diff --git a/test/project/GluePkgs/GlueDep.jl/Project.toml b/test/project/GluePkgs/GlueDep.jl/Project.toml new file mode 100644 index 00000000000000..a075e6219c5bdc --- /dev/null +++ b/test/project/GluePkgs/GlueDep.jl/Project.toml @@ -0,0 +1,3 @@ +name = "GlueDep" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" diff --git a/test/project/GluePkgs/GlueDep.jl/src/GlueDep.jl b/test/project/GluePkgs/GlueDep.jl/src/GlueDep.jl new file mode 100644 index 00000000000000..1e79be6dd1a601 --- /dev/null +++ b/test/project/GluePkgs/GlueDep.jl/src/GlueDep.jl @@ -0,0 +1,5 @@ +module GlueDep + +struct GlueDepStruct end + +end # module GlueDep diff --git a/test/project/GluePkgs/GlueDep2/Project.toml b/test/project/GluePkgs/GlueDep2/Project.toml new file mode 100644 index 00000000000000..4672bdbef8fa85 --- /dev/null +++ b/test/project/GluePkgs/GlueDep2/Project.toml @@ -0,0 +1,3 @@ +name = "GlueDep2" +uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +version = "0.1.0" diff --git a/test/project/GluePkgs/GlueDep2/src/GlueDep2.jl b/test/project/GluePkgs/GlueDep2/src/GlueDep2.jl new file mode 100644 index 00000000000000..8bb9f4e7fd835d --- /dev/null +++ b/test/project/GluePkgs/GlueDep2/src/GlueDep2.jl @@ -0,0 +1,5 @@ +module GlueDep2 + +greet() = print("Hello World!") + +end # module GlueDep2 diff --git a/test/project/GluePkgs/HasDepWithGluePkgs.jl/Manifest.toml b/test/project/GluePkgs/HasDepWithGluePkgs.jl/Manifest.toml new file mode 100644 index 00000000000000..2bd02a308a1632 --- /dev/null +++ b/test/project/GluePkgs/HasDepWithGluePkgs.jl/Manifest.toml @@ -0,0 +1,25 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.0-DEV" +manifest_format = "2.0" +project_hash = "7cbe1857ecc6692a8cc8be428a5ad5073531ff98" + +[[deps.GlueDep]] +path = "../GlueDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.GlueDep2]] +path = "../GlueDep2" +uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +version = "0.1.0" + +[[deps.HasGluePkgs]] +gluedeps = ["GlueDep", "GlueDep2"] +path = "../HasGluePkgs.jl" +uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +version = "0.1.0" + + [deps.HasGluePkgs.gluepkgs] + GluePkg = "GlueDep" + GluePkgFolder = ["GlueDep", "GlueDep2"] diff --git a/test/project/GluePkgs/HasDepWithGluePkgs.jl/Project.toml b/test/project/GluePkgs/HasDepWithGluePkgs.jl/Project.toml new file mode 100644 index 00000000000000..f644cec26180c7 --- /dev/null +++ b/test/project/GluePkgs/HasDepWithGluePkgs.jl/Project.toml @@ -0,0 +1,8 @@ +name = "HasDepWithGluePkgs" +uuid = "d4ef3d4a-8e22-4710-85d8-c6cf2eb9efca" +version = "0.1.0" + +[deps] +GlueDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" +GlueDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +HasGluePkgs = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" diff --git a/test/project/GluePkgs/HasDepWithGluePkgs.jl/src/HasDepWithGluePkgs.jl b/test/project/GluePkgs/HasDepWithGluePkgs.jl/src/HasDepWithGluePkgs.jl new file mode 100644 index 00000000000000..c61e26731683fd --- /dev/null +++ b/test/project/GluePkgs/HasDepWithGluePkgs.jl/src/HasDepWithGluePkgs.jl @@ -0,0 +1,13 @@ +module HasDepWithGluePkgs + +using HasGluePkgs: HasGluePkgs, HasGluePkgsStruct +using GlueDep: GlueDepStruct +# Loading GlueDep makes the glue module "GluePkg" load + +function do_something() + HasGluePkgs.foo(HasGluePkgsStruct()) == 1 || error() + HasGluePkgs.foo(GlueDepStruct()) == 2 || error() + return true +end + +end # module diff --git a/test/project/GluePkgs/HasGluePkgs.jl/Manifest.toml b/test/project/GluePkgs/HasGluePkgs.jl/Manifest.toml new file mode 100644 index 00000000000000..55f7958701a75a --- /dev/null +++ b/test/project/GluePkgs/HasGluePkgs.jl/Manifest.toml @@ -0,0 +1,7 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.0-DEV" +manifest_format = "2.0" +project_hash = "c87947f1f1f070eea848950c304d668a112dec3d" + +[deps] diff --git a/test/project/GluePkgs/HasGluePkgs.jl/Project.toml b/test/project/GluePkgs/HasGluePkgs.jl/Project.toml new file mode 100644 index 00000000000000..f7520ccaaabf9a --- /dev/null +++ b/test/project/GluePkgs/HasGluePkgs.jl/Project.toml @@ -0,0 +1,11 @@ +name = "HasGluePkgs" +uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +version = "0.1.0" + +[gluedeps] +GlueDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" +GlueDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" + +[gluepkgs] +GluePkg = "GlueDep" +GluePkgFolder = ["GlueDep", "GlueDep2"] diff --git a/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkg.jl b/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkg.jl new file mode 100644 index 00000000000000..05b9608a9d679b --- /dev/null +++ b/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkg.jl @@ -0,0 +1,11 @@ +module GluePkg + +using HasGluePkgs, GlueDep + +HasGluePkgs.foo(::GlueDep.GlueDepStruct) = 2 + +function __init__() + HasGluePkgs.glue_loaded = true +end + +end diff --git a/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkgFolder/GluePkgFolder.jl b/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkgFolder/GluePkgFolder.jl new file mode 100644 index 00000000000000..28ae155e174be7 --- /dev/null +++ b/test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkgFolder/GluePkgFolder.jl @@ -0,0 +1,9 @@ +module GluePkgFolder + +using GlueDep, GlueDep2, HasGluePkgs + +function __init__() + HasGluePkgs.glue_folder_loaded = true +end + +end diff --git a/test/project/GluePkgs/HasGluePkgs.jl/src/HasGluePkgs.jl b/test/project/GluePkgs/HasGluePkgs.jl/src/HasGluePkgs.jl new file mode 100644 index 00000000000000..5607d1d936cda2 --- /dev/null +++ b/test/project/GluePkgs/HasGluePkgs.jl/src/HasGluePkgs.jl @@ -0,0 +1,10 @@ +module HasGluePkgs + +struct HasGluePkgsStruct end + +foo(::HasGluePkgsStruct) = 1 + +glue_loaded = false +glue_folder_loaded = false + +end # module