From 2bd513d6591161d271221bb76bc1420bd3c2b3e3 Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Sat, 3 Jul 2021 11:40:24 -0700
Subject: config: ensure that options like `--debug` are set first

`spack --debug config edit` was not working properly -- it would not do show a
stack trace for configuration errors.

- [x] Rework `_main()` and add some notes for maintainers on where things need
      to go for configuration to work properly.
- [x] Move config setup to *after* command-line parsing is done.

Co-authored-by: scheibelp <scheibel1@llnl.gov>
---
 lib/spack/spack/main.py | 55 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 38 insertions(+), 17 deletions(-)

(limited to 'lib')

diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py
index 6e9e8f4f65..651de68c49 100644
--- a/lib/spack/spack/main.py
+++ b/lib/spack/spack/main.py
@@ -743,6 +743,14 @@ def _main(argv=None):
             the executable name. If None, parses from sys.argv.
 
     """
+    # ------------------------------------------------------------------------
+    # main() is tricky to get right, so be careful where you put things.
+    #
+    # Things in this first part of `main()` should *not* require any
+    # configuration. This doesn't include much -- setting up th parser,
+    # restoring some key environment variables, very simple CLI options, etc.
+    # ------------------------------------------------------------------------
+
     # Create a parser with a simple positional argument first.  We'll
     # lazily load the subcommand(s) we need later. This allows us to
     # avoid loading all the modules from spack.cmd when we don't need
@@ -765,20 +773,6 @@ def _main(argv=None):
         if stored_var_name in os.environ:
             os.environ[var] = os.environ[stored_var_name]
 
-    # make spack.config aware of any command line configuration scopes
-    if args.config_scopes:
-        spack.config.command_line_scopes = args.config_scopes
-
-    # activate an environment if one was specified on the command line
-    if not args.no_env:
-        env = spack.cmd.find_environment(args)
-        if env:
-            ev.activate(env, args.use_env_repo)
-
-    if args.print_shell_vars:
-        print_setup_info(*args.print_shell_vars.split(','))
-        return 0
-
     # Just print help and exit if run with no arguments at all
     no_args = (len(sys.argv) == 1) if argv is None else (len(argv) == 0)
     if no_args:
@@ -793,13 +787,40 @@ def _main(argv=None):
     elif args.help:
         sys.stdout.write(parser.format_help(level=args.help))
         return 0
-    elif not args.command:
-        parser.print_help()
-        return 1
+
+    # ------------------------------------------------------------------------
+    # This part of the `main()` sets up Spack's configuration.
+    #
+    # We set command line options (like --debug), then command line config
+    # scopes, then environment configuration here.
+    # ------------------------------------------------------------------------
 
     # ensure options on spack command come before everything
     setup_main_options(args)
 
+    # make spack.config aware of any command line configuration scopes
+    if args.config_scopes:
+        spack.config.command_line_scopes = args.config_scopes
+
+    # activate an environment if one was specified on the command line
+    if not args.no_env:
+        env = spack.cmd.find_environment(args)
+        if env:
+            ev.activate(env, args.use_env_repo)
+
+    # ------------------------------------------------------------------------
+    # Things that require configuration should go below here
+    # ------------------------------------------------------------------------
+    if args.print_shell_vars:
+        print_setup_info(*args.print_shell_vars.split(','))
+        return 0
+
+    # At this point we've considered all the options to spack itself, so we
+    # need a command or we're done.
+    if not args.command:
+        parser.print_help()
+        return 1
+
     # Try to load the particular command the caller asked for.
     cmd_name = args.command[0]
     cmd_name = aliases.get(cmd_name, cmd_name)
-- 
cgit v1.2.3-70-g09d2