Skip to content

Commit 1090435

Browse files
committed
store the dependency list in the serialized image header
1 parent c1398a3 commit 1090435

File tree

4 files changed

+124
-24
lines changed

4 files changed

+124
-24
lines changed

base/docs/helpdb.jl

+15
Original file line numberDiff line numberDiff line change
@@ -14564,6 +14564,21 @@ Evaluate the contents of a source file in the current context. During including,
1456414564
"""
1456514565
include
1456614566

14567+
doc"""
14568+
```rst
14569+
::
14570+
include_dependency(path::AbstractString)
14571+
14572+
In a module, declare that the file specified by `path` (relative or
14573+
absolute) is a dependency for precompilation; that is, the
14574+
module will need to be recompiled if this file changes.
14575+
14576+
This is only needed if your module depends on a file that is not
14577+
used via `include`. It has no effect outside of compilation.
14578+
```
14579+
"""
14580+
include_dependency
14581+
1456714582
doc"""
1456814583
```rst
1456914584
::

base/loading.jl

+28-4
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,11 @@ end
122122

123123
# require always works in Main scope and loads files from node 1
124124
toplevel_load = true
125-
function _require(mod::Symbol, track_dependencies::Bool)
125+
function require(mod::Symbol)
126+
# dependency-tracking is only used for one top-level include(path),
127+
# and is not applied recursively to imported modules:
126128
old_track_dependencies = _track_dependencies[1]
127-
_track_dependencies[1] = track_dependencies
129+
_track_dependencies[1] = false
128130

129131
global toplevel_load
130132
loading = get(package_locks, mod, false)
@@ -176,8 +178,6 @@ function _require(mod::Symbol, track_dependencies::Bool)
176178
nothing
177179
end
178180

179-
require(mod::Symbol) = _require(mod, false)
180-
181181
# remote/parallel load
182182

183183
include_string(txt::ByteString, fname::ByteString) =
@@ -267,6 +267,7 @@ function create_expr_cache(input::AbstractString, output::AbstractString)
267267
task_local_storage()[:SOURCE_PATH] = $(source)
268268
end)
269269
end
270+
serialize(io, :(Base._track_dependencies[1] = true))
270271
serialize(io, :(Base.include($(abspath(input)))))
271272
if source !== nothing
272273
serialize(io, quote
@@ -291,3 +292,26 @@ function compile(name::ByteString)
291292
create_expr_cache(path, cachefile)
292293
return cachefile
293294
end
295+
296+
module_uuid(m::Module) = ccall(:jl_module_uuid, UInt64, (Any,), m)
297+
298+
function cache_dependencies(cachefile::AbstractString)
299+
modules = Tuple{ByteString,UInt64}[]
300+
files = ByteString[]
301+
open(cachefile, "r") do f
302+
while true
303+
n = ntoh(read(f, Int32))
304+
n == 0 && break
305+
push!(modules,
306+
(bytestring(readbytes(f, n)), # module name
307+
ntoh(read(f, UInt64)))) # module UUID (timestamp)
308+
end
309+
read(f, Int64) # total bytes for file dependencies
310+
while true
311+
n = ntoh(read(f, Int32))
312+
n == 0 && break
313+
push!(files, bytestring(readbytes(f, n)))
314+
end
315+
end
316+
return modules, files
317+
end

src/dump.c

+63-10
Original file line numberDiff line numberDiff line change
@@ -132,46 +132,48 @@ static jl_array_t *datatype_list=NULL; // (only used in MODE_SYSTEM_IMAGE)
132132
#define write_int8(s, n) write_uint8(s, n)
133133
#define read_int8(s) read_uint8(s)
134134

135+
/* read and write in network (bigendian) order: */
136+
135137
static void write_int32(ios_t *s, int32_t i)
136138
{
137-
write_uint8(s, i & 0xff);
138-
write_uint8(s, (i>> 8) & 0xff);
139-
write_uint8(s, (i>>16) & 0xff);
140139
write_uint8(s, (i>>24) & 0xff);
140+
write_uint8(s, (i>>16) & 0xff);
141+
write_uint8(s, (i>> 8) & 0xff);
142+
write_uint8(s, i & 0xff);
141143
}
142144

143145
static int32_t read_int32(ios_t *s)
144146
{
145-
int b0 = read_uint8(s);
146-
int b1 = read_uint8(s);
147-
int b2 = read_uint8(s);
148147
int b3 = read_uint8(s);
148+
int b2 = read_uint8(s);
149+
int b1 = read_uint8(s);
150+
int b0 = read_uint8(s);
149151
return b0 | (b1<<8) | (b2<<16) | (b3<<24);
150152
}
151153

152154
static void write_uint64(ios_t *s, uint64_t i)
153155
{
154-
write_int32(s, i & 0xffffffff);
155156
write_int32(s, (i>>32) & 0xffffffff);
157+
write_int32(s, i & 0xffffffff);
156158
}
157159

158160
static uint64_t read_uint64(ios_t *s)
159161
{
160-
uint64_t b0 = (uint32_t)read_int32(s);
161162
uint64_t b1 = (uint32_t)read_int32(s);
163+
uint64_t b0 = (uint32_t)read_int32(s);
162164
return b0 | (b1<<32);
163165
}
164166

165167
static void write_uint16(ios_t *s, uint16_t i)
166168
{
167-
write_uint8(s, i & 0xff);
168169
write_uint8(s, (i>> 8) & 0xff);
170+
write_uint8(s, i & 0xff);
169171
}
170172

171173
static uint16_t read_uint16(ios_t *s)
172174
{
173-
int b0 = read_uint8(s);
174175
int b1 = read_uint8(s);
176+
int b0 = read_uint8(s);
175177
return b0 | (b1<<8);
176178
}
177179

@@ -949,6 +951,54 @@ void jl_serialize_mod_list(ios_t *s)
949951
write_int32(s, 0);
950952
}
951953

954+
// serialize the global _require_dependencies array of pathnames that
955+
// are include depenencies
956+
void jl_serialize_dependency_list(ios_t *s)
957+
{
958+
size_t total_size = 0;
959+
static jl_array_t *deps = NULL;
960+
if (!deps)
961+
deps = (jl_array_t*)jl_get_global(jl_base_module, jl_symbol("_require_dependencies"));
962+
if (deps) {
963+
// sort!(deps) so that we can easily eliminate duplicates
964+
static jl_value_t *sort_func = NULL;
965+
if (!sort_func)
966+
sort_func = jl_get_global(jl_base_module, jl_symbol("sort!"));
967+
jl_apply((jl_function_t*)sort_func, (jl_value_t**)&deps, 1);
968+
969+
size_t l = jl_array_len(deps);
970+
jl_value_t *prev = NULL;
971+
for (size_t i=0; i < l; i++) {
972+
jl_value_t *dep = jl_cellref(deps, i);
973+
size_t slen = jl_string_len(dep);
974+
if (!prev ||
975+
memcmp(jl_string_data(dep), jl_string_data(prev), slen)) {
976+
total_size += 4 + slen;
977+
}
978+
prev = dep;
979+
}
980+
total_size += 4;
981+
}
982+
// write the total size so that we can quickly seek past all of the
983+
// dependencies if we don't need them
984+
write_uint64(s, total_size);
985+
if (deps) {
986+
size_t l = jl_array_len(deps);
987+
jl_value_t *prev = NULL;
988+
for (size_t i=0; i < l; i++) {
989+
jl_value_t *dep = jl_cellref(deps, i);
990+
size_t slen = jl_string_len(dep);
991+
if (!prev ||
992+
memcmp(jl_string_data(dep), jl_string_data(prev), slen)) {
993+
write_int32(s, slen);
994+
ios_write(s, jl_string_data(dep), slen);
995+
}
996+
prev = dep;
997+
}
998+
write_int32(s, 0); // terminator, for ease of reading
999+
}
1000+
}
1001+
9521002
// --- deserialize ---
9531003

9541004
static jl_fptr_t jl_deserialize_fptr(ios_t *s)
@@ -1893,6 +1943,7 @@ DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist)
18931943
}
18941944
serializer_worklist = worklist;
18951945
jl_serialize_mod_list(&f); // this can throw, keep it early (before any actual initialization)
1946+
jl_serialize_dependency_list(&f);
18961947

18971948
JL_SIGATOMIC_BEGIN();
18981949
arraylist_new(&reinit_list, 0);
@@ -1937,6 +1988,8 @@ static jl_array_t *_jl_restore_incremental(ios_t *f)
19371988
ios_close(f);
19381989
return NULL;
19391990
}
1991+
size_t deplen = read_uint64(f);
1992+
ios_skip(f, deplen); // skip past the dependency list
19401993
JL_SIGATOMIC_BEGIN();
19411994
arraylist_new(&backref_list, 4000);
19421995
arraylist_push(&backref_list, jl_main_module);

test/compile.jl

+18-10
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,34 @@ try
1313
print(f, """
1414
module $Foo_module
1515
@doc "foo function" foo(x) = x + 1
16+
include_dependency("foo.jl")
1617
module Bar
1718
@doc "bar function" bar(x) = x + 2
19+
include_dependency("bar.jl")
1820
end
1921
end
2022
""")
2123
end
2224

23-
Base.compile(Foo_module)
25+
cachefile = Base.compile(Foo_module)
2426
eval(Main, :(import $Foo_module))
27+
28+
let Foo = eval(Main, Foo_module)
29+
@test Foo.foo(17) == 18
30+
@test Foo.Bar.bar(17) == 19
31+
32+
# issue #12284:
33+
@test stringmime("text/plain", Base.Docs.doc(Foo.foo)) == "foo function\n"
34+
@test stringmime("text/plain", Base.Docs.doc(Foo.Bar.bar)) == "bar function\n"
35+
36+
deps = Base.cache_dependencies(cachefile)
37+
@test sort(deps[1]) == map(n -> (n, Base.module_uuid(eval(symbol(n)))),
38+
["Base","Core","Main"])
39+
@test sort(deps[2]) == [file,joinpath(dir,"bar.jl"),joinpath(dir,"foo.jl")]
40+
end
41+
2542
finally
2643
splice!(Base.LOAD_CACHE_PATH, 1)
2744
splice!(LOAD_PATH, 1)
2845
rm(dir, recursive=true)
2946
end
30-
31-
let Foo = eval(Main, Foo_module)
32-
@test Foo.foo(17) == 18
33-
@test Foo.Bar.bar(17) == 19
34-
35-
# issue #12284:
36-
@test stringmime("text/plain", Base.Docs.doc(Foo.foo)) == "foo function\n"
37-
@test stringmime("text/plain", Base.Docs.doc(Foo.Bar.bar)) == "bar function\n"
38-
end

0 commit comments

Comments
 (0)