[ANN] BaseDirs.jl (was XDG)

After much indecision regarding the package name, BaseDirs is finally registered! :partying_face:

See the Pre-ANN/RFC thread for a recap on what the package is, but the TLDR; is it provides a cross-platform way of finding the appropriate place to look for/put files, because .julia is not a panacea.

This is a very small and lightweight package (with zero dependencies), using Julia 1.9-rc1 it takes ~25ms to load on my machine.

If you work on a package that asks any questions like “where should I put some cache files?”, “where can I find system fonts?”, “what’s a good place to save a document?” or similar, I’d recommend giving this package a look.

For more info, see the (somewhat detailed) docs: https://tecosaur.github.io/BaseDirs.jl


To finish off, here’s some example usage of the package:

julia> using BaseDirs

julia> BaseDirs.CONFIG_HOME[]
"/home/tec/.config"

julia> BaseDirs.User.config()
"/home/tec/.config"

julia> BaseDirs.User.config("sub", "dir/")
"/home/tec/.config/sub/dir/"

julia> BaseDirs.User.config(BaseDirs.Project("mything"), "config.conf", create=true)
"/home/tec/.config/mything/config.conf"

See GitHub - JuliaPackaging/Scratch.jl: Scratch spaces for all your persistent mutable data needs that features automatic garbage collection and optional versioning.

From a glance, since Scratch.jl seems to want to make interacting with cache files easier, I’d suggest that Scratch.jl itself may want to consider using BaseDirs to put the cache files in the platform-appropriate location.

Version 1.3 has just been released, and includes a few nice improvements:

  • The accessor functions BaseDirs.User.cache, BaseDirs.User.runtime and, BaseDirs.User.state have been copied into the top-level module since they don’t conflict with any system directories.
  • A BaseDirs.Project can now be constructed from a module, and modules can be passed as the first argument of an accessor function.
  • Together these first two changes let you simplify BaseDirs.User.cache(BaseDirs.Project("MyModule"), ...) to BaseDirs.cache(MyModule, ...)
  • Warnings when a BaseDirs result is illegally baked into precompilation are now raised
  • TTFX has been further improved, I’m now seeing 3.5ms on my machine

It’s funny, this is exactly the sort of package you’d think could have a v1.0 release and be done/complete, but there’s a long tail of subtle bugfixes/polish/tweaks to be made. Along these lines, I’d like to announce:

Version 1.4 of BaseDirs

Future breaking changes

  • Project has been renamed to the more apt App (with a depwarn’d alias for backward compat until v2.0)

Linux improvements

  • On systemd sandbox DynamicUser runtime, state, cache, and configuration sandbox paths are now recognised and honoured. We use the (rather generically named) systemd env vars only when the current process’ cgroup ends with .service. If multiple directories are listed, we just use the first.
  • We support the recently enabled XDG projects directory (User.projects)
  • We now read the system default dirs (/etc/xdg/user-dirs.defaults) before applying the user’s dir customisations
  • Fixed an inversion of the per-user desktop directory config check

Other improvements

  • App (ex. Project) is now fully concrete (fixed oversight)
  • Reduced allocations when doing ASCII path-folding
  • Windows now defaults to ~/bin when there is no user bin-like directory on PATH
  • More consistent handling of XDG dir env vars on non-unix systems (obey if set)
  • App(::Module) now uses moduleroot to ensure that nested submodules resolve to the correct package name when using App(@__MODULE__)
  • A whole bunch of docstring typo fixes
  • TTFX is all the way down to <1ms on Julia 1.13-rc1

I also looked into JuliaC support for this release. It turns out that BaseDirs works a treat with JuliaC as-is …except for the precompilation warning code. As soon as there’s a way to exclude that code from compilation, BaseDirs will “just work” with JuliaC (I imagine this could be useful for anybody wanting to create a well-behaved cross-platform executable — using the system state/cache dirs becomes particularly important when you can’t assume ~/.julia exists).

Thanks for the work on that package, it’s one of these tools that I keep using without even thinking about it anymore!