Skip to content

Commit 1b4be2a

Browse files
authored
Bundle preferences with apps/libraries (#852)
1 parent 8cf8eff commit 1b4be2a

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

docs/src/apps.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,18 @@ that does some simple arithmetic. It is instructive to see how the [artifact
202202
file](https://github.com/JuliaLang/PackageCompiler.jl/blob/master/examples/MyApp/Artifacts.toml)
203203
is [used in the source](https://github.com/JuliaLang/PackageCompiler.jl/blob/d722a3d91abe328ebd239e2f45660be35263ebe1/examples/MyApp/src/MyApp.jl#L7-L8).
204204

205+
### [Preferences](@id app-preferences)
206+
207+
Compile-time preferences used by any of the packages included in the app will be stored in
208+
the sysimage. To support runtime preferences, all preferences that the app "sees" during the
209+
compilation process are stored in the app bundle under
210+
`<app_dir>/share/julia/LocalPreferences.toml`. Note that preferences loaded at compile time
211+
are *not* affected by the values in the `LocalPreferences.toml`, but modifying the file
212+
*will* change the value of preferences loaded at runtime.
213+
214+
To learn more about compile time preferences and runtime preferences, please refer to the
215+
[Preferences.jl docs](https://juliapackaging.github.io/Preferences.jl/stable/).
216+
205217
### Reverse engineering the compiled app
206218

207219
While the created app is relocatable and no source code is bundled with it,
@@ -281,3 +293,13 @@ CodeInfo(
281293
│ %10 = Base.repr(%8)
282294
...
283295
```
296+
297+
#### Preferences in `<app_dir>/share/julia`
298+
As described [above](@ref app-preferences), a TOML file with all preferences active during
299+
the compilation process will be stored with the app bundle. If your preferences may contain
300+
confidential information, you can either delete the
301+
`<app_dir>/share/julia/LocalPreferences.toml` file before distributing the app bundle, or
302+
suppress the preference file generation by passing `include_preferences=false` to
303+
`create_app`. Note, however, that if the preference file is not present, any preference
304+
loaded in your app at *runtime* will use their default value (or crash, if no default is
305+
provided).

docs/src/libs.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,15 @@ Note that on Unix-like operating systems (including Mac), your library must have
117117

118118
See [here](https://github.com/simonbyrne/libcg) for a more complete example of how this might look.
119119

120+
### [Preferences](@id library-preferences)
121+
122+
Compile-time preferences used by any of the packages included in the library will be stored in
123+
the sysimage. To support runtime preferences, all preferences that the library "sees" during the
124+
compilation process are stored in the library bundle under
125+
`<dest_dir>/share/julia/LocalPreferences.toml`. Note that preferences loaded at compile time
126+
are *not* affected by the values in the `LocalPreferences.toml`, but modifying the file
127+
*will* change the value of preferences loaded at runtime.
128+
129+
To learn more about compile time preferences and runtime preferences, please refer to the
130+
[Preferences.jl docs](https://juliapackaging.github.io/Preferences.jl/stable/).
131+

src/PackageCompiler.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,9 @@ compiler (can also include extra arguments to the compiler, like `-g`).
778778
transitive dependencies into the sysimage. This only makes a difference if some
779779
packages do not load all their dependencies when themselves are loaded. Defaults to `true`.
780780
781+
- `include_preferences::Bool`: If `true`, store all preferences visible by the project in
782+
`package_dir` in the app bundle. Defaults to `true`.
783+
781784
### Advanced keyword arguments
782785
783786
- `cpu_target::String`: The value to use for `JULIA_CPU_TARGET` when building the system image.
@@ -800,6 +803,7 @@ function create_app(package_dir::String,
800803
include_lazy_artifacts::Bool=false,
801804
sysimage_build_args::Cmd=``,
802805
include_transitive_dependencies::Bool=true,
806+
include_preferences::Bool=true,
803807
script::Union{Nothing, String}=nothing)
804808
warn_official()
805809
if filter_stdlibs && incremental
@@ -822,6 +826,7 @@ function create_app(package_dir::String,
822826
bundle_julia_libraries(app_dir, stdlibs)
823827
bundle_julia_executable(app_dir)
824828
bundle_project(ctx, app_dir)
829+
include_preferences && bundle_preferences(ctx, app_dir)
825830
bundle_cert(app_dir)
826831

827832
sysimage_path = joinpath(app_dir, "lib", "julia", "sys." * Libdl.dlext)
@@ -970,6 +975,9 @@ compiler (can also include extra arguments to the compiler, like `-g`).
970975
transitive dependencies into the sysimage. This only makes a difference if some
971976
packages do not load all their dependencies when themselves are loaded. Defaults to `true`.
972977
978+
- `include_preferences::Bool`: If `true`, store all preferences visible by the project in
979+
`project_or_package` in the library bundle. Defaults to `true`.
980+
973981
- `script::String`: Path to a file that gets executed in the `--output-o` process.
974982
975983
### Advanced keyword arguments
@@ -996,6 +1004,7 @@ function create_library(package_or_project::String,
9961004
include_lazy_artifacts::Bool=false,
9971005
sysimage_build_args::Cmd=``,
9981006
include_transitive_dependencies::Bool=true,
1007+
include_preferences::Bool=true,
9991008
script::Union{Nothing,String}=nothing
10001009
)
10011010

@@ -1032,6 +1041,7 @@ function create_library(package_or_project::String,
10321041
bundle_artifacts(ctx, dest_dir; include_lazy_artifacts)
10331042
bundle_headers(dest_dir, header_files)
10341043
bundle_project(ctx, dest_dir)
1044+
include_preferences && bundle_preferences(ctx, dest_dir)
10351045
bundle_cert(dest_dir)
10361046

10371047
lib_dir = Sys.iswindows() ? joinpath(dest_dir, "bin") : joinpath(dest_dir, "lib")
@@ -1442,4 +1452,38 @@ function bundle_cert(dest_dir)
14421452
cp(cert_path, joinpath(share_path, "cert.pem"))
14431453
end
14441454

1455+
# Write preferences for packages in project `project_dir` to `io`
1456+
function dump_preferences(io::IO, project_dir)
1457+
# Note: in `command` we cannot just use `Base.get_preferences()`, since this API was
1458+
# only introduced in Julia v1.8
1459+
command = """
1460+
using TOML, Pkg
1461+
# For each dependency pair (UUID => PackageInfo), store preferences in Dict
1462+
prefs = Dict{String,Any}(last(dep).name => Base.get_preferences(first(dep)) for dep in Pkg.dependencies())
1463+
# Filter out packages without preferences
1464+
filter!(p -> !isempty(last(p)), prefs)
1465+
TOML.print(prefs, sorted=true)
1466+
"""
1467+
prefs = read(`$(Base.julia_cmd()) --project=$project_dir -e "$command"`, String)
1468+
write(io, prefs)
1469+
1470+
nothing
1471+
end
1472+
dump_preferences(project_dir) = dump_preferences(stdout, project_dir)
1473+
1474+
# Collect all preferences of the active project and store them in the `LOAD_PATH`
1475+
# Note: for apps/libraries, the `LOAD_PATH` defaults to `<dest_dir>/share/julia`
1476+
function bundle_preferences(ctx, dest_dir)
1477+
share_path = joinpath(dest_dir, "share", "julia")
1478+
mkpath(share_path)
1479+
preferences_path = joinpath(share_path, "LocalPreferences.toml")
1480+
project_dir = dirname(ctx.env.project_file)
1481+
1482+
open(preferences_path, "w") do io
1483+
dump_preferences(io, project_dir)
1484+
end
1485+
1486+
return
1487+
end
1488+
14451489
end # module

0 commit comments

Comments
 (0)