Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ See the docstring for `set_preferences!()` for the full details of how to set pr
Preferences that are accessed during compilation are automatically marked as compile-time preferences, and any change recorded to these preferences will cause the Julia compiler to recompile any cached precompilation `.ji` files for that module.
This allows preferences to be used to influence code generation.
When your package sets a compile-time preference, it is usually best to suggest to the user that they should restart Julia, to allow recompilation to occur.
It is possible to disable this marking by passing `disable_invalidation=true` to `load_preference()`.

Note that the package can be installed on Julia v1.0+ but is only functional on Julia v1.6+.

Expand Down Expand Up @@ -66,7 +67,7 @@ function do_computation()
end


# A non-compiletime preference
# A preference that does not trigger invalidation
function set_username(username::String)
@set_preferences!("username" => username)
end
Expand Down
44 changes: 28 additions & 16 deletions src/Preferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,51 @@ export load_preference, @load_preference,
include("utils.jl")

"""
load_preference(uuid_or_module_or_name, key, default = nothing)
load_preference(uuid_or_module_or_name, key, default = nothing; disable_invalidation = false)

Load a particular preference from the `Preferences.toml` file, shallowly merging keys
as it walks the hierarchy of load paths, loading preferences from all environments that
list the given UUID as a direct dependency.

If `disable_invalidation` is `false` (the default) and Julia is currently precompiling,
the preference is recorded as a compile-time dependency so that the precompiled cache is
automatically invalidated when the preference changes. Set `disable_invalidation` to
`true` to opt out of this: the preference will still be loaded during precompilation, but
changes to it will **not** cause the package to be recompiled. Be careful when setting
this. If a preference is somehow cached in the module (e.g. as a global
variable) changing it will **not** invalidate the precompiled cache.

Most users should use the `@load_preference` convenience macro which auto-determines the
calling `Module`.
"""
function load_preference end
function load_preference(uuid::UUID, key::String, default = nothing)
function load_preference(uuid::UUID, key::String, default = nothing; disable_invalidation::Bool = false)
# Re-use definition in `base/loading.jl` so as to not repeat code.
d = Base.get_preferences(uuid)
if currently_compiling()
if !disable_invalidation && currently_compiling()
Base.record_compiletime_preference(uuid, key)
end
return drop_clears(get(d, key, default))
end
function load_preference(m::Module, key::String, default = nothing)
return load_preference(get_uuid(m), key, default)
function load_preference(m::Module, key::String, default = nothing; disable_invalidation::Bool = false)
return load_preference(get_uuid(m), key, default; disable_invalidation)
end
function load_preference(name::String, key::String, default = nothing)
function load_preference(name::String, key::String, default = nothing; disable_invalidation::Bool = false)
uuid = get_uuid(name)
if uuid === nothing
package_lookup_error(name)
end
return load_preference(uuid, key, default)
return load_preference(uuid, key, default; disable_invalidation)
end

"""
@load_preference(key)
@load_preference(key, default = nothing)

Convenience macro to call `load_preference()` for the current package.

To opt out of compile-time preference tracking, use the function form directly:

load_preference(@__MODULE__, key, default; disable_invalidation = true)
"""
macro load_preference(key, default = nothing)
return quote
Expand All @@ -56,26 +68,26 @@ macro load_preference(key, default = nothing)
end

"""
has_preference(uuid_or_module_or_name, key)
has_preference(uuid_or_module_or_name, key; disable_invalidation = false)

Return `true` if the particular preference is found, and `false` otherwise.

See the `has_preference` docstring for more details.
See the `load_preference` docstring for details on the `disable_invalidation` keyword.
"""
function has_preference end
function has_preference(uuid::UUID, key::String)
value = load_preference(uuid, key, nothing)
function has_preference(uuid::UUID, key::String; disable_invalidation::Bool = false)
value = load_preference(uuid, key, nothing; disable_invalidation)
return !(value isa Nothing)
end
function has_preference(m::Module, key::String)
return has_preference(get_uuid(m), key)
function has_preference(m::Module, key::String; disable_invalidation::Bool = false)
return has_preference(get_uuid(m), key; disable_invalidation)
end
function has_preference(name::String, key::String)
function has_preference(name::String, key::String; disable_invalidation::Bool = false)
uuid = get_uuid(name)
if uuid === nothing
package_lookup_error(name)
end
return has_preference(uuid, key)
return has_preference(uuid, key; disable_invalidation)
end

"""
Expand Down
5 changes: 4 additions & 1 deletion test/UsesPreferences/src/UsesPreferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ end

const backend = @load_preference("backend", "OpenCL")

# A compile-time preference that does NOT trigger recompilation when changed
const backend_noncached = load_preference(@__MODULE__, "backend_noncached", "OpenCL"; disable_invalidation=true)

# An example that helps us to prove that things are happening at compile-time
function do_computation()
@static if backend == "OpenCL"
Expand All @@ -32,7 +35,7 @@ function do_computation()
end


# A non-compiletime preference
# A preference that does not trigger invalidation
function set_username(username::String)
@set_preferences!("username" => username)
end
Expand Down
20 changes: 19 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,25 @@ up_path = joinpath(@__DIR__, "UsesPreferences")
output = activate_and_run(up_path, cuda_test_didnt_precompile; env=Dict("JULIA_DEBUG" => "loading"))
@test !did_precompile(output)

# Test non-compiletime preferences a bit
# Test that changing a preference loaded with `disable_invalidation=true` does not
# trigger recompilation, even though it was read at module-load time.
activate_and_run(up_path, """
using Preferences
using Base: UUID
set_preferences!($(repr(up_uuid)), "backend_noncached" => "CUDA"; force=true)
""")
noncached_test = """
using Test
# Make sure `UsesPreferences` is already precompiled
isdefined(Base, :isprecompiled) && @test Base.isprecompiled(Base.identify_package("UsesPreferences"))
using UsesPreferences
isdefined(Base, :isprecompiled) && @test Base.isprecompiled(Base.identify_package("UsesPreferences"))
@test UsesPreferences.backend_noncached == "OpenCL" # stale cached value
"""
output = activate_and_run(up_path, noncached_test; env=Dict("JULIA_DEBUG" => "loading"))
@test !did_precompile(output)

# Test preferences with invalidation disabled
activate_and_run(up_path, """
using UsesPreferences, Test, Preferences
using Base: UUID
Expand Down
Loading