I am trying to make a minimal example from my real-world problem. Let’s say I have two packages PkgA
and PkgB
. Currently, PkgA
has a julia compat of 1.0
, while PkgB
has a julia compat of 1.7
.
I now want to use some function of PkgB
inside of PkgA
, but only if VERSION >= v"1.7"
(i.e. PkgB
is compatible). In all other julia versions, I can replace this function with some trivial line of code. If instead of PkgB
I use something from julia Base (with no new syntax), this just works with a @static if
, but PkgB
makes it necessary to declare this dependency.
How can I input the above into the Project.toml
file of PkgA
? I know that this could be achieved using weakdeps, but I would prefer a way where a user would not need to add an dependency on PkgB
on their own.
2 Likes
Would it work if you make a weakdep but then have a @static if VERSION>... using PkgB end
?
That way the weakdep solves the issue of having to include a dependency and the @static if VERSION>=1.7 using PkgB end
solves the issue of the user of PkgA having to explicitly install PkgB.
using
a weakdep seems to lead to an error:
julia> using PkgA
Precompiling PkgA
Info Given PkgA was explicitly requested, output will be shown live
ERROR: LoadError: ArgumentError: Package PkgA does not have PkgB in its dependencies:
- You may have a partially installed environment. Try `Pkg.instantiate()`
to ensure all packages in the environment are installed.
- Or, if you have PkgA checked out for development and have
added PkgB as a dependency but haven't updated your primary
environment's manifest file, try `Pkg.resolve()`.
- Otherwise you may need to report an issue with PkgA
Stacktrace: ....
This might be rather ugly, but it works on my end:
PkgB
is ExplicitImports
because I did not want to worry about getting something wrong when setting up dependencies between unregistered packages.
❯ cat PkgA/Project.toml
name = "PkgA"
uuid = "6bbb1ba1-2d7b-4510-8e93-778dc605bb72"
authors = ["Stefan Krastanov <stefan@krastanov.org>"]
version = "0.1.0"
[weakdeps]
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
[compat]
julia = "1"
❯ cat PkgA/src/PkgA.jl
module PkgA
function __init__()
if VERSION>=v"1.9"
@eval Main begin # !!! importantly, we evaluate in the Main module, not in PkgA
import Pkg
Pkg.add("ExplicitImports")
using ExplicitImports
end
end
end
greetA() = print("Hello World from A!")
end # module PkgA
An ugly trick to import ExplicitImports
when your package is imported, but not have access to it. To actually get access to it, you also need to build out the weakdep.
Edit: nope, this does not work, it hangs when precompiling the extension… which might be bug in julia?
Here is the complete solution that also takes care of the precompilation loop for the extension:
PkgB
is ExplicitImports
because I did not want to worry about getting something wrong when setting up dependencies between unregistered packages.
A big assumption of mine here is that you do NOT want to have users of PkgA
also type using PkgB
. As you alluded to, if you require the users to simply do using PkgB
, a weakdep would be a much neater and simpler solution
❯ cat PkgA/src/PkgA.jl
module PkgA
function __init__()
if VERSION>=v"1.9"
@eval Main begin # note that we import in module Main to avoid the dependency error
import Pkg
Pkg.add("ExplicitImports")
using ExplicitImports
end
end
end
# some declared function with no methods that will be extended by the weakdep
function f end
end # module PkgA
❯ cat PkgA/ext/E/E.jl
module E
__precompile__(false) # otherwise the `__init__` causes a bad dependency loop in precompilation
# this will lead to slow import times if this extension contains a lot of code
import PkgA: f
import ExplicitImports
function f()
println("this module exists")
end
end
❯ cat PkgA/Project.toml
name = "PkgA"
uuid = "6bbb1ba1-2d7b-4510-8e93-778dc605bb72"
authors = ["Stefan Krastanov <stefan@krastanov.org>"]
version = "0.1.0"
[weakdeps]
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
[extensions]
E = "ExplicitImports"
[compat]
julia = "1"
(Afk due to vacation.)
Thanks for your suggestions. Unfortunately the @eval Main
thing seems to edit some Project.toml file on execution that should not get touched.
I think julia unfortunately does not offer a suitable solution. What I thought about as a possible workaround is a dummy package to but into the dependency chain that has different major versions depending on the julia versions with different sets of dependencies. This could then have PkgB in the list only for julia 1.7+ or something like this.