Skip to content

Commit

Permalink
add "#pragma compilable [=true]" for opt-in to automatic compilation …
Browse files Browse the repository at this point in the history
…when a module is required (closes JuliaLang#12462)
  • Loading branch information
stevengj committed Aug 5, 2015
1 parent 1fcfa4a commit e017c21
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 11 deletions.
73 changes: 64 additions & 9 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,45 @@ function find_all_in_cache_path(mod::Symbol)
paths
end

const r_compilable = r"^#\s*pragma\s+compilable(\s*=\s*true)?\s*$"
const r_ncompilable = r"^#\s*pragma\s+compilable\s*=\s*false\s*$"

# return true if "#pragma compilable [=true]" appears in the file before
# any Julia code, false for "#pragma compilable=false", default otherwise
function compilable(path::AbstractString, default::Bool=false)
return open(path, "r") do f
for line in eachline(f)
s = lstrip(line)
if !isempty(s)
if s[1] == '#'
ismatch(r_compilable, s) && return true
ismatch(r_ncompilable, s) && return false
else
return default
end
end
end
return default
end
end

# compile path on node 1 if path is #pragma compilable,
# returning the cachefile path, or nothing otherwise
function autocompile_on_node1(mod::Symbol, path::AbstractString)
if myid() == 1
if compilable(path)
if isinteractive()
info("Compiling module $mod from $path...")
end
return compile(mod)
else
return nothing
end
else
return remotecall_fetch(1, autocompile_on_node1, mod, path)
end
end

function _include_from_serialized(content::Vector{UInt8})
return ccall(:jl_restore_incremental_from_buf, Any, (Ptr{Uint8},Int), content, sizeof(content))
end
Expand Down Expand Up @@ -84,6 +123,14 @@ function _require_from_serialized(node::Int, path_to_try::ByteString, toplevel_l
restored = _include_from_serialized(content)
end
# otherwise, continue search

if restored !== nothing
for M in restored
if isdefined(M, :__META__)
push!(Base.Docs.modules, M)
end
end
end
return restored
end

Expand Down Expand Up @@ -119,15 +166,9 @@ function require(mod::Symbol)
last = toplevel_load::Bool
try
toplevel_load = false
restored = _require_from_serialized(1, mod, last)
if restored !== nothing
for M in restored
if isdefined(M, :__META__)
push!(Base.Docs.modules, M)
end
end
return true
end
if nothing !== _require_from_serialized(1, mod, last)
return
end
if JLOptions().incremental != 0
# spawn off a new incremental compile task from node 1 for recursive `require` calls
cachefile = compile(mod)
Expand All @@ -140,6 +181,19 @@ function require(mod::Symbol)
name = string(mod)
path = find_in_node_path(name, source_dir(), 1)
path === nothing && throw(ArgumentError("$name not found in path"))

if last || nprocs() == 1
cachefile = autocompile_on_node1(mod, path)
if cachefile !== nothing
if nothing === _require_from_serialized(1, cachefile, last)
warn("require failed to create a precompiled cache file")
else
return
end
end
end

# could not compile, just include(path)
if last && myid() == 1 && nprocs() > 1
# broadcast top-level import/using from node 1 (only)
content = open(readall, path)
Expand Down Expand Up @@ -262,6 +316,7 @@ function compile(name::ByteString)
myid() == 1 || error("can only compile from node 1")
path = find_in_path(name)
path === nothing && throw(ArgumentError("$name not found in path"))
!compilable(path, true) && throw(ArgumentError("$name has #pragma compilable=false"))
cachepath = LOAD_CACHE_PATH[1]
if !isdir(cachepath)
mkpath(cachepath)
Expand Down
10 changes: 8 additions & 2 deletions test/compile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ try

open(file, "w") do f
print(f, """
# Auto-compile this module:
# pragma compilable
module $Foo_module
@doc "foo function" foo(x) = x + 1
module Bar
Expand All @@ -20,8 +22,12 @@ try
""")
end

Base.compile(Foo_module)
eval(Main, :(import $Foo_module))
# make sure the "#pragma compilable" causes Foo to be compiled
cachefile = Base.autocompile_on_node1(Foo_module, file)
@test cachefile !== nothing

# make sure the compiled module is successfully loaded:
@test nothing !== Base._require_from_serialized(1, cachefile, true)
finally
splice!(Base.LOAD_CACHE_PATH, 1)
splice!(LOAD_PATH, 1)
Expand Down

0 comments on commit e017c21

Please sign in to comment.