Skip to content

Commit

Permalink
Store the evaluating module in precompile cache
Browse files Browse the repository at this point in the history
This ensures that it's possible to re-evaluate modified code on a file-by-file basis (as with Revise)
  • Loading branch information
timholy committed Oct 3, 2017
1 parent 5ed9551 commit a053453
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 21 deletions.
34 changes: 19 additions & 15 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,17 @@ const include_callbacks = Any[]

# used to optionally track dependencies when requiring a module:
const _concrete_dependencies = Any[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
const _require_dependencies = Any[] # a list of (path, mtime) tuples that are the file dependencies of the module currently being precompiled
const _require_dependencies = Any[] # a list of (mod, path, mtime) tuples that are the file dependencies of the module currently being precompiled
const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies
function _include_dependency(_path::AbstractString)
function _include_dependency(modstring::AbstractString, _path::AbstractString)
prev = source_path(nothing)
if prev === nothing
path = abspath(_path)
else
path = joinpath(dirname(prev), _path)
end
if _track_dependencies[]
push!(_require_dependencies, (path, mtime(path)))
push!(_require_dependencies, (modstring, path, mtime(path)))
end
return path, prev
end
Expand All @@ -234,7 +234,7 @@ This is only needed if your module depends on a file that is not used via `inclu
no effect outside of compilation.
"""
function include_dependency(path::AbstractString)
_include_dependency(path)
_include_dependency("#__external__", path)
return nothing
end

Expand Down Expand Up @@ -522,7 +522,7 @@ end

include_relative(mod::Module, path::AbstractString) = include_relative(mod, String(path))
function include_relative(mod::Module, _path::String)
path, prev = _include_dependency(_path)
path, prev = _include_dependency(string(mod), _path)
for callback in include_callbacks # to preserve order, must come before Core.include
invokelatest(callback, mod, path)
end
Expand Down Expand Up @@ -678,13 +678,17 @@ function parse_cache_header(f::IO)
end
totbytes = ntoh(read(f, Int64)) # total bytes for file dependencies
# read the list of files
files = Tuple{String,Float64}[]
files = Tuple{String,String,Float64}[]
while true
n = ntoh(read(f, Int32))
n == 0 && break
totbytes -= 4 + n + 8
@assert n >= 0 "EOF while reading cache header" # probably means this wasn't a valid file to be read by Base.parse_cache_header
push!(files, (String(read(f, n)), ntoh(read(f, Float64))))
n1 = ntoh(read(f, Int32))
n1 == 0 && break
@assert n1 >= 0 "EOF while reading cache header" # probably means this wasn't a valid file to be read by Base.parse_cache_header
modname = String(read(f, n1))
n2 = ntoh(read(f, Int32))
@assert n2 >= 0 "EOF while reading cache header" # probably means this wasn't a valid file to be read by Base.parse_cache_header
filename = String(read(f, n2))
push!(files, (modname, filename, ntoh(read(f, Float64))))
totbytes -= 8 + n1 + n2 + 8
end
@assert totbytes == 4 "header of cache file appears to be corrupt"
# read the list of modules that are required to be present during loading
Expand All @@ -711,7 +715,7 @@ end

function cache_dependencies(f::IO)
defs, files, modules = parse_cache_header(f)
return modules, files
return modules, map(mod_fl_mt -> (mod_fl_mt[2], mod_fl_mt[3]), files) # discard the module
end

function cache_dependencies(cachefile::String)
Expand Down Expand Up @@ -763,11 +767,11 @@ function stale_cachefile(modpath::String, cachefile::String)
end

# now check if this file is fresh relative to its source files
if !samefile(files[1][1], modpath)
DEBUG_LOADING[] && info("JL_DEBUG_LOADING: Rejecting cache file $cachefile because it is for file $(files[1][1])) not file $modpath.")
if !samefile(files[1][2], modpath)
DEBUG_LOADING[] && info("JL_DEBUG_LOADING: Rejecting cache file $cachefile because it is for file $(files[1][2])) not file $modpath.")
return true # cache file was compiled from a different path
end
for (f, ftime_req) in files
for (_, f, ftime_req) in files
# Issue #13606: compensate for Docker images rounding mtimes
# Issue #20837: compensate for GlusterFS truncating mtimes to microseconds
ftime = mtime(f)
Expand Down
14 changes: 10 additions & 4 deletions src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ static void write_mod_list(ios_t *s, jl_array_t *a)
}

// "magic" string and version header of .ji file
static const int JI_FORMAT_VERSION = 3;
static const int JI_FORMAT_VERSION = 4;
static const char JI_MAGIC[] = "\373jli\r\n\032\n"; // based on PNG signature
static const uint16_t BOM = 0xFEFF; // byte-order marker
static void write_header(ios_t *s)
Expand Down Expand Up @@ -1089,7 +1089,9 @@ static void write_dependency_list(ios_t *s)
for (size_t i=0; i < l; i++) {
jl_value_t *dep = jl_fieldref(jl_array_ptr_ref(udeps, i), 0);
size_t slen = jl_string_len(dep);
total_size += 4 + slen + 8;
dep = jl_fieldref(jl_array_ptr_ref(udeps, i), 1);
slen += jl_string_len(dep);
total_size += 8 + slen + 8;
}
total_size += 4;
}
Expand All @@ -1100,11 +1102,15 @@ static void write_dependency_list(ios_t *s)
size_t l = jl_array_len(udeps);
for (size_t i=0; i < l; i++) {
jl_value_t *deptuple = jl_array_ptr_ref(udeps, i);
jl_value_t *dep = jl_fieldref(deptuple, 0);
jl_value_t *dep = jl_fieldref(deptuple, 0); // evaluating module (as string)
size_t slen = jl_string_len(dep);
write_int32(s, slen);
ios_write(s, jl_string_data(dep), slen);
write_float64(s, jl_unbox_float64(jl_fieldref(deptuple, 1)));
dep = jl_fieldref(deptuple, 1); // file abspath
slen = jl_string_len(dep);
write_int32(s, slen);
ios_write(s, jl_string_data(dep), slen);
write_float64(s, jl_unbox_float64(jl_fieldref(deptuple, 2))); // mtime
}
write_int32(s, 0); // terminator, for ease of reading
}
Expand Down
5 changes: 3 additions & 2 deletions test/compile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,17 @@ try
@test stringmime("text/plain", Base.Docs.doc(Foo.Bar.bar)) == "bar function\n"

modules, deps, required_modules = Base.parse_cache_header(cachefile)
discard_module = mod_fl_mt -> (mod_fl_mt[2], mod_fl_mt[3])
@test modules == Dict(Foo_module => Base.module_uuid(Foo))
@test map(x -> x[1], sort(deps)) == [Foo_file, joinpath(dir, "bar.jl"), joinpath(dir, "foo.jl")]
@test map(x -> x[1], sort(discard_module.(deps))) == [Foo_file, joinpath(dir, "bar.jl"), joinpath(dir, "foo.jl")]

modules, deps1 = Base.cache_dependencies(cachefile)
@test modules == merge(Dict(s => Base.module_uuid(getfield(Foo, s)) for s in
[:Base, :Core, Foo2_module, FooBase_module, :Main, :Test]),
# plus modules included in the system image
Dict(s => Base.module_uuid(Base.root_module(s)) for s in
[:DelimitedFiles,:Mmap]))
@test deps == deps1
@test discard_module.(deps) == deps1

@test current_task()(0x01, 0x4000, 0x30031234) == 2
@test nothing(0x01, 0x4000, 0x30031234) == 52
Expand Down

0 comments on commit a053453

Please sign in to comment.