Repository: ESMCI/cime

Issue:
fortran unit tests need to run on systems other than yellowstone
This involves installing PFUNIT https://sourceforge.net/projects/pfunit/


Discussion:
See also https://github.com/ESMCI/cime/issues/1141 - which includes instructions for building pFUnit.

@jgfouca - note that with #1396 you'll just need to provide a _serial_ build of pFUnit - not an mpi-enabled build like we needed in the past. I'm working on the documentation of how to build pFUnit and set up a new machine for unit testing, which I'll push to the documentation page soon.
ok, thanks
@billsacks did you finish the documentation for pfunit?
@jgfouca - yes, this documentation should be correct now:

http://esmci.github.io/cime/doc/build/html/users_guide/unit_testing.html#how-to-add-unit-testing-support-on-your-machine
@billsacks that link gives me a 404
Try http://esmci.github.io/cime/users_guide/unit_testing.html#adding-machine-support

Resolving patch:
diff --git a/scripts/lib/CIME/BuildTools/configure.py b/scripts/lib/CIME/BuildTools/configure.py
--- a/scripts/lib/CIME/BuildTools/configure.py
+++ b/scripts/lib/CIME/BuildTools/configure.py
@@ -66,7 +66,6 @@ def _copy_depends_files(machine_name, machines_dir, output_dir, compiler):
             if os.path.isfile(dfile) and not os.path.isfile(outputdfile):
                 shutil.copyfile(dfile, outputdfile)
 
-
 def _generate_env_mach_specific(output_dir, machobj, compiler, mpilib, debug,
                                 sysos, unit_testing):
     """
@@ -79,6 +78,7 @@ def _generate_env_mach_specific(output_dir, machobj, compiler, mpilib, debug,
     ems_file = EnvMachSpecific(output_dir, unit_testing=unit_testing)
     ems_file.populate(machobj)
     ems_file.write()
+    ems_file.load_env(compiler, debug, mpilib)
     for shell in ('sh', 'csh'):
         ems_file.make_env_mach_specific_file(compiler, debug, mpilib, shell)
         shell_path = os.path.join(output_dir, ".env_mach_specific." + shell)
diff --git a/scripts/lib/CIME/BuildTools/macrowriterbase.py b/scripts/lib/CIME/BuildTools/macrowriterbase.py
--- a/scripts/lib/CIME/BuildTools/macrowriterbase.py
+++ b/scripts/lib/CIME/BuildTools/macrowriterbase.py
@@ -13,6 +13,7 @@
 import os
 from abc import ABCMeta, abstractmethod
 from CIME.XML.standard_module_setup import *
+from CIME.utils import get_cime_root
 logger = logging.getLogger(__name__)
 
 def _get_components(value):
@@ -234,6 +235,7 @@ def write_macros_file_v1(macros, compiler, os_, machine, macros_file="Macros", o
 '''#
 # cmake Macros generated from $compiler_file
 #
+set(CMAKE_MODULE_PATH %s)
 include(Compilers)
 set(CMAKE_C_FLAGS_RELEASE "" CACHE STRING "Flags used by c compiler." FORCE)
 set(CMAKE_C_FLAGS_DEBUG "" CACHE STRING "Flags used by c compiler." FORCE)
@@ -241,7 +243,7 @@ def write_macros_file_v1(macros, compiler, os_, machine, macros_file="Macros", o
 set(CMAKE_Fortran_FLAGS_DEBUG "" CACHE STRING "Flags used by Fortran compiler." FORCE)
 set(all_build_types "None Debug Release RelWithDebInfo MinSizeRel")
 set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: ${all_build_types}." FORCE)
-''')
+''' % os.path.join(get_cime_root(), "src", "CMake"))
 
             # print the settings out to the Macros file, do it in
             # two passes so that path values appear first in the
@@ -254,7 +256,10 @@ def write_macros_file_v1(macros, compiler, os_, machine, macros_file="Macros", o
                     if key.endswith("_PATH"):
                         if value.startswith("$"):
                             value = "$ENV{}".format(value[1:])
-                        fd.write("set({} {})\n".format(key, value))
+
+
+                        cmake_var = key.replace("NETCDF_PATH", "NetCDF_PATH").replace("PNETCDF_PATH", "Pnetcdf_PATH")
+                        fd.write("set({} {})\n".format(cmake_var, value))
                         fd.write("list(APPEND CMAKE_PREFIX_PATH {})\n\n".format(value))
 
             for key, value in sorted(macros.iteritems()):
@@ -268,6 +273,7 @@ def write_macros_file_v1(macros, compiler, os_, machine, macros_file="Macros", o
 
                             idx = 0
                             for is_shell, component in components:
+                                component = component.replace("NETCDF", "NetCDF").replace("PNETCDF_PATH", "Pnetcdf_PATH")
                                 if is_shell:
                                     fd.write('execute_process(COMMAND {} OUTPUT_VARIABLE TEMP{:d})\n'.format(component, idx))
                                     fd.write('string(REGEX REPLACE "\\n$" "" TEMP{:d} "${{TEMP{:d}}}")\n'.format(idx, idx))
@@ -281,13 +287,13 @@ def write_macros_file_v1(macros, compiler, os_, machine, macros_file="Macros", o
                             fd.write('set(TEMP "{}")\n'.format(value))
 
                         if "CFLAGS" in key:
-                            fd.write("add_flags(CMAKE_C_FLAGS ${TEMP})\n\n")
+                            fd.write("add_flags(CFLAGS ${TEMP})\n\n")
                         elif "FFLAGS" in key:
-                            fd.write("add_flags(CMAKE_Fortran_FLAGS ${TEMP})\n\n")
+                            fd.write("add_flags(FFLAGS ${TEMP})\n\n")
                         elif "CPPDEFS" in key:
-                            fd.write("list(APPEND COMPILE_DEFINITIONS ${TEMP})\n\n")
+                            fd.write("list(APPEND CPPDEFS ${TEMP})\n\n")
                         elif "SLIBS" in key or "LDFLAGS" in key:
-                            fd.write("add_flags(CMAKE_EXE_LINKER_FLAGS ${TEMP})\n\n")
+                            fd.write("add_flags(LDFLAGS ${TEMP})\n\n")
 
         # Recursively print the conditionals, combining tests to avoid repetition
         _parse_hash(macros["_COND_"], fd, 0, output_format)


Test patch:
diff --git a/scripts/fortran_unit_testing/run_tests.py b/scripts/fortran_unit_testing/run_tests.py
--- a/scripts/fortran_unit_testing/run_tests.py
+++ b/scripts/fortran_unit_testing/run_tests.py
@@ -146,7 +146,7 @@ def parse_command_line(args):
         args.use_openmp, args.xml_test_list, args.verbose
 
 
-def cmake_stage(name, test_spec_dir, build_optimized, use_mpiserial, mpirun_command, output,
+def cmake_stage(name, test_spec_dir, build_optimized, use_mpiserial, mpirun_command, output, pfunit_path,
                 cmake_args=None, clean=False, verbose=False, enable_genf90=True, color=True):
     """Run cmake in the current working directory.
 
@@ -178,10 +178,12 @@ def cmake_stage(name, test_spec_dir, build_optimized, use_mpiserial, mpirun_comm
 
         cmake_command = [
             "cmake",
+            "-C Macros.cmake",
             test_spec_dir,
             "-DCIME_CMAKE_MODULE_DIRECTORY="+os.path.abspath(os.path.join(_CIMEROOT,"src","CMake")),
             "-DCMAKE_BUILD_TYPE="+build_type,
             "-DPFUNIT_MPIRUN='"+mpirun_command+"'",
+            "-DPFUNIT_PATH="+pfunit_path
             ]
         if use_mpiserial:
             cmake_command.append("-DUSE_MPI_SERIAL=ON")
@@ -241,6 +243,7 @@ def find_pfunit(compilerobj, mpilib, use_openmp):
            """PFUNIT_PATH not found for this machine and compiler, with MPILIB={} and compile_threaded={}.
 You must specify PFUNIT_PATH in config_compilers.xml, with attributes MPILIB and compile_threaded.""".format(mpilib, attrs['compile_threaded']))
     logger.info("Using PFUNIT_PATH: {}".format(pfunit_path.text))
+    return pfunit_path.text
 
 #=================================================
 # Iterate over input suite specs, building the tests.
@@ -306,7 +309,7 @@ def _main():
 
     compilerobj = Compilers(machobj, compiler=compiler, mpilib=mpilib)
 
-    find_pfunit(compilerobj, mpilib=mpilib, use_openmp=use_openmp)
+    pfunit_path = find_pfunit(compilerobj, mpilib=mpilib, use_openmp=use_openmp)
 
     debug = not build_optimized
     os_ = machobj.get_value("OS")
@@ -375,7 +378,7 @@ def _main():
             if not os.path.islink("Macros.cmake"):
                 os.symlink(os.path.join(build_dir,"Macros.cmake"), "Macros.cmake")
             use_mpiserial = not use_mpi
-            cmake_stage(name, directory, build_optimized, use_mpiserial, mpirun_command, output, verbose=verbose,
+            cmake_stage(name, directory, build_optimized, use_mpiserial, mpirun_command, output, pfunit_path, verbose=verbose,
                         enable_genf90=enable_genf90, cmake_args=cmake_args)
             make_stage(name, output, make_j, clean=clean, verbose=verbose)
 
@@ -399,8 +402,5 @@ def _main():
             run_cmd_no_fail(" ".join(ctest_command), from_dir=label, arg_stdout=None, arg_stderr=subprocess.STDOUT)
 
 
-
-
-
 if __name__ == "__main__":
     _main()