11using Pkg, Serialization
22
33
4- function snoop (tomlpath, snoopfile, outputfile, reuse = false )
5- packages = extract_using (snoopfile)
4+ function snoop (package, tomlpath, snoopfile, outputfile, reuse = false )
5+
66 command = """
77 using Pkg, PackageCompiler
88 """
9+
910 if tomlpath != nothing
1011 command *= """
1112 Pkg.activate($(repr (tomlpath)) )
1213 Pkg.instantiate()
1314 """
1415 end
16+
1517 command *= """
1618 # let's wrap the snoop file in a try catch...
1719 # This way we still do some snooping even if there is an error in the tests!
@@ -24,29 +26,48 @@ function snoop(tomlpath, snoopfile, outputfile, reuse = false)
2426
2527 # let's use a file in the PackageCompiler dir,
2628 # so it doesn't get lost if later steps fail
27- tmp_file = sysimg_folder (" precompile_tmp.jl" )
29+ tmp_file = package_folder (" precompile_tmp.jl" )
2830 if ! reuse
2931 run_julia (command, compile = " all" , O = 0 , g = 1 , trace_compile = tmp_file)
3032 end
31- actually_used = extract_used_packages (tmp_file)
33+ used_packages = Set {String} () # e.g. from test/REQUIRE
34+ if package != nothing
35+ push! (used_packages, string (package))
36+ end
3237 if tomlpath != nothing
3338 # add toml packages, in case extract_used_packages misses a package
3439 deps = get (TOML. parsefile (tomlpath), " deps" , Dict {String, Any} ())
35- union! (actually_used, string .(keys (deps)))
40+ union! (used_packages, string .(keys (deps)))
41+ end
42+ usings = if isempty (used_packages)
43+ " "
44+ else
45+ packages = join (used_packages, " , " )
46+ """
47+ using $packages
48+ for Mod in [$packages ]
49+ isdefined(Mod, :__init__) && Mod.__init__()
50+ end
51+ """
3652 end
3753
3854 line_idx = 0 ; missed = 0
3955 open (outputfile, " w" ) do io
4056 println (io, """
41- # We need to use all used packages in the precompile file for maximum
42- # usage of the precompile statements.
43- # Since this can be any recursive dependency of the package we AOT compile,
44- # we decided to just use them without installing them. An added
45- # benefit is, that we can call __init__ this way more easily, since
46- # incremental sysimage compilation won't call __init__ on `using`
47- # https://github.com/JuliaLang/julia/issues/22910
48- using PackageCompiler
49- PackageCompiler.require_uninstalled.($(repr (actually_used)) , (@__MODULE__,))
57+ # We need to use all used packages in the precompile file for maximum
58+ # usage of the precompile statements.
59+ # Since this can be any recursive dependency of the package we AOT compile,
60+ # we decided to just use them without installing them. An added
61+ # benefit is, that we can call __init__ this way more easily, since
62+ # incremental sysimage compilation won't call __init__ on `using`
63+ # https://github.com/JuliaLang/julia/issues/22910
64+ $usings
65+ # bring recursive dependencies of used packages and standard libraries into namespace
66+ for Mod in Base.loaded_modules_array()
67+ if !Core.isdefined(@__MODULE__, nameof(Mod))
68+ Core.eval(@__MODULE__, Expr(:const, Expr(:(=), nameof(Mod), Mod)))
69+ end
70+ end
5071 """ )
5172 for line in eachline (tmp_file)
5273 line_idx += 1
@@ -61,17 +82,47 @@ function snoop(tomlpath, snoopfile, outputfile, reuse = false)
6182 if expr. head != :incomplete
6283 # after all this, we still need to wrap into try catch,
6384 # since some anonymous symbols won't be found...
64- println (io, " try;" , line, " ; catch e; @warn \" couldn't precompile statement $line_idx \" exception = e; end" )
85+ println (io, " try;" , line, " ; catch e; @debug \" couldn't precompile statement $line_idx \" exception = e; end" )
6586 else
6687 missed += 1
67- @warn " Incomplete line in precompile file: $line "
88+ @debug " Incomplete line in precompile file: $line "
6889 end
6990 catch e
7091 missed += 1
71- @warn " Parse error in precompile file: $line " exception= e
92+ @debug " Parse error in precompile file: $line " exception= e
7293 end
7394 end
7495 end
7596 @info " used $(line_idx - missed) out of $line_idx precompile statements"
7697 outputfile
7798end
99+
100+
101+ """
102+ snoop_userimg(userimg, packages::Tuple{String, String}...)
103+
104+ Traces all function calls in packages and writes out `precompile` statements into the file `userimg`
105+ """
106+ function snoop_userimg (userimg, packages:: Tuple{String, String} ...; additional_packages = Symbol[])
107+ snooped_precompiles = map (packages) do package_snoopfile
108+ package, snoopfile = package_snoopfile
109+ module_name = Symbol (package)
110+ toml, runtests = package_toml (module_name)
111+ pkg_root = normpath (joinpath (dirname (runtests), " .." ))
112+ file2snoop = if isfile (pkg_root, snoopfile)
113+ joinpath (pkg_root, snoopfile)
114+ else
115+ joinpath (pkg_root, snoopfile)
116+ end
117+ precompile_file = package_folder (package, " precompile.jl" )
118+ snoop (package, toml, file2snoop, precompile_file)
119+ return precompile_file
120+ end
121+ # merge all of the temporary files into a single output
122+ open (userimg, " w" ) do output
123+ for path in snooped_precompiles
124+ open (input -> write (output, input), path)
125+ end
126+ end
127+ nothing
128+ end
0 commit comments