Skip to content

Shared spack#47615

Open
scheibelp wants to merge 482 commits intospack:developfrom
scheibelp:features/shared-spack-5
Open

Shared spack#47615
scheibelp wants to merge 482 commits intospack:developfrom
scheibelp:features/shared-spack-5

Conversation

@scheibelp
Copy link
Copy Markdown
Member

@scheibelp scheibelp commented Nov 15, 2024

Closes #15939
Closes #49939
Closes #11871
Fixes #52251

This is a redo of #11871, which aims to make Spack pip/apt-installable. Use cases are described in detail here (they are numbered, and this PR description may refer to them like "U1"), along with parts of 11871 that are not handled here.

Moving Spack-generated artifacts outside of $spack

The biggest blocker to creating a shared spack instance is that Spack will default to writing data into its own prefix. For new clones of Spack, this PR moves all generated files out of the cloned directory and into ~. This includes:

  1. installs
  2. where environments are stored
  3. cached downloads
  4. gpg keys
  5. where modules are generated

For old clones:

  • If no new spack clones are generated and only old spack clones exist, then each of them will continue using their old install location inside of their respective $spack directories.
  • If a new clone starts writing into ~ and an old spack instance git pulls this logic, the old instance will start using ~. A warning is generated in this case

1-3 are large. Before this PR there were config options to relocate each of these individually (and those still take precedence). This PR offers additional mechanisms to set a single env var or config option in order to relocate all of them together.

Controlling install location

This PR establishes the following order:

  1. config:install_tree:root:...
  2. SPACK_DATA_HOME env var if that is set
  3. SPACK_HOME env var if that is set
  4. config:locations:data if that is set
  5. config:locations:home if that is set
  6. XDG_DATA_HOME env var if that is set
  7. Under the default for XDG_DATA_HOME: ~/.local/share/spack

If an older clone of spack pulls this, and has existing installs in $spack/opt/spack (the old default), then:

  • [1-5] always take precedence
  • [6,7] take precedence if they are not empty

Other notes:

  • This PR maintains a default setting for config:install_tree:root so that users who do spack config get config will see that setting, but it uses a special default variable that effectively behaves as if it were unset.
  • This applies similarly for envs, but the associated config vars is config:environments_root
  • Downloads are relocated unconditionally (although are still overridden by config:source_cache)
  • All of 1-3 from the prior section can be relocated by setting SPACK_DATA_HOME, XDG_DATA_HOME or config:locations:data
  • Everything (old and new) that would be written to $spack or ~ before can be relocated by setting SPACK_HOME or config:locations:home, except for the user config scope
    • Update Feb 11 2026: etc/spack/include.yaml sets path: "$spack_home/.config/spack/" for the user scope. In order to influence this, the user must set the SPACK_HOME env var, or they must set config:locations:home at a scope that is lower priority than the spack scope (right now that is awkward - see: Shared spack #47615 (comment))
    • Update Mar 5, 2026: undoing change from prior point: allowing include: config to reference config-based location variables causes too many headaches
  • The user can disable env vars by setting config:locations:disable_env:true (i.e. 2, 3, 6 will be ignored).

Admin management, pip-installed spack

  • When this PR was first made, new installations would prefer to install in $spack. As of now, everything goes into ~. I think this default behavior will be sufficient to support pip installed spack
  • If admins want to provide an upstream to users but avoid using it themselves, they can use existing include: config in the spack scope:
    • Include a scope that adds upstreams:x config when: SPACK_ADMIN not in env
    • Include a scope that adds install_tree:root:x config when: SPACK_ADMIN in env
    • As long as all admins make sure to define this env var, this will satisfy [5a/b], and

avoiding ~

Before this PR, you could do

export SPACK_USER_CACHE_PATH=x
export SPACK_DISABLE_LOCAL_CONFIG=1

and nothing would be read from or written to ~

With this PR, that would not be enough. Some ways to do that here:

  • This PR includes a new spack isolate command that you can run to configure spack to write into $spack
  • If you set SPACK_HOME=x SPACK_USER_CONFIG_PATH=x/config, everything will be written to x
  • Or you can do SPACK_HOME=x SPACK_DISABLE_LOCAL_CONFIG=1
  • If you set SPACK_DATA_HOME or config:locations:data outside of ~, this will relocate everything that takes up significant space (envs, installs, cached downloads)

To completely move everything, in an env you can do:

include:: []
config:
  locations:
    home: x

Users who have write access to $spack can do

spack config --scope=spack edit include
spack config --scope=spack add config:locations:home:x

XDG compliance

Everything that was written into ~/.spack is now relocated to XDG-compliant alternatives:

  • Default user config scope location (now in ~/.config/spack)
  • Default user cache path location (which includes the misc cache) now in ~/.local/state/spack
    • Overridden by $SPACK_USER_CACHE_PATH (highest priority), $SPACK_STATE_HOME, and $XDG_STATE_HOME

Everything moving out of $spack is moving into XDG-compliant default locations under ~.

  • (4) gpg keys move to $SPACK_DATA_HOME/gpg-keys
  • (5) Default module location moved to $SPACK_DATA_HOME (e.g. $SPACK_DATA_HOME/lmod)

See also: #49939

Crosstalk between spack instances

  • The misc. cache now includes a per-spack component (based on a hash of the Spack root)
    • config:misc_cache still exists, so users can share a single misc cache among multiple instances of spack
  • Ideally each individual spack would have a distinct highest-level write scope, but for now they all share ~/.config/spack

Use cases/considerations

All items here are addressed, although there is some friction with [4]

  1. apt install of Spack
  2. pip install of Spack
    a. sub-case, with --user (i.e. user may have write permissions to the install location)
  3. Admin provides spack to users
    a. but users might choose to clone spack themselves
    b. and they may or may not want to use an admin-curated upstream: it should be possible for users to get an upstream DB by default from an admin without running any extra commands
  4. ~ may or may not have significant storage space
  5. simple config management
    a. admin should be able to define config that only applies to users, not admins
    b. Multiple admins may all want to share a config, but not provide that config to an end-user
    c. users may want their install location to differ on a per-spack basis (but still live outside of the spack prefix)
  6. Handling new/old spack instances: the logic should make sense for...
    a. new git clones of spack
    b. git pulls in existing clones (and e.g. should be able to use gpg keys)
    c. Discarding a pre-existing download cache is not a problem

TODOs

  • Testing

Maybe TODOs

  • Admins may want to provide gpg keys to users, but right now that is not configurable
  • It could make sense to provide a command that relocates gpg keys, cached downloads, modules, and environments from their "old" locations (inside of Spack) to their "new" locations
  • In the case of a pip install, this is only intended to be useable as a cmd-line tool and not importable: nevertheless, it may be useful to strip out vendored libs (i.e. code in lib/spack/external/) and handle it w/requirements.txt.
    • That is possibly complicated if the versions we vendor (intended to support back to Python 3.6) are no longer supported/available
    • UPDATE: since this PR came out, the top-level vendored modules moved into a spack.vendor namespace, so long as those top-level modules do not themselves require vendored libs, this would prevent vendored Spack python deps from interfering with imports.

Things not translated from #11871

If prefixed w/ "drop", I think we can skip it entirely

  • (todo, possibly in another PR) Upstreams were refactored to work without configuration
    • This is considered a good idea, but isn't strictly required to make Spack apt/pip-installable
  • (todo, possibly in another PR) 11871 moved module roots to the install tree root. This another improvement for upstreams, although not strictly required to support admin/end-user use cases
  • (drop) 11871 had a notion of managing multiple install trees and being able to select them with e.g. spack --install-tree=... install
    • I claim this implementation covers the same use cases without depending on this concept. For example there was a notion of an end-user install tree and an admin install tree, and user-install trees were automatically directed to the admin install tree as an upstream
  • (drop) 11871 introduced a configuration scope for each installation root. Roughly speaking, this is awkward because store.py depends on config (so this would introduce a cyclic dependency).
    • (drop) Permissions customization for install trees: with per-install-tree config, it became useful to distinguish desired permissions for the admin install tree vs. the end-user install tree: this use case is now addressed with admin-specific config scopes

Other changes

Not essential to this PR, but related to other changes made here

  • This has a rudimentary audit to detect writes into the Spack prefix (sys.addaudithook); it is enabled with --guard-writes-into-spack

@spackbot-app spackbot-app bot added commands core PR affects Spack core functionality defaults environments modules tests General test capability(ies) utilities labels Nov 15, 2024
@scheibelp scheibelp marked this pull request as draft November 15, 2024 08:29
@psakievich
Copy link
Copy Markdown
Contributor

@matthewcurry

@matthewcurry
Copy link
Copy Markdown
Contributor

matthewcurry commented Jan 15, 2025

I've driven this around extensively, poking in particular at upstream use cases. It would appear that upstreams has advanced enough since #11871 that a number of problems fixed in the original patch set are no longer an issue.

So far as upstreams working without configuration, I also noticed that a shared installation could configure itself as an upstream in $spack/etc/spack/defaults/upstreams.yaml, and that just worked for both the user managing that spack instance and a different user loading that spack instance. Neat! Probably not the greatest way to do it, but allows for doing something close until we have time to do another PR.

@scheibelp
Copy link
Copy Markdown
Member Author

I also noticed that a shared installation could configure itself as an upstream in $spack/etc/spack/defaults/upstreams.yaml, and that just worked for both the user managing that spack instance and a different user loading that spack instance. Neat! Probably not the greatest way to do it, but allows for doing something close until we have time to do another PR.

FWIW I added the end-user config scope so that this could be handled in a conceptually straightforward way:

  • Admins put config in /etc/spack/end-user setting their spack install tree as an upstream
  • When they run, they do spack --disable-end-user-config install... and in their case, the upstream isn't used

In other words, I didn't expect what you did to work. We might want to prevent that from happening, because I'm not sure what is actually going on when the upstream is the same as the root.

(this is mentioned in the subsection "New config scopes for user/admin interaction"; it would also be possible to implicitly omit an upstream if it matches the root)

@matthewcurry
Copy link
Copy Markdown
Contributor

I generally think this is very useful, and is specifically useful for pip/apt. What do we need to do to get this beyond a draft? What can I do to help?

Comment thread lib/spack/spack/test/data/config/config.yaml Outdated
@matthewcurry
Copy link
Copy Markdown
Contributor

It would be good to add to the documentation for scopes. Here's something I wrote for configuration.rst, but would go much better in this PR:


#. **site-admin**: Stored in ``$(prefix)/etc/site-admin``. Settings
   here affect only *this instance* of Spack. This scope is designed
   to allow a system administrator to make settings available to other
   privileged users, e.g. those that have read access to the
   site-admin configuration directory. Those who cannot read this
   directory will skip this scope.

and


#. **this-spack**: Stored in the user configuration directory:
   ``~/.spack/$(spack_instance_id)``. This configuration affects a
   particular instance of Spack denoted by the instance ID, much
   like the site scope. A user of a read-only Spack instance can use
   this scope to modify its behavior.

Copy link
Copy Markdown
Contributor

@psakievich psakievich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lot's of great work here. Specific comments are added for sections I have design concerns on. I would also like to see some unit-tests for:

  • XDG mapping/fall back behaviors. Seems like a lot of permutations are possible here. Seeing the tests would help me to spot and/or understand corner cases.
  • site admin stuff (I'm kind of just trusting this works as intended without a use case or docs in front of me)
  • file permission issue handling for users interfacing with an admin install

IMO Those seem to be the main new features that are not tested in the current test-suite.

Comment thread lib/spack/spack/paths.py Outdated
Comment thread lib/spack/spack/paths.py Outdated
Comment thread etc/spack/defaults/base/config.yaml Outdated
Comment thread lib/spack/spack/paths.py Outdated
Comment thread lib/spack/spack/util/path.py Outdated
Comment thread lib/spack/spack/util/path.py Outdated
Copy link
Copy Markdown
Contributor

@psakievich psakievich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly commented on docs. Overall I think it's cool to see the way this has come together. There's certainly a lot more nuance and detail to this effort than I realized when I first got involved. Major thanks to @scheibelp to keeping with it.

Comment on lines +92 to +99
#. ``SPACK_DATA_HOME`` env var if that is set
#. Under ``SPACK_HOME`` env var; for ``$data_home``, it is ``$SPACK_HOME/.local/share/spack``
#. ``config:locations:data``
#. Under ``config:locations:home``; for ``$data_home`` it is ``$spack_home/.local/share/spack``
#. ``XDG_DATA_HOME/spack`` if XDG_DATA_HOME is set
#. Under the default for ``XDG_DATA_HOME``: ``~/.local/share/spack``

``config:locations:home`` / ``SPACK_HOME`` can be used to control all 3 of ``data_home``, ``cache_home``, and ``state_home``.
Copy link
Copy Markdown
Contributor

@psakievich psakievich Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ordering is clever with the way you've weaved in and out of XDG. But it is also complex the way we weave from env variable to config back to env variable.

This is kind of addressed in the upper section, but I'd lay out the intention of XDG in a more upfront manner. There are at least 4 distinctive layers of customization profiles here:

  1. User env has no XDG_*, gets pure defaults (which fall back to XDG inspired defaults)
  2. User env has XDG_*, but other wise pure defaults (data starts moving around)
  3. User sets SPACK_HOME (Move everything together)
  4. User starts playing with all the other knobs to get truly custom data movements (with a further hierarchy between config and env variables).

This was a little confusing to me when reading. I'd reorder for the explanation.

I'd go with a table showing the defaults and XDG mappings. Then I'd say something like:

"This is intended to map to the XDG standard which allows users to collectively customize where data goes by default across many programs. We have also provided a way to customize specifically for spack."

  1. Everything is designed to nest under $SPACK_HOME for a wholistic data relocation strategy
  2. $SPACK_HOME is set by
  • (Env) SPACK_HOME
  • (Config) config:locations:home
  • (Default) XDG_CONFIG_HOME or ~/.local/spack
  1. Specific sections can still be re-located independent of SPACK_HOME or XDG via env-variables or config

Then show the hierarchy for $data_home, or maybe present them all as a table?

Comment thread lib/spack/spack/schema/config.py
Copy link
Copy Markdown
Member

@haampie haampie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably one of the most breaking PRs in a long time 😬 and to me the benefits are marginal.

I think it's very risky to merge this, cause it undermines the narrative that Spack v1 is a stable tool. I would not merge this before it is made backward compatible.

Review from a user perspective without looking at the code and pr description (say I just updated Spack).

First thing I see is multiline warnings on each command:

==> Warning: Bypassing config in '/Users/user/.spack' in favor of '/Users/user/.config/spack'.
    Detected config in old location: '/Users/user/.config/spack'.
    Set `SPACK_USER_CONFIG_PATH` or modify your include.yaml to use '/Users/user/.spack'
    or move files from '/Users/user/.spack' to '/Users/user/.config/spack' to suppress this warning.
  • What does "bypassing config" mean and why does the second line say "detected"? Raises the question if you detect it, why can't you use it?
  • I have no means to judge whether (1) SPACK_USER_CONFIG, (2) include.yaml (I've never used this and wouldn't even know the syntax, also... what include.yaml?), or (3) "move files" (what files?) is better.

As a Spack developer I happen to know that mv ~/.spack ~/.config/spack is not what is meant. Other users won't have a clue. They certainly won't know that ~/.spack/bootstrap is not relocatable.

Normally (buildcache, package repo, config deprecations) these warning messages show a command to run to migrate. Not here...

Next thing I tried was spack find, which worked 🤞. Then spack config blame c<tab> and it spits out the 4 line warning again (annoying). That's how I learned that the warning indeed means it does not respect my user config.

Running spack install zlib it installs zlib in $spack/opt/spack but clones a fresh spack-packages on a different commit. That's going to frustrate so many users.

I do not get why Spack is backwards compatible with install_tree, but that it refuses to use my config from ~/.spack, even though I never had a ~/.local/spack dir.

In my opinion you should make this backwards compatible and just branch on existence of ~/.spack and non-existence of ~/.local/spack, if so assume old Spack structure. Then deprecate ~/.spack in v1.3 or v1.4.


Few more thoughts:

  • It's likely breaking existing CI workflows: fresh clone of spack to $ci_temp_dir/spack; the expectation was that installs go into $ci_temp_dir/spack/opt/spack, but now they end up in ~/.local/share/spack/installs. If there's no isolation (jenkins, self-hosted gitlab runners) these are possibly persistent. That will probably raise a bunch of user complaints. How should user configure this?
  • ~/.local/share/spack/installs is shared, that complicates having two Spack instances say v1.3 and v1.4 where v1.4 migrates the database to a new format as a side effect of saving; now the v1.3 instance is no longer usable.
  • the path ~/.local/share/spack/installs is longer than my previous ~/spack/opt/spack; deeply nested paths are bad for performance (kernel has to resolve them, realpath is more stat calls) and can run into path length and other limits faster.
  • On HPC systems, the home dir is not particularly fast; I would normally always cd to the faster shared filesystem, clone Spack there, and then run install. Now I would have to clone it, configure it, and then install. That's another step, I'm sure we'll get complaints about that, given that all our examples of using spack are git clone; . setup-env.sh; spack install. (In that sense I'm also confused that people asked for this...)

@alalazo
Copy link
Copy Markdown
Member

alalazo commented Apr 15, 2026

I also tried the PR briefly, and agree with @haampie that we should make it backward compatible first. Asking the users to update manually after a git pull if they want to keep using their configuration doesn't seem very user friendly.

At a high-level I think it would be better to have a single marker that tells Spack which "layout" is in use, instead of migrating parts of the configuration per-path. The current per-path fallback heuristics are fragile because they have no single source of truth. A single marker solves this:

  • Once a user is on XDG, all paths use XDG defaults consistently.
  • If on old layout, all paths use old layout defaults.

If possible, I'd try to stick to:

New spack instances should get XDG paths automatically, but existing users should only move when they ask to.

and avoid emitting warnings on each command.

More detailed considerations on warnings and defaults

The warnings are really shown every time:
Screenshot from 2026-04-15 09-04-38

which will likely cause a lot of gripes (see above where I ask for --help on a command). As a minor point, the old location is wrong in the message (that's the new location).

On top of not accounting for the default config location, it seems to me that moving defaults (e.g. from $spack to $data_home) may also break people, if they relied on the previous default values:

  1. $spack/var/spack/cache -> $data_home/downloads may cause unwanted downloads if people relied on the source cache. Wondering if we have use cases where $spack is persisted somewhere to avoid network usage, but $data_home is not which would cause downloads every new run / job in CI.
  2. $spack/share/spack/{modules,lmod} -> $data_home/{modules,lmod} will break spack module {tcl,lmod} refresh etc. if users relied on the previous default directory for module files
  3. Packages that requires license files will fail to find them in the old location.
  4. Existing binary cache signing setups that rely on keys in $spack/var/spack/gpg will break

Comment thread lib/spack/spack/paths.py
Comment on lines +414 to +415
Run this first-thing when starting a spack child process that is
used for builds. Resolve all variables that are XDG-dependent, in
Copy link
Copy Markdown
Member

@haampie haampie Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels suboptimal. For any new lazy constant added, you have to remember to extend it. Also, you don't call it in the new installer (and preferably shouldn't).

It'd be better to wrap these constants in an other object and pass them as function argument (i.e. serialization in the spawn/forkserver case):

  1. In the spawn case you can't expect the subprocess to reinitialize the state exactly as the parent process.
  2. Not all functions that use multiprocessing want to use GlobalStateMarshaler, because it has enormous overhead: it serializes the active environment (afaik redundantly, but well...).

…ncluding gpg keys, install paths); new checkouts will write into new (shared) paths; tests are partially updated

Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
…ey are now getting used)

Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
… paths in default config)

Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Signed-off-by: Peter Scheibel <scheibel1@llnl.gov>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

binary-caches bootstrap Anything that has to do with Spack building its own dependencies. ci Issues related to Continuous Integration commands config core PR affects Spack core functionality defaults docs documentation Improvements or additions to documentation environments modules new-command shell-support solver style tests General test capability(ies) unit-tests utilities versions workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't call poll() before start() & attempting to write-lock with --disable-locks (2 interrelated errors?) XDG compliance in Spack