1 30
module SymbolServer
2
import Sockets
3

4
pipename = length(ARGS) > 1 ? ARGS[2] : nothing
5

6
conn = pipename !== nothing ? Sockets.connect(pipename) : nothing
7

8
start_time = time_ns()
9

10
# Try to lower the priority of this process so that it doesn't block the
11
# user system.
12
@static if Sys.iswindows()
13
    # Get process handle
14 12
    p_handle = ccall(:GetCurrentProcess, stdcall, Ptr{Cvoid}, ())
15

16
    # Set BELOW_NORMAL_PRIORITY_CLASS
17 12
    ret = ccall(:SetPriorityClass, stdcall, Cint, (Ptr{Cvoid}, Culong), p_handle, 0x00004000)
18 12
    ret != 1 && @warn "Something went wrong when setting BELOW_NORMAL_PRIORITY_CLASS."
19
else
20 18
    ret = ccall(:nice, Cint, (Cint,), 1)
21
    # We don't check the return value because it doesn't really matter
22
end
23

24 30
module LoadingBay
25
end
26

27
# Make sure we can load stdlibs
28
!in("@stdlib", LOAD_PATH) && push!(LOAD_PATH, "@stdlib")
29

30
using Serialization, Pkg, SHA
31
using Base: UUID
32

33
include("faketypes.jl")
34
include("symbols.jl")
35
include("utils.jl")
36

37
store_path = length(ARGS) > 0 ? ARGS[1] : abspath(joinpath(@__DIR__, "..", "store"))
38

39
ctx = try
40
    Pkg.Types.Context()
41
catch err
42
    @info "Package environment can't be read."
43
    exit()
44
end
45
# Add some methods to check whether a package is part of the standard library and so
46
# won't need recaching.
47
if isdefined(Pkg.Types, :is_stdlib)
48 20
    is_stdlib(uuid::UUID) = Pkg.Types.is_stdlib(uuid)
49
else
50 10
    is_stdlib(uuid::UUID) = uuid in keys(ctx.stdlibs)
51
end
52

53
server = Server(store_path, ctx, Dict{UUID,Package}())
54

55
function load_package(c::Pkg.Types.Context, uuid, conn)
56 30
    isinmanifest(c, uuid isa String ? Base.UUID(uuid) : uuid) || return
57 30
    pe_name = packagename(c, uuid)
58 30
    pid = Base.PkgId(uuid isa String ? Base.UUID(uuid) : uuid, pe_name)
59 30
    if pid in keys(Base.loaded_modules)
60 30
        conn !== nothing && println(conn, "PROCESSPKG;$pe_name;$uuid;noversion")
61 30
        LoadingBay.eval(:($(Symbol(pe_name)) = $(Base.loaded_modules[pid])))
62 30
        m = getfield(LoadingBay, Symbol(pe_name))
63
    else
64 30
        m = try
65 30
            conn !== nothing && println(conn, "STARTLOAD;$pe_name;$uuid;noversion")
66 30
            LoadingBay.eval(:(import $(Symbol(pe_name))))
67 30
            conn !== nothing && println(conn, "STOPLOAD;$pe_name")
68 30
            m = getfield(LoadingBay, Symbol(pe_name))
69
        catch e
70 30
            return
71
        end
72
    end
73
end
74

75
function write_cache(name, pkg)
76 30
    open(joinpath(server.storedir, name), "w") do io
77 10
        serialize(io, pkg)
78
    end
79
end
80

81
function write_depot(server, ctx, written_caches)
82 30
    for (uuid, pkg) in server.depot
83 30
        filename = get_filename_from_name(ctx.env.manifest, uuid)
84 30
        filename === nothing && continue
85 30
        cache_path = joinpath(server.storedir, filename)
86 30
        cache_path in written_caches && continue
87 25
        push!(written_caches, cache_path)
88 25
        @info "Now writing to disc $uuid"
89 30
        write_cache(cache_path, pkg)
90
    end
91
end
92
# List of caches that have already been written
93
written_caches = String[]
94

95
# First get a list of all package UUIds that we want to cache
96
toplevel_pkgs = deps(project(ctx))
97
packages_to_load = []
98
# Next make sure the cache is up-to-date for all of these
99
for (pk_name, uuid) in toplevel_pkgs
100
    file_name = get_filename_from_name(ctx.env.manifest, uuid)
101
    # We sometimes have UUIDs in the project file that are not in the
102
    # manifest file. That seems like something that shouldn't happen, but
103
    # in practice is not under our control. For now, we just skip these
104
    # packages
105
    file_name === nothing && continue
106
    cache_path = joinpath(server.storedir, file_name)
107

108
    if isfile(cache_path)
109
        if is_package_deved(ctx.env.manifest, uuid)
110
            cached_version = open(cache_path) do io
111 0
                deserialize(io)
112
            end
113
            if sha_pkg(frommanifest(ctx.env.manifest, uuid)) != cached_version.sha
114
                @info "Outdated sha, will recache package $pk_name ($uuid)"
115
                push!(packages_to_load, uuid)
116
            else
117
                @info "Package $pk_name ($uuid) is cached."
118
            end
119
        else
120
            @info "Package $pk_name ($uuid) is cached."
121
        end
122
    else
123
        @info "Will cache package $pk_name ($uuid)"
124
        push!(packages_to_load, uuid)
125
    end
126
end
127

128
# Load all packages together
129 20
for uuid in packages_to_load
130 30
    load_package(ctx, uuid, conn)
131
end
132

133
# Create image of whole package env. This creates the module structure only.
134
env_symbols = getenvtree()
135

136
# Populate the above with symbols, skipping modules that don't need caching.
137
# symbols (env_symbols)
138
visited = Base.IdSet{Module}([Base, Core]) # don't need to cache these each time...
139 20
for (pid, m) in Base.loaded_modules
140 30
    if pid.uuid !== nothing && is_stdlib(pid.uuid) &&
141
        (file_name = get_filename_from_name(ctx.env.manifest, pid.uuid)) !== nothing &&
142
        isfile(joinpath(server.storedir, file_name))
143 0
        push!(visited, m)
144 5
        delete!(env_symbols, Symbol(pid.name))
145
    end
146
end
147

148
symbols(env_symbols, nothing, SymbolServer.getallns(), visited)
149

150
# Wrap the `ModuleStore`s as `Package`s.
151 20
for (pkg_name, cache) in env_symbols
152 30
    pkg_name = String(pkg_name)
153 30
    !isinmanifest(ctx, pkg_name) && continue
154 30
    uuid = packageuuid(ctx, String(pkg_name))
155 30
    pe = frommanifest(ctx, uuid)
156 30
    server.depot[uuid] = Package(String(pkg_name), cache, version(pe), uuid, sha_pkg(pe))
157
end
158

159
# Write to disc
160
write_depot(server, server.context, written_caches)
161
end_time = time_ns()
162

163
elapsed_time_in_s = (end_time - start_time) / 1e9
164
@info "Symbol server indexing took $elapsed_time_in_s seconds."
165

166
end

Read our documentation on viewing source code .

Loading