From a8e2961010e3bc010325b40a97d3b8891d980f33 Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Fri, 31 Mar 2023 14:46:47 -0400 Subject: [PATCH] Allow configurable stage names (#36509) Add `config:stage_name` which is a Spec format string that can customize the names of stages created by Spack. This was primarily created to allow generating shorter stage names on Windows (along with `config:build_stage`, this can be used to create stages with short absolute paths). By default, this is not set and the prior name stage format is used. This also removes the username component that is always added to Stage paths on Windows (if users want to include this, they can add it to the `build_stage`). --- etc/spack/defaults/windows/config.yaml | 1 + lib/spack/spack/package_base.py | 5 ++--- lib/spack/spack/schema/config.py | 1 + lib/spack/spack/stage.py | 13 ++++++++++++- lib/spack/spack/test/stage.py | 7 +++++-- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/etc/spack/defaults/windows/config.yaml b/etc/spack/defaults/windows/config.yaml index 367bf831cf..53116391cc 100644 --- a/etc/spack/defaults/windows/config.yaml +++ b/etc/spack/defaults/windows/config.yaml @@ -3,3 +3,4 @@ config: concretizer: clingo build_stage:: - '$spack/.staging' + stage_name: '{name}-{version}-{hash:7}' diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py index 470a41c460..98bf4d9c23 100644 --- a/lib/spack/spack/package_base.py +++ b/lib/spack/spack/package_base.py @@ -57,7 +57,7 @@ from spack.filesystem_view import YamlFilesystemView from spack.install_test import TestFailure, TestSuite from spack.installer import InstallError, PackageInstaller -from spack.stage import ResourceStage, Stage, StageComposite, stage_prefix +from spack.stage import ResourceStage, Stage, StageComposite, compute_stage_name from spack.util.executable import ProcessError, which from spack.util.package_hash import package_hash from spack.util.prefix import Prefix @@ -1022,8 +1022,7 @@ def _make_root_stage(self, fetcher): ) # Construct a path where the stage should build.. s = self.spec - stage_name = "{0}{1}-{2}-{3}".format(stage_prefix, s.name, s.version, s.dag_hash()) - + stage_name = compute_stage_name(s) stage = Stage( fetcher, mirror_paths=mirror_paths, diff --git a/lib/spack/spack/schema/config.py b/lib/spack/spack/schema/config.py index abe207cac0..106e45ce7a 100644 --- a/lib/spack/spack/schema/config.py +++ b/lib/spack/spack/schema/config.py @@ -61,6 +61,7 @@ "build_stage": { "oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}] }, + "stage_name": {"type": "string"}, "test_stage": {"type": "string"}, "extensions": {"type": "array", "items": {"type": "string"}}, "template_dirs": {"type": "array", "items": {"type": "string"}}, diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py index f6ed3c1ef4..97e05f365e 100644 --- a/lib/spack/spack/stage.py +++ b/lib/spack/spack/stage.py @@ -35,6 +35,7 @@ import spack.fetch_strategy as fs import spack.mirror import spack.paths +import spack.spec import spack.util.lock import spack.util.path as sup import spack.util.pattern as pattern @@ -49,6 +50,13 @@ stage_prefix = "spack-stage-" +def compute_stage_name(spec): + """Determine stage name given a spec""" + default_stage_structure = "spack-stage-{name}-{version}-{hash}" + stage_name_structure = spack.config.get("config:stage_name", default=default_stage_structure) + return spec.format(format_string=stage_name_structure) + + def create_stage_root(path: str) -> None: """Create the stage root directory and ensure appropriate access perms.""" assert os.path.isabs(path) and len(path.strip()) > 1 @@ -150,7 +158,10 @@ def _resolve_paths(candidates): # Ensure the path is unique per user. can_path = sup.canonicalize_path(path) - if user not in can_path: + # When multiple users share a stage root, we can avoid conflicts between + # them by adding a per-user subdirectory. + # Avoid doing this on Windows to keep stage absolute path as short as possible. + if user not in can_path and not sys.platform == "win32": can_path = os.path.join(can_path, user) paths.append(can_path) diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py index 1cc1f20472..f790a66d0e 100644 --- a/lib/spack/spack/test/stage.py +++ b/lib/spack/spack/test/stage.py @@ -765,8 +765,11 @@ def test_resolve_paths(self): # resolved path without user appends user paths = [os.path.join(os.path.sep, "a", "b", "c")] + can_paths = [paths[0]] user = getpass.getuser() - can_paths = [os.path.join(paths[0], user)] + + if sys.platform != "win32": + can_paths = [os.path.join(paths[0], user)] assert spack.stage._resolve_paths(paths) == can_paths # resolved path with node including user does not append user @@ -789,7 +792,7 @@ def test_resolve_paths(self): res_paths[1] = can_tempdir res_paths[2] = os.path.join(can_tempdir, user) res_paths[3] = os.path.join(can_tempdir, "stage", user) - else: + elif sys.platform != "win32": res_paths[0] = os.path.join(res_paths[0], user) assert spack.stage._resolve_paths(paths) == res_paths