Skip to content

Commit

Permalink
Add support for "glue packages" to code loading
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
KristofferC committed Nov 24, 2022
1 parent 726bbd7 commit 78db1b1
Show file tree
Hide file tree
Showing 21 changed files with 461 additions and 29 deletions.
6 changes: 5 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------

Expand Down
300 changes: 275 additions & 25 deletions base/loading.jl

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bc5c8b639fc1e42b88b41d686a5dd35a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aba0e09bbbfe78767c3d8fe8f6e14ddc9c73aa5c052e04a784d536b7f122a96ba7a2f618fae2775eb00f908434a55e61fbba280712130847788544b72932f8e5

This file was deleted.

This file was deleted.

39 changes: 39 additions & 0 deletions doc/src/manual/code-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion stdlib/Pkg.version
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PKG_BRANCH = master
PKG_SHA1 = ed6a5497e46ed541b2718c404c0f468b7f92263a
PKG_SHA1 = 8664a237780031c1ad5235c1c1f488fe32dea8eb
PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git
PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1
29 changes: 29 additions & 0 deletions test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 with requiring precomilation, once where it is already
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
'`
run(cmd)
end
finally
copy!(DEPOT_PATH, old_depot_path)
end
end


empty!(Base.DEPOT_PATH)
append!(Base.DEPOT_PATH, original_depot_path)
3 changes: 3 additions & 0 deletions test/project/GluePkgs/GlueDep.jl/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = "GlueDep"
uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c"
version = "0.1.0"
5 changes: 5 additions & 0 deletions test/project/GluePkgs/GlueDep.jl/src/GlueDep.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module GlueDep

struct GlueDepStruct end

end # module GlueDep
3 changes: 3 additions & 0 deletions test/project/GluePkgs/GlueDep2/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = "GlueDep2"
uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d"
version = "0.1.0"
5 changes: 5 additions & 0 deletions test/project/GluePkgs/GlueDep2/src/GlueDep2.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module GlueDep2

greet() = print("Hello World!")

end # module GlueDep2
25 changes: 25 additions & 0 deletions test/project/GluePkgs/HasDepWithGluePkgs.jl/Manifest.toml
Original file line number Diff line number Diff line change
@@ -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"]
8 changes: 8 additions & 0 deletions test/project/GluePkgs/HasDepWithGluePkgs.jl/Project.toml
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions test/project/GluePkgs/HasGluePkgs.jl/Manifest.toml
Original file line number Diff line number Diff line change
@@ -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]
11 changes: 11 additions & 0 deletions test/project/GluePkgs/HasGluePkgs.jl/Project.toml
Original file line number Diff line number Diff line change
@@ -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"]
11 changes: 11 additions & 0 deletions test/project/GluePkgs/HasGluePkgs.jl/glue/GluePkg.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module GluePkg

using HasGluePkgs, GlueDep

HasGluePkgs.foo(::GlueDep.GlueDepStruct) = 2

function __init__()
HasGluePkgs.glue_loaded = true
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module GluePkgFolder

using GlueDep, GlueDep2, HasGluePkgs

function __init__()
HasGluePkgs.glue_folder_loaded = true
end

end
10 changes: 10 additions & 0 deletions test/project/GluePkgs/HasGluePkgs.jl/src/HasGluePkgs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module HasGluePkgs

struct HasGluePkgsStruct end

foo(::HasGluePkgsStruct) = 1

glue_loaded = false
glue_folder_loaded = false

end # module

0 comments on commit 78db1b1

Please sign in to comment.