Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly support XDG Base Directory Specification #6

Closed
hvr opened this Issue Oct 25, 2014 · 17 comments

Comments

Projects
None yet
7 participants
@hvr
Copy link
Member

hvr commented Oct 25, 2014

directory provides a function getAppUserDataDirectory :: String -> IO FilePath which on Linux simply constructs the resulting path as ${HOME}/.${APPNAME}.

However, that doesn't conform to the XDG Base Directory Specification

Further reading:

@hvr hvr changed the title Properly support for XDG Base Directory Specification Properly support XDG Base Directory Specification Oct 25, 2014

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Oct 25, 2014

@simonmar commented some time ago:

To do this properly, we should really add getAppConfigDirectory, getAppCacheDirectory etc.

@nomeata

This comment has been minimized.

Copy link

nomeata commented Oct 25, 2014

Clearly, we cannot simply chnage getAppUserDataDirectory, as that would breakt lots of apps. But deprecating it (without a plan to remove it) in favour of additional functions would work.

Or providing a function that returns ${HOME}/.${APPNAME} when available and uses the proper locations otherwise.

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Oct 25, 2014

@nomeata I'm afraid deprecating getAppUserDataDirectory in order to add a new function won't work here, as directory is supposed to be a platform-abstracted package, and getAppUserDataDirectory is already supposed to get you the special folder according the OS-specific conventions (which on Windows is accomplished by using the SHGetFolderPath function)

If we have to deprecate the current getAppUserDataDirectory function incarnation each time we discover the need to update the scheme to conform to some new OS-specific convention, we'll end up with several such functions.

Isn't there a way to modify getAppUserDataDirectory in such a way that it's backward compatible (like you seem to suggest by returning the old-style ${HOME}/.${APPNAME} location if it exists, and only otherwise attempt to be XDG-compliant?)

How did other Linux software manage the migration from pre-XDG to XDG-style per-user application data directories?

@nomeata

This comment has been minimized.

Copy link

nomeata commented Oct 25, 2014

Isn't there a way to modify getAppUserDataDirectory in such a way that it's backward compatible (like you seem to suggest by returning the old-style ${HOME}/.${APPNAME} location if it exists, and only otherwise attempt to be XDG-compliant?)

That would be possible as well, and might be good enough. I think its also what other programs do, unless they actually move one directory to the other (which programs using this function could do as well, of course)

@JLimperg

This comment has been minimized.

Copy link

JLimperg commented Oct 25, 2014

I believe any change to getAppUserDataDirectory would be backwards-incompatible in a strict sense because the documentation specifies that its result must be $HOME/.appName on Unix(-likes). So, is it considered acceptable to break code that relies on this property?

@kmarekspartz

This comment has been minimized.

Copy link

kmarekspartz commented Oct 25, 2014

Does XDG apply for many/most of the programs using this? Isn't XDG primarily for GUI apps?

@pjones

This comment has been minimized.

Copy link

pjones commented Oct 25, 2014

@zeckalpha while the XDG directory specification was pioneered in the GUI world, many daemons and command-line tools use it too. It's especially useful for managing application data vs. configuration.

@hvr How do you feel about a new function getAppUserConfigFile? That function plus getAppUserDataDirectory would probably be enough for most applications to fully support XDG.

I'm traveling the next few days which means I'll have some free time in the evenings to work on this if you need help. I like the idea of getAppUserDataDirectory returning ${HOME}/.${APPNAME} if it exists, and the XDG data directory otherwise. That should be pretty easy to code up and mostly backwards compatibly. It will, of course, break the documentation for tools that expect ${HOME}/.${APPNAME}.

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Oct 25, 2014

@pjones getAppUserConfigFile sounds useful, but what would it return on Windows and/or on other non-XDG platforms? (Maybe the same value as getAppUserDataDirectory?)

@pjones

This comment has been minimized.

Copy link

pjones commented Oct 25, 2014

@hvr In that case I think it should be:

dir <- getAppUserDataDirectory appName
return (dir </> appName)

for consistency. An alternative to using the appName twice would be "config" instead.

@odi

This comment has been minimized.

Copy link

odi commented Oct 27, 2014

What is if I have two different environments for one application. On has set the XDG Base Directory an one hast not. Maybe the application would conduct different in this case and the solution will be better to split these functions in XDG based functions and not XDG based functions and the user of this library should decide for he's own if he uses the XDG based functions or not.

The other way is to make this decision in the library itself e.g.

getAppUserDataDirectory appname = do
  dir <- lookupEnv "XDG_DATA_HOME"
  case dir of
   Nothing -> do
     home <- getEnv "HOME"
     return $ home ++ '/' : '.' : appname
   Just path -> return $ path ++ '/' : appname

IMHO this should be the base decision of this discussion.

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Nov 6, 2014

I just became aware of the related Xmonad Issue 484: Use XDG Base Directory Specification (which links also back here)

@Rufflewind Rufflewind added discussion and removed help wanted labels Mar 3, 2015

@Rufflewind

This comment has been minimized.

Copy link
Member

Rufflewind commented Mar 8, 2015

If we have to deprecate the current getAppUserDataDirectory function incarnation each time we discover the need to update the scheme to conform to some new OS-specific convention, we'll end up with several such functions.

I don't think new OS conventions would appear so rapidly as to be a problem. I also don't think there's a way to implement this in a backward-compatible manner: changing the location will cause surprises for both developers and end-users downstream.

If we do add a new function, getAppUserDataDirectory should not be deprecated, since some may prefer the traditional behavior. From my experience, quite a few programs (probably the majority) still use the traditional location.

We could add the following functions:

  • getXdgUserDataDirectory: XDG_DATA_HOME (~/.local/share); Windows: AppData/Roaming?
  • getXdgUserConfigDirectory: XDG_CONFIG_HOME (~/.config); Windows: AppData/Roaming
  • getXdgUserCacheDirectory: XDG_CACHE_HOME (~/.cache); Windows: AppData/Local
  • getXdgUserRuntimeDirectory: XDG_RUNTIME_DIR (/tmp/<user>/<app>?); Windows: AppData/Local/Temp?

and perhaps some utility functions for obtaining various search paths:

  • getExecutableDirectories: PATH (not relevant to this issue though)
  • getXdgDataDirectories: XDG_DATA_DIRS (/usr/local/share/:/usr/share); Windows: none?
  • getXdgConfigDirectories: XDG_CONFIG_DIRS (/etc/xdg); Windows: none?

However, I don't know if we should add all of them right away since not all of them are necessarily in demand. (Do people even use XDG_RUNTIME_DIR? Using /etc/xdg seems very unnecessary, IMO.)

@Rufflewind Rufflewind added this to the 1.2.3.0 milestone Mar 11, 2015

Rufflewind added a commit to Rufflewind/directory that referenced this issue Apr 24, 2015

Implement support for XDG-conforming application directories
This addresses issue haskell#6 in a backward-compatible way by adding a new
function that conforms to the XDG Base Directory Specification.

Rufflewind added a commit to Rufflewind/directory that referenced this issue Apr 27, 2015

Implement support for XDG-conforming application directories
This addresses issue haskell#6 in a backward-compatible way by adding a new
function that conforms to the XDG Base Directory Specification.
@Rufflewind

This comment has been minimized.

Copy link
Member

Rufflewind commented Apr 27, 2015

Fixed in ab9d081

(I did not implement the utility functions nor XDG_RUNTIME_DIR however. Nonetheless, if anyone else has a need for them feel free to open another ticket.)

@Rufflewind Rufflewind closed this Apr 27, 2015

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Apr 27, 2015

@Rufflewind I'm wondering, with the given operation, what's the recommended upgrade path for code currently using getAppUserDataDirectory? If we have a recommendation, which may want to document it in the Haddocks...

@Rufflewind

This comment has been minimized.

Copy link
Member

Rufflewind commented Apr 27, 2015

Note: I don't intend to deprecate getAppUserDataDirectory, so applications are not required to migrate to getXdgDirectory unless they really want to.

Unfortunately, migrating is not as simple as tweaking the code:

  1. Using the new location will obviously break any existing setup, thus a user-friendly application should seamlessly migrate the files to the new location. (And update any old documentation, tutorials, etc, to reflect this.)

  2. One must consider where the files are to be migrated. Depending on how strict the application wants to conform to the XDG specs, it will have to decide between

    • ~/.local/share (equivalent to /usr/share but user-specific),
    • ~/.config (equivalent to /etc but user-specific),
    • ~/.cache (equivalent to /var/cache but user-specific),

    or some subset of the three. If the developer doesn't care about these pesky details, ~/.config seems like a safe bet, I think.

  3. Updating the code to use the XDG directories is pretty straightforward. Just replace

    getAppUserDataDirectory "myapp"
    

    with

    getXdgDirectory XdgConfig "myapp"
    

    Substitute XdgConfig with XdgData or XdgCache as needed.

Ultimately, the hardest part is performing the migration correctly :\

Edit: this comment has been updated to use the new two-parameter API instead of the previous one-parameter API, as suggested by hvr's comment below

@hvr

This comment has been minimized.

Copy link
Member Author

hvr commented Apr 27, 2015

I think any guidance we can give via the documentation helps users (to avoid having everyone having to figure it out for themselves individually, as well as reducing the risk of mistakes)

bikeshed-warning Looking at your last example, I wonder if there are any cases where you want the base-folder returned by

getXdgDirectory :: XdgDirectory -> IO FilePath

rather than having the slightly more convenient form

getXdgDirectory :: XdgDirectory -> FilePath -> IO FilePath

which would encode the policy that you're expected to refer to an app-specific subfolder in the respective Xdg-base-folder... (and if you deliberately pass the empty string or ".", you'd still get the base-folder)

@Rufflewind

This comment has been minimized.

Copy link
Member

Rufflewind commented Apr 27, 2015

I agree the latter is more convenient. I picked the former because it felt simpler and didn't make any assumptions as to what the user intended to do with it.

However, I think passing an empty string to get the base directory is reasonable as long as it's documented behavior. For getAppUserDataDirectory it's currently unspecified, but that can be fixed. (On second thought: I'm not sure if there's a reason to fix that.)

bgamari pushed a commit to bgamari/directory that referenced this issue Jul 29, 2016

Refactor: don't use reverse explicitly (haskell#6)
I couldn't decide between the different implementations of dropWhileEnd and
takeWhileEnd from https://ghc.haskell.org/trac/ghc/ticket/9623#comment:7, so I
choose the simplest solution using two times reverse instead of foldr. See
also:
https://www.haskell.org/pipermail/libraries/2014-September/023835.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.