I have a package named MyPkg, which depends on MyPkg2. When I load MyPkg, I get the following error:
julia> using MyPkg
[ Info: Precompiling MyPkg [<MyPkg uuid>]
ERROR: LoadError: Evaluation into the closed module `MyPkg2` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `MyPkg2` with `eval` during precompilation - don't do this.
I tried to construct a MWE reproducing this error, but packages defined in REPL don’t seem to produce this error.
This makes me question the meaning of “closed module” in the error message: does it refer to a module defined as a package, compared to a module defined in REPL?
I know eval is producing the error. I am trying to update the package such that it achieves the desired functionality without producing this error. For that, I was trying to reproduce the same error by modules defined in REPL that have similar structures as my packages. Once I have the modules producing the same error, I would be able to tweak the modules until I remove the error.
However, I am not able to produce the same error by the modules defined in REPL. (The reported error message is from packages.) So, I was wondering if this is because my modules in REPL are too simplified to mimic the behaviors of the packages, or because the modules defined in REPL are not “closed modules” that the error message is mentioning. If the latter is the case, I would never be able to reproduce the error by modules defined in REPL, so my attempt to find the solution to the error by tweaking modules defined in REPL will be in vain.
I have found (1.11 and 1.10) that any module other than the one currently being precompiled, is considered closed, even a locally used Module() object is considered closed. However, submodules of the current module are open, which you can use as temporary modules.
Make sure you’re testing with a Pkg loaded package, not a REPL defined module.
module PkgTest4
using Inherit
init_fn::Union{Function, Nothing} = nothing
macro greet()
if !isdefined(@__MODULE__, :shadow)
Core.eval(@__MODULE__, :(
module shadow
end
# using .shadow
))
end
# tmpmod = Module()
# Core.eval(tmpmod, :(println("inside temp module "))) #NOTE: cannot eval into a temp module
Core.eval(shadow, :(@warn("eval inside $(@__MODULE__) "))) #can eval into the current module or submodule during compile time (not yet closed)
if isprecompiling()
@info("Hello macro! (precompiling)")
global init_fn = eval(:(() -> println("I'm a function created inside a macro"))) #don't use @info in code that will be called from __init__()
else
@info("Hello macro! (not precompiling)") #no problem with @info here
end
end
function init_greet()
if isprecompiling()
println("Hello fn! (precompiling)") #there's no compilation with println, but 200ms with @info even inside a function!
else
println("Hello fn! (not precompiling)")
end
end
@greet
function __init__()
init_greet() #1.5ms no compilation with println, but very high with @info!
init_fn()
@greet # does nothing because the macro returns nothing
# println(1 + 2) #15ms
# @info("PkgTest4.__init__") #190ms
end
@info("bottom of file")
end # module PkgTest4
@show @__DIR__
cd(joinpath(@__DIR__, ".."))
@show pwd()
using Pkg
Pkg.activate(".")
begin
@time import PackageExtensionsExample
@time import Inherit
@time import PkgTest4
end
begin
import PkgTest4
@time PkgTest4.@greet #prints "Hello macro! (not precompiling)" because it's being compiled as a script statement
end
I mean… don’tuseevalfrominside macros. It’s almost never what you want to do. Hard to know what you’re trying to do, but I’d highly recommend avoiding this problem altogether by avoiding eval in macros.