Repository: ESMCI/cime

Issue:
case.build --cleanall does not clean all the mod files
when --cleanall (and I assume --clean) is called from case.build, there are some .mod files left around.  I assume these are copies or something.  for instance, I see .mod files in

bld/intel/mpich2/nodebug/nothreads/include
bld/lib/include
bld/intel/mpich2/nodebug/nothreads/*/*/c1a1l1i1o1r1g1w1e1/include

Just wanted to point that out in case there is something more that clean should do.


Discussion:
I don't think there is any negative issue associated with these .mod files if you see one please tell us. 
I am wondering why these are left around after a cleanall command? If there
is no reason for them to be there, I think we should remove them. Is there
a reason for them not to be cleaned?

On Wed, Jan 25, 2017 at 1:47 PM, jedwards4b <notifications@github.com>
wrote:

> I don't think there is any negative issue associated with these .mod files
> if you see one please tell us.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/ESMCI/cime/issues/1045#issuecomment-275228138>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AHlxE4ph4SC6fUO8xLxBrkTAvwogQlTuks5rV7TagaJpZM4Lt9pG>
> .
>

It's a little harder than it may appear - copies of .h and .mod files are copied from the individual bld obj directories to the general include directory.   The clean command cleans the object directory but it doesn't know how to selectively clean the include directory (it would need to identify only the mod files that originated in the directory you are requesting to clean)  
Maybe cleanall should just remove the bld directory completely and clean can leave behind the extra copies of the mod files.  That's just a suggestion.  Most users will probably use cleanall.  I suspect clean is only going to be used by experts that have an idea how the underlying build works and in that case, leaving the extra copies of the mod files seems ok.
Actually clean with no arguments cleans all the prognostic model obj directories and cleanall also cleans the support libraries (pio, mct, csm_share) - mostly you don't need to clean those and clean is sufficient for most users. 
@jedwards4b, I think you made @apcraig's point. There is already not much left after `--cleanall` but sometimes it is enough to mess up the rebuild. I always have had to remove the bld directory anyway and removing it does not cost much (if any) time on the rebuild so I second the motion that `--cleanall` removes the bld directory.
@gold2718 said " I always have had to remove the bld directory "  what?  why?  If there is a real reproducible problem I want to fix it.   Can any of you come up with a test case that indicates that the current tools are not sufficient, or is this an alternative truth?
Is there a reason to not remove the bld directory? If this is cleaner it
should be done this way. I have also had problems in the past that required
me to remove the bld directory - but I don't remember the exact details. I
can't understand why we would argue about a cleaner implementation if there
is one that is simple to do.

On Thu, Jan 26, 2017 at 7:46 AM, jedwards4b <notifications@github.com>
wrote:

> @gold2718 <https://github.com/gold2718> said " I always have had to
> remove the bld directory " what? why? If there is a real reproducible
> problem I want to fix it. Can any of you come up with a test case that
> indicates that the current tools are not sufficient, or is this an
> alternative truth?
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/ESMCI/cime/issues/1045#issuecomment-275405535>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AHlxE7_pn8ZVZvY1SFHTfZscIBlx0S8dks5rWLHAgaJpZM4Lt9pG>
> .
>

An option called "--cleanall" should clean **all**.
Just adding a link to #1052 as the sharedlibroot is something that I wasn't entirely aware of before today, especially with regard to individual tests, and it may need to be considered in terms of how clean or cleanall work when using a test to carry out development.

Resolving patch:
diff --git a/utils/python/CIME/build.py b/utils/python/CIME/build.py
--- a/utils/python/CIME/build.py
+++ b/utils/python/CIME/build.py
@@ -93,6 +93,8 @@ def build_model(build_threaded, exeroot, clm_config_opts, incroot, complist,
     config_dir = os.path.join(cimeroot, "driver_cpl", "cime_config")
     f = open(file_build, "w")
     bldroot = os.path.join(exeroot, "cpl", "obj")
+    if not os.path.isdir(bldroot):
+        os.makedirs(bldroot)
     stat = run_cmd("%s/buildexe %s %s %s" %
                    (config_dir, caseroot, libroot, bldroot),
                    from_dir=bldroot, verbose=True, arg_stdout=f,
@@ -503,43 +505,47 @@ def _build_model_thread(config_dir, compclass, caseroot, libroot, bldroot, incro
     for mod_file in glob.glob(os.path.join(bldroot, "*_[Cc][Oo][Mm][Pp]_*.mod")):
         shutil.copy(mod_file, incroot)
 
+
 ###############################################################################
-def clean(case, cleanlist=None):
+def clean(case, cleanlist=None, clean_all=False):
 ###############################################################################
+    caseroot = case.get_value("CASEROOT")
+    if clean_all:
+        # If cleanlist is empty just remove the bld directory
+        exeroot = case.get_value("EXEROOT")
+        expect(exeroot is not None,"No EXEROOT defined in case")
+        if os.path.isdir(exeroot):
+            logging.info("cleaning directory %s" %exeroot)
+            shutil.rmtree(exeroot)
+        # if clean_all is True also remove the sharedlibpath
+        sharedlibroot = case.get_value("SHAREDLIBROOT")
+        expect(sharedlibroot is not None,"No SHAREDLIBROOT defined in case")
+        if sharedlibroot != exeroot and os.path.isdir(sharedlibroot):
+            logging.warn("cleaning directory %s" %sharedlibroot)
+            shutil.rmtree(sharedlibroot)
+    else:
+        expect(cleanlist is not None and len(cleanlist) > 0,"Empty cleanlist not expected")
+        debug           = case.get_value("DEBUG")
+        use_esmf_lib    = case.get_value("USE_ESMF_LIB")
+        build_threaded  = case.get_value("BUILD_THREADED")
+        gmake           = case.get_value("GMAKE")
+        caseroot        = case.get_value("CASEROOT")
+        casetools       = case.get_value("CASETOOLS")
+        clm_config_opts = case.get_value("CLM_CONFIG_OPTS")
 
-    clm_config_opts = case.get_value("CLM_CONFIG_OPTS")
-    comp_lnd = case.get_value("COMP_LND")
-    if cleanlist is None:
-        cleanlist = case.get_values("COMP_CLASSES")
-        cleanlist = [x.lower().replace('drv','cpl') for x in cleanlist]
-        testcase        = case.get_value("TESTCASE")
-        # we only want to clean clm here if it is clm4_0 otherwise remove
-        # it from the cleanlist
-        if testcase is not None and comp_lnd == "clm" and \
-                clm_config_opts is not None and "lnd" in cleanlist and \
-                "clm4_0" not in clm_config_opts:
-            cleanlist.remove('lnd')
-
-    debug           = case.get_value("DEBUG")
-    use_esmf_lib    = case.get_value("USE_ESMF_LIB")
-    build_threaded  = case.get_value("BUILD_THREADED")
-    gmake           = case.get_value("GMAKE")
-    caseroot        = case.get_value("CASEROOT")
-    casetools       = case.get_value("CASETOOLS")
-
-    os.environ["DEBUG"]           = stringify_bool(debug)
-    os.environ["USE_ESMF_LIB"]    = stringify_bool(use_esmf_lib)
-    os.environ["BUILD_THREADED"]  = stringify_bool(build_threaded)
-    os.environ["CASEROOT"]        = case.get_value("CASEROOT")
-    os.environ["COMP_INTERFACE"]  = case.get_value("COMP_INTERFACE")
-    os.environ["PIO_VERSION"]     = str(case.get_value("PIO_VERSION"))
-    os.environ["CLM_CONFIG_OPTS"] = clm_config_opts  if clm_config_opts is not None else ""
-
-    cmd = gmake + " -f " + casetools + "/Makefile"
-    for item in cleanlist:
-        cmd = cmd + " clean" + item
-    logger.info("calling %s "%(cmd))
-    run_cmd_no_fail(cmd)
+        os.environ["DEBUG"]           = stringify_bool(debug)
+        os.environ["USE_ESMF_LIB"]    = stringify_bool(use_esmf_lib)
+        os.environ["BUILD_THREADED"]  = stringify_bool(build_threaded)
+        os.environ["CASEROOT"]        = caseroot
+        os.environ["COMP_INTERFACE"]  = case.get_value("COMP_INTERFACE")
+        os.environ["PIO_VERSION"]     = str(case.get_value("PIO_VERSION"))
+        os.environ["CLM_CONFIG_OPTS"] = clm_config_opts  if clm_config_opts is not None else ""
+
+        cmd = gmake + " -f " + os.path.join(casetools, "Makefile")
+        for item in cleanlist:
+            tcmd = cmd + " clean" + item
+            logger.info("calling %s "%(tcmd))
+            run_cmd_no_fail(tcmd)
 
     # unlink Locked files directory
     unlock_file("env_build.xml")
@@ -552,5 +558,9 @@ def clean(case, cleanlist=None):
     case.flush()
 
     # append call of to CaseStatus
-    msg = "cleanbuild %s "%" ".join(cleanlist)
+    if cleanlist:
+        msg = "clean %s "%" ".join(cleanlist)
+    elif clean_all:
+        msg = "clean_all"
+
     append_status(msg, caseroot=caseroot, sfile="CaseStatus")


Test patch:
diff --git a/README.unit_testing b/README.unit_testing
--- a/README.unit_testing
+++ b/README.unit_testing
@@ -1,3 +1,10 @@
 # To run all CIME unit tests on caldera, run the following command:
 # (Note that this must be done from an interactive caldera session, not from yellowstone)
+# Note also that this requires module load all-python-libs
+#
+# We would encourage you to port these tests to other platforms.
+# The test requires an install of pFunit available from
+# https://sourceforge.net/projects/pfunit/
+#
+
 tools/unit_testing/run_tests.py --test-spec-dir=. --compiler=intel --mpilib=mpich2 --use-openmp --mpirun-command=mpirun.lsf
diff --git a/utils/python/CIME/SystemTests/system_tests_common.py b/utils/python/CIME/SystemTests/system_tests_common.py
--- a/utils/python/CIME/SystemTests/system_tests_common.py
+++ b/utils/python/CIME/SystemTests/system_tests_common.py
@@ -118,6 +118,8 @@ def build_indv(self, sharedlib_only=False, model_only=False):
                          sharedlib_only=sharedlib_only, model_only=model_only)
 
     def clean_build(self, comps=None):
+        if comps is None:
+            comps = [x.lower() for x in self._case.get_values("COMP_CLASSES")]
         build.clean(self._case, cleanlist=comps)
 
     def run(self):