• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37502

pending completion
#37502

push

local

web-flow
delete unnecessary `getindex` method (#49310)

xref: <https://github.com/JuliaLang/julia/pull/47154#discussion_r1161062960>

70609 of 82885 relevant lines covered (85.19%)

32589426.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.39
/base/loading.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# Base.require is the implementation for the `import` statement
4
const require_lock = ReentrantLock()
5

6
# Cross-platform case-sensitive path canonicalization
7

8
if Sys.isunix() && !Sys.isapple()
9
    # assume case-sensitive filesystems, don't have to do anything
10
    isfile_casesensitive(path) = isaccessiblefile(path)
1,244,784✔
11
elseif Sys.iswindows()
12
    # GetLongPathName Win32 function returns the case-preserved filename on NTFS.
13
    function isfile_casesensitive(path)
×
14
        isaccessiblefile(path) || return false  # Fail fast
×
15
        basename(Filesystem.longpath(path)) == basename(path)
×
16
    end
17
elseif Sys.isapple()
18
    # HFS+ filesystem is case-preserving. The getattrlist API returns
19
    # a case-preserved filename. In the rare event that HFS+ is operating
20
    # in case-sensitive mode, this will still work but will be redundant.
21

22
    # Constants from <sys/attr.h>
23
    const ATRATTR_BIT_MAP_COUNT = 5
24
    const ATTR_CMN_NAME = 1
25
    const BITMAPCOUNT = 1
26
    const COMMONATTR = 5
27
    const FSOPT_NOFOLLOW = 1  # Don't follow symbolic links
28

29
    const attr_list = zeros(UInt8, 24)
30
    attr_list[BITMAPCOUNT] = ATRATTR_BIT_MAP_COUNT
31
    attr_list[COMMONATTR] = ATTR_CMN_NAME
32

33
    # This essentially corresponds to the following C code:
34
    # attrlist attr_list;
35
    # memset(&attr_list, 0, sizeof(attr_list));
36
    # attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
37
    # attr_list.commonattr = ATTR_CMN_NAME;
38
    # struct Buffer {
39
    #    u_int32_t total_length;
40
    #    u_int32_t filename_offset;
41
    #    u_int32_t filename_length;
42
    #    char filename[max_filename_length];
43
    # };
44
    # Buffer buf;
45
    # getattrpath(path, &attr_list, &buf, sizeof(buf), FSOPT_NOFOLLOW);
46
    function isfile_casesensitive(path)
×
47
        isaccessiblefile(path) || return false
×
48
        path_basename = String(basename(path))
×
49
        local casepreserved_basename
×
50
        header_size = 12
×
51
        buf = Vector{UInt8}(undef, length(path_basename) + header_size + 1)
×
52
        while true
×
53
            ret = ccall(:getattrlist, Cint,
×
54
                        (Cstring, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Culong),
55
                        path, attr_list, buf, sizeof(buf), FSOPT_NOFOLLOW)
56
            systemerror(:getattrlist, ret ≠ 0)
×
57
            filename_length = GC.@preserve buf unsafe_load(
×
58
              convert(Ptr{UInt32}, pointer(buf) + 8))
59
            if (filename_length + header_size) > length(buf)
×
60
                resize!(buf, filename_length + header_size)
×
61
                continue
×
62
            end
63
            casepreserved_basename =
×
64
              view(buf, (header_size+1):(header_size+filename_length-1))
65
            break
×
66
        end
×
67
        # Hack to compensate for inability to create a string from a subarray with no allocations.
68
        codeunits(path_basename) == casepreserved_basename && return true
×
69

70
        # If there is no match, it's possible that the file does exist but HFS+
71
        # performed unicode normalization. See  https://developer.apple.com/library/mac/qa/qa1235/_index.html.
72
        isascii(path_basename) && return false
×
73
        codeunits(Unicode.normalize(path_basename, :NFD)) == casepreserved_basename
×
74
    end
75
else
76
    # Generic fallback that performs a slow directory listing.
77
    function isfile_casesensitive(path)
×
78
        isaccessiblefile(path) || return false
×
79
        dir, filename = splitdir(path)
×
80
        any(readdir(dir) .== filename)
×
81
    end
82
end
83

84
# Check if the file is accessible. If stat fails return `false`
85

86
function isaccessibledir(dir)
1✔
87
    return try
1✔
88
        isdir(dir)
1✔
89
    catch err
90
        err isa IOError || rethrow()
×
91
        false
×
92
    end
93
end
94

95
function isaccessiblefile(file)
1,244,785✔
96
    return try
1,244,785✔
97
        isfile(file)
1,244,785✔
98
    catch err
99
        err isa IOError || rethrow()
×
100
        false
×
101
    end
102
end
103

104
function isaccessiblepath(path)
1✔
105
    return try
1✔
106
        ispath(path)
1✔
107
    catch err
108
        err isa IOError || rethrow()
×
109
        false
×
110
    end
111
end
112

113
## SHA1 ##
114

115
struct SHA1
116
    bytes::NTuple{20, UInt8}
17,711✔
117
end
118
function SHA1(bytes::Vector{UInt8})
17,711✔
119
    length(bytes) == 20 ||
17,711✔
120
        throw(ArgumentError("wrong number of bytes for SHA1 hash: $(length(bytes))"))
121
    return SHA1(ntuple(i->bytes[i], Val(20)))
371,931✔
122
end
123
SHA1(s::AbstractString) = SHA1(hex2bytes(s))
17,500✔
124
parse(::Type{SHA1}, s::AbstractString) = SHA1(s)
2✔
125
function tryparse(::Type{SHA1}, s::AbstractString)
1✔
126
    try
1✔
127
        return parse(SHA1, s)
1✔
128
    catch e
129
        if isa(e, ArgumentError)
1✔
130
            return nothing
1✔
131
        end
132
        rethrow(e)
×
133
    end
134
end
135

136
string(hash::SHA1) = bytes2hex(hash.bytes)
200✔
137
print(io::IO, hash::SHA1) = bytes2hex(io, hash.bytes)
27✔
138
show(io::IO, hash::SHA1) = print(io, "SHA1(\"", hash, "\")")
1✔
139

140
isless(a::SHA1, b::SHA1) = isless(a.bytes, b.bytes)
3✔
141
hash(a::SHA1, h::UInt) = hash((SHA1, a.bytes), h)
×
142
==(a::SHA1, b::SHA1) = a.bytes == b.bytes
17✔
143

144
# fake uuid5 function (for self-assigned UUIDs)
145
# TODO: delete and use real uuid5 once it's in stdlib
146

147
function uuid5(namespace::UUID, key::String)
111,409✔
148
    u::UInt128 = 0
×
149
    h = hash(namespace)
111,409✔
150
    for _ = 1:sizeof(u)÷sizeof(h)
111,409✔
151
        u <<= sizeof(h) << 3
222,818✔
152
        u |= (h = hash(key, h))
222,818✔
153
    end
334,227✔
154
    u &= 0xffffffffffff0fff3fffffffffffffff
111,409✔
155
    u |= 0x00000000000050008000000000000000
111,409✔
156
    return UUID(u)
111,409✔
157
end
158

159
const ns_dummy_uuid = UUID("fe0723d6-3a44-4c41-8065-ee0f42c8ceab")
160

161
function dummy_uuid(project_file::String)
112,283✔
162
    @lock require_lock begin
112,283✔
163
    cache = LOADING_CACHE[]
112,283✔
164
    if cache !== nothing
112,283✔
165
        uuid = get(cache.dummy_uuid, project_file, nothing)
2,831✔
166
        uuid === nothing || return uuid
2,831✔
167
    end
168
    project_path = try
111,317✔
169
        realpath(project_file)
111,317✔
170
    catch ex
171
        ex isa IOError || rethrow()
×
172
        project_file
111,317✔
173
    end
174
    uuid = uuid5(ns_dummy_uuid, project_path)
111,317✔
175
    if cache !== nothing
111,317✔
176
        cache.dummy_uuid[project_file] = uuid
899✔
177
    end
178
    return uuid
111,317✔
179
    end
180
end
181

182
## package path slugs: turning UUID + SHA1 into a pair of 4-byte "slugs" ##
183

184
const slug_chars = String(['A':'Z'; 'a':'z'; '0':'9'])
185

186
function slug(x::UInt32, p::Int)
×
187
    y::UInt32 = x
35,453✔
188
    sprint(sizehint=p) do io
35,453✔
189
        n = length(slug_chars)
35,452✔
190
        for i = 1:p
70,904✔
191
            y, d = divrem(y, n)
159,927✔
192
            write(io, slug_chars[1+d])
159,927✔
193
        end
159,927✔
194
    end
195
end
196

197
function package_slug(uuid::UUID, p::Int=5)
×
198
    crc = _crc32c(uuid)
1,074✔
199
    return slug(crc, p)
537✔
200
end
201

202
function version_slug(uuid::UUID, sha1::SHA1, p::Int=5)
35,054✔
203
    crc = _crc32c(uuid)
52,387✔
204
    crc = _crc32c(sha1.bytes, crc)
34,860✔
205
    return slug(crc, p)
34,860✔
206
end
207

208
mutable struct CachedTOMLDict
209
    path::String
1,262✔
210
    inode::UInt64
211
    mtime::Float64
212
    size::Int64
213
    hash::UInt32
214
    d::Dict{String, Any}
215
end
216

217
function CachedTOMLDict(p::TOML.Parser, path::String)
1,262✔
218
    s = stat(path)
1,262✔
219
    content = read(path)
1,262✔
220
    crc32 = _crc32c(content)
1,262✔
221
    TOML.reinit!(p, String(content); filepath=path)
1,262✔
222
    d = TOML.parse(p)
1,262✔
223
    return CachedTOMLDict(
1,262✔
224
        path,
225
        s.inode,
226
        s.mtime,
227
        s.size,
228
        crc32,
229
        d,
230
   )
231
end
232

233
function get_updated_dict(p::TOML.Parser, f::CachedTOMLDict)
314,375✔
234
    s = stat(f.path)
314,375✔
235
    # note, this might miss very rapid in-place updates, such that mtime is
236
    # identical but that is solvable by not doing in-place updates, and not
237
    # rapidly changing these files
238
    if s.inode != f.inode || s.mtime != f.mtime || f.size != s.size
628,750✔
239
        content = read(f.path)
2✔
240
        new_hash = _crc32c(content)
2✔
241
        if new_hash != f.hash
2✔
242
            f.inode = s.inode
2✔
243
            f.mtime = s.mtime
2✔
244
            f.size = s.size
2✔
245
            f.hash = new_hash
2✔
246
            TOML.reinit!(p, String(content); filepath=f.path)
2✔
247
            return f.d = TOML.parse(p)
2✔
248
        end
249
    end
250
    return f.d
314,373✔
251
end
252

253
struct LoadingCache
254
    load_path::Vector{String}
1,609✔
255
    dummy_uuid::Dict{String, UUID}
256
    env_project_file::Dict{String, Union{Bool, String}}
257
    project_file_manifest_path::Dict{String, Union{Nothing, String}}
258
    require_parsed::Set{String}
259
    identified_where::Dict{Tuple{PkgId, String}, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}}
260
    identified::Dict{String, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}}
261
    located::Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{Union{String, Nothing}, Union{String, Nothing}}, Nothing}}
262
end
263
const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing)
264
LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict())
1,609✔
265

266

267
struct TOMLCache
268
    p::TOML.Parser
269
    d::Dict{String, CachedTOMLDict}
270
end
271
const TOML_CACHE = TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}())
272

273
parsed_toml(project_file::AbstractString) = parsed_toml(project_file, TOML_CACHE, require_lock)
318,631✔
274
function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_lock::ReentrantLock)
24✔
275
    lock(toml_lock) do
318,771✔
276
        cache = LOADING_CACHE[]
318,771✔
277
        dd = if !haskey(toml_cache.d, project_file)
318,771✔
278
            d = CachedTOMLDict(toml_cache.p, project_file)
1,262✔
279
            toml_cache.d[project_file] = d
1,262✔
280
            d.d
1,262✔
281
        else
282
            d = toml_cache.d[project_file]
317,509✔
283
            # We are in a require call and have already parsed this TOML file
284
            # assume that it is unchanged to avoid hitting disk
285
            if cache !== nothing && project_file in cache.require_parsed
317,509✔
286
                d.d
3,134✔
287
            else
288
                get_updated_dict(toml_cache.p, d)
636,280✔
289
            end
290
        end
291
        if cache !== nothing
318,771✔
292
            push!(cache.require_parsed, project_file)
5,590✔
293
        end
294
        return dd
318,771✔
295
    end
296
end
297

298
## package identification: determine unique identity of package to be loaded ##
299

300
# Used by Pkg but not used in loading itself
301
function find_package(arg)
2✔
302
    pkgenv = identify_package_env(arg)
2✔
303
    pkgenv === nothing && return nothing
2✔
304
    pkg, env = pkgenv
1✔
305
    return locate_package(pkg, env)
1✔
306
end
307

308
"""
309
    Base.identify_package_env(name::String)::Union{Tuple{PkgId, String}, Nothing}
310
    Base.identify_package_env(where::Union{Module,PkgId}, name::String)::Union{Tuple{PkgId, String} Nothing}
311

312
Same as [`Base.identify_package`](@ref) except that the path to the environment where the package is identified
313
is also returned.
314
"""
315
identify_package_env(where::Module, name::String) = identify_package_env(PkgId(where), name)
1,654✔
316
function identify_package_env(where::PkgId, name::String)
66,869✔
317
    cache = LOADING_CACHE[]
66,869✔
318
    if cache !== nothing
66,869✔
319
        pkg_env = get(cache.identified_where, (where, name), nothing)
2,459✔
320
        pkg_env === nothing || return pkg_env
2,459✔
321
    end
322
    pkg_env = nothing
24✔
323
    if where.name === name
66,809✔
324
        pkg_env = where, nothing
21,066✔
325
    elseif where.uuid === nothing
45,743✔
326
        pkg_env = identify_package_env(name) # ignore `where`
1,549✔
327
    else
328
        for env in load_path()
44,194✔
329
            pkgid = manifest_deps_get(env, where, name)
73,911✔
330
            pkgid === nothing && continue # not found--keep looking
73,911✔
331
            if pkgid.uuid !== nothing
44,194✔
332
                pkg_env = pkgid, env # found in explicit environment--use it
18,633✔
333
            end
334
            break # found in implicit environment--return "not found"
44,194✔
335
        end
29,717✔
336
    end
337
    if cache !== nothing
66,809✔
338
        cache.identified_where[(where, name)] = pkg_env
4,672✔
339
    end
340
    return pkg_env
66,809✔
341
end
342
function identify_package_env(name::String)
11,728✔
343
    cache = LOADING_CACHE[]
11,728✔
344
    if cache !== nothing
11,728✔
345
        pkg_env = get(cache.identified, name, nothing)
1,501✔
346
        pkg_env === nothing || return pkg_env
1,501✔
347
    end
348
    pkg_env = nothing
24✔
349
    for env in load_path()
11,727✔
350
        pkg = project_deps_get(env, name)
18,739✔
351
        if pkg !== nothing
14,099✔
352
            pkg_env = pkg, env # found--return it
11,472✔
353
            break
11,472✔
354
        end
355
    end
2,627✔
356
    if cache !== nothing
11,727✔
357
        cache.identified[name] = pkg_env
2,992✔
358
    end
359
    return pkg_env
11,727✔
360
end
361

362
_nothing_or_first(x) = x === nothing ? nothing : first(x)
97,443✔
363

364
"""
365
    Base.identify_package(name::String)::Union{PkgId, Nothing}
366
    Base.identify_package(where::Union{Module,PkgId}, name::String)::Union{PkgId, Nothing}
367

368
Identify the package by its name from the current environment stack, returning
369
its `PkgId`, or `nothing` if it cannot be found.
370

371
If only the `name` argument is provided, it searches each environment in the
372
stack and its named direct dependencies.
373

374
There `where` argument provides the context from where to search for the
375
package: in this case it first checks if the name matches the context itself,
376
otherwise it searches all recursive dependencies (from the resolved manifest of
377
each environment) until it locates the context `where`, and from there
378
identifies the dependency with the corresponding name.
379

380
```julia-repl
381
julia> Base.identify_package("Pkg") # Pkg is a dependency of the default environment
382
Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f]
383

384
julia> using LinearAlgebra
385

386
julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of LinearAlgebra
387
```
388
"""
389
identify_package(where::Module, name::String) = _nothing_or_first(identify_package_env(where, name))
91✔
390
identify_package(where::PkgId, name::String)  = _nothing_or_first(identify_package_env(where, name))
104,809✔
391
identify_package(name::String)                = _nothing_or_first(identify_package_env(name))
20,104✔
392

393
function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)
48,234✔
394
    cache = LOADING_CACHE[]
48,621✔
395
    if cache !== nothing
48,234✔
396
        pathenv = get(cache.located, (pkg, stopenv), nothing)
751✔
397
        pathenv === nothing || return pathenv
751✔
398
    end
399
    path = nothing
×
400
    env′ = nothing
×
401
    if pkg.uuid === nothing
48,165✔
402
        for env in load_path()
3,130✔
403
            env′ = env
×
404
            # look for the toplevel pkg `pkg.name` in this entry
405
            found = project_deps_get(env, pkg.name)
3,289✔
406
            if found !== nothing
3,289✔
407
                @assert found.name == pkg.name
3,127✔
408
                if found.uuid === nothing
3,127✔
409
                    # pkg.name is present in this directory or project file,
410
                    # return the path the entry point for the code, if it could be found
411
                    # otherwise, signal failure
412
                    path = implicit_manifest_uuid_path(env, pkg)
3,127✔
413
                    @goto done
3,127✔
414
                end
415
            end
416
            stopenv == env && @goto done
2✔
417
        end
162✔
418
    else
419
        for env in load_path()
45,035✔
420
            env′ = env
×
421
            path = manifest_uuid_path(env, pkg)
75,376✔
422
            # missing is used as a sentinel to stop looking further down in envs
423
            if path === missing
75,376✔
424
                path = nothing
×
425
                @goto done
1✔
426
            end
427
            if path !== nothing
75,375✔
428
                path = entry_path(path, pkg.name)
38,651✔
429
                @goto done
38,651✔
430
            end
431
            stopenv == env && break
208✔
432
        end
43,101✔
433
        # Allow loading of stdlibs if the name/uuid are given
434
        # e.g. if they have been explicitly added to the project/manifest
435
        mbypath = manifest_uuid_path(Sys.STDLIB, pkg)
6,383✔
436
        if mbypath isa String
6,383✔
437
            path = entry_path(mbypath, pkg.name)
2✔
438
            @goto done
×
439
        end
440
    end
441
    @label done
×
442
    if cache !== nothing
48,165✔
443
        cache.located[(pkg, stopenv)] = path, env′
613✔
444
    end
445
    return path, env′
48,165✔
446
end
447

448
"""
449
    Base.locate_package(pkg::PkgId)::Union{String, Nothing}
450

451
The path to the entry-point file for the package corresponding to the identifier
452
`pkg`, or `nothing` if not found. See also [`identify_package`](@ref).
453

454
```julia-repl
455
julia> pkg = Base.identify_package("Pkg")
456
Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f]
457

458
julia> Base.locate_package(pkg)
459
"/path/to/julia/stdlib/v$(VERSION.major).$(VERSION.minor)/Pkg/src/Pkg.jl"
460
```
461
"""
462
function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Union{Nothing,String}
94,700✔
463
    _nothing_or_first(locate_package_env(pkg, stopenv))
95,220✔
464
end
465

466
"""
467
    pathof(m::Module)
468

469
Return the path of the `m.jl` file that was used to `import` module `m`,
470
or `nothing` if `m` was not imported from a package.
471

472
Use [`dirname`](@ref) to get the directory part and [`basename`](@ref)
473
to get the file name part of the path.
474
"""
475
function pathof(m::Module)
267✔
476
    @lock require_lock begin
267✔
477
    pkgid = get(module_keys, m, nothing)
359✔
478
    pkgid === nothing && return nothing
267✔
479
    origin = get(pkgorigins, pkgid, nothing)
181✔
480
    origin === nothing && return nothing
92✔
481
    path = origin.path
89✔
482
    path === nothing && return nothing
89✔
483
    return fixup_stdlib_path(path)
89✔
484
    end
485
end
486

487
"""
488
    pkgdir(m::Module[, paths::String...])
489

490
Return the root directory of the package that imported module `m`,
491
or `nothing` if `m` was not imported from a package. Optionally further
492
path component strings can be provided to construct a path within the
493
package root.
494

495
To get the root directory of the package that imported the current module
496
the form `pkgdir(@__MODULE__)` can be used.
497

498
```julia-repl
499
julia> pkgdir(Foo)
500
"/path/to/Foo.jl"
501

502
julia> pkgdir(Foo, "src", "file.jl")
503
"/path/to/Foo.jl/src/file.jl"
504
```
505

506
!!! compat "Julia 1.7"
507
    The optional argument `paths` requires at least Julia 1.7.
508
"""
509
function pkgdir(m::Module, paths::String...)
52✔
510
    rootmodule = moduleroot(m)
52✔
511
    path = pathof(rootmodule)
52✔
512
    path === nothing && return nothing
52✔
513
    return joinpath(dirname(dirname(path)), paths...)
49✔
514
end
515

516
function get_pkgversion_from_path(path)
4✔
517
    project_file = locate_project_file(path)
4✔
518
    if project_file isa String
4✔
519
        d = parsed_toml(project_file)
4✔
520
        v = get(d, "version", nothing)
8✔
521
        if v !== nothing
4✔
522
            return VersionNumber(v::String)
4✔
523
        end
524
    end
525
    return nothing
×
526
end
527

528
"""
529
    pkgversion(m::Module)
530

531
Return the version of the package that imported module `m`,
532
or `nothing` if `m` was not imported from a package, or imported
533
from a package without a version field set.
534

535
The version is read from the package's Project.toml during package
536
load.
537

538
To get the version of the package that imported the current module
539
the form `pkgversion(@__MODULE__)` can be used.
540

541
!!! compat "Julia 1.9"
542
    This function was introduced in Julia 1.9.
543
"""
544
function pkgversion(m::Module)
5✔
545
    path = pkgdir(m)
9✔
546
    path === nothing && return nothing
5✔
547
    @lock require_lock begin
4✔
548
        v = get_pkgversion_from_path(path)
4✔
549
        pkgorigin = get(pkgorigins, PkgId(moduleroot(m)), nothing)
8✔
550
        # Cache the version
551
        if pkgorigin !== nothing && pkgorigin.version === nothing
4✔
552
            pkgorigin.version = v
4✔
553
        end
554
        return v
4✔
555
    end
556
end
557

558
## generic project & manifest API ##
559

560
const project_names = ("JuliaProject.toml", "Project.toml")
561
const manifest_names = ("JuliaManifest.toml", "Manifest.toml")
562
const preferences_names = ("JuliaLocalPreferences.toml", "LocalPreferences.toml")
563

564
function locate_project_file(env::String)
75,069✔
565
    for proj in project_names
75,069✔
566
        project_file = joinpath(env, proj)
150,138✔
567
        if isfile_casesensitive(project_file)
150,138✔
568
            return project_file
4✔
569
        end
570
    end
225,199✔
571
    return true
75,065✔
572
end
573

574
# classify the LOAD_PATH entry to be one of:
575
#  - `false`: nonexistent / nothing to see here
576
#  - `true`: `env` is an implicit environment
577
#  - `path`: the path of an explicit project file
578
function env_project_file(env::String)::Union{Bool,String}
174,226✔
579
    @lock require_lock begin
174,226✔
580
    cache = LOADING_CACHE[]
174,226✔
581
    if cache !== nothing
174,226✔
582
        project_file = get(cache.env_project_file, env, nothing)
8,506✔
583
        project_file === nothing || return project_file
8,506✔
584
    end
585
    if isdir(env)
171,513✔
586
        project_file = locate_project_file(env)
75,065✔
587
    elseif basename(env) in project_names && isfile_casesensitive(env)
192,896✔
588
        project_file = env
95,517✔
589
    else
590
        project_file = false
×
591
    end
592
    if cache !== nothing
171,513✔
593
        cache.env_project_file[env] = project_file
3,862✔
594
    end
595
    return project_file
171,513✔
596
    end
597
end
598

599
function project_deps_get(env::String, name::String)::Union{Nothing,PkgId}
48✔
600
    project_file = env_project_file(env)
17,388✔
601
    if project_file isa String
17,388✔
602
        pkg_uuid = explicit_project_deps_get(project_file, name)
5,961✔
603
        pkg_uuid === nothing || return PkgId(pkg_uuid, name)
10,601✔
604
    elseif project_file
11,427✔
605
        return implicit_project_deps_get(env, name)
10,738✔
606
    end
607
    return nothing
2,010✔
608
end
609

610
function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothing,PkgId}
73,911✔
611
    uuid = where.uuid
73,911✔
612
    @assert uuid !== nothing
73,911✔
613
    project_file = env_project_file(env)
73,911✔
614
    if project_file isa String
73,911✔
615
        # first check if `where` names the Project itself
616
        proj = project_file_name_uuid(project_file, where.name)
43,941✔
617
        if proj == where
83,687✔
618
            # if `where` matches the project, use [deps] section as manifest, and stop searching
619
            pkg_uuid = explicit_project_deps_get(project_file, name)
4,195✔
620
            return PkgId(pkg_uuid, name)
4,195✔
621
        end
622
        # look for manifest file and `where` stanza
623
        return explicit_manifest_deps_get(project_file, where, name)
39,746✔
624
    elseif project_file
29,970✔
625
        # if env names a directory, search it
626
        return implicit_manifest_deps_get(env, where, name)
29,676✔
627
    end
628
    return nothing
294✔
629
end
630

631
function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missing}
81,759✔
632
    project_file = env_project_file(env)
81,759✔
633
    if project_file isa String
81,759✔
634
        proj = project_file_name_uuid(project_file, pkg.name)
46,416✔
635
        if proj == pkg
89,607✔
636
            # if `pkg` matches the project, return the project itself
637
            return project_file_path(project_file)
3,225✔
638
        end
639
        # look for manifest file and `where` stanza
640
        return explicit_manifest_uuid_path(project_file, pkg)
43,191✔
641
    elseif project_file
35,343✔
642
        # if env names a directory, search it
643
        return implicit_manifest_uuid_path(env, pkg)
35,183✔
644
    end
645
    return nothing
160✔
646
end
647

648
# find project file's top-level UUID entry (or nothing)
649
function project_file_name_uuid(project_file::String, name::String)::PkgId
129,025✔
650
    d = parsed_toml(project_file)
129,025✔
651
    uuid′ = get(d, "uuid", nothing)::Union{String, Nothing}
166,646✔
652
    uuid = uuid′ === nothing ? dummy_uuid(project_file) : UUID(uuid′)
166,646✔
653
    name = get(d, "name", name)::String
232,365✔
654
    return PkgId(uuid, name)
129,025✔
655
end
656

657
function project_file_path(project_file::String)
×
658
    d = parsed_toml(project_file)
3,225✔
659
    joinpath(dirname(project_file), get(d, "path", "")::String)
3,693✔
660
end
661

662
# find project file's corresponding manifest file
663
function project_file_manifest_path(project_file::String)::Union{Nothing,String}
83,008✔
664
    @lock require_lock begin
83,008✔
665
    cache = LOADING_CACHE[]
83,008✔
666
    if cache !== nothing
83,008✔
667
        manifest_path = get(cache.project_file_manifest_path, project_file, missing)
1,836✔
668
        manifest_path === missing || return manifest_path
1,836✔
669
    end
670
    dir = abspath(dirname(project_file))
82,200✔
671
    d = parsed_toml(project_file)
82,200✔
672
    explicit_manifest = get(d, "manifest", nothing)::Union{String, Nothing}
82,200✔
673
    manifest_path = nothing
×
674
    if explicit_manifest !== nothing
82,200✔
675
        manifest_file = normpath(joinpath(dir, explicit_manifest))
×
676
        if isfile_casesensitive(manifest_file)
×
677
            manifest_path = manifest_file
×
678
        end
679
    end
680
    if manifest_path === nothing
82,200✔
681
        for mfst in manifest_names
82,200✔
682
            manifest_file = joinpath(dir, mfst)
164,400✔
683
            if isfile_casesensitive(manifest_file)
164,400✔
684
                manifest_path = manifest_file
×
685
                break
82,200✔
686
            end
687
        end
82,200✔
688
    end
689
    if cache !== nothing
82,200✔
690
        cache.project_file_manifest_path[project_file] = manifest_path
440✔
691
    end
692
    return manifest_path
82,200✔
693
    end
694
end
695

696
# given a directory (implicit env from LOAD_PATH) and a name,
697
# check if it is an implicit package
698
function entry_point_and_project_file_inside(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}}
×
699
    path = normpath(joinpath(dir, "src", "$name.jl"))
89,562✔
700
    isfile_casesensitive(path) || return nothing, nothing
111,946✔
701
    for proj in project_names
67,178✔
702
        project_file = normpath(joinpath(dir, proj))
134,356✔
703
        isfile_casesensitive(project_file) || continue
134,356✔
704
        return path, project_file
38,502✔
705
    end
124,530✔
706
    return path, nothing
28,676✔
707
end
708

709
# given a project directory (implicit env from LOAD_PATH) and a name,
710
# find an entry point for `name`, and see if it has an associated project file
711
function entry_point_and_project_file(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}}
78,724✔
712
    path = normpath(joinpath(dir, "$name.jl"))
78,724✔
713
    isfile_casesensitive(path) && return path, nothing
78,724✔
714
    dir = joinpath(dir, name)
78,370✔
715
    path, project_file = entry_point_and_project_file_inside(dir, name)
145,548✔
716
    path === nothing || return path, project_file
145,548✔
717
    dir = dir * ".jl"
11,192✔
718
    path, project_file = entry_point_and_project_file_inside(dir, name)
11,192✔
719
    path === nothing || return path, project_file
11,192✔
720
    return nothing, nothing
11,192✔
721
end
722

723
# given a path and a name, return the entry point
724
function entry_path(path::String, name::String)::Union{Nothing,String}
38,653✔
725
    isfile_casesensitive(path) && return normpath(path)
38,653✔
726
    path = normpath(joinpath(path, "src", "$name.jl"))
20,156✔
727
    isfile_casesensitive(path) && return path
20,156✔
728
    return nothing # source not found
1,422✔
729
end
730

731
## explicit project & manifest API ##
732

733
# find project file root or deps `name => uuid` mapping
734
# return `nothing` if `name` is not found
735
function explicit_project_deps_get(project_file::String, name::String)::Union{Nothing,UUID}
20,826✔
736
    d = parsed_toml(project_file)
20,826✔
737
    root_uuid = dummy_uuid(project_file)
20,826✔
738
    if get(d, "name", nothing)::Union{String, Nothing} === name
39,454✔
739
        uuid = get(d, "uuid", nothing)::Union{String, Nothing}
1,290✔
740
        return uuid === nothing ? root_uuid : UUID(uuid)
1,095✔
741
    end
742
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
31,650✔
743
    if deps !== nothing
19,731✔
744
        uuid = get(deps, name, nothing)::Union{String, Nothing}
21,248✔
745
        uuid === nothing || return UUID(uuid)
21,248✔
746
    end
747
    return nothing
10,402✔
748
end
749

750
function is_v1_format_manifest(raw_manifest::Dict)
83,013✔
751
    if haskey(raw_manifest, "manifest_format")
83,013✔
752
        mf = raw_manifest["manifest_format"]
1,313✔
753
        if mf isa Dict && haskey(mf, "uuid")
1,313✔
754
            # the off-chance where an old format manifest has a dep called "manifest_format"
755
            return true
×
756
        end
757
        return false
1,313✔
758
    else
759
        return true
81,700✔
760
    end
761
end
762

763
# returns a deps list for both old and new manifest formats
764
function get_deps(raw_manifest::Dict)
2✔
765
    if is_v1_format_manifest(raw_manifest)
83,010✔
766
        return raw_manifest
81,699✔
767
    else
768
        # if the manifest has no deps, there won't be a `deps` field
769
        return get(Dict{String, Any}, raw_manifest, "deps")::Dict{String, Any}
1,311✔
770
    end
771
end
772

773
# find `where` stanza and return the PkgId for `name`
774
# return `nothing` if it did not find `where` (indicating caller should continue searching)
775
function explicit_manifest_deps_get(project_file::String, where::PkgId, name::String)::Union{Nothing,PkgId}
39,746✔
776
    manifest_file = project_file_manifest_path(project_file)
39,746✔
777
    manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH
39,746✔
778
    d = get_deps(parsed_toml(manifest_file))
40,497✔
779
    found_where = false
×
780
    found_name = false
×
781
    for (dep_name, entries) in d
79,492✔
782
        entries::Vector{Any}
121,263✔
783
        for entry in entries
121,263✔
784
            entry = entry::Dict{String, Any}
198,950✔
785
            uuid = get(entry, "uuid", nothing)::Union{String, Nothing}
397,900✔
786
            uuid === nothing && continue
198,950✔
787
            if UUID(uuid) === where.uuid
198,950✔
788
                found_where = true
×
789
                # deps is either a list of names (deps = ["DepA", "DepB"]) or
790
                # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."}
791
                deps = get(entry, "deps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing}
46,502✔
792
                if deps isa Vector{String}
29,283✔
793
                    found_name = name in deps
4,104✔
794
                    break
2,489✔
795
                elseif deps isa Dict{String, Any}
26,794✔
796
                    deps = deps::Dict{String, Any}
14,730✔
797
                    for (dep, uuid) in deps
29,460✔
798
                        uuid::String
18,336✔
799
                        if dep === name
18,336✔
800
                            return PkgId(UUID(uuid), name)
10,862✔
801
                        end
802
                    end
7,474✔
803
                end
804
            else # Check for extensions
805
                extensions = get(entry, "extensions", nothing)
170,417✔
806
                if extensions !== nothing
169,667✔
807
                    if haskey(extensions, where.name) && where.uuid == uuid5(UUID(uuid), where.name)
796✔
808
                        found_where = true
×
809
                        if name == dep_name
46✔
810
                            return PkgId(UUID(uuid), name)
22✔
811
                        end
812
                        exts = extensions[where.name]::Union{String, Vector{String}}
24✔
813
                        if (exts isa String && name == exts) || (exts isa Vector{String} && name in exts)
40✔
814
                            weakdeps = get(entry, "weakdeps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing}
48✔
815
                            if weakdeps !== nothing
24✔
816
                                if weakdeps isa Vector{String}
24✔
817
                                    found_name = name in weakdeps
30✔
818
                                    break
24✔
819
                                elseif weakdeps isa Dict{String, Any}
×
820
                                    weakdeps = weakdeps::Dict{String, Any}
×
821
                                    for (dep, uuid) in weakdeps
×
822
                                        uuid::String
×
823
                                        if dep === name
×
824
                                            return PkgId(UUID(uuid), name)
×
825
                                        end
826
                                    end
×
827
                                end
828
                            end
829
                        end
830
                        # `name` is not an ext, do standard lookup as if this was the parent
831
                        return identify_package(PkgId(UUID(uuid), dep_name), name)
×
832
                    end
833
                end
834
            end
835
        end
185,553✔
836
    end
191,896✔
837
    found_where || return nothing
39,279✔
838
    found_name || return PkgId(name)
34,925✔
839
    # Only reach here if deps was not a dict which mean we have a unique name for the dep
840
    name_deps = get(d, name, nothing)::Union{Nothing, Vector{Any}}
3,930✔
841
    if name_deps === nothing || length(name_deps) != 1
3,930✔
842
        error("expected a single entry for $(repr(name)) in $(repr(project_file))")
×
843
    end
844
    entry = first(name_deps::Vector{Any})::Dict{String, Any}
1,965✔
845
    uuid = get(entry, "uuid", nothing)::Union{String, Nothing}
3,930✔
846
    uuid === nothing && return nothing
1,965✔
847
    return PkgId(UUID(uuid), name)
1,965✔
848
end
849

850
# find `uuid` stanza, return the corresponding path
851
function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{Nothing,String,Missing}
43,191✔
852
    manifest_file = project_file_manifest_path(project_file)
43,191✔
853
    manifest_file === nothing && return nothing # no manifest, skip env
43,191✔
854

855
    d = get_deps(parsed_toml(manifest_file))
43,704✔
856
    entries = get(d, pkg.name, nothing)::Union{Nothing, Vector{Any}}
84,296✔
857
    if entries !== nothing
43,191✔
858
        for entry in entries
41,105✔
859
            entry = entry::Dict{String, Any}
62,251✔
860
            uuid = get(entry, "uuid", nothing)::Union{Nothing, String}
124,502✔
861
            uuid === nothing && continue
62,251✔
862
            if UUID(uuid) === pkg.uuid
62,251✔
863
                return explicit_manifest_entry_path(manifest_file, pkg, entry)
34,019✔
864
            end
865
        end
35,318✔
866
    end
867
    # Extensions
868
    for (name, entries) in d
18,343✔
869
        entries = entries::Vector{Any}
29,714✔
870
        for entry in entries
29,714✔
871
            uuid = get(entry, "uuid", nothing)::Union{Nothing, String}
40,578✔
872
            extensions = get(entry, "extensions", nothing)::Union{Nothing, Dict{String, Any}}
40,578✔
873
            if extensions !== nothing && haskey(extensions, pkg.name) && uuid !== nothing && uuid5(UUID(uuid), pkg.name) == pkg.uuid
40,591✔
874
                parent_path = locate_package(PkgId(UUID(uuid), name))
13✔
875
                if parent_path === nothing
13✔
876
                    error("failed to find source of parent package: \"$name\"")
×
877
                end
878
                p = normpath(dirname(parent_path), "..")
13✔
879
                extfiledir = joinpath(p, "ext", pkg.name, pkg.name * ".jl")
13✔
880
                isfile(extfiledir) && return extfiledir
13✔
881
                return joinpath(p, "ext", pkg.name * ".jl")
8✔
882
            end
883
        end
70,266✔
884
    end
50,244✔
885
    return nothing
9,159✔
886
end
887

888
function explicit_manifest_entry_path(manifest_file::String, pkg::PkgId, entry::Dict{String,Any})
34,019✔
889
    path = get(entry, "path", nothing)::Union{Nothing, String}
42,538✔
890
    if path !== nothing
34,019✔
891
        path = normpath(abspath(dirname(manifest_file), path))
8,519✔
892
        return path
8,519✔
893
    end
894
    hash = get(entry, "git-tree-sha1", nothing)::Union{Nothing, String}
42,820✔
895
    hash === nothing && return nothing
25,500✔
896
    hash = SHA1(hash)
17,320✔
897
    # Keep the 4 since it used to be the default
898
    uuid = pkg.uuid::UUID # checked within `explicit_manifest_uuid_path`
17,320✔
899
    for slug in (version_slug(uuid, hash), version_slug(uuid, hash, 4))
17,320✔
900
        for depot in DEPOT_PATH
17,321✔
901
            path = joinpath(depot, "packages", pkg.name, slug)
32,126✔
902
            ispath(path) && return abspath(path)
32,126✔
903
        end
14,809✔
904
    end
3✔
905
    # no depot contains the package, return missing to stop looking
906
    return missing
1✔
907
end
908

909
## implicit project & manifest API ##
910

911
# look for an entry point for `name` from a top-level package (no environment)
912
# otherwise return `nothing` to indicate the caller should keep searching
913
function implicit_project_deps_get(dir::String, name::String)::Union{Nothing,PkgId}
24✔
914
    path, project_file = entry_point_and_project_file(dir, name)
20,697✔
915
    if project_file === nothing
10,738✔
916
        path === nothing && return nothing
7,003✔
917
        return PkgId(name)
6,224✔
918
    end
919
    proj = project_file_name_uuid(project_file, name)
3,735✔
920
    proj.name == name || return nothing
3,735✔
921
    return proj
3,735✔
922
end
923

924
# look for an entry-point for `name`, check that UUID matches
925
# if there's a project file, look up `name` in its deps and return that
926
# otherwise return `nothing` to indicate the caller should keep searching
927
function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Union{Nothing,PkgId}
29,676✔
928
    @assert where.uuid !== nothing
29,676✔
929
    project_file = entry_point_and_project_file(dir, where.name)[2]
57,574✔
930
    project_file === nothing && return nothing # a project file is mandatory for a package with a uuid
29,676✔
931
    proj = project_file_name_uuid(project_file, where.name)
17,898✔
932
    proj == where || return nothing # verify that this is the correct project file
25,126✔
933
    # this is the correct project, so stop searching here
934
    pkg_uuid = explicit_project_deps_get(project_file, name)
10,670✔
935
    return PkgId(pkg_uuid, name)
10,670✔
936
end
937

938
# look for an entry-point for `pkg` and return its path if UUID matches
939
function implicit_manifest_uuid_path(dir::String, pkg::PkgId)::Union{Nothing,String}
×
940
    path, project_file = entry_point_and_project_file(dir, pkg.name)
67,985✔
941
    if project_file === nothing
38,310✔
942
        pkg.uuid === nothing || return nothing
39,755✔
943
        return path
3,127✔
944
    end
945
    proj = project_file_name_uuid(project_file, pkg.name)
16,869✔
946
    proj == pkg || return nothing
24,161✔
947
    return path
9,577✔
948
end
949

950
## other code loading functionality ##
951

952
function find_source_file(path::AbstractString)
35✔
953
    (isabspath(path) || isfile(path)) && return path
35✔
954
    base_path = joinpath(Sys.BINDIR, DATAROOTDIR, "julia", "base", path)
16✔
955
    return isfile(base_path) ? normpath(base_path) : nothing
16✔
956
end
957

958
cache_file_entry(pkg::PkgId) = joinpath(
764✔
959
    "compiled",
960
    "v$(VERSION.major).$(VERSION.minor)",
961
    pkg.uuid === nothing ? ""       : pkg.name),
962
    pkg.uuid === nothing ? pkg.name : package_slug(pkg.uuid)
963

964
function find_all_in_cache_path(pkg::PkgId)
478✔
965
    paths = String[]
478✔
966
    entrypath, entryfile = cache_file_entry(pkg)
478✔
967
    for path in joinpath.(DEPOT_PATH, entrypath)
478✔
968
        isdir(path) || continue
1,932✔
969
        for file in readdir(path, sort = false) # no sort given we sort later
912✔
970
            if !((pkg.uuid === nothing && file == entryfile * ".ji") ||
11,409✔
971
                 (pkg.uuid !== nothing && startswith(file, entryfile * "_") &&
972
                  endswith(file, ".ji")))
973
                 continue
4,754✔
974
            end
975
            filepath = joinpath(path, file)
979✔
976
            isfile_casesensitive(filepath) && push!(paths, filepath)
979✔
977
        end
6,625✔
978
    end
2,410✔
979
    if length(paths) > 1
478✔
980
        # allocating the sort vector is less expensive than using sort!(.. by=mtime), which would
981
        # call the relatively slow mtime multiple times per path
982
        p = sortperm(mtime.(paths), rev = true)
582✔
983
        return paths[p]
291✔
984
    else
985
        return paths
187✔
986
    end
987
end
988

989
ocachefile_from_cachefile(cachefile) = string(chopsuffix(cachefile, ".ji"), ".", Base.Libc.dlext)
1✔
990
cachefile_from_ocachefile(cachefile) = string(chopsuffix(cachefile, ".$(Base.Libc.dlext)"), ".ji")
×
991

992

993
# use an Int counter so that nested @time_imports calls all remain open
994
const TIMING_IMPORTS = Threads.Atomic{Int}(0)
995

996
# these return either the array of modules loaded from the path / content given
997
# or an Exception that describes why it couldn't be loaded
998
# and it reconnects the Base.Docs.META
999
function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any})
467✔
1000
    assert_havelock(require_lock)
934✔
1001
    timing_imports = TIMING_IMPORTS[] > 0
467✔
1002
    try
467✔
1003
    if timing_imports
467✔
1004
        t_before = time_ns()
1✔
1005
        cumulative_compile_timing(true)
1✔
1006
        t_comp_before = cumulative_compile_time_ns()
1✔
1007
    end
1008

1009
    if ocachepath !== nothing
467✔
1010
        @debug "Loading object cache file $ocachepath for $pkg"
×
1011
        sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint), ocachepath, depmods, false)
×
1012
    else
1013
        @debug "Loading cache file $path for $pkg"
467✔
1014
        sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint), path, depmods, false)
467✔
1015
    end
1016
    if isa(sv, Exception)
467✔
1017
        return sv
×
1018
    end
1019

1020
    restored = register_restored_modules(sv, pkg, path)
467✔
1021

1022
    for M in restored
467✔
1023
        M = M::Module
551✔
1024
        if parentmodule(M) === M && PkgId(M) == pkg
551✔
1025
            if timing_imports
466✔
1026
                elapsed = round((time_ns() - t_before) / 1e6, digits = 1)
1✔
1027
                comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before
1✔
1028
                print(lpad(elapsed, 9), " ms  ")
1✔
1029
                parentid = get(EXT_PRIMED, pkg, nothing)
1✔
1030
                if parentid !== nothing
1✔
1031
                    print(parentid.name, " → ")
×
1032
                end
1033
                print(pkg.name)
1✔
1034
                if comp_time > 0
1✔
1035
                    printstyled(" ", Ryu.writefixed(Float64(100 * comp_time / (elapsed * 1e6)), 2), "% compilation time", color = Base.info_color())
×
1036
                end
1037
                if recomp_time > 0
1✔
1038
                    perc = Float64(100 * recomp_time / comp_time)
×
1039
                    printstyled(" (", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% recompilation)", color = Base.warn_color())
×
1040
                end
1041
                println()
1✔
1042
            end
1043
            return M
466✔
1044
        end
1045
    end
86✔
1046
    return ErrorException("Required dependency $pkg failed to load from a cache file.")
1✔
1047

1048
    finally
1049
        timing_imports && cumulative_compile_timing(false)
467✔
1050
    end
1051
end
1052

1053
function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
467✔
1054
    # This function is also used by PkgCacheInspector.jl
1055
    restored = sv[1]::Vector{Any}
467✔
1056
    for M in restored
467✔
1057
        M = M::Module
551✔
1058
        if isdefined(M, Base.Docs.META) && getfield(M, Base.Docs.META) !== nothing
551✔
1059
            push!(Base.Docs.modules, M)
357✔
1060
        end
1061
        if parentmodule(M) === M
551✔
1062
            register_root_module(M)
467✔
1063
        end
1064
    end
1,018✔
1065

1066
    # Register this cache path now - If Requires.jl is loaded, Revise may end
1067
    # up looking at the cache path during the init callback.
1068
    get!(PkgOrigin, pkgorigins, pkg).cachepath = path
467✔
1069

1070
    inits = sv[2]::Vector{Any}
467✔
1071
    if !isempty(inits)
467✔
1072
        unlock(require_lock) # temporarily _unlock_ during these callbacks
336✔
1073
        try
174✔
1074
            ccall(:jl_init_restored_modules, Cvoid, (Any,), inits)
174✔
1075
        finally
1076
            lock(require_lock)
174✔
1077
        end
1078
    end
1079
    return restored
467✔
1080
end
1081

1082
function run_package_callbacks(modkey::PkgId)
476✔
1083
    run_extension_callbacks(modkey)
476✔
1084
    assert_havelock(require_lock)
952✔
1085
    unlock(require_lock)
949✔
1086
    try
476✔
1087
        for callback in package_callbacks
780✔
1088
            invokelatest(callback, modkey)
198✔
1089
        end
370✔
1090
    catch
1091
        # Try to continue loading if a callback errors
1092
        errs = current_exceptions()
×
1093
        @error "Error during package callback" exception=errs
476✔
1094
    finally
1095
        lock(require_lock)
477✔
1096
    end
1097
    nothing
476✔
1098
end
1099

1100

1101
##############
1102
# Extensions #
1103
##############
1104

1105
mutable struct ExtensionId
1106
    const id::PkgId
13✔
1107
    const parentid::PkgId # just need the name, for printing
1108
    ntriggers::Int # how many more packages must be defined until this is loaded
1109
end
1110

1111
const EXT_PRIMED = Dict{PkgId, PkgId}() # Extension -> Parent
1112
const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() # Trigger -> Extensions that can be triggered by it
1113
const EXT_DORMITORY_FAILED = ExtensionId[]
1114

1115
function insert_extension_triggers(pkg::PkgId)
1✔
1116
    pkg.uuid === nothing && return
475✔
1117
    path_env_loc = locate_package_env(pkg)
387✔
1118
    path_env_loc === nothing && return
×
1119
    path, env_loc = path_env_loc
387✔
1120
    if path === nothing || env_loc === nothing
774✔
1121
        return
×
1122
    end
1123
    insert_extension_triggers(env_loc, pkg)
387✔
1124
end
1125

1126
function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing}
388✔
1127
    project_file = env_project_file(env)
388✔
1128
    if project_file isa String
388✔
1129
        manifest_file = project_file_manifest_path(project_file)
71✔
1130
        manifest_file === nothing && return
71✔
1131
        d = get_deps(parsed_toml(manifest_file))
117✔
1132
        for (dep_name, entries) in d
142✔
1133
            entries::Vector{Any}
206✔
1134
            for entry in entries
206✔
1135
                entry = entry::Dict{String, Any}
215✔
1136
                uuid = get(entry, "uuid", nothing)::Union{String, Nothing}
430✔
1137
                uuid === nothing && continue
215✔
1138
                if UUID(uuid) == pkg.uuid
430✔
1139
                    weakdeps = get(entry, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}}
54✔
1140
                    extensions = get(entry, "extensions", nothing)::Union{Nothing, Dict{String, Any}}
54✔
1141
                    extensions === nothing && return
46✔
1142
                    weakdeps === nothing && return
8✔
1143
                    if weakdeps isa Dict{String, Any}
8✔
1144
                        return _insert_extension_triggers(pkg, extensions, weakdeps)
×
1145
                    end
1146

1147
                    d_weakdeps = Dict{String, String}()
8✔
1148
                    for (dep_name, entries) in d
16✔
1149
                        dep_name in weakdeps || continue
68✔
1150
                        entries::Vector{Any}
15✔
1151
                        if length(entries) != 1
15✔
1152
                            error("expected a single entry for $(repr(dep_name)) in $(repr(project_file))")
×
1153
                        end
1154
                        entry = first(entries)::Dict{String, Any}
15✔
1155
                        uuid = entry["uuid"]::String
15✔
1156
                        d_weakdeps[dep_name] = uuid
15✔
1157
                    end
54✔
1158
                    @assert length(d_weakdeps) == length(weakdeps)
8✔
1159
                    return _insert_extension_triggers(pkg, extensions, d_weakdeps)
8✔
1160
                end
1161
            end
169✔
1162
        end
295✔
1163
    end
1164
    return nothing
342✔
1165
end
1166

1167
function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any})
8✔
1168
    for (ext::String, triggers::Union{String, Vector{String}}) in extensions
8✔
1169
        triggers isa String && (triggers = [triggers])
15✔
1170
        id = PkgId(uuid5(parent.uuid, ext), ext)
15✔
1171
        if id in keys(EXT_PRIMED) || haskey(Base.loaded_modules, id)
30✔
1172
            continue  # extension is already primed or loaded, don't add it again
×
1173
        end
1174
        EXT_PRIMED[id] = parent
13✔
1175
        gid = ExtensionId(id, parent, 1 + length(triggers))
13✔
1176
        trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent)
13✔
1177
        push!(trigger1, gid)
13✔
1178
        for trigger in triggers
13✔
1179
            # TODO: Better error message if this lookup fails?
1180
            uuid_trigger = UUID(weakdeps[trigger]::String)
19✔
1181
            trigger_id = PkgId(uuid_trigger, trigger)
19✔
1182
            if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id)
20✔
1183
                trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id)
18✔
1184
                push!(trigger1, gid)
18✔
1185
            else
1186
                gid.ntriggers -= 1
1✔
1187
            end
1188
        end
19✔
1189
    end
15✔
1190
end
1191

1192
function run_extension_callbacks(extid::ExtensionId)
11✔
1193
    assert_havelock(require_lock)
22✔
1194
    succeeded = try
11✔
1195
        _require_prelocked(extid.id)
11✔
1196
        @debug "Extension $(extid.id.name) of $(extid.parentid.name) loaded"
11✔
1197
        true
11✔
1198
    catch
1199
        # Try to continue loading if loading an extension errors
1200
        errs = current_exceptions()
×
1201
        @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \
×
1202
                use `Base.retry_load_extensions()` to retry." exception=errs
1203
        false
11✔
1204
    end
1205
    return succeeded
11✔
1206
end
1207

1208
function run_extension_callbacks(pkgid::PkgId)
476✔
1209
    assert_havelock(require_lock)
952✔
1210
    # take ownership of extids that depend on this pkgid
1211
    extids = pop!(EXT_DORMITORY, pkgid, nothing)
933✔
1212
    extids === nothing && return
476✔
1213
    for extid in extids
19✔
1214
        if extid.ntriggers > 0
29✔
1215
            # It is possible that pkgid was loaded in an environment
1216
            # below the one of the parent. This will cause a load failure when the
1217
            # pkg ext tries to load the triggers. Therefore, check this first
1218
            # before loading the pkg ext.
1219
            pkgenv = identify_package_env(extid.id, pkgid.name)
29✔
1220
            ext_not_allowed_load = false
×
1221
            if pkgenv === nothing
29✔
1222
                ext_not_allowed_load = true
×
1223
            else
1224
                pkg, env = pkgenv
29✔
1225
                path = locate_package(pkg, env)
58✔
1226
                if path === nothing
29✔
1227
                    ext_not_allowed_load = true
×
1228
                end
1229
            end
1230
            if ext_not_allowed_load
29✔
1231
                @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \
×
1232
                        since $(pkgid.name) loaded in environment lower in load path"
1233
                # indicate extid is expected to fail
1234
                extid.ntriggers *= -1
×
1235
            else
1236
                # indicate pkgid is loaded
1237
                extid.ntriggers -= 1
29✔
1238
            end
1239
        end
1240
        if extid.ntriggers < 0
29✔
1241
            # indicate pkgid is loaded
1242
            extid.ntriggers += 1
×
1243
            succeeded = false
×
1244
        else
1245
            succeeded = true
×
1246
        end
1247
        if extid.ntriggers == 0
29✔
1248
            # actually load extid, now that all dependencies are met,
1249
            # and record the result
1250
            succeeded = succeeded && run_extension_callbacks(extid)
11✔
1251
            succeeded || push!(EXT_DORMITORY_FAILED, extid)
11✔
1252
        end
1253
    end
48✔
1254
    return
19✔
1255
end
1256

1257
"""
1258
    retry_load_extensions()
1259

1260
Loads all the (not yet loaded) extensions that have their extension-dependencies loaded.
1261
This is used in cases where the automatic loading of an extension failed
1262
due to some problem with the extension. Instead of restarting the Julia session,
1263
the extension can be fixed, and this function run.
1264
"""
1265
function retry_load_extensions()
×
1266
    @lock require_lock begin
×
1267
    # this copy is desired since run_extension_callbacks will release this lock
1268
    # so this can still mutate the list to drop successful ones
1269
    failed = copy(EXT_DORMITORY_FAILED)
×
1270
    empty!(EXT_DORMITORY_FAILED)
×
1271
    filter!(failed) do extid
×
1272
        return !run_extension_callbacks(extid)
×
1273
    end
1274
    prepend!(EXT_DORMITORY_FAILED, failed)
×
1275
    end
1276
    return
×
1277
end
1278

1279
"""
1280
    get_extension(parent::Module, extension::Symbol)
1281

1282
Return the module for `extension` of `parent` or return `nothing` if the extension is not loaded.
1283
"""
1284
get_extension(parent::Module, ext::Symbol) = get_extension(PkgId(parent), ext)
16✔
1285
function get_extension(parentid::PkgId, ext::Symbol)
17✔
1286
    parentid.uuid === nothing && return nothing
17✔
1287
    extid = PkgId(uuid5(parentid.uuid, string(ext)), string(ext))
17✔
1288
    return get(loaded_modules, extid, nothing)
17✔
1289
end
1290

1291
# End extensions
1292

1293
# loads a precompile cache file, after checking stale_cachefile tests
1294
function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt128)
2,782✔
1295
    assert_havelock(require_lock)
5,564✔
1296
    loaded = nothing
×
1297
    if root_module_exists(modkey)
2,782✔
1298
        loaded = root_module(modkey)
2,758✔
1299
    else
1300
        loaded = start_loading(modkey)
24✔
1301
        if loaded === nothing
24✔
1302
            try
24✔
1303
                modpath = locate_package(modkey)
24✔
1304
                modpath === nothing && return nothing
24✔
1305
                set_pkgorigin_version_path(modkey, String(modpath))
24✔
1306
                loaded = _require_search_from_serialized(modkey, String(modpath), build_id)
24✔
1307
            finally
1308
                end_loading(modkey, loaded)
24✔
1309
            end
1310
            if loaded isa Module
24✔
1311
                insert_extension_triggers(modkey)
42✔
1312
                run_package_callbacks(modkey)
24✔
1313
            end
1314
        end
1315
    end
1316
    if !(loaded isa Module) || PkgId(loaded) != modkey
5,564✔
1317
        return ErrorException("Required dependency $modkey failed to load from a cache file.")
×
1318
    end
1319
    return loaded
2,782✔
1320
end
1321

1322
# loads a precompile cache file, ignoring stale_cachefile tests
1323
# assuming all depmods are already loaded and everything is valid
1324
function _tryrequire_from_serialized(modkey::PkgId, path::String, ocachepath::Union{Nothing, String}, sourcepath::String, depmods::Vector{Any})
16✔
1325
    assert_havelock(require_lock)
32✔
1326
    loaded = nothing
×
1327
    if root_module_exists(modkey)
16✔
1328
        loaded = root_module(modkey)
2✔
1329
    else
1330
        loaded = start_loading(modkey)
14✔
1331
        if loaded === nothing
14✔
1332
            try
14✔
1333
                for i in 1:length(depmods)
28✔
1334
                    dep = depmods[i]
536✔
1335
                    dep isa Module && continue
536✔
1336
                    _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128}
3✔
1337
                    @assert root_module_exists(depkey)
3✔
1338
                    dep = root_module(depkey)
3✔
1339
                    depmods[i] = dep
3✔
1340
                end
1,058✔
1341
                set_pkgorigin_version_path(modkey, sourcepath)
14✔
1342
                loaded = _include_from_serialized(modkey, path, ocachepath, depmods)
14✔
1343
            finally
1344
                end_loading(modkey, loaded)
14✔
1345
            end
1346
            if loaded isa Module
14✔
1347
                insert_extension_triggers(modkey)
22✔
1348
                run_package_callbacks(modkey)
14✔
1349
            end
1350
        end
1351
    end
1352
    if !(loaded isa Module) || PkgId(loaded) != modkey
32✔
1353
        return ErrorException("Required dependency $modkey failed to load from a cache file.")
×
1354
    end
1355
    return loaded
16✔
1356
end
1357

1358
# loads a precompile cache file, ignoring stale_cachefile tests
1359
# load the best available (non-stale) version of all dependent modules first
1360
function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String})
71✔
1361
    assert_havelock(require_lock)
142✔
1362
    local depmodnames
×
1363
    io = open(path, "r")
71✔
1364
    try
71✔
1365
        iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.")
71✔
1366
        _, _, depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io)
71✔
1367
        pkgimage = !isempty(clone_targets)
71✔
1368
        if pkgimage
71✔
1369
            ocachepath !== nothing || return ArgumentError("Expected ocachepath to be provided")
×
1370
            isfile(ocachepath) || return ArgumentError("Ocachepath $ocachepath is not a file.")
×
1371
            ocachepath == ocachefile_from_cachefile(path) || return ArgumentError("$ocachepath is not the expected ocachefile")
×
1372
            # TODO: Check for valid clone_targets?
1373
            isvalid_pkgimage_crc(io, ocachepath) || return ArgumentError("Invalid checksum in cache file $ocachepath.")
×
1374
        else
1375
            @assert ocachepath === nothing
×
1376
        end
1377
        isvalid_file_crc(io) || return ArgumentError("Invalid checksum in cache file $path.")
71✔
1378
    finally
1379
        close(io)
71✔
1380
    end
1381
    ndeps = length(depmodnames)
71✔
1382
    depmods = Vector{Any}(undef, ndeps)
71✔
1383
    for i in 1:ndeps
142✔
1384
        modkey, build_id = depmodnames[i]
2,744✔
1385
        dep = _tryrequire_from_serialized(modkey, build_id)
2,744✔
1386
        if !isa(dep, Module)
2,744✔
1387
            return dep
×
1388
        end
1389
        depmods[i] = dep
2,744✔
1390
    end
5,417✔
1391
    # then load the file
1392
    return _include_from_serialized(pkg, path, ocachepath, depmods)
71✔
1393
end
1394

1395
# returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it
1396
# returns the set of modules restored if the cache load succeeded
1397
@constprop :none function _require_search_from_serialized(pkg::PkgId, sourcepath::String, build_id::UInt128)
456✔
1398
    assert_havelock(require_lock)
912✔
1399
    paths = find_all_in_cache_path(pkg)
456✔
1400
    for path_to_try in paths::Vector{String}
510✔
1401
        staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try)
419✔
1402
        if staledeps === true
419✔
1403
            continue
37✔
1404
        end
1405
        staledeps, ocachefile = staledeps::Tuple{Vector{Any}, Union{Nothing, String}}
382✔
1406
        # finish checking staledeps module graph
1407
        for i in 1:length(staledeps)
764✔
1408
            dep = staledeps[i]
14,582✔
1409
            dep isa Module && continue
14,582✔
1410
            modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128}
16✔
1411
            modpaths = find_all_in_cache_path(modkey)
16✔
1412
            for modpath_to_try in modpaths
16✔
1413
                modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try)
16✔
1414
                if modstaledeps === true
16✔
1415
                    continue
×
1416
                end
1417
                modstaledeps, modocachepath = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}}
16✔
1418
                staledeps[i] = (modpath, modkey, modpath_to_try, modstaledeps, modocachepath)
16✔
1419
                @goto check_next_dep
16✔
1420
            end
×
1421
            @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache."
×
1422
            @goto check_next_path
×
1423
            @label check_next_dep
×
1424
        end
28,782✔
1425
        try
382✔
1426
            touch(path_to_try) # update timestamp of precompilation file
382✔
1427
        catch ex # file might be read-only and then we fail to update timestamp, which is fine
1428
            ex isa IOError || rethrow()
×
1429
        end
1430
        # finish loading module graph into staledeps
1431
        for i in 1:length(staledeps)
764✔
1432
            dep = staledeps[i]
14,582✔
1433
            dep isa Module && continue
14,582✔
1434
            modpath, modkey, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, String, Vector{Any}, Union{Nothing, String}}
16✔
1435
            dep = _tryrequire_from_serialized(modkey, modcachepath, modocachepath, modpath, modstaledeps)
16✔
1436
            if !isa(dep, Module)
16✔
1437
                @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep
×
1438
                @goto check_next_path
×
1439
            end
1440
            staledeps[i] = dep
16✔
1441
        end
28,782✔
1442
        restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps)
382✔
1443
        isa(restored, Module) && return restored
382✔
1444
        @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored
×
1445
        continue
×
1446
        @label check_next_path
×
1447
    end
57✔
1448
    return nothing
74✔
1449
end
1450

1451
# to synchronize multiple tasks trying to import/using something
1452
const package_locks = Dict{PkgId,Pair{Task,Threads.Condition}}()
1453

1454
debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but more complete algorithm that can handle simultaneous tasks.
1455
                               # This only triggers if you have multiple tasks trying to load the same package at the same time,
1456
                               # so it is unlikely to make a difference normally.
1457
function start_loading(modkey::PkgId)
487✔
1458
    # handle recursive calls to require
1459
    assert_havelock(require_lock)
974✔
1460
    loading = get(package_locks, modkey, nothing)
491✔
1461
    if loading !== nothing
487✔
1462
        # load already in progress for this module on the task
1463
        task, cond = loading
4✔
1464
        deps = String[modkey.name]
4✔
1465
        pkgid = modkey
×
1466
        assert_havelock(cond.lock)
8✔
1467
        if debug_loading_deadlocks && current_task() !== task
4✔
1468
            waiters = Dict{Task,Pair{Task,PkgId}}() # invert to track waiting tasks => loading tasks
3✔
1469
            for each in package_locks
6✔
1470
                cond2 = each[2][2]
11✔
1471
                assert_havelock(cond2.lock)
22✔
1472
                for waiting in cond2.waitq
14✔
1473
                    push!(waiters, waiting => (each[2][1] => each[1]))
3✔
1474
                end
3✔
1475
            end
19✔
1476
            while true
5✔
1477
                running = get(waiters, task, nothing)
8✔
1478
                running === nothing && break
5✔
1479
                task, pkgid = running
3✔
1480
                push!(deps, pkgid.name)
3✔
1481
                task === current_task() && break
3✔
1482
            end
2✔
1483
        end
1484
        if current_task() === task
4✔
1485
            others = String[modkey.name] # repeat this to emphasize the cycle here
2✔
1486
            for each in package_locks # list the rest of the packages being loaded too
4✔
1487
                if each[2][1] === task
8✔
1488
                    other = each[1].name
4✔
1489
                    other == modkey.name || other == pkgid.name || push!(others, other)
7✔
1490
                end
1491
            end
14✔
1492
            msg = sprint(deps, others) do io, deps, others
2✔
1493
                print(io, "deadlock detected in loading ")
2✔
1494
                join(io, deps, " -> ")
2✔
1495
                print(io, " -> ")
2✔
1496
                join(io, others, " && ")
2✔
1497
            end
1498
            throw(ConcurrencyViolationError(msg))
2✔
1499
        end
1500
        return wait(cond)
2✔
1501
    end
1502
    package_locks[modkey] = current_task() => Threads.Condition(require_lock)
483✔
1503
    return
483✔
1504
end
1505

1506
function end_loading(modkey::PkgId, @nospecialize loaded)
4✔
1507
    loading = pop!(package_locks, modkey)
483✔
1508
    notify(loading[2], loaded, all=true)
483✔
1509
    nothing
483✔
1510
end
1511

1512
# to notify downstream consumers that a module was successfully loaded
1513
# Callbacks take the form (mod::Base.PkgId) -> nothing.
1514
# WARNING: This is an experimental feature and might change later, without deprecation.
1515
const package_callbacks = Any[]
1516
# to notify downstream consumers that a file has been included into a particular module
1517
# Callbacks take the form (mod::Module, filename::String) -> nothing
1518
# WARNING: This is an experimental feature and might change later, without deprecation.
1519
const include_callbacks = Any[]
1520

1521
# used to optionally track dependencies when requiring a module:
1522
const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
1523
const _require_dependencies = Any[] # a list of (mod, path, mtime) tuples that are the file dependencies of the module currently being precompiled
1524
const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies
1525
function _include_dependency(mod::Module, _path::AbstractString)
791✔
1526
    prev = source_path(nothing)
1,114✔
1527
    if prev === nothing
791✔
1528
        path = abspath(_path)
323✔
1529
    else
1530
        path = normpath(joinpath(dirname(prev), _path))
468✔
1531
    end
1532
    if _track_dependencies[]
791✔
1533
        @lock require_lock begin
360✔
1534
        push!(_require_dependencies, (mod, path, mtime(path)))
180✔
1535
        end
1536
    end
1537
    return path, prev
791✔
1538
end
1539

1540
"""
1541
    include_dependency(path::AbstractString)
1542

1543
In a module, declare that the file, directory, or symbolic link specified by `path`
1544
(relative or absolute) is a dependency for precompilation; that is, the module will need
1545
to be recompiled if the modification time of `path` changes.
1546

1547
This is only needed if your module depends on a path that is not used via [`include`](@ref). It has
1548
no effect outside of compilation.
1549
"""
1550
function include_dependency(path::AbstractString)
23✔
1551
    _include_dependency(Main, path)
23✔
1552
    return nothing
23✔
1553
end
1554

1555
# we throw PrecompilableError when a module doesn't want to be precompiled
1556
struct PrecompilableError <: Exception end
1557
function show(io::IO, ex::PrecompilableError)
×
1558
    print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.")
×
1559
end
1560
precompilableerror(ex::PrecompilableError) = true
×
1561
precompilableerror(ex::WrappedException) = precompilableerror(ex.error)
6✔
1562
precompilableerror(@nospecialize ex) = false
×
1563

1564
# Call __precompile__(false) at the top of a tile prevent it from being precompiled (false)
1565
"""
1566
    __precompile__(isprecompilable::Bool)
1567

1568
Specify whether the file calling this function is precompilable, defaulting to `true`.
1569
If a module or file is *not* safely precompilable, it should call `__precompile__(false)` in
1570
order to throw an error if Julia attempts to precompile it.
1571
"""
1572
@noinline function __precompile__(isprecompilable::Bool=true)
4✔
1573
    if !isprecompilable && ccall(:jl_generating_output, Cint, ()) != 0
4✔
1574
        throw(PrecompilableError())
2✔
1575
    end
1576
    nothing
2✔
1577
end
1578

1579
# require always works in Main scope and loads files from node 1
1580
const toplevel_load = Ref(true)
1581

1582
"""
1583
    require(into::Module, module::Symbol)
1584

1585
This function is part of the implementation of [`using`](@ref) / [`import`](@ref), if a module is not
1586
already defined in `Main`. It can also be called directly to force reloading a module,
1587
regardless of whether it has been loaded before (for example, when interactively developing
1588
libraries).
1589

1590
Loads a source file, in the context of the `Main` module, on every active node, searching
1591
standard locations for files. `require` is considered a top-level operation, so it sets the
1592
current `include` path but does not use it to search for files (see help for [`include`](@ref)).
1593
This function is typically used to load library code, and is implicitly called by `using` to
1594
load packages.
1595

1596
When searching for files, `require` first looks for package code in the global array
1597
[`LOAD_PATH`](@ref). `require` is case-sensitive on all platforms, including those with
1598
case-insensitive filesystems like macOS and Windows.
1599

1600
For more details regarding code loading, see the manual sections on [modules](@ref modules) and
1601
[parallel computing](@ref code-availability).
1602
"""
1603
function require(into::Module, mod::Symbol)
1,608✔
1604
    @lock require_lock begin
1,608✔
1605
    LOADING_CACHE[] = LoadingCache()
1,608✔
1606
    try
1,608✔
1607
        uuidkey_env = identify_package_env(into, String(mod))
1,608✔
1608
        # Core.println("require($(PkgId(into)), $mod) -> $uuidkey_env")
1609
        if uuidkey_env === nothing
1,608✔
1610
            where = PkgId(into)
6✔
1611
            if where.uuid === nothing
6✔
1612
                hint, dots = begin
×
1613
                    if isdefined(into, mod) && getfield(into, mod) isa Module
6✔
1614
                        true, "."
2✔
1615
                    elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module
4✔
1616
                        true, ".."
1✔
1617
                    else
1618
                        false, ""
10✔
1619
                    end
1620
                end
1621
                hint_message = hint ? ", maybe you meant `import/using $(dots)$(mod)`" : ""
6✔
1622
                start_sentence = hint ? "Otherwise, run" : "Run"
6✔
1623
                throw(ArgumentError("""
6✔
1624
                    Package $mod not found in current path$hint_message.
1625
                    - $start_sentence `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package."""))
1626
            else
1627
                throw(ArgumentError("""
×
1628
                Package $(where.name) does not have $mod in its dependencies:
1629
                - You may have a partially installed environment. Try `Pkg.instantiate()`
1630
                  to ensure all packages in the environment are installed.
1631
                - Or, if you have $(where.name) checked out for development and have
1632
                  added $mod as a dependency but haven't updated your primary
1633
                  environment's manifest file, try `Pkg.resolve()`.
1634
                - Otherwise you may need to report an issue with $(where.name)"""))
1635
            end
1636
        end
1637
        uuidkey, env = uuidkey_env
1,602✔
1638
        if _track_dependencies[]
1,602✔
1639
            push!(_require_dependencies, (into, binpack(uuidkey), 0.0))
137✔
1640
        end
1641
        return _require_prelocked(uuidkey, env)
3,193✔
1642
    finally
1643
        LOADING_CACHE[] = nothing
1,608✔
1644
    end
1645
    end
1646
end
1647

1648
require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey)
127✔
1649

1650
const REPL_PKGID = PkgId(UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL")
1651

1652
function _require_prelocked(uuidkey::PkgId, env=nothing)
1,751✔
1653
    assert_havelock(require_lock)
3,618✔
1654
    if !root_module_exists(uuidkey)
1,740✔
1655
        newm = _require(uuidkey, env)
440✔
1656
        if newm === nothing
437✔
1657
            error("package `$(uuidkey.name)` did not define the expected \
1✔
1658
                  module `$(uuidkey.name)`, check for typos in package module name")
1659
        end
1660
        insert_extension_triggers(uuidkey)
797✔
1661
        # After successfully loading, notify downstream consumers
1662
        run_package_callbacks(uuidkey)
436✔
1663
        if uuidkey == REPL_PKGID
872✔
1664
            REPL_MODULE_REF[] = newm
×
1665
        end
1666
    else
1667
        newm = root_module(uuidkey)
1,300✔
1668
    end
1669
    return newm
1,736✔
1670
end
1671

1672
mutable struct PkgOrigin
1673
    path::Union{String,Nothing}
595✔
1674
    cachepath::Union{String,Nothing}
1675
    version::Union{VersionNumber,Nothing}
1676
end
1677
PkgOrigin() = PkgOrigin(nothing, nothing, nothing)
595✔
1678
const pkgorigins = Dict{PkgId,PkgOrigin}()
1679

1680
const loaded_modules = Dict{PkgId,Module}()
1681
const loaded_modules_order = Vector{Module}()
1682
const module_keys = IdDict{Module,PkgId}() # the reverse
1683

1684
is_root_module(m::Module) = @lock require_lock haskey(module_keys, m)
81,096✔
1685
root_module_key(m::Module) = @lock require_lock module_keys[m]
59,197✔
1686

1687
@constprop :none function register_root_module(m::Module)
591✔
1688
    # n.b. This is called from C after creating a new module in `Base.__toplevel__`,
1689
    # instead of adding them to the binding table there.
1690
    @lock require_lock begin
715✔
1691
    key = PkgId(m, String(nameof(m)))
1,025✔
1692
    if haskey(loaded_modules, key)
591✔
1693
        oldm = loaded_modules[key]
2✔
1694
        if oldm !== m
2✔
1695
            if (0 != ccall(:jl_generating_output, Cint, ())) && (JLOptions().incremental != 0)
2✔
1696
                error("Replacing module `$(key.name)`")
×
1697
            else
1698
                @warn "Replacing module `$(key.name)`"
2✔
1699
            end
1700
        end
1701
    end
1702
    push!(loaded_modules_order, m)
591✔
1703
    loaded_modules[key] = m
591✔
1704
    module_keys[m] = key
591✔
1705
    end
1706
    nothing
591✔
1707
end
1708

1709
register_root_module(Core)
1710
register_root_module(Base)
1711
register_root_module(Main)
1712

1713
# This is used as the current module when loading top-level modules.
1714
# It has the special behavior that modules evaluated in it get added
1715
# to the loaded_modules table instead of getting bindings.
1716
baremodule __toplevel__
1717
using Base
1718
end
1719

1720
# get a top-level Module from the given key
1721
root_module(key::PkgId) = @lock require_lock loaded_modules[key]
78,968✔
1722
function root_module(where::Module, name::Symbol)
46✔
1723
    key = identify_package(where, String(name))
91✔
1724
    key isa PkgId || throw(KeyError(name))
47✔
1725
    return root_module(key)
45✔
1726
end
1727
maybe_root_module(key::PkgId) = @lock require_lock get(loaded_modules, key, nothing)
627✔
1728

1729
root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key)
20,242✔
1730
loaded_modules_array() = @lock require_lock copy(loaded_modules_order)
275✔
1731

1732
function unreference_module(key::PkgId)
1✔
1733
    if haskey(loaded_modules, key)
1✔
1734
        m = pop!(loaded_modules, key)
1✔
1735
        # need to ensure all modules are GC rooted; will still be referenced
1736
        # in module_keys
1737
    end
1738
end
1739

1740
# whoever takes the package_locks[pkg] must call this function immediately
1741
function set_pkgorigin_version_path(pkg::PkgId, path::Union{String,Nothing})
480✔
1742
    assert_havelock(require_lock)
960✔
1743
    pkgorigin = get!(PkgOrigin, pkgorigins, pkg)
480✔
1744
    if path !== nothing
480✔
1745
        # Pkg needs access to the version of packages in the sysimage.
1746
        if Core.Compiler.generating_sysimg()
901✔
1747
            pkgorigin.version = get_pkgversion_from_path(joinpath(dirname(path), ".."))
×
1748
        end
1749
    end
1750
    pkgorigin.path = path
480✔
1751
    nothing
480✔
1752
end
1753

1754
# A hook to allow code load to use Pkg.precompile
1755
const PKG_PRECOMPILE_HOOK = Ref{Function}()
1756

1757
# Returns `nothing` or the new(ish) module
1758
function _require(pkg::PkgId, env=nothing)
441✔
1759
    assert_havelock(require_lock)
882✔
1760
    loaded = start_loading(pkg)
441✔
1761
    loaded === nothing || return loaded
441✔
1762

1763
    last = toplevel_load[]
441✔
1764
    try
441✔
1765
        toplevel_load[] = false
441✔
1766
        # perform the search operation to select the module file require intends to load
1767
        path = locate_package(pkg, env)
441✔
1768
        if path === nothing
441✔
1769
            throw(ArgumentError("""
×
1770
                Package $pkg is required but does not seem to be installed:
1771
                 - Run `Pkg.instantiate()` to install all recorded dependencies.
1772
                """))
1773
        end
1774
        set_pkgorigin_version_path(pkg, path)
441✔
1775

1776
        pkg_precompile_attempted = false # being safe to avoid getting stuck in a Pkg.precompile loop
×
1777

1778
        # attempt to load the module file via the precompile cache locations
1779
        if JLOptions().use_compiled_modules != 0
441✔
1780
            @label load_from_cache
×
1781
            m = _require_search_from_serialized(pkg, path, UInt128(0))
432✔
1782
            if m isa Module
432✔
1783
                return m
358✔
1784
            end
1785
        end
1786

1787
        # if the module being required was supposed to have a particular version
1788
        # but it was not handled by the precompile loader, complain
1789
        for (concrete_pkg, concrete_build_id) in _concrete_dependencies
148✔
1790
            if pkg == concrete_pkg
2,057✔
1791
                @warn """Module $(pkg.name) with build ID $((UUID(concrete_build_id))) is missing from the cache.
×
1792
                     This may mean $pkg does not support precompilation but is imported by a module that does."""
1793
                if JLOptions().incremental != 0
×
1794
                    # during incremental precompilation, this should be fail-fast
1795
                    throw(PrecompilableError())
×
1796
                end
1797
            end
1798
        end
1,126✔
1799

1800
        if JLOptions().use_compiled_modules != 0
83✔
1801
            if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0)
92✔
1802
                if !pkg_precompile_attempted && isinteractive() && isassigned(PKG_PRECOMPILE_HOOK)
74✔
1803
                    pkg_precompile_attempted = true
×
1804
                    unlock(require_lock)
×
1805
                    try
×
1806
                        PKG_PRECOMPILE_HOOK[](pkg.name, _from_loading = true)
×
1807
                    finally
1808
                        lock(require_lock)
×
1809
                    end
1810
                    @goto load_from_cache
×
1811
                end
1812
                # spawn off a new incremental pre-compile task for recursive `require` calls
1813
                cachefile = compilecache(pkg, path)
74✔
1814
                if isa(cachefile, Exception)
71✔
1815
                    if precompilableerror(cachefile)
×
1816
                        verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug
1✔
1817
                        @logmsg verbosity "Skipping precompilation since __precompile__(false). Importing $pkg."
1✔
1818
                    else
1819
                        @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m
1✔
1820
                    end
1821
                    # fall-through to loading the file locally
1822
                else
1823
                    cachefile, ocachefile = cachefile::Tuple{String, Union{Nothing, String}}
70✔
1824
                    m = _tryrequire_from_serialized(pkg, cachefile, ocachefile)
70✔
1825
                    if !isa(m, Module)
70✔
1826
                        @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m
1✔
1827
                    else
1828
                        return m
69✔
1829
                    end
1830
                end
1831
            end
1832
        end
1833

1834
        # just load the file normally via include
1835
        # for unknown dependencies
1836
        uuid = pkg.uuid
11✔
1837
        uuid = (uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, uuid))
18✔
1838
        old_uuid = ccall(:jl_module_uuid, NTuple{2, UInt64}, (Any,), __toplevel__)
11✔
1839
        if uuid !== old_uuid
11✔
1840
            ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, uuid)
7✔
1841
        end
1842
        unlock(require_lock)
22✔
1843
        try
11✔
1844
            include(__toplevel__, path)
11✔
1845
            loaded = get(loaded_modules, pkg, nothing)
21✔
1846
        finally
1847
            lock(require_lock)
11✔
1848
            if uuid !== old_uuid
11✔
1849
                ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, old_uuid)
10✔
1850
            end
1851
        end
1852
    finally
1853
        toplevel_load[] = last
441✔
1854
        end_loading(pkg, loaded)
441✔
1855
    end
1856
    return loaded
11✔
1857
end
1858

1859
# Only used from test/precompile.jl
1860
function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Union{String, Nothing})
1✔
1861
    @lock require_lock begin
1✔
1862
    set_pkgorigin_version_path(uuidkey, nothing)
2✔
1863
    newm = _tryrequire_from_serialized(uuidkey, path, ocachepath)
1✔
1864
    newm isa Module || throw(newm)
1✔
1865
    insert_extension_triggers(uuidkey)
1✔
1866
    # After successfully loading, notify downstream consumers
1867
    run_package_callbacks(uuidkey)
1✔
1868
    return newm
1✔
1869
    end
1870
end
1871

1872

1873

1874
# relative-path load
1875

1876
"""
1877
    include_string([mapexpr::Function,] m::Module, code::AbstractString, filename::AbstractString="string")
1878

1879
Like [`include`](@ref), except reads code from the given string rather than from a file.
1880

1881
The optional first argument `mapexpr` can be used to transform the included code before
1882
it is evaluated: for each parsed expression `expr` in `code`, the `include_string` function
1883
actually evaluates `mapexpr(expr)`.  If it is omitted, `mapexpr` defaults to [`identity`](@ref).
1884

1885
!!! compat "Julia 1.5"
1886
    Julia 1.5 is required for passing the `mapexpr` argument.
1887
"""
1888
function include_string(mapexpr::Function, mod::Module, code::AbstractString,
909✔
1889
                        filename::AbstractString="string")
1890
    loc = LineNumberNode(1, Symbol(filename))
909✔
1891
    try
908✔
1892
        ast = Meta.parseall(code, filename=filename)
908✔
1893
        @assert Meta.isexpr(ast, :toplevel)
908✔
1894
        result = nothing
3✔
1895
        line_and_ex = Expr(:toplevel, loc, nothing)
908✔
1896
        for ex in ast.args
914✔
1897
            if ex isa LineNumberNode
36,370✔
1898
                loc = ex
18,185✔
1899
                line_and_ex.args[1] = ex
18,185✔
1900
                continue
18,185✔
1901
            end
1902
            ex = mapexpr(ex)
8✔
1903
            # Wrap things to be eval'd in a :toplevel expr to carry line
1904
            # information as part of the expr.
1905
            line_and_ex.args[2] = ex
18,185✔
1906
            result = Core.eval(mod, line_and_ex)
18,185✔
1907
        end
37,138✔
1908
        return result
880✔
1909
    catch exc
1910
        # TODO: Now that stacktraces are more reliable we should remove
1911
        # LoadError and expose the real error type directly.
1912
        rethrow(LoadError(filename, loc.line, exc))
39✔
1913
    end
1914
end
1915

1916
include_string(m::Module, txt::AbstractString, fname::AbstractString="string") =
166✔
1917
    include_string(identity, m, txt, fname)
1918

1919
function source_path(default::Union{AbstractString,Nothing}="")
21✔
1920
    s = current_task().storage
932✔
1921
    if s !== nothing
919✔
1922
        s = s::IdDict{Any,Any}
887✔
1923
        if haskey(s, :SOURCE_PATH)
1,464✔
1924
            return s[:SOURCE_PATH]::Union{Nothing,String}
577✔
1925
        end
1926
    end
1927
    return default
342✔
1928
end
1929

1930
function source_dir()
1✔
1931
    p = source_path(nothing)
1✔
1932
    return p === nothing ? pwd() : dirname(p)
1✔
1933
end
1934

1935
"""
1936
    Base.include([mapexpr::Function,] m::Module, path::AbstractString)
1937

1938
Evaluate the contents of the input source file in the global scope of module `m`.
1939
Every module (except those defined with [`baremodule`](@ref)) has its own
1940
definition of `include` omitting the `m` argument, which evaluates the file in that module.
1941
Returns the result of the last evaluated expression of the input file. During including,
1942
a task-local include path is set to the directory containing the file. Nested calls to
1943
`include` will search relative to that path. This function is typically used to load source
1944
interactively, or to combine files in packages that are broken into multiple source files.
1945

1946
The optional first argument `mapexpr` can be used to transform the included code before
1947
it is evaluated: for each parsed expression `expr` in `path`, the `include` function
1948
actually evaluates `mapexpr(expr)`.  If it is omitted, `mapexpr` defaults to [`identity`](@ref).
1949

1950
!!! compat "Julia 1.5"
1951
    Julia 1.5 is required for passing the `mapexpr` argument.
1952
"""
1953
Base.include # defined in Base.jl
1954

1955
# Full include() implementation which is used after bootstrap
1956
function _include(mapexpr::Function, mod::Module, _path::AbstractString)
768✔
1957
    @noinline # Workaround for module availability in _simplify_include_frames
×
1958
    path, prev = _include_dependency(mod, _path)
768✔
1959
    for callback in include_callbacks # to preserve order, must come before eval in include_string
1,535✔
1960
        invokelatest(callback, mod, path)
1✔
1961
    end
2✔
1962
    code = read(path, String)
768✔
1963
    tls = task_local_storage()
764✔
1964
    tls[:SOURCE_PATH] = path
764✔
1965
    try
764✔
1966
        return include_string(mapexpr, mod, code, path)
784✔
1967
    finally
1968
        if prev === nothing
738✔
1969
            delete!(tls, :SOURCE_PATH)
592✔
1970
        else
1971
            tls[:SOURCE_PATH] = prev
1,180✔
1972
        end
1973
    end
1974
end
1975

1976
"""
1977
    evalfile(path::AbstractString, args::Vector{String}=String[])
1978

1979
Load the file into an anonymous module using [`include`](@ref), evaluate all expressions,
1980
and return the value of the last expression.
1981
The optional `args` argument can be used to set the input arguments of the script (i.e. the global `ARGS` variable).
1982
Note that definitions (e.g. methods, globals) are evaluated in the anonymous module and do not affect the current module.
1983

1984
# Example
1985

1986
```jldoctest
1987
julia> write("testfile.jl", \"\"\"
1988
           @show ARGS
1989
           1 + 1
1990
       \"\"\");
1991

1992
julia> x = evalfile("testfile.jl", ["ARG1", "ARG2"]);
1993
ARGS = ["ARG1", "ARG2"]
1994

1995
julia> x
1996
2
1997

1998
julia> rm("testfile.jl")
1999
```
2000
"""
2001
function evalfile(path::AbstractString, args::Vector{String}=String[])
×
2002
    return Core.eval(Module(:__anon__),
×
2003
        Expr(:toplevel,
2004
             :(const ARGS = $args),
2005
             :(eval(x) = $(Expr(:core, :eval))(__anon__, x)),
×
2006
             :(include(x) = $(Expr(:top, :include))(__anon__, x)),
×
2007
             :(include(mapexpr::Function, x) = $(Expr(:top, :include))(mapexpr, __anon__, x)),
×
2008
             :(include($path))))
2009
end
2010
evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...])
×
2011

2012
function load_path_setup_code(load_path::Bool=true)
2✔
2013
    code = """
2✔
2014
    append!(empty!(Base.DEPOT_PATH), $(repr(map(abspath, DEPOT_PATH))))
2015
    append!(empty!(Base.DL_LOAD_PATH), $(repr(map(abspath, DL_LOAD_PATH))))
2016
    """
2017
    if load_path
1✔
2018
        load_path = map(abspath, Base.load_path())
1✔
2019
        path_sep = Sys.iswindows() ? ';' : ':'
×
2020
        any(path -> path_sep in path, load_path) &&
5✔
2021
            error("LOAD_PATH entries cannot contain $(repr(path_sep))")
2022
        code *= """
1✔
2023
        append!(empty!(Base.LOAD_PATH), $(repr(load_path)))
2024
        ENV["JULIA_LOAD_PATH"] = $(repr(join(load_path, Sys.iswindows() ? ';' : ':')))
2025
        Base.set_active_project(nothing)
2026
        """
2027
    end
2028
    return code
1✔
2029
end
2030

2031
# this is called in the external process that generates precompiled package files
2032
function include_package_for_output(pkg::PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String},
117✔
2033
                                    concrete_deps::typeof(_concrete_dependencies), source::Union{Nothing,String})
2034
    append!(empty!(Base.DEPOT_PATH), depot_path)
117✔
2035
    append!(empty!(Base.DL_LOAD_PATH), dl_load_path)
117✔
2036
    append!(empty!(Base.LOAD_PATH), load_path)
117✔
2037
    ENV["JULIA_LOAD_PATH"] = join(load_path, Sys.iswindows() ? ';' : ':')
117✔
2038
    set_active_project(nothing)
117✔
2039
    Base._track_dependencies[] = true
117✔
2040
    get!(Base.PkgOrigin, Base.pkgorigins, pkg).path = input
117✔
2041
    append!(empty!(Base._concrete_dependencies), concrete_deps)
117✔
2042
    uuid_tuple = pkg.uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, pkg.uuid)
163✔
2043

2044
    ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, uuid_tuple)
117✔
2045
    if source !== nothing
101✔
2046
        task_local_storage()[:SOURCE_PATH] = source
101✔
2047
    end
2048

2049
    ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred)
117✔
2050
    Core.Compiler.track_newly_inferred.x = true
117✔
2051
    try
117✔
2052
        Base.include(Base.__toplevel__, input)
122✔
2053
    catch ex
2054
        precompilableerror(ex) || rethrow()
8✔
2055
        @debug "Aborting `create_expr_cache'" exception=(ErrorException("Declaration of __precompile__(false) not allowed"), catch_backtrace())
2✔
2056
        exit(125) # we define status = 125 means PrecompileableError
117✔
2057
    finally
2058
        Core.Compiler.track_newly_inferred.x = false
115✔
2059
    end
2060
end
2061

2062
const PRECOMPILE_TRACE_COMPILE = Ref{String}()
2063
function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String},
117✔
2064
                           concrete_deps::typeof(_concrete_dependencies), internal_stderr::IO = stderr, internal_stdout::IO = stdout)
2065
    @nospecialize internal_stderr internal_stdout
×
2066
    rm(output, force=true)   # Remove file if it exists
117✔
2067
    output_o === nothing || rm(output_o, force=true)
×
2068
    depot_path = map(abspath, DEPOT_PATH)
117✔
2069
    dl_load_path = map(abspath, DL_LOAD_PATH)
117✔
2070
    load_path = map(abspath, Base.load_path())
117✔
2071
    path_sep = Sys.iswindows() ? ';' : ':'
×
2072
    any(path -> path_sep in path, load_path) &&
747✔
2073
        error("LOAD_PATH entries cannot contain $(repr(path_sep))")
2074

2075
    deps_strs = String[]
117✔
2076
    function pkg_str(_pkg::PkgId)
6,964✔
2077
        if _pkg.uuid === nothing
6,847✔
2078
            "Base.PkgId($(repr(_pkg.name)))"
1,687✔
2079
        else
2080
            "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))"
5,160✔
2081
        end
2082
    end
2083
    for (pkg, build_id) in concrete_deps
122✔
2084
        push!(deps_strs, "$(pkg_str(pkg)) => $(repr(build_id))")
6,730✔
2085
    end
6,842✔
2086

2087
    if output_o !== nothing
×
2088
        cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing)
×
2089
        opt_level = Base.JLOptions().opt_level
×
2090
        opts = `-O$(opt_level) --output-o $(output_o) --output-ji $(output) --output-incremental=yes`
×
2091
    else
2092
        cpu_target = nothing
×
2093
        opts = `-O0 --output-ji $(output) --output-incremental=yes`
117✔
2094
    end
2095

2096
    deps_eltype = sprint(show, eltype(concrete_deps); context = :module=>nothing)
117✔
2097
    deps = deps_eltype * "[" * join(deps_strs, ",") * "]"
117✔
2098
    trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])` : ``
234✔
2099
    io = open(pipeline(addenv(`$(julia_cmd(;cpu_target)::Cmd) $(opts)
181✔
2100
                              --startup-file=no --history-file=no --warn-overwrite=yes
2101
                              --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no")
2102
                              $trace
2103
                              -`,
2104
                              "OPENBLAS_NUM_THREADS" => 1,
2105
                              "JULIA_NUM_THREADS" => 1),
2106
                       stderr = internal_stderr, stdout = internal_stdout),
2107
              "w", stdout)
2108
    # write data over stdin to avoid the (unlikely) case of exceeding max command line size
2109
    write(io.in, """
218✔
2110
        empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated
2111
        Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)),
2112
            $(repr(load_path)), $deps, $(repr(source_path(nothing))))
2113
        """)
2114
    close(io.in)
117✔
2115
    return io
117✔
2116
end
2117

2118
function compilecache_dir(pkg::PkgId)
×
2119
    entrypath, entryfile = cache_file_entry(pkg)
117✔
2120
    return joinpath(DEPOT_PATH[1], entrypath)
117✔
2121
end
2122

2123
function compilecache_path(pkg::PkgId, prefs_hash::UInt64)::String
123✔
2124
    entrypath, entryfile = cache_file_entry(pkg)
123✔
2125
    cachepath = joinpath(DEPOT_PATH[1], entrypath)
123✔
2126
    isdir(cachepath) || mkpath(cachepath)
127✔
2127
    if pkg.uuid === nothing
123✔
2128
        abspath(cachepath, entryfile) * ".ji"
67✔
2129
    else
2130
        crc = _crc32c(something(Base.active_project(), ""))
110✔
2131
        crc = _crc32c(unsafe_string(JLOptions().image_file), crc)
56✔
2132
        crc = _crc32c(unsafe_string(JLOptions().julia_bin), crc)
56✔
2133
        crc = _crc32c(ccall(:jl_cache_flags, UInt8, ()), crc)
56✔
2134

2135
        cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing)
56✔
2136
        if cpu_target === nothing
56✔
2137
            cpu_target = unsafe_string(JLOptions().cpu_target)
56✔
2138
        end
2139
        crc = _crc32c(cpu_target, crc)
56✔
2140

2141
        crc = _crc32c(prefs_hash, crc)
56✔
2142
        project_precompile_slug = slug(crc, 5)
56✔
2143
        abspath(cachepath, string(entryfile, "_", project_precompile_slug, ".ji"))
56✔
2144
    end
2145
end
2146

2147
"""
2148
    Base.compilecache(module::PkgId)
2149

2150
Creates a precompiled cache file for a module and all of its dependencies.
2151
This can be used to reduce package load times. Cache files are stored in
2152
`DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref)
2153
for important notes.
2154
"""
2155
function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout)
76✔
2156
    @nospecialize internal_stderr internal_stdout
76✔
2157
    path = locate_package(pkg)
76✔
2158
    path === nothing && throw(ArgumentError("$pkg not found during precompilation"))
38✔
2159
    return compilecache(pkg, path, internal_stderr, internal_stdout)
38✔
2160
end
2161

2162
const MAX_NUM_PRECOMPILE_FILES = Ref(10)
2163

2164
function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout,
155✔
2165
                      keep_loaded_modules::Bool = true)
2166

2167
    @nospecialize internal_stderr internal_stdout
38✔
2168
    # decide where to put the resulting cache file
2169
    cachepath = compilecache_dir(pkg)
229✔
2170

2171
    # build up the list of modules that we want the precompile process to preserve
2172
    concrete_deps = copy(_concrete_dependencies)
117✔
2173
    if keep_loaded_modules
117✔
2174
        for mod in loaded_modules_array()
112✔
2175
            if !(mod === Main || mod === Core || mod === Base)
11,804✔
2176
                push!(concrete_deps, PkgId(mod) => module_build_id(mod))
5,622✔
2177
            end
2178
        end
6,070✔
2179
    end
2180
    # run the expression and cache the result
2181
    verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug
117✔
2182
    @logmsg verbosity "Precompiling $pkg"
117✔
2183

2184
    # create a temporary file in `cachepath` directory, write the cache in it,
2185
    # write the checksum, _and then_ atomically move the file to `cachefile`.
2186
    mkpath(cachepath)
117✔
2187
    cache_objects = JLOptions().use_pkgimages != 0
117✔
2188
    tmppath, tmpio = mktemp(cachepath)
117✔
2189

2190
    if cache_objects
117✔
2191
        tmppath_o, tmpio_o = mktemp(cachepath)
×
2192
        tmppath_so, tmpio_so = mktemp(cachepath)
×
2193
    else
2194
        tmppath_o = nothing
×
2195
    end
2196
    local p
×
2197
    try
117✔
2198
        close(tmpio)
117✔
2199
        if cache_objects
117✔
2200
            close(tmpio_o)
×
2201
            close(tmpio_so)
×
2202
        end
2203
        p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, internal_stderr, internal_stdout)
117✔
2204

2205
        if success(p)
117✔
2206
            if cache_objects
112✔
2207
                # Run linker over tmppath_o
2208
                Linking.link_image(tmppath_o, tmppath_so)
×
2209
            end
2210

2211
            # Read preferences hash back from .ji file (we can't precompute because
2212
            # we don't actually know what the list of compile-time preferences are without compiling)
2213
            prefs_hash = preferences_hash(tmppath)
112✔
2214
            cachefile = compilecache_path(pkg, prefs_hash)
112✔
2215
            ocachefile = cache_objects ? ocachefile_from_cachefile(cachefile) : nothing
112✔
2216

2217
            # append checksum for so to the end of the .ji file:
2218
            crc_so = UInt32(0)
112✔
2219
            if cache_objects
112✔
2220
                crc_so = open(_crc32c, tmppath_so, "r")
×
2221
            end
2222

2223
            # append extra crc to the end of the .ji file:
2224
            open(tmppath, "r+") do f
112✔
2225
                if iszero(isvalid_cache_header(f))
112✔
2226
                    error("Invalid header for $pkg in new cache file $(repr(tmppath)).")
×
2227
                end
2228
                seekend(f)
112✔
2229
                write(f, crc_so)
112✔
2230
                seekstart(f)
112✔
2231
                write(f, _crc32c(f))
112✔
2232
            end
2233

2234
            # inherit permission from the source file (and make them writable)
2235
            chmod(tmppath, filemode(path) & 0o777 | 0o200)
112✔
2236
            if cache_objects
112✔
2237
                # Ensure that the user can execute the `.so` we're generating
2238
                # Note that on windows, `filemode(path)` typically returns `0o666`, so this
2239
                # addition of the execute bit for the user is doubly needed.
2240
                chmod(tmppath_so, filemode(path) & 0o777 | 0o333)
×
2241
            end
2242

2243
            # prune the directory with cache files
2244
            if pkg.uuid !== nothing
112✔
2245
                entrypath, entryfile = cache_file_entry(pkg)
46✔
2246
                cachefiles = filter!(x -> startswith(x, entryfile * "_") && endswith(x, ".ji"), readdir(cachepath))
95✔
2247
                if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES[]
46✔
2248
                    idx = findmin(mtime.(joinpath.(cachepath, cachefiles)))[2]
×
2249
                    evicted_cachefile = joinpath(cachepath, cachefiles[idx])
×
2250
                    @debug "Evicting file from cache" evicted_cachefile
×
2251
                    rm(evicted_cachefile; force=true)
×
2252
                    try
×
2253
                        rm(ocachefile_from_cachefile(evicted_cachefile); force=true)
×
2254
                        @static if Sys.isapple()
×
2255
                            rm(ocachefile_from_cachefile(evicted_cachefile) * ".dSYM"; force=true, recursive=true)
2256
                        end
2257
                    catch e
2258
                        e isa IOError || rethrow()
×
2259
                    end
2260
                end
2261
            end
2262

2263
            if cache_objects
112✔
2264
                try
×
2265
                    rename(tmppath_so, ocachefile::String; force=true)
×
2266
                catch e
2267
                    e isa IOError || rethrow()
×
2268
                    isfile(ocachefile::String) || rethrow()
×
2269
                    # Windows prevents renaming a file that is in use so if there is a Julia session started
2270
                    # with a package image loaded, we cannot rename that file.
2271
                    # The code belows append a `_i` to the name of the cache file where `i` is the smallest number such that
2272
                    # that cache file does not exist.
2273
                    ocachename, ocacheext = splitext(ocachefile::String)
×
2274
                    old_cachefiles = Set(readdir(cachepath))
×
2275
                    num = 1
×
2276
                    while true
×
2277
                        ocachefile = ocachename * "_$num" * ocacheext
×
2278
                        in(basename(ocachefile), old_cachefiles) || break
×
2279
                        num += 1
×
2280
                    end
×
2281
                    # TODO: Risk for a race here if some other process grabs this name before us
2282
                    cachefile = cachefile_from_ocachefile(ocachefile)
×
2283
                    rename(tmppath_so, ocachefile::String; force=true)
×
2284
                end
2285
                @static if Sys.isapple()
×
2286
                    run(`$(Linking.dsymutil()) $ocachefile`, Base.DevNull(), Base.DevNull(), Base.DevNull())
2287
                end
2288
            end
2289
            # this is atomic according to POSIX (not Win32):
2290
            rename(tmppath, cachefile; force=true)
112✔
2291
            return cachefile, ocachefile
117✔
2292
        end
2293
    finally
2294
        rm(tmppath, force=true)
117✔
2295
        if cache_objects
117✔
2296
            rm(tmppath_o::String, force=true)
×
2297
            rm(tmppath_so, force=true)
×
2298
        end
2299
    end
2300
    if p.exitcode == 125
5✔
2301
        return PrecompilableError()
2✔
2302
    else
2303
        error("Failed to precompile $pkg to $(repr(tmppath)).")
3✔
2304
    end
2305
end
2306

2307
function module_build_id(m::Module)
77✔
2308
    hi, lo = ccall(:jl_module_build_id, NTuple{2,UInt64}, (Any,), m)
21,334✔
2309
    return (UInt128(hi) << 64) | lo
21,334✔
2310
end
2311

2312
function isvalid_cache_header(f::IOStream)
752✔
2313
    pkgimage = Ref{UInt8}()
752✔
2314
    checksum = ccall(:jl_read_verify_header, UInt64, (Ptr{Cvoid}, Ptr{UInt8}, Ptr{Int64}, Ptr{Int64}), f.ios, pkgimage, Ref{Int64}(), Ref{Int64}()) # returns checksum id or zero
752✔
2315

2316
    if !iszero(checksum) && pkgimage[] != 0
752✔
2317
        @debug "Cache header was for pkgimage"
×
2318
        return UInt64(0) # We somehow read the header for a pkgimage and not a ji
×
2319
    end
2320
    return checksum
752✔
2321
end
2322
isvalid_file_crc(f::IOStream) = (_crc32c(seekstart(f), filesize(f) - 4) == read(f, UInt32))
475✔
2323

2324
function isvalid_pkgimage_crc(f::IOStream, ocachefile::String)
×
2325
    seekstart(f) # TODO necessary
×
2326
    seek(f, filesize(f) - 8)
×
2327
    expected_crc_so = read(f, UInt32)
×
2328
    crc_so = open(_crc32c, ocachefile, "r")
×
2329
    expected_crc_so == crc_so
×
2330
end
2331

2332
struct CacheHeaderIncludes
2333
    id::PkgId
2,434✔
2334
    filename::String
2335
    mtime::Float64
2336
    modpath::Vector{String}   # seemingly not needed in Base, but used by Revise
2337
end
2338

2339
function parse_cache_header(f::IO)
640✔
2340
    flags = read(f, UInt8)
640✔
2341
    modules = Vector{Pair{PkgId, UInt64}}()
640✔
2342
    while true
1,280✔
2343
        n = read(f, Int32)
1,280✔
2344
        n == 0 && break
1,280✔
2345
        sym = String(read(f, n)) # module name
640✔
2346
        uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID
640✔
2347
        build_id = read(f, UInt64) # build UUID (mostly just a timestamp)
640✔
2348
        push!(modules, PkgId(uuid, sym) => build_id)
640✔
2349
    end
640✔
2350
    totbytes = read(f, Int64) # total bytes for file dependencies + preferences
640✔
2351
    # read the list of requirements
2352
    # and split the list into include and requires statements
2353
    includes = CacheHeaderIncludes[]
640✔
2354
    requires = Pair{PkgId, PkgId}[]
640✔
2355
    while true
4,510✔
2356
        n2 = read(f, Int32)
4,510✔
2357
        totbytes -= 4
4,510✔
2358
        if n2 == 0
4,510✔
2359
            break
640✔
2360
        end
2361
        depname = String(read(f, n2))
3,870✔
2362
        totbytes -= n2
3,870✔
2363
        mtime = read(f, Float64)
3,870✔
2364
        totbytes -= 8
3,870✔
2365
        n1 = read(f, Int32)
3,870✔
2366
        totbytes -= 4
3,870✔
2367
        # map ids to keys
2368
        modkey = (n1 == 0) ? PkgId("") : modules[n1].first
7,078✔
2369
        modpath = String[]
3,870✔
2370
        if n1 != 0
3,870✔
2371
            # determine the complete module path
2372
            while true
3,366✔
2373
                n1 = read(f, Int32)
3,366✔
2374
                totbytes -= 4
3,366✔
2375
                if n1 == 0
3,366✔
2376
                    break
3,208✔
2377
                end
2378
                push!(modpath, String(read(f, n1)))
158✔
2379
                totbytes -= n1
158✔
2380
            end
158✔
2381
        end
2382
        if depname[1] == '\0'
7,740✔
2383
            push!(requires, modkey => binunpack(depname))
1,436✔
2384
        else
2385
            push!(includes, CacheHeaderIncludes(modkey, depname, mtime, modpath))
2,434✔
2386
        end
2387
    end
3,870✔
2388
    prefs = String[]
640✔
2389
    while true
640✔
2390
        n2 = read(f, Int32)
640✔
2391
        totbytes -= 4
640✔
2392
        if n2 == 0
640✔
2393
            break
640✔
2394
        end
2395
        push!(prefs, String(read(f, n2)))
×
2396
        totbytes -= n2
×
2397
    end
×
2398
    prefs_hash = read(f, UInt64)
640✔
2399
    totbytes -= 8
640✔
2400
    srctextpos = read(f, Int64)
640✔
2401
    totbytes -= 8
640✔
2402
    @assert totbytes == 0 "header of cache file appears to be corrupt (totbytes == $(totbytes))"
640✔
2403
    # read the list of modules that are required to be present during loading
2404
    required_modules = Vector{Pair{PkgId, UInt128}}()
640✔
2405
    while true
25,169✔
2406
        n = read(f, Int32)
25,169✔
2407
        n == 0 && break
25,169✔
2408
        sym = String(read(f, n)) # module name
24,529✔
2409
        uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID
24,529✔
2410
        build_id = UInt128(read(f, UInt64)) << 64
24,529✔
2411
        build_id |= read(f, UInt64)
24,529✔
2412
        push!(required_modules, PkgId(uuid, sym) => build_id)
24,529✔
2413
    end
24,529✔
2414
    l = read(f, Int32)
640✔
2415
    clone_targets = read(f, l)
640✔
2416

2417
    return modules, (includes, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags
640✔
2418
end
2419

2420
function parse_cache_header(cachefile::String; srcfiles_only::Bool=false)
8✔
2421
    io = open(cachefile, "r")
4✔
2422
    try
4✔
2423
        iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile."))
4✔
2424
        ret = parse_cache_header(io)
4✔
2425
        srcfiles_only || return ret
7✔
2426
        _, (includes, _), _, srctextpos, _... = ret
1✔
2427
        srcfiles = srctext_files(io, srctextpos)
1✔
2428
        delidx = Int[]
1✔
2429
        for (i, chi) in enumerate(includes)
2✔
2430
            chi.filename ∈ srcfiles || push!(delidx, i)
5✔
2431
        end
5✔
2432
        deleteat!(includes, delidx)
1✔
2433
        return ret
1✔
2434
    finally
2435
        close(io)
4✔
2436
    end
2437
end
2438

2439
preferences_hash(f::IO) = parse_cache_header(f)[6]
112✔
2440
function preferences_hash(cachefile::String)
112✔
2441
    io = open(cachefile, "r")
112✔
2442
    try
112✔
2443
        if iszero(isvalid_cache_header(io))
112✔
2444
            throw(ArgumentError("Invalid header in cache file $cachefile."))
×
2445
        end
2446
        return preferences_hash(io)
112✔
2447
    finally
2448
        close(io)
112✔
2449
    end
2450
end
2451

2452
function cache_dependencies(f::IO)
1✔
2453
    _, (includes, _), modules, _... = parse_cache_header(f)
1✔
2454
    return modules, map(chi -> (chi.filename, chi.mtime), includes)  # return just filename and mtime
4✔
2455
end
2456

2457
function cache_dependencies(cachefile::String)
1✔
2458
    io = open(cachefile, "r")
1✔
2459
    try
1✔
2460
        iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile."))
1✔
2461
        return cache_dependencies(io)
1✔
2462
    finally
2463
        close(io)
1✔
2464
    end
2465
end
2466

2467
function read_dependency_src(io::IO, filename::AbstractString)
×
2468
    srctextpos = parse_cache_header(io)[4]
3✔
2469
    srctextpos == 0 && error("no source-text stored in cache file")
3✔
2470
    seek(io, srctextpos)
3✔
2471
    return _read_dependency_src(io, filename)
3✔
2472
end
2473

2474
function _read_dependency_src(io::IO, filename::AbstractString)
3✔
2475
    while !eof(io)
5✔
2476
        filenamelen = read(io, Int32)
5✔
2477
        filenamelen == 0 && break
5✔
2478
        fn = String(read(io, filenamelen))
3✔
2479
        len = read(io, UInt64)
3✔
2480
        if fn == filename
3✔
2481
            return String(read(io, len))
1✔
2482
        end
2483
        seek(io, position(io) + len)
2✔
2484
    end
2✔
2485
    error(filename, " is not stored in the source-text cache")
2✔
2486
end
2487

2488
function read_dependency_src(cachefile::String, filename::AbstractString)
3✔
2489
    io = open(cachefile, "r")
3✔
2490
    try
3✔
2491
        iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile."))
3✔
2492
        return read_dependency_src(io, filename)
5✔
2493
    finally
2494
        close(io)
3✔
2495
    end
2496
end
2497

2498
function srctext_files(f::IO, srctextpos::Int64)
1✔
2499
    files = Set{String}()
1✔
2500
    srctextpos == 0 && return files
1✔
2501
    seek(f, srctextpos)
1✔
2502
    while !eof(f)
2✔
2503
        filenamelen = read(f, Int32)
2✔
2504
        filenamelen == 0 && break
2✔
2505
        fn = String(read(f, filenamelen))
1✔
2506
        len = read(f, UInt64)
1✔
2507
        push!(files, fn)
1✔
2508
        seek(f, position(f) + len)
1✔
2509
    end
1✔
2510
    return files
1✔
2511
end
2512

2513
# Test to see if this UUID is mentioned in this `Project.toml`; either as
2514
# the top-level UUID (e.g. that of the project itself), as a dependency,
2515
# or as an extra/weakdep for Preferences.
2516
function get_uuid_name(project::Dict{String, Any}, uuid::UUID)
304✔
2517
    uuid_p = get(project, "uuid", nothing)::Union{Nothing, String}
368✔
2518
    name = get(project, "name", nothing)::Union{Nothing, String}
369✔
2519
    if name !== nothing && uuid_p !== nothing && UUID(uuid_p) == uuid
304✔
2520
        return name
14✔
2521
    end
2522
    deps = get(project, "deps", nothing)::Union{Nothing, Dict{String, Any}}
578✔
2523
    if deps !== nothing
290✔
2524
        for (k, v) in deps
576✔
2525
            if uuid == UUID(v::String)
361✔
2526
                return k
28✔
2527
            end
2528
        end
406✔
2529
    end
2530
    for subkey in ("deps", "extras", "weakdeps")
262✔
2531
        subsection = get(project, subkey, nothing)::Union{Nothing, Dict{String, Any}}
1,046✔
2532
        if subsection !== nothing
784✔
2533
            for (k, v) in subsection
524✔
2534
                if uuid == UUID(v::String)
313✔
2535
                    return k
2✔
2536
                end
2537
            end
362✔
2538
        end
2539
    end
1,042✔
2540
    return nothing
260✔
2541
end
2542

2543
function get_uuid_name(project_toml::String, uuid::UUID)
1✔
2544
    project = parsed_toml(project_toml)
1✔
2545
    return get_uuid_name(project, uuid)
1✔
2546
end
2547

2548
# If we've asked for a specific UUID, this function will extract the prefs
2549
# for that particular UUID.  Otherwise, it returns all preferences.
2550
function filter_preferences(prefs::Dict{String, Any}, pkg_name)
×
2551
    if pkg_name === nothing
×
2552
        return prefs
×
2553
    else
2554
        return get(Dict{String, Any}, prefs, pkg_name)::Dict{String, Any}
49✔
2555
    end
2556
end
2557

2558
function collect_preferences(project_toml::String, uuid::Union{UUID,Nothing})
305✔
2559
    # We'll return a list of dicts to be merged
2560
    dicts = Dict{String, Any}[]
305✔
2561

2562
    project = parsed_toml(project_toml)
305✔
2563
    pkg_name = nothing
×
2564
    if uuid !== nothing
303✔
2565
        # If we've been given a UUID, map that to the name of the package as
2566
        # recorded in the preferences section.  If we can't find that mapping,
2567
        # exit out, as it means there's no way preferences can be set for that
2568
        # UUID, as we only allow actual dependencies to have preferences set.
2569
        pkg_name = get_uuid_name(project, uuid)
303✔
2570
        if pkg_name === nothing
303✔
2571
            return dicts
260✔
2572
        end
2573
    end
2574

2575
    # Look first inside of `Project.toml` to see we have preferences embedded within there
2576
    proj_preferences = get(Dict{String, Any}, project, "preferences")::Dict{String, Any}
49✔
2577
    push!(dicts, filter_preferences(proj_preferences, pkg_name))
47✔
2578

2579
    # Next, look for `(Julia)LocalPreferences.toml` files next to this `Project.toml`
2580
    project_dir = dirname(project_toml)
45✔
2581
    for name in preferences_names
45✔
2582
        toml_path = joinpath(project_dir, name)
88✔
2583
        if isfile(toml_path)
88✔
2584
            prefs = parsed_toml(toml_path)
4✔
2585
            push!(dicts, filter_preferences(prefs, pkg_name))
6✔
2586

2587
            # If we find `JuliaLocalPreferences.toml`, don't look for `LocalPreferences.toml`
2588
            break
4✔
2589
        end
2590
    end
84✔
2591

2592
    return dicts
45✔
2593
end
2594

2595
"""
2596
    recursive_prefs_merge(base::Dict, overrides::Dict...)
2597

2598
Helper function to merge preference dicts recursively, honoring overrides in nested
2599
dictionaries properly.
2600
"""
2601
function recursive_prefs_merge(base::Dict{String, Any}, overrides::Dict{String, Any}...)
308✔
2602
    new_base = Base._typeddict(base, overrides...)
308✔
2603

2604
    for override in overrides
48✔
2605
        # Clear entries are keys that should be deleted from any previous setting.
2606
        override_clear = get(override, "__clear__", nothing)
54✔
2607
        if override_clear isa Vector{String}
52✔
2608
            for k in override_clear
2✔
2609
                delete!(new_base, k)
2✔
2610
            end
4✔
2611
        end
2612

2613
        for (k, override_k) in override
63✔
2614
            # Note that if `base` has a mapping that is _not_ a `Dict`, and `override`
2615
            new_base_k = get(new_base, k, nothing)
32✔
2616
            if new_base_k isa Dict{String, Any} && override_k isa Dict{String, Any}
21✔
2617
                new_base[k] = recursive_prefs_merge(new_base_k, override_k)
3✔
2618
            else
2619
                new_base[k] = override_k
18✔
2620
            end
2621
        end
31✔
2622
    end
12✔
2623
    return new_base
308✔
2624
end
2625

2626
function get_preferences(uuid::Union{UUID,Nothing} = nothing)
393✔
2627
    merged_prefs = Dict{String,Any}()
393✔
2628
    for env in reverse(load_path())
392✔
2629
        project_toml = env_project_file(env)
780✔
2630
        if !isa(project_toml, String)
780✔
2631
            continue
475✔
2632
        end
2633

2634
        # Collect all dictionaries from the current point in the load path, then merge them in
2635
        dicts = collect_preferences(project_toml, uuid)
305✔
2636
        merged_prefs = recursive_prefs_merge(merged_prefs, dicts...)
305✔
2637
    end
1,172✔
2638
    return merged_prefs
392✔
2639
end
2640

2641
function get_preferences_hash(uuid::Union{UUID, Nothing}, prefs_list::Vector{String})
390✔
2642
    # Start from a predictable hash point to ensure that the same preferences always
2643
    # hash to the same value, modulo changes in how Dictionaries are hashed.
2644
    h = UInt(0)
×
2645
    uuid === nothing && return UInt64(h)
×
2646

2647
    # Load the preferences
2648
    prefs = get_preferences(uuid)
390✔
2649

2650
    # Walk through each name that's called out as a compile-time preference
2651
    for name in prefs_list
780✔
2652
        prefs_value = get(prefs, name, nothing)
×
2653
        if prefs_value !== nothing
×
2654
            h = hash(prefs_value, h)::UInt
×
2655
        end
2656
    end
×
2657
    # We always return a `UInt64` so that our serialization format is stable
2658
    return UInt64(h)
390✔
2659
end
2660

2661
get_preferences_hash(m::Module, prefs_list::Vector{String}) = get_preferences_hash(PkgId(m).uuid, prefs_list)
112✔
2662

2663
# This is how we keep track of who is using what preferences at compile-time
2664
const COMPILETIME_PREFERENCES = Dict{UUID,Set{String}}()
2665

2666
# In `Preferences.jl`, if someone calls `load_preference(@__MODULE__, key)` while we're precompiling,
2667
# we mark that usage as a usage at compile-time and call this method, so that at the end of `.ji` generation,
2668
# we can record the list of compile-time preferences and embed that into the `.ji` header
2669
function record_compiletime_preference(uuid::UUID, key::String)
×
2670
    pref = get!(Set{String}, COMPILETIME_PREFERENCES, uuid)
×
2671
    push!(pref, key)
×
2672
    return nothing
×
2673
end
2674
get_compiletime_preferences(uuid::UUID) = collect(get(Vector{String}, COMPILETIME_PREFERENCES, uuid))
46✔
2675
get_compiletime_preferences(m::Module) = get_compiletime_preferences(PkgId(m).uuid)
112✔
2676
get_compiletime_preferences(::Nothing) = String[]
66✔
2677

2678
function check_clone_targets(clone_targets)
×
2679
    try
×
2680
        ccall(:jl_check_pkgimage_clones, Cvoid, (Ptr{Cchar},), clone_targets)
×
2681
        return true
×
2682
    catch
2683
        return false
×
2684
    end
2685
end
2686

2687
struct CacheFlags
2688
    # OOICCDDP - see jl_cache_flags
2689
    use_pkgimages::Bool
2690
    debug_level::Int
2691
    check_bounds::Int
2692
    inline::Bool
2693
    opt_level::Int
2694

2695
    function CacheFlags(f::UInt8)
1✔
2696
        use_pkgimages = Bool(f & 1)
3✔
2697
        debug_level = Int((f >> 1) & 3)
2✔
2698
        check_bounds = Int((f >> 3) & 3)
2✔
2699
        inline = Bool((f >> 5) & 1)
4✔
2700
        opt_level = Int((f >> 6) & 3) # define OPT_LEVEL in statiddata_utils
2✔
2701
        new(use_pkgimages, debug_level, check_bounds, inline, opt_level)
2✔
2702
    end
2703
end
2704
CacheFlags(f::Int) = CacheFlags(UInt8(f))
1✔
2705
CacheFlags() = CacheFlags(ccall(:jl_cache_flags, UInt8, ()))
2✔
2706

2707
function show(io::IO, cf::CacheFlags)
1✔
2708
    print(io, "use_pkgimages = ", cf.use_pkgimages)
1✔
2709
    print(io, ", debug_level = ", cf.debug_level)
1✔
2710
    print(io, ", check_bounds = ", cf.check_bounds)
1✔
2711
    print(io, ", inline = ", cf.inline)
1✔
2712
    print(io, ", opt_level = ", cf.opt_level)
1✔
2713
end
2714

2715
# returns true if it "cachefile.ji" is stale relative to "modpath.jl" and build_id for modkey
2716
# otherwise returns the list of dependencies to also check
2717
@constprop :none function stale_cachefile(modpath::String, cachefile::String; ignore_loaded::Bool = false)
21✔
2718
    return stale_cachefile(PkgId(""), UInt128(0), modpath, cachefile; ignore_loaded)
13✔
2719
end
2720
@constprop :none function stale_cachefile(modkey::PkgId, build_id::UInt128, modpath::String, cachefile::String; ignore_loaded::Bool = false)
896✔
2721
    io = open(cachefile, "r")
448✔
2722
    try
448✔
2723
        checksum = isvalid_cache_header(io)
448✔
2724
        if iszero(checksum)
448✔
2725
            @debug "Rejecting cache file $cachefile due to it containing an invalid cache header"
×
2726
            return true # invalid cache file
×
2727
        end
2728
        modules, (includes, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags = parse_cache_header(io)
448✔
2729
        if isempty(modules)
448✔
2730
            return true # ignore empty file
×
2731
        end
2732
        if ccall(:jl_match_cache_flags, UInt8, (UInt8,), flags) == 0
448✔
2733
            @debug """
38✔
2734
            Rejecting cache file $cachefile for $modkey since the flags are mismatched
2735
              current session: $(CacheFlags())
2736
              cache file:      $(CacheFlags(flags))
2737
            """
2738
            return true
38✔
2739
        end
2740
        pkgimage = !isempty(clone_targets)
410✔
2741
        if pkgimage
410✔
2742
            ocachefile = ocachefile_from_cachefile(cachefile)
×
2743
            if JLOptions().use_pkgimages == 0
×
2744
                # presence of clone_targets means native code cache
2745
                @debug "Rejecting cache file $cachefile for $modkey since it would require usage of pkgimage"
×
2746
                return true
×
2747
            end
2748
            if !check_clone_targets(clone_targets)
×
2749
                @debug "Rejecting cache file $cachefile for $modkey since pkgimage can't be loaded on this target"
×
2750
                return true
×
2751
            end
2752
            if !isfile(ocachefile)
×
2753
                @debug "Rejecting cache file $cachefile for $modkey since pkgimage $ocachefile was not found"
×
2754
                return true
×
2755
            end
2756
        else
2757
            ocachefile = nothing
×
2758
        end
2759
        id = first(modules)
410✔
2760
        if id.first != modkey && modkey != PkgId("")
410✔
2761
            @debug "Rejecting cache file $cachefile for $modkey since it is for $id instead"
×
2762
            return true
×
2763
        end
2764
        if build_id != UInt128(0)
410✔
2765
            id_build = (UInt128(checksum) << 64) | id.second
40✔
2766
            if id_build != build_id
40✔
2767
                @debug "Ignoring cache file $cachefile for $modkey ($((UUID(id_build)))) since it is does not provide desired build_id ($((UUID(build_id))))"
×
2768
                return true
×
2769
            end
2770
        end
2771
        id = id.first
410✔
2772
        modules = Dict{PkgId, UInt64}(modules)
410✔
2773

2774
        # Check if transitive dependencies can be fulfilled
2775
        ndeps = length(required_modules)
410✔
2776
        depmods = Vector{Any}(undef, ndeps)
410✔
2777
        for i in 1:ndeps
820✔
2778
            req_key, req_build_id = required_modules[i]
15,661✔
2779
            # Module is already loaded
2780
            if root_module_exists(req_key)
15,661✔
2781
                M = root_module(req_key)
15,635✔
2782
                if PkgId(M) == req_key && module_build_id(M) === req_build_id
15,635✔
2783
                    depmods[i] = M
15,635✔
2784
                elseif ignore_loaded
×
2785
                    # Used by Pkg.precompile given that there it's ok to precompile different versions of loaded packages
2786
                    @goto locate_branch
×
2787
                else
2788
                    @debug "Rejecting cache file $cachefile because module $req_key is already loaded and incompatible."
×
2789
                    return true # Won't be able to fulfill dependency
15,635✔
2790
                end
2791
            else
2792
                @label locate_branch
×
2793
                path = locate_package(req_key)
26✔
2794
                if path === nothing
26✔
2795
                    @debug "Rejecting cache file $cachefile because dependency $req_key not found."
3✔
2796
                    return true # Won't be able to fulfill dependency
3✔
2797
                end
2798
                depmods[i] = (path, req_key, req_build_id)
23✔
2799
            end
2800
        end
30,909✔
2801

2802
        # check if this file is going to provide one of our concrete dependencies
2803
        # or if it provides a version that conflicts with our concrete dependencies
2804
        # or neither
2805
        skip_timecheck = false
×
2806
        for (req_key, req_build_id) in _concrete_dependencies
777✔
2807
            build_id = get(modules, req_key, UInt64(0))
1,864✔
2808
            if build_id !== UInt64(0)
1,844✔
2809
                build_id |= UInt128(checksum) << 64
20✔
2810
                if build_id === req_build_id
20✔
2811
                    skip_timecheck = true
×
2812
                    break
20✔
2813
                end
2814
                @debug "Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID(build_id)))) for $req_key (want $(UUID(req_build_id)))"
×
2815
                return true # cachefile doesn't provide the required version of the dependency
×
2816
            end
2817
        end
1,824✔
2818

2819
        # now check if this file is fresh relative to its source files
2820
        if !skip_timecheck
407✔
2821
            if !samefile(includes[1].filename, modpath) && !samefile(fixup_stdlib_path(includes[1].filename), modpath)
387✔
2822
                @debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath"
2✔
2823
                return true # cache file was compiled from a different path
2✔
2824
            end
2825
            for (modkey, req_modkey) in requires
442✔
2826
                # verify that `require(modkey, name(req_modkey))` ==> `req_modkey`
2827
                if identify_package(modkey, req_modkey.name) != req_modkey
2,238✔
2828
                    @debug "Rejecting cache file $cachefile because uuid mapping for $modkey => $req_modkey has changed"
×
2829
                    return true
×
2830
                end
2831
            end
1,447✔
2832
            for chi in includes
385✔
2833
                f, ftime_req = chi.filename, chi.mtime
1,952✔
2834
                if !ispath(f)
1,952✔
2835
                    _f = fixup_stdlib_path(f)
×
2836
                    if isfile(_f) && startswith(_f, Sys.STDLIB)
×
2837
                        # mtime is changed by extraction
2838
                        @debug "Skipping mtime check for file $f used by $cachefile, since it is a stdlib"
×
2839
                        continue
×
2840
                    end
2841
                    @debug "Rejecting stale cache file $cachefile because file $f does not exist"
×
2842
                    return true
×
2843
                end
2844
                ftime = mtime(f)
1,952✔
2845
                is_stale = ( ftime != ftime_req ) &&
1,952✔
2846
                           ( ftime != floor(ftime_req) ) &&           # Issue #13606, PR #13613: compensate for Docker images rounding mtimes
2847
                           ( ftime != ceil(ftime_req) ) &&            # PR: #47433 Compensate for CirceCI's truncating of timestamps in its caching
2848
                           ( ftime != trunc(ftime_req, digits=6) ) && # Issue #20837, PR #20840: compensate for GlusterFS truncating mtimes to microseconds
2849
                           ( ftime != 1.0 )  &&                       # PR #43090: provide compatibility with Nix mtime.
2850
                           !( 0 < (ftime_req - ftime) < 1e-6 )        # PR #45552: Compensate for Windows tar giving mtimes that may be incorrect by up to one microsecond
2851
                if is_stale
1,952✔
2852
                    @debug "Rejecting stale cache file $cachefile (mtime $ftime_req) because file $f (mtime $ftime) has changed"
2✔
2853
                    return true
2✔
2854
                end
2855
            end
2,333✔
2856
        end
2857

2858
        if !isvalid_file_crc(io)
403✔
2859
            @debug "Rejecting cache file $cachefile because it has an invalid checksum"
1✔
2860
            return true
1✔
2861
        end
2862

2863
        if pkgimage
402✔
2864
            if !isvalid_pkgimage_crc(io, ocachefile::String)
×
2865
                @debug "Rejecting cache file $cachefile because $ocachefile has an invalid checksum"
×
2866
                return true
×
2867
            end
2868
        end
2869

2870
        curr_prefs_hash = get_preferences_hash(id.uuid, prefs)
746✔
2871
        if prefs_hash != curr_prefs_hash
402✔
2872
            @debug "Rejecting cache file $cachefile because preferences hash does not match 0x$(string(prefs_hash, base=16)) != 0x$(string(curr_prefs_hash, base=16))"
×
2873
            return true
×
2874
        end
2875

2876
        return depmods, ocachefile # fresh cachefile
402✔
2877
    finally
2878
        close(io)
448✔
2879
    end
2880
end
2881

2882
"""
2883
    @__FILE__ -> String
2884

2885
Expand to a string with the path to the file containing the
2886
macrocall, or an empty string if evaluated by `julia -e <expr>`.
2887
Return `nothing` if the macro was missing parser source information.
2888
Alternatively see [`PROGRAM_FILE`](@ref).
2889
"""
2890
macro __FILE__()
81✔
2891
    __source__.file === nothing && return nothing
81✔
2892
    return String(__source__.file::Symbol)
81✔
2893
end
2894

2895
"""
2896
    @__DIR__ -> String
2897

2898
Expand to a string with the absolute path to the directory of the file
2899
containing the macrocall.
2900
Return the current working directory if run from a REPL or if evaluated by `julia -e <expr>`.
2901
"""
2902
macro __DIR__()
69✔
2903
    __source__.file === nothing && return nothing
69✔
2904
    _dirname = dirname(String(__source__.file::Symbol))
69✔
2905
    return isempty(_dirname) ? pwd() : abspath(_dirname)
69✔
2906
end
2907

2908
"""
2909
    precompile(f, argtypes::Tuple{Vararg{Any}})
2910

2911
Compile the given function `f` for the argument tuple (of types) `argtypes`, but do not execute it.
2912
"""
2913
function precompile(@nospecialize(f), @nospecialize(argtypes::Tuple))
23✔
2914
    precompile(Tuple{Core.Typeof(f), argtypes...})
23✔
2915
end
2916

2917
const ENABLE_PRECOMPILE_WARNINGS = Ref(false)
2918
function precompile(@nospecialize(argt::Type))
44✔
2919
    ret = ccall(:jl_compile_hint, Int32, (Any,), argt) != 0
44✔
2920
    if !ret && ENABLE_PRECOMPILE_WARNINGS[]
44✔
2921
        @warn "Inactive precompile statement" maxlog=100 form=argt _module=nothing _file=nothing _line=0
×
2922
    end
2923
    return ret
44✔
2924
end
2925

2926
# Variants that work for `invoke`d calls for which the signature may not be sufficient
2927
precompile(mi::Core.MethodInstance, world::UInt=get_world_counter()) =
2✔
2928
    (ccall(:jl_compile_method_instance, Cvoid, (Any, Any, UInt), mi, C_NULL, world); return true)
1✔
2929

2930
"""
2931
    precompile(f, argtypes::Tuple{Vararg{Any}}, m::Method)
2932

2933
Precompile a specific method for the given argument types. This may be used to precompile
2934
a different method than the one that would ordinarily be chosen by dispatch, thus
2935
mimicking `invoke`.
2936
"""
2937
function precompile(@nospecialize(f), @nospecialize(argtypes::Tuple), m::Method)
1✔
2938
    precompile(Tuple{Core.Typeof(f), argtypes...}, m)
1✔
2939
end
2940

2941
function precompile(@nospecialize(argt::Type), m::Method)
1✔
2942
    atype, sparams = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argt, m.sig)::SimpleVector
2✔
2943
    mi = Core.Compiler.specialize_method(m, atype, sparams)
1✔
2944
    return precompile(mi)
1✔
2945
end
2946

2947
precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing))
2948
precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String))
2949
precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), IO, IO))
2950
precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), IO, IO))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc