Work with deps with more restrictive julia compat

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.