Source code for CIME.case.case_clone

"""
create_clone is a member of the Case class from file case.py
"""
import os, glob, shutil
from CIME.XML.standard_module_setup import *
from CIME.utils import expect, check_name, safe_copy
from CIME.simple_compare            import compare_files
from CIME.locked_files         import lock_file
from CIME.user_mod_support import apply_user_mods

logger = logging.getLogger(__name__)

[docs]def create_clone(self, newcaseroot, keepexe=False, mach_dir=None, project=None, cime_output_root=None, exeroot=None, rundir=None, user_mods_dir=None): """ Create a case clone If exeroot or rundir are provided (not None), sets these directories to the given paths; if not provided, uses default values for these directories. It is an error to provide exeroot if keepexe is True. """ if cime_output_root is None: cime_output_root = self.get_value("CIME_OUTPUT_ROOT") newcaseroot = os.path.abspath(newcaseroot) expect(not os.path.isdir(newcaseroot), "New caseroot directory {} already exists".format(newcaseroot)) newcasename = os.path.basename(newcaseroot) expect(check_name(newcasename), "New case name invalid {} ".format(newcasename)) newcase_cimeroot = os.path.abspath(get_cime_root()) # create clone from case to case clone_cimeroot = self.get_value("CIMEROOT") if newcase_cimeroot != clone_cimeroot: logger.warning(" case CIMEROOT is {} ".format(newcase_cimeroot)) logger.warning(" clone CIMEROOT is {} ".format(clone_cimeroot)) logger.warning(" It is NOT recommended to clone cases from different versions of CIME.") # *** create case object as deepcopy of clone object *** srcroot = os.path.join(newcase_cimeroot,"..") newcase = self.copy(newcasename, newcaseroot, newsrcroot=srcroot) with newcase: newcase.set_value("CIMEROOT", newcase_cimeroot) # if we are cloning to a different user modify the output directory olduser = self.get_value("USER") newuser = os.environ.get("USER") if olduser != newuser: cime_output_root = cime_output_root.replace(olduser, newuser) newcase.set_value("USER", newuser) newcase.set_value("CIME_OUTPUT_ROOT", cime_output_root) # try to make the new output directory and raise an exception # on any error other than directory already exists. if os.path.isdir(cime_output_root): expect(os.access(cime_output_root, os.W_OK), "Directory {} is not writable " "by this user. Use the --cime-output-root flag to provide a writable " "scratch directory".format(cime_output_root)) else: if not os.path.isdir(cime_output_root): os.makedirs(cime_output_root) # determine if will use clone executable or not if keepexe: orig_exeroot = self.get_value("EXEROOT") newcase.set_value("EXEROOT", orig_exeroot) newcase.set_value("BUILD_COMPLETE","TRUE") orig_bld_complete = self.get_value("BUILD_COMPLETE") if not orig_bld_complete: logger.warning("\nWARNING: Creating a clone with --keepexe before building the original case may cause PIO_TYPENAME to be invalid in the clone") logger.warning("Avoid this message by building case one before you clone.\n") else: newcase.set_value("BUILD_COMPLETE","FALSE") # set machdir if mach_dir is not None: newcase.set_value("MACHDIR", mach_dir) # set exeroot and rundir if requested if exeroot is not None: expect(not keepexe, "create_case_clone: if keepexe is True, " "then exeroot cannot be set") newcase.set_value("EXEROOT", exeroot) if rundir is not None: newcase.set_value("RUNDIR", rundir) # Set project id # Note: we do not just copy this from the clone because it seems likely that # users will want to change this sometimes, especially when cloning another # user's case. However, note that, if a project is not given, the fallback will # be to copy it from the clone, just like other xml variables are copied. if project is None: project = self.get_value("PROJECT", subgroup=self.get_primary_job()) if project is not None: newcase.set_value("PROJECT", project) # create caseroot newcase.create_caseroot(clone=True) # Many files in the case will be links back to the source tree # but users may have broken links to modify files locally. In this case # copy the locally modified file. We only want to do this for files that # already exist in the clone. #pylint: disable=protected-access self._copy_user_modified_to_clone(self.get_value("CASEROOT"), newcase.get_value("CASEROOT")) self._copy_user_modified_to_clone(self.get_value("CASETOOLS"), newcase.get_value("CASETOOLS")) newcase.flush(flushall=True) # copy user_ files cloneroot = self.get_case_root() files = glob.glob(cloneroot + '/user_*') for item in files: safe_copy(item, newcaseroot) # copy SourceMod and Buildconf files # if symlinks exist, copy rather than follow links for casesub in ("SourceMods", "Buildconf"): shutil.copytree(os.path.join(cloneroot, casesub), os.path.join(newcaseroot, casesub), symlinks=True) # copy the postprocessing directory if it exists if os.path.isdir(os.path.join(cloneroot, "postprocess")): shutil.copytree(os.path.join(cloneroot, "postprocess"), os.path.join(newcaseroot, "postprocess"), symlinks=True) # lock env_case.xml in new case lock_file("env_case.xml", newcaseroot) # apply user_mods if appropriate newcase_root = newcase.get_value("CASEROOT") if user_mods_dir is not None: if keepexe: # If keepexe CANNOT change any env_build.xml variables - so make a temporary copy of # env_build.xml and verify that it has not been modified safe_copy(os.path.join(newcaseroot, "env_build.xml"), os.path.join(newcaseroot, "LockedFiles", "env_build.xml")) # Now apply contents of user_mods directory apply_user_mods(newcase_root, user_mods_dir, keepexe=keepexe) # Determine if env_build.xml has changed if keepexe: success, comment = compare_files(os.path.join(newcaseroot, "env_build.xml"), os.path.join(newcaseroot, "LockedFiles", "env_build.xml")) if not success: logger.warning(comment) shutil.rmtree(newcase_root) expect(False, "env_build.xml cannot be changed via usermods if keepexe is an option: \n " "Failed to clone case, removed {}\n".format(newcase_root)) # if keep executable, then remove the new case SourceMods directory and link SourceMods to # the clone directory if keepexe: shutil.rmtree(os.path.join(newcase_root, "SourceMods")) os.symlink(os.path.join(cloneroot, "SourceMods"), os.path.join(newcase_root, "SourceMods")) # Update README.case fclone = open(cloneroot + "/README.case", "r") fnewcase = open(newcaseroot + "/README.case", "a") fnewcase.write("\n *** original clone README follows ****") fnewcase.write("\n " + fclone.read()) clonename = self.get_value("CASE") logger.info(" Successfully created new case {} from clone case {} ".format(newcasename, clonename)) newcase.case_setup() return newcase
# pylint: disable=unused-argument def _copy_user_modified_to_clone(self, origpath, newpath): """ If file_ exists and is a link in newpath, and exists but is not a link in origpath, copy origpath file to newpath """ for file_ in os.listdir(newpath): if (os.path.islink(os.path.join(newpath, file_)) and os.path.isfile(os.path.join(origpath, file_)) and not os.path.islink(os.path.join(origpath, file_))): logger.info("Copying user modified file {} to clone".format(file_)) os.unlink(os.path.join(newpath, file_)) safe_copy(os.path.join(origpath, file_), newpath)