Repository: ESMCI/cime

Issue:
case.setup refuses to fix corrupted env_mach_specfic.xml
Introduced with #3449 if the env_mach_specific.xml file in the case is corrupted or missing 
case.setup --reset will not run
```
 ./case.setup --reset

Successfully cleaned env_mach_specific.xml
Successfully cleaned Macros.make
Traceback (most recent call last):
  File "./case.setup", line 62, in <module>
    _main_func(__doc__)
  File "./case.setup", line 59, in _main_func
    case.case_setup(clean=clean, test_mode=test_mode, reset=reset)
  File "/glade/work/jedwards/sandboxes/cesm2_x_nuopc/cime/scripts/Tools/../../scripts/lib/CIME/case/case.py", line 195, in __exit__
    self.flush()
  File "/glade/work/jedwards/sandboxes/cesm2_x_nuopc/cime/scripts/Tools/../../scripts/lib/CIME/case/case.py", line 270, in flush
    env_file.write(force_write=flushall)
  File "/glade/work/jedwards/sandboxes/cesm2_x_nuopc/cime/scripts/Tools/../../scripts/lib/CIME/XML/generic_xml.py", line 325, in write
    self.check_timestamp()
  File "/glade/work/jedwards/sandboxes/cesm2_x_nuopc/cime/scripts/Tools/../../scripts/lib/CIME/XML/generic_xml.py", line 317, in check_timestamp
    timestamp_file  = os.path.getmtime(self.filename)
  File "/usr/lib64/python2.7/genericpath.py", line 62, in getmtime
    return os.stat(filename).st_mtime
OSError: [Errno 2] No such file or directory: '/glade/scratch/jedwards/SMS_D_Ln5_Vnuopc.f10_f10_mg37.QPC6.cheyenne_intel.cam-outfrq9s.20200317_164905_wiaa4z/env_mach_specific.xml'
```


Discussion:
@jgfouca have you looked into this at all?   I realize that I frequently want to change the module commands or env variables for a particular case by editing env_mach_specific.xml and I can't do this anymore because it is being overwritten - can we add a flag to keep the old behavior?
Hey @jedwards4b , this dropped-off my radar, sry! I will look at it this afternoon.

Resolving patch:
diff --git a/scripts/lib/CIME/case/case_setup.py b/scripts/lib/CIME/case/case_setup.py
--- a/scripts/lib/CIME/case/case_setup.py
+++ b/scripts/lib/CIME/case/case_setup.py
@@ -67,7 +67,7 @@ def _build_usernl_files(case, model, comp):
                     safe_copy(model_nl, nlfile)
 
 ###############################################################################
-def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False):
+def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False, keep=None):
 ###############################################################################
     os.chdir(caseroot)
 
@@ -86,7 +86,7 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False):
         batch_script = get_batch_script_for_job(case.get_primary_job())
         files_to_clean = [batch_script, "env_mach_specific.xml", "Macros.make", "Macros.cmake"]
         for file_to_clean in files_to_clean:
-            if os.path.exists(file_to_clean):
+            if os.path.exists(file_to_clean) and not (keep and file_to_clean in keep):
                 os.remove(file_to_clean)
                 logger.info("Successfully cleaned {}".format(file_to_clean))
 
@@ -229,11 +229,11 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False):
         logger.info("You can now run './preview_run' to get more info on how your case will be run")
 
 ###############################################################################
-def case_setup(self, clean=False, test_mode=False, reset=False):
+def case_setup(self, clean=False, test_mode=False, reset=False, keep=None):
 ###############################################################################
     caseroot, casebaseid = self.get_value("CASEROOT"), self.get_value("CASEBASEID")
     phase = "setup.clean" if clean else "case.setup"
-    functor = lambda: _case_setup_impl(self, caseroot, clean, test_mode, reset)
+    functor = lambda: _case_setup_impl(self, caseroot, clean=clean, test_mode=test_mode, reset=reset, keep=keep)
 
     if self.get_value("TEST") and not test_mode:
         test_name = casebaseid if casebaseid is not None else self.get_value("CASE")


Test patch: