Skip to content

Commit 919cefc

Browse files
committed
Add support for "glue packages" to code loading
This allows packages to define "glue packages" which are modules that are automatically loaded when a set of other packages are loaded into the Julia session.
1 parent 726bbd7 commit 919cefc

File tree

16 files changed

+458
-26
lines changed

16 files changed

+458
-26
lines changed

NEWS.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ Julia v1.10 Release Notes
44
New language features
55
---------------------
66

7-
7+
New feature for packages that allows loading a piece of code based on other
8+
packages being loaded in the Julia session.
9+
This has similar applications as the Requires.jl package but also
10+
supports precompilation and setting compatibility.
11+
Look in the documentation for Pkg.jl for "glue packages" for more information.
812
Language changes
913
----------------
1014

base/loading.jl

+275-25
Large diffs are not rendered by default.

doc/src/manual/code-loading.md

+39
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,46 @@ The subscripted `rootsᵢ`, `graphᵢ` and `pathsᵢ` variables correspond to th
348348
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).
349349

350350
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.
351+
### "Glue" packages and dependencies
351352

353+
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.
354+
These are defined by adding the following two sections to a package's `Project.toml` file:
355+
356+
```toml
357+
name = "MyPackage"
358+
359+
[gluedeps]
360+
GlueDep = "c9a23..." # uuid
361+
OtherGlueDep = "862e..." # uuid
362+
363+
[gluepkgs]
364+
GlueFoo = "GlueDep"
365+
GlueBar = ["GlueDep", "OtherGlueDep"]
366+
...
367+
```
368+
369+
The keys under `gluepkgs` are the name of the glue packages.
370+
They are loaded when all the packages on the right hand side (the glue dependencies) of the glue package are loaded.
371+
If a glue package only has one glue dependency the lit of glue dependencies can be written as just a string for breviety.
372+
The location for the entry point of the glue package is either in `glue/GlueFoo.jl` or `glue/GlueFoo/GlueFoo.jl` for
373+
glue package `GlueFoo`.
374+
The glue package can be viewed as a somewhat normal package that has the glue dependencies and the main package as dependencies.
375+
The content of a glue package is often structured as:
376+
377+
```
378+
module GlueFoo
379+
380+
# Load main package and glue dependencies
381+
using MyPackage, GlueDep
382+
383+
# Extend functionality in main package with types from the glue dependencies
384+
MyPackage.func(x::GlueDep.SomeStruct) = ...
385+
386+
end
387+
```
388+
389+
When a package with glue packages is added to an environment, the `gluedeps` and `gluepkgs` sections
390+
are stored in the manifest file in the section for that package.
352391
### Package/Environment Preferences
353392

354393
Preferences are dictionaries of metadata that influence package behavior within an environment.

test/loading.jl

+29
Original file line numberDiff line numberDiff line change
@@ -991,5 +991,34 @@ end
991991
end
992992
end
993993

994+
995+
@testset "GluePkgs" begin
996+
old_depot_path = copy(DEPOT_PATH)
997+
try
998+
tmp = mktempdir()
999+
push!(empty!(DEPOT_PATH), joinpath(tmp, "depot"))
1000+
1001+
proj = joinpath(@__DIR__, "project", "GluePkgs", "HasDepWithGluePkgs.jl")
1002+
for i in 1:2 # Once with requiring precomilation, once where it is already
1003+
cmd = `$(Base.julia_cmd()) --project=$proj --startup-file=no -e '
1004+
begin
1005+
using HasGluePkgs
1006+
HasGluePkgs.glue_loaded && error()
1007+
using HasDepWithGluePkgs
1008+
HasGluePkgs.glue_loaded || error()
1009+
HasGluePkgs.glue_folder_loaded && error()
1010+
HasDepWithGluePkgs.do_something() || error()
1011+
using GlueDep2
1012+
HasGluePkgs.glue_folder_loaded || error()
1013+
end
1014+
'`
1015+
run(cmd)
1016+
end
1017+
finally
1018+
copy!(DEPOT_PATH, old_depot_path)
1019+
end
1020+
end
1021+
1022+
9941023
empty!(Base.DEPOT_PATH)
9951024
append!(Base.DEPOT_PATH, original_depot_path)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = "GlueDep"
2+
uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c"
3+
version = "0.1.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module GlueDep
2+
3+
struct GlueDepStruct end
4+
5+
end # module GlueDep
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = "GlueDep2"
2+
uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d"
3+
version = "0.1.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module GlueDep2
2+
3+
greet() = print("Hello World!")
4+
5+
end # module GlueDep2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
julia_version = "1.10.0-DEV"
4+
manifest_format = "2.0"
5+
project_hash = "7cbe1857ecc6692a8cc8be428a5ad5073531ff98"
6+
7+
[[deps.GlueDep]]
8+
path = "../GlueDep.jl"
9+
uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c"
10+
version = "0.1.0"
11+
12+
[[deps.GlueDep2]]
13+
path = "../GlueDep2"
14+
uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d"
15+
version = "0.1.0"
16+
17+
[[deps.HasGluePkgs]]
18+
gluedeps = ["GlueDep", "GlueDep2"]
19+
path = "../HasGluePkgs.jl"
20+
uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8"
21+
version = "0.1.0"
22+
23+
[deps.HasGluePkgs.gluepkgs]
24+
GluePkg = "GlueDep"
25+
GluePkgFolder = ["GlueDep", "GlueDep2"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name = "HasDepWithGluePkgs"
2+
uuid = "d4ef3d4a-8e22-4710-85d8-c6cf2eb9efca"
3+
version = "0.1.0"
4+
5+
[deps]
6+
GlueDep = "fa069be4-f60b-4d4c-8b95-f8008775090c"
7+
GlueDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d"
8+
HasGluePkgs = "4d3288b3-3afc-4bb6-85f3-489fffe514c8"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module HasDepWithGluePkgs
2+
3+
using HasGluePkgs: HasGluePkgs, HasGluePkgsStruct
4+
using GlueDep: GlueDepStruct
5+
# Loading GlueDep makes the glue module "GluePkg" load
6+
7+
function do_something()
8+
HasGluePkgs.foo(HasGluePkgsStruct()) == 1 || error()
9+
HasGluePkgs.foo(GlueDepStruct()) == 2 || error()
10+
return true
11+
end
12+
13+
end # module
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
julia_version = "1.10.0-DEV"
4+
manifest_format = "2.0"
5+
project_hash = "c87947f1f1f070eea848950c304d668a112dec3d"
6+
7+
[deps]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name = "HasGluePkgs"
2+
uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8"
3+
version = "0.1.0"
4+
5+
[gluedeps]
6+
GlueDep = "fa069be4-f60b-4d4c-8b95-f8008775090c"
7+
GlueDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d"
8+
9+
[gluepkgs]
10+
GluePkg = "GlueDep"
11+
GluePkgFolder = ["GlueDep", "GlueDep2"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module GluePkg
2+
3+
using HasGluePkgs, GlueDep
4+
5+
HasGluePkgs.foo(::GlueDep.GlueDepStruct) = 2
6+
7+
function __init__()
8+
HasGluePkgs.glue_loaded = true
9+
end
10+
11+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module GluePkgFolder
2+
3+
using GlueDep, GlueDep2, HasGluePkgs
4+
5+
function __init__()
6+
HasGluePkgs.glue_folder_loaded = true
7+
end
8+
9+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module HasGluePkgs
2+
3+
struct HasGluePkgsStruct end
4+
5+
foo(::HasGluePkgsStruct) = 1
6+
7+
glue_loaded = false
8+
glue_folder_loaded = false
9+
10+
end # module

0 commit comments

Comments
 (0)