From 715214c1a16e1c955f98337c9da3803dd6f5ee32 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Mon, 6 May 2024 10:00:23 +0200 Subject: [PATCH] `spack env create `: dir if dir-like (#44024) A named env cannot contain `.` and `/`. So when a user runs `spack env create ./here` do not error but treat it as `spack env create -d ./here`. Also fix help string of `spack env create`, which seems to have been copied from `activate` incorrectly. --- lib/spack/docs/environments.rst | 37 +++++++++++++-------- lib/spack/spack/cmd/env.py | 58 ++++++++++++++++----------------- lib/spack/spack/test/cmd/env.py | 2 +- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/lib/spack/docs/environments.rst b/lib/spack/docs/environments.rst index 4104af619f..78a903a77e 100644 --- a/lib/spack/docs/environments.rst +++ b/lib/spack/docs/environments.rst @@ -142,12 +142,8 @@ user's prompt to begin with the environment name in brackets. $ spack env activate -p myenv [myenv] $ ... -The ``activate`` command can also be used to create a new environment, if it is -not already defined, by adding the ``--create`` flag. Managed and anonymous -environments, anonymous environments are explained in the next section, -can both be created using the same flags that `spack env create` accepts. -If an environment already exists then spack will simply activate it and ignore the -create specific flags. +The ``activate`` command can also be used to create a new environment if it does not already +exist. .. code-block:: console @@ -176,21 +172,36 @@ environment will remove the view from the user environment. Anonymous Environments ^^^^^^^^^^^^^^^^^^^^^^ -Any directory can be treated as an environment if it contains a file -``spack.yaml``. To load an anonymous environment, use: +Apart from managed environments, Spack also supports anonymous environments. + +Anonymous environments can be placed in any directory of choice. + +.. note:: + + When uninstalling packages, Spack asks the user to confirm the removal of packages + that are still used in a managed environment. This is not the case for anonymous + environments. + +To create an anonymous environment, use one of the following commands: .. code-block:: console - $ spack env activate -d /path/to/directory + $ spack env create --dir my_env + $ spack env create ./my_env -Anonymous specs can be created in place using the command: +As a shorthand, you can also create an anonymous environment upon activation if it does not +already exist: .. code-block:: console - $ spack env create -d . + $ spack env activate --create ./my_env + +For convenience, Spack can also place an anonymous environment in a temporary directory for you: + +.. code-block:: console + + $ spack env activate --temp -In this case Spack simply creates a ``spack.yaml`` file in the requested -directory. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Environment Sensitive Commands diff --git a/lib/spack/spack/cmd/env.py b/lib/spack/spack/cmd/env.py index d308b89a6c..13f67e74e6 100644 --- a/lib/spack/spack/cmd/env.py +++ b/lib/spack/spack/cmd/env.py @@ -16,7 +16,7 @@ import llnl.util.filesystem as fs import llnl.util.tty as tty from llnl.util.tty.colify import colify -from llnl.util.tty.color import colorize +from llnl.util.tty.color import cescape, colorize import spack.cmd import spack.cmd.common @@ -61,14 +61,7 @@ # def env_create_setup_parser(subparser): """create a new environment""" - subparser.add_argument( - "env_name", - metavar="env", - help=( - "name of managed environment or directory of the anonymous env " - "(when using --dir/-d) to activate" - ), - ) + subparser.add_argument("env_name", metavar="env", help="name or directory of environment") subparser.add_argument( "-d", "--dir", action="store_true", help="create an environment in a specific directory" ) @@ -114,7 +107,7 @@ def env_create(args): env = _env_create( args.env_name, init_file=args.envfile, - dir=args.dir, + dir=args.dir or os.path.sep in args.env_name or args.env_name in (".", ".."), with_view=with_view, keep_relative=args.keep_relative, ) @@ -123,34 +116,39 @@ def env_create(args): env.regenerate_views() -def _env_create(name_or_path, *, init_file=None, dir=False, with_view=None, keep_relative=False): +def _env_create( + name_or_path: str, + *, + init_file: Optional[str] = None, + dir: bool = False, + with_view: Optional[str] = None, + keep_relative: bool = False, +): """Create a new environment, with an optional yaml description. Arguments: - name_or_path (str): name of the environment to create, or path to it - init_file (str or file): optional initialization file -- can be - a JSON lockfile (*.lock, *.json) or YAML manifest file - dir (bool): if True, create an environment in a directory instead - of a named environment - keep_relative (bool): if True, develop paths are copied verbatim into - the new environment file, otherwise they may be made absolute if the - new environment is in a different location + name_or_path: name of the environment to create, or path to it + init_file: optional initialization file -- can be a JSON lockfile (*.lock, *.json) or YAML + manifest file + dir: if True, create an environment in a directory instead of a managed environment + keep_relative: if True, develop paths are copied verbatim into the new environment file, + otherwise they may be made absolute if the new environment is in a different location """ if not dir: env = ev.create( name_or_path, init_file=init_file, with_view=with_view, keep_relative=keep_relative ) - tty.msg("Created environment '%s' in %s" % (name_or_path, env.path)) - tty.msg("You can activate this environment with:") - tty.msg(" spack env activate %s" % (name_or_path)) - return env - - env = ev.create_in_dir( - name_or_path, init_file=init_file, with_view=with_view, keep_relative=keep_relative - ) - tty.msg("Created environment in %s" % env.path) - tty.msg("You can activate this environment with:") - tty.msg(" spack env activate %s" % env.path) + tty.msg( + colorize( + f"Created environment @c{{{cescape(env.name)}}} in: @c{{{cescape(env.path)}}}" + ) + ) + else: + env = ev.create_in_dir( + name_or_path, init_file=init_file, with_view=with_view, keep_relative=keep_relative + ) + tty.msg(colorize(f"Created anonymous environment in: @c{{{cescape(env.path)}}}")) + tty.msg(f"Activate with: {colorize(f'@c{{spack env activate {cescape(name_or_path)}}}')}") return env diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 264ea5de0d..a05222db6b 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -3290,7 +3290,7 @@ def test_create_and_activate_managed(tmp_path): def test_create_and_activate_anonymous(tmp_path): with fs.working_dir(str(tmp_path)): env_dir = os.path.join(str(tmp_path), "foo") - shell = env("activate", "--without-view", "--create", "--sh", "-d", env_dir) + shell = env("activate", "--without-view", "--create", "--sh", env_dir) active_env_var = next(line for line in shell.splitlines() if ev.spack_env_var in line) assert str(env_dir) in active_env_var assert ev.is_env_dir(env_dir)