-
-
Notifications
You must be signed in to change notification settings - Fork 283
WIP: Support for weak dependencies #3216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
449f52e
read/write weak dependencies from manifest and project
KristofferC 0f5f5ac
add support for weak dependencies in `Pkg.status`
KristofferC d89a8c2
do Pkg.precompile on any deps that have lost weak deps after rm
IanButterworth f61f68b
fixes and add test
af9d97d
add reading weak deps/compat from registry
cb9bd41
collect set of weak deps for fixed packages
99815de
only check if uuids conflict in deps and weakdeps sections independently
ff59c3d
some tests
f9cd4c4
add some basic docs for weak deps
KristofferC File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,77 @@ | ||||||
| # Weak dependencies | ||||||
|
|
||||||
| It is sometimes desirable to be able to extend some functionality of a package without having to | ||||||
| unconditionally take on the cost (in terms of e.g. load time) of adding an extra dependency. | ||||||
| A *weak* dependency is a package that is only available to load if some other package in the | ||||||
| current environment has that package as a normal (or strong) dependency. | ||||||
|
|
||||||
| Weak dependencies are listed in a `Project.toml` file under the `[weakdeps]` section which can be compared to a | ||||||
| (strong) dependency which is under the `[deps]` section. | ||||||
| Compatibility on weak dependencies is specified like a normal dependency in the `[compat]` section. | ||||||
|
|
||||||
| A useful application of weak dependencies could be for a plotting package that should be able to plot | ||||||
| objects from different Julia packages. Adding all those different Julia packages as dependencies | ||||||
| could be expensive. Instead, these packages are added as weak dependencies so that they are available only | ||||||
| if there is a chance that someone might call the function with such a type. | ||||||
|
|
||||||
| Below is an example of how the code can be structured for a use case as outlined above. | ||||||
|
|
||||||
| `Project.toml`: | ||||||
| ```toml | ||||||
| name = "Plotting" | ||||||
| version = "0.1.0" | ||||||
| uuid = "..." | ||||||
|
|
||||||
| [deps] # strong dependencies | ||||||
| Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" | ||||||
|
|
||||||
| [weakdeps] | ||||||
| Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" | ||||||
|
|
||||||
| [compat] # compat can also be given on weak dependencies | ||||||
| Colors = "0.12.8" | ||||||
| Contour = "0.6.2" | ||||||
| ``` | ||||||
|
|
||||||
| `src/Plotting.jl`: | ||||||
| ```julia | ||||||
| module Plotting | ||||||
|
|
||||||
| using Colors | ||||||
|
|
||||||
| if Base.@hasdep Contour | ||||||
| using Contour | ||||||
| function plot(c::Contour.ContourCollection | ||||||
| ... | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| end # module | ||||||
| ``` | ||||||
|
|
||||||
| ## Compatibility with older Julia versions. | ||||||
|
|
||||||
| It is possible to have a dependency be a weak version in Julia versions that support it and be a strong dependency in earlier | ||||||
| Julia versions. This is done by having the dependency as *both* a strong and weak dependency. Older Julia versions will ignore | ||||||
| the specification of the dependency as weak while new Julia versions will tag it as a weak dependency. | ||||||
|
|
||||||
| The above code would then look like this: | ||||||
|
|
||||||
| ```julia | ||||||
| module Plotting | ||||||
|
|
||||||
| using Colors | ||||||
|
|
||||||
| if !isdefined(Base, :hasdep) || Base.hasdep(@__MODULE__, :Contour) | ||||||
| using Contour | ||||||
| function plot(c::Contour.ContourCollection | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ... | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| end # module | ||||||
| ``` | ||||||
|
|
||||||
| where the "conditional code" is executed unconditionally on old Julia versions and based on the presence of the weak | ||||||
| dependency on the new Julia version. Here the functional form of `@hasdep` was used which requires a module as the first | ||||||
| argument. | ||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -154,7 +154,7 @@ for f in (:develop, :add, :rm, :up, :pin, :free, :test, :build, :status, :why) | |||
| pkgs = deepcopy(pkgs) # don't mutate input | ||||
| foreach(handle_package_input!, pkgs) | ||||
| ret = $f(ctx, pkgs; kwargs...) | ||||
| $(f in (:add, :up, :pin, :free, :build)) && Pkg._auto_precompile(ctx) | ||||
| $(f in (:add, :up, :pin, :free, :build)) && Pkg._auto_precompile(ctx) # rm does too, but it's handled differently | ||||
| $(f in (:up, :pin, :free, :rm)) && Pkg._auto_gc(ctx) | ||||
| return ret | ||||
| end | ||||
|
|
@@ -301,9 +301,39 @@ function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode=PKGMODE_PROJECT, all_p | |||
| ensure_resolved(ctx, ctx.env.manifest, pkgs) | ||||
|
|
||||
| Operations.rm(ctx, pkgs; mode) | ||||
|
|
||||
| # After a `rm`, weak deps may have been removed and thus packages that were precompiled | ||||
| # with them present will need re-precompiling, so precompile those. | ||||
| # This autoprecomp is more targetted than after other pkg actions because `rm` is lighter-touch. | ||||
| deps_that_may_need_re_precomp = identify_deps_with_inactive_weak_deps(ctx) | ||||
| if !isempty(deps_that_may_need_re_precomp) | ||||
| Pkg._auto_precompile(ctx, deps_that_may_need_re_precomp, already_instantiated = true) | ||||
| end | ||||
| return | ||||
| end | ||||
|
|
||||
| # Identifies any deps that have weak deps that were strong, but removed from | ||||
| # the manifest in the previous pkg action. | ||||
| function identify_deps_with_inactive_weak_deps(ctx::Context) | ||||
| dep_list = String[] | ||||
| new_uuids = keys(ctx.env.manifest) | ||||
| old_uuids = keys(ctx.env.original_manifest) | ||||
| for (uuid, pkgentry) in ctx.env.manifest.deps | ||||
| (isnothing(pkgentry.weakdeps) || isempty(pkgentry.weakdeps)) && continue | ||||
| weak_uuids = values(pkgentry.weakdeps) | ||||
| old_num_promoted = count(in(old_uuids), weak_uuids) | ||||
| new_num_promoted = count(in(new_uuids), weak_uuids) | ||||
|
|
||||
| # @show pkgentry pkgentry.weakdeps old_uuids new_uuids new_num_promoted old_num_promoted | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
|
|
||||
| if new_num_promoted < old_num_promoted | ||||
| push!(dep_list, pkgentry.name) | ||||
| end | ||||
| end | ||||
| # @show dep_list | ||||
| return dep_list | ||||
| end | ||||
|
|
||||
| function append_all_pkgs!(pkgs, ctx, mode) | ||||
| if mode == PKGMODE_PROJECT || mode == PKGMODE_COMBINED | ||||
| for (name::String, uuid::UUID) in ctx.env.project.deps | ||||
|
|
@@ -1154,6 +1184,8 @@ function precompile(ctx::Context, pkgs::Vector{String}=String[]; internal_call:: | |||
| isempty(depsmap) && pkgerror("No direct dependencies found matching $(repr(pkgs))") | ||||
| end | ||||
|
|
||||
| target = string(isempty(pkgs) ? "project" : join(pkgs, ", "), "...") | ||||
|
|
||||
| pkg_queue = Base.PkgId[] | ||||
| failed_deps = Dict{Base.PkgId, String}() | ||||
| skipped_deps = Base.PkgId[] | ||||
|
|
@@ -1195,7 +1227,7 @@ function precompile(ctx::Context, pkgs::Vector{String}=String[]; internal_call:: | |||
| wait(first_started) | ||||
| (isempty(pkg_queue) || interrupted_or_done.set) && return | ||||
| fancyprint && lock(print_lock) do | ||||
| printpkgstyle(io, :Precompiling, "project...") | ||||
| printpkgstyle(io, :Precompiling, target) | ||||
| print(io, ansi_disablecursor) | ||||
| end | ||||
| t = Timer(0; interval=1/10) | ||||
|
|
@@ -1300,7 +1332,7 @@ function precompile(ctx::Context, pkgs::Vector{String}=String[]; internal_call:: | |||
| iob = IOBuffer() | ||||
| name = is_direct_dep ? pkg.name : string(color_string(pkg.name, :light_black)) | ||||
| !fancyprint && lock(print_lock) do | ||||
| isempty(pkg_queue) && printpkgstyle(io, :Precompiling, "project...") | ||||
| isempty(pkg_queue) && printpkgstyle(io, :Precompiling, target) | ||||
| end | ||||
| push!(pkg_queue, pkg) | ||||
| started[pkg] = true | ||||
|
|
@@ -1606,13 +1638,15 @@ end | |||
|
|
||||
| @deprecate status(mode::PackageMode) status(mode=mode) | ||||
|
|
||||
| function status(ctx::Context, pkgs::Vector{PackageSpec}; diff::Bool=false, mode=PKGMODE_PROJECT, outdated::Bool=false, compat::Bool=false, io::IO=stdout_f(), kwargs...) | ||||
| function status(ctx::Context, pkgs::Vector{PackageSpec}; diff::Bool=false, mode=PKGMODE_PROJECT, outdated::Bool=false, compat::Bool=false, weak::Bool=false, io::IO=stdout_f(), kwargs...) | ||||
| if compat | ||||
| diff && pkgerror("Compat status has no `diff` mode") | ||||
| outdated && pkgerror("Compat status has no `outdated` mode") | ||||
| weak && pkgerror("Compat status has no `weak` mode") | ||||
|
|
||||
| Operations.print_compat(ctx, pkgs; io) | ||||
| else | ||||
| Operations.status(ctx.env, ctx.registries, pkgs; mode, git_diff=diff, io, outdated) | ||||
| Operations.status(ctx.env, ctx.registries, pkgs; mode, git_diff=diff, io, outdated, weak) | ||||
| end | ||||
| return nothing | ||||
| end | ||||
|
|
||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.