Skip to content

Commit c76bf28

Browse files
vtjnashararslan
authored andcommitted
loading: make __precompile__ the default
fix #26282
1 parent cb6f5e2 commit c76bf28

File tree

42 files changed

+68
-194
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+68
-194
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ Language changes
233233
`size`, `length`, and `@inbounds`. To optionally enforce conventional indices,
234234
you can `@assert !has_offset_axes(A)`.
235235

236+
* Module pre-compilation is now the default for code loading. Adding a
237+
`__precompile__()` declaration is no longer necessary, although
238+
`__precompile__(false)` can still be used to opt-out ([#26991]).
239+
236240
Breaking changes
237241
----------------
238242

base/loading.jl

+31-61
Original file line numberDiff line numberDiff line change
@@ -744,46 +744,30 @@ function include_dependency(path::AbstractString)
744744
return nothing
745745
end
746746

747-
# We throw PrecompilableError(true) when a module wants to be precompiled but isn't,
748-
# and PrecompilableError(false) when a module doesn't want to be precompiled but is
749-
struct PrecompilableError <: Exception
750-
isprecompilable::Bool
751-
end
747+
# we throw PrecompilableError when a module doesn't want to be precompiled
748+
struct PrecompilableError <: Exception end
752749
function show(io::IO, ex::PrecompilableError)
753-
if ex.isprecompilable
754-
print(io, "Declaring __precompile__(true) is only allowed in module files being imported.")
755-
else
756-
print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.")
757-
end
750+
print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.")
758751
end
759-
precompilableerror(ex::PrecompilableError, c) = ex.isprecompilable == c
760-
precompilableerror(ex::WrappedException, c) = precompilableerror(ex.error, c)
761-
precompilableerror(ex, c) = false
752+
precompilableerror(ex::PrecompilableError) = true
753+
precompilableerror(ex::WrappedException) = precompilableerror(ex.error)
754+
precompilableerror(@nospecialize ex) = false
762755

763-
# Call __precompile__ at the top of a file to force it to be precompiled (true), or
764-
# to be prevent it from being precompiled (false). __precompile__(true) is
765-
# ignored except within "require" call.
756+
# Call __precompile__(false) at the top of a tile prevent it from being precompiled (false)
766757
"""
767-
__precompile__(isprecompilable::Bool=true)
768-
769-
Specify whether the file calling this function is precompilable. If `isprecompilable` is
770-
`true`, then `__precompile__` throws an exception when the file is loaded by
771-
`using`/`import`/`require` *unless* the file is being precompiled, and in a module file it
772-
causes the module to be automatically precompiled when it is imported. Typically,
773-
`__precompile__()` should occur before the `module` declaration in the file.
758+
__precompile__(false)
774759
760+
Specify that the file calling this function is not precompilable.
775761
If a module or file is *not* safely precompilable, it should call `__precompile__(false)` in
776762
order to throw an error if Julia attempts to precompile it.
777-
778-
`__precompile__()` should *not* be used in a module unless all of its dependencies are also
779-
using `__precompile__()`. Failure to do so can result in a runtime error when loading the module.
780763
"""
781-
function __precompile__(isprecompilable::Bool=true)
782-
if (JLOptions().use_compiled_modules != 0 &&
783-
isprecompilable != (0 != ccall(:jl_generating_output, Cint, ())) &&
784-
!(isprecompilable && toplevel_load[]))
785-
throw(PrecompilableError(isprecompilable))
764+
@noinline function __precompile__(isprecompilable::Bool=true)
765+
if isprecompilable
766+
depwarn("__precompile__() is now the default", :__precompile__)
767+
elseif 0 != ccall(:jl_generating_output, Cint, ())
768+
throw(PrecompilableError())
786769
end
770+
nothing
787771
end
788772

789773
# require always works in Main scope and loads files from node 1
@@ -932,10 +916,9 @@ function _require(pkg::PkgId)
932916
end
933917

934918
# attempt to load the module file via the precompile cache locations
935-
doneprecompile = false
936919
if JLOptions().use_compiled_modules != 0
937-
doneprecompile = _require_search_from_serialized(pkg, path)
938-
if !isa(doneprecompile, Bool)
920+
m = _require_search_from_serialized(pkg, path)
921+
if !isa(m, Bool)
939922
return
940923
end
941924
end
@@ -948,21 +931,24 @@ function _require(pkg::PkgId)
948931
This may mean module $name does not support precompilation but is imported by a module that does."""
949932
if JLOptions().incremental != 0
950933
# during incremental precompilation, this should be fail-fast
951-
throw(PrecompilableError(false))
934+
throw(PrecompilableError())
952935
end
953936
end
954937
end
955938

956-
if doneprecompile === true || JLOptions().incremental != 0
957-
# spawn off a new incremental pre-compile task for recursive `require` calls
958-
# or if the require search declared it was pre-compiled before (and therefore is expected to still be pre-compilable)
959-
cachefile = compilecache(pkg)
960-
m = _require_from_serialized(cachefile)
961-
if isa(m, Exception)
962-
@warn "The call to compilecache failed to create a usable precompiled cache file for module $name" exception=m
963-
# fall-through, TODO: disable __precompile__(true) error so that the normal include will succeed
964-
else
965-
return
939+
if JLOptions().use_compiled_modules != 0
940+
if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0)
941+
# spawn off a new incremental pre-compile task for recursive `require` calls
942+
# or if the require search declared it was pre-compiled before (and therefore is expected to still be pre-compilable)
943+
cachefile = compilecache(pkg)
944+
m = _require_from_serialized(cachefile)
945+
if isa(m, Exception)
946+
if !precompilableerror(m)
947+
@warn "The call to compilecache failed to create a usable precompiled cache file for module $name" exception=m
948+
end
949+
else
950+
return
951+
end
966952
end
967953
end
968954

@@ -977,22 +963,6 @@ function _require(pkg::PkgId)
977963
try
978964
include_relative(__toplevel__, path)
979965
return
980-
catch ex
981-
if uuid !== old_uuid
982-
ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, old_uuid)
983-
end
984-
if doneprecompile === true || JLOptions().use_compiled_modules == 0 || !precompilableerror(ex, true)
985-
rethrow() # rethrow non-precompilable=true errors
986-
end
987-
# the file requested `__precompile__`, so try to build a cache file and use that
988-
cachefile = compilecache(pkg)
989-
m = _require_from_serialized(cachefile)
990-
if isa(m, Exception)
991-
@warn """Module `$name` declares `__precompile__(true)` but `require` failed
992-
to create a usable precompiled cache file""" exception=m
993-
# TODO: disable __precompile__(true) error and do normal include instead of error
994-
error("Module $name declares __precompile__(true) but require failed to create a usable precompiled cache file.")
995-
end
996966
finally
997967
if uuid !== old_uuid
998968
ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, old_uuid)

doc/src/manual/modules.md

+23-22
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,14 @@ This prevents name conflicts for globals initialized after load time.
217217
### Module initialization and precompilation
218218

219219
Large modules can take several seconds to load because executing all of the statements in a module
220-
often involves compiling a large amount of code. Julia provides the ability to create precompiled
221-
versions of modules to reduce this time.
220+
often involves compiling a large amount of code.
221+
Julia creates precompiled caches of the module to reduce this time.
222222

223-
To create an incremental precompiled module file, add `__precompile__()` at the top of your module
224-
file (before the `module` starts). This will cause it to be automatically compiled the first time
223+
The incremental precompiled module file are created and used automatically when using `import`
224+
or `using` to load a module. This will cause it to be automatically compiled the first time
225225
it is imported. Alternatively, you can manually call `Base.compilecache(modulename)`. The resulting
226226
cache files will be stored in `DEPOT_PATH[1]/compiled/`. Subsequently, the module is automatically
227-
recompiled upon `import` whenever any of its dependencies change; dependencies are modules it
227+
recompiled upon `using` or `import` whenever any of its dependencies change; dependencies are modules it
228228
imports, the Julia build, files it includes, or explicit dependencies declared by `include_dependency(path)`
229229
in the module file(s).
230230

@@ -240,20 +240,22 @@ incompatibilities between the running system and the precompile cache. If you wa
240240
to the source reflected in the running system, you should call `reload("Module")` on the module
241241
you changed, and any module that depended on it in which you want to see the change reflected.
242242

243-
Precompiling a module also recursively precompiles any modules that are imported therein. If you
244-
know that it is *not* safe to precompile your module (for the reasons described below), you should
245-
put `__precompile__(false)` in the module file to cause `Base.compilecache` to throw an error
246-
(and thereby prevent the module from being imported by any other precompiled module).
247-
248-
`__precompile__()` should *not* be used in a module unless all of its dependencies are also using
249-
`__precompile__()`. Failure to do so can result in a runtime error when loading the module.
250-
251-
In order to make your module work with precompilation, however, you may need to change your module
252-
to explicitly separate any initialization steps that must occur at *runtime* from steps that can
253-
occur at *compile time*. For this purpose, Julia allows you to define an `__init__()` function
254-
in your module that executes any initialization steps that must occur at runtime. This function
255-
will not be called during compilation (`--output-*` or `__precompile__()`). You may, of course,
256-
call it manually if necessary, but the default is to assume this function deals with computing
243+
If you know that a module is *not* safe to precompile your module
244+
(for example, for one of the reasons described below), you should
245+
put `__precompile__(false)` in the module file (typically placed at the top).
246+
This will cause `Base.compilecache` to throw an error, and will cause `using` / `import` to load it
247+
directly into the current process and skip the precompile and caching.
248+
This also thereby prevents the module from being imported by any other precompiled module.
249+
250+
You may need to be aware of certain behaviors inherent in the creation of incremental shared libraries
251+
which may require care when writing your module. For example, external state is not preserved.
252+
To accommodate this, explicitly separate any initialization steps that must occur at *runtime*
253+
from steps that can occur at *compile time*.
254+
For this purpose, Julia allows you to define an `__init__()` function in your module that executes
255+
any initialization steps that must occur at runtime.
256+
This function will not be called during compilation (`--output-*`).
257+
Effectively, you can assume it will be run exactly once in the lifetime of the code.
258+
You may, of course, call it manually if necessary, but the default is to assume this function deals with computing
257259
state for the local machine, which does not need to be – or even should not be – captured
258260
in the compiled image. It will be called after the module is loaded into a process, including
259261
if it is being loaded into an incremental compile (`--output-incremental=yes`), but not if it
@@ -382,6 +384,5 @@ It is sometimes helpful during module development to turn off incremental precom
382384
command line flag `--compiled-modules={yes|no}` enables you to toggle module precompilation on and
383385
off. When Julia is started with `--compiled-modules=no` the serialized modules in the compile cache
384386
are ignored when loading modules and module dependencies. `Base.compilecache` can still be called
385-
manually and it will respect `__precompile__()` directives for the module. The state of this command
386-
line flag is passed to [`Pkg.build`] to disable automatic precompilation triggering when installing,
387-
updating, and explicitly building packages.
387+
manually. The state of this command line flag is passed to `Pkg.build` to disable automatic
388+
precompilation triggering when installing, updating, and explicitly building packages.

stdlib/Base64/src/Base64.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module Base64
64

75
using Base: has_offset_axes

stdlib/CRC32c/src/CRC32c.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Standard library module for computing the CRC-32c checksum.
75

stdlib/Dates/src/Dates.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Dates
75

stdlib/DelimitedFiles/src/DelimitedFiles.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Utilities for reading and writing delimited files, for example ".csv".
75
See [`readdlm`](@ref) and [`writedlm`](@ref).

stdlib/Distributed/src/Distributed.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Tools for distributed parallel processing.
75
"""

stdlib/FileWatching/src/FileWatching.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Utilities for monitoring files and file descriptors for events.
75
"""

stdlib/Future/src/Future.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"The `Future` module implements future behavior of already existing functions,
64
which will replace the current version in a future release of Julia."
75
module Future

stdlib/InteractiveUtils/src/InteractiveUtils.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module InteractiveUtils
64

75
export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo,

stdlib/LibGit2/src/LibGit2.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Interface to [libgit2](https://libgit2.github.com/).
75
"""

stdlib/Libdl/src/Libdl.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module Libdl
64
@doc """
75
Interface to libdl. Provides dynamic linking support.

stdlib/LinearAlgebra/src/LinearAlgebra.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Linear algebra module. Provides array arithmetic,
75
matrix factorizations and other linear algebra related

stdlib/Logging/src/Logging.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module Logging
64

75
# For now, simply import most names from Base - we don't want to fully

stdlib/Markdown/src/Markdown.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Tools for working with the Markdown file format. Mainly for documentation.
75
"""

stdlib/Mmap/src/Mmap.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Low level module for mmap (memory mapping of files).
75
"""

stdlib/OldPkg/src/OldPkg.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
OldPkg
75

stdlib/Pkg/src/API.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,9 @@ function precompile(ctx::Context)
462462
end
463463
end
464464
if !found_matching_precompile
465-
# Only precompile packages that has contains `__precompile__` or `__precompile__(true)`
465+
# Only attempt precompile packages that don't appear to contain `__precompile__(false)`
466466
source = read(sourcepath, String)
467-
if occursin(r"__precompile__\(\)|__precompile__\(true\)", source)
467+
if !occursin(r"__precompile__\(false\)", source)
468468
push!(needs_to_be_precompiled, pkg.name)
469469
end
470470
end

stdlib/Pkg/src/Pkg.jl

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
43
module Pkg
54

65
import Random

stdlib/Pkg/test/test_packages/UnregisteredWithoutProject/src/UnregisteredWithoutProject.jl

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__()
43
module UnregisteredWithoutProject
54

65
if !isfile(joinpath(@__DIR__, "..", "deps", "deps.jl"))

stdlib/Printf/src/Printf.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module Printf
64
# the macro implementations here exactly mirrors the
75
# macros left in base/printf.jl, and uses the utility there

stdlib/Profile/src/Profile.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
"""
64
Profiling support, main entry point is the [`@profile`](@ref) macro.
75
"""

stdlib/REPL/src/REPL.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module REPL
64

75
using Base.Meta, Sockets

stdlib/Random/src/Random.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module Random
64

75
include("DSFMT.jl")

stdlib/SHA/src/SHA.jl

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
__precompile__(true)
4-
53
module SHA
64

75
# Export convenience functions, context types, update!() and digest!() functions

0 commit comments

Comments
 (0)