Repository: ESMCI/cime

Issue:
compilers v2 problems
It seems that if any MACH=X COMPILER=Y section replaces something then all MACH=X compiler sections must replace it, this is not right.


Discussion:
Currently the Macros file is fashioned such that the user can select a compiler at build time - that is a given cime case could be built using any compiler defined for the machine.   I would propose that this feature is unused and needlessly complicates things.   @rljacob @jgfouca would there be any objection to removing this feature and just creating a Macros file specific to the requested compiler?

Resolving patch:
diff --git a/scripts/lib/CIME/BuildTools/possiblevalues.py b/scripts/lib/CIME/BuildTools/possiblevalues.py
--- a/scripts/lib/CIME/BuildTools/possiblevalues.py
+++ b/scripts/lib/CIME/BuildTools/possiblevalues.py
@@ -108,7 +108,7 @@ def ambiguity_check(self):
             for other in self.settings[i+1:]:
                 expect(not self.settings[i].is_ambiguous_with(other),
                        "Variable "+self.name+" is set ambiguously in "
-                       "config_build.xml. Check the file for these "
+                       "config_compilers.xml. Check the file for these "
                        "conflicting settings: \n1: {}\n2: {}".format(
                            self.settings[i].conditions, other.conditions))
 
diff --git a/scripts/lib/CIME/XML/compilerblock.py b/scripts/lib/CIME/XML/compilerblock.py
--- a/scripts/lib/CIME/XML/compilerblock.py
+++ b/scripts/lib/CIME/XML/compilerblock.py
@@ -3,7 +3,7 @@
 
 The main "public" class here is Build. It is initialized with machine-specific
 information, and its write_macros method is the driver for translating the
-config_build.xml file into a Makefile or CMake-format Macros file.
+config_compilers.xml file into a Makefile or CMake-format Macros file.
 
 For developers, here's the role of the other classes in the process:
 
@@ -188,8 +188,10 @@ def _elem_to_setting(self, elem):
         value_text = self._handle_references(elem, set_up,
                                              tear_down, depends)
         # Create the setting object.
-        setting = ValueSetting(value_text, self._db.name(elem) == "append",
+        append = self._db.name(elem) == "append" or (self._db.name(elem) == "base" and self._compiler and self._db.compiler != self._compiler)
+        setting = ValueSetting(value_text, append,
                                conditions, set_up, tear_down)
+
         return (setting, depends)
 
     def _add_elem_to_lists(self, name, elem, value_lists):


Test patch:
diff --git a/scripts/tests/scripts_regression_tests.py b/scripts/tests/scripts_regression_tests.py
--- a/scripts/tests/scripts_regression_tests.py
+++ b/scripts/tests/scripts_regression_tests.py
@@ -2067,7 +2067,7 @@ def get_default_compiler(self):
 
 
 def get_macros(macro_maker, build_xml, build_system):
-    """Generate build system ("Macros" file) output from config_build XML.
+    """Generate build system ("Macros" file) output from config_compilers XML.
 
     Arguments:
     macro_maker - The underlying Build object.
@@ -2093,16 +2093,16 @@ def get_macros(macro_maker, build_xml, build_system):
     return str(output.getvalue())
 
 
-def _wrap_config_build_xml(inner_string):
-    """Utility function to create a config_build XML string.
+def _wrap_config_compilers_xml(inner_string):
+    """Utility function to create a config_compilers XML string.
 
     Pass this function a string containing <compiler> elements, and it will add
     the necessary header/footer to the file.
     """
     _xml_template = """<?xml version="1.0" encoding="UTF-8"?>
-<config_build>
+<config_compilers>
 {}
-</config_build>
+</config_compilers>
 """
 
     return _xml_template.format(inner_string)
@@ -2311,7 +2311,7 @@ def test_script_is_callable(self):
         """The test script can be called on valid output without dying."""
         # This is really more a smoke test of this script than anything else.
         maker = Compilers(MockMachines("mymachine", "SomeOS"), version=2.0)
-        test_xml = _wrap_config_build_xml("<compiler><SUPPORTS_CXX>FALSE</SUPPORTS_CXX></compiler>")
+        test_xml = _wrap_config_compilers_xml("<compiler><SUPPORTS_CXX>FALSE</SUPPORTS_CXX></compiler>")
         get_macros(maker, test_xml, "Makefile")
 
     def test_script_rejects_bad_xml(self):
@@ -2341,16 +2341,16 @@ class H_TestMakeMacros(unittest.TestCase):
     Aside from the usual setUp and test methods, this class has a utility method
     (xml_to_tester) that converts XML input directly to a MakefileTester object.
     """
-
-    test_os = "SomeOS"
-    test_machine = "mymachine"
-
     def setUp(self):
+        self.test_os = "SomeOS"
+        self.test_machine = "mymachine"
+        self.test_compiler = MACHINE.get_default_compiler() if TEST_COMPILER is None else TEST_COMPILER
+
         self._maker = Compilers(MockMachines(self.test_machine, self.test_os), version=2.0)
 
     def xml_to_tester(self, xml_string):
         """Helper that directly converts an XML string to a MakefileTester."""
-        test_xml = _wrap_config_build_xml(xml_string)
+        test_xml = _wrap_config_compilers_xml(xml_string)
         return MakefileTester(self, get_macros(self._maker, test_xml, "Makefile"))
 
     def test_generic_item(self):
@@ -2389,6 +2389,25 @@ def test_os_specific_item(self):
         tester = self.xml_to_tester(xml2+xml1)
         tester.assert_variable_equals("SUPPORTS_CXX", "TRUE")
 
+    def test_mach_other_compiler(self):
+        """The macro writer compiler-specific logic works as expected."""
+        xml1 = """<compiler COMPILER="{}"><CFLAGS><base>a b c</base></CFLAGS></compiler>""".format(self.test_compiler)
+        xml2 = """<compiler MACH="{}" COMPILER="other"><CFLAGS><base>x y z</base></CFLAGS></compiler>""".format(self.test_machine)
+        xml3 = """<compiler MACH="{}" COMPILER="{}"><CFLAGS><append>x y z</append></CFLAGS></compiler>""".format(self.test_machine,self.test_compiler)
+        xml4 = """<compiler MACH="{}" COMPILER="{}"><CFLAGS><base>x y z</base></CFLAGS></compiler>""".format(self.test_machine,self.test_compiler)
+        tester = self.xml_to_tester(xml1)
+        tester.assert_variable_equals("CFLAGS", "a b c",env={"COMPILER":"{}".format(self.test_compiler)})
+        tester = self.xml_to_tester(xml1+xml2)
+        tester.assert_variable_equals("CFLAGS", "a b c",env={"COMPILER":"{}".format(self.test_compiler)})
+        tester = self.xml_to_tester(xml2+xml1)
+        tester.assert_variable_equals("CFLAGS", "a b c",env={"COMPILER":"{}".format(self.test_compiler)})
+        tester = self.xml_to_tester(xml1+xml3)
+        tester.assert_variable_equals("CFLAGS", "a b c x y z",env={"COMPILER":"{}".format(self.test_compiler)})
+        tester = self.xml_to_tester(xml1+xml4)
+        tester.assert_variable_equals("CFLAGS", "x y z",env={"COMPILER":"{}".format(self.test_compiler)})
+        tester = self.xml_to_tester(xml4+xml1)
+        tester.assert_variable_equals("CFLAGS", "x y z",env={"COMPILER":"{}".format(self.test_compiler)})
+
     def test_mach_beats_os(self):
         """The macro writer chooses machine-specific over os-specific matches."""
         xml1 = """<compiler OS="{}"><SUPPORTS_CXX>FALSE</SUPPORTS_CXX></compiler>""".format(self.test_os)
@@ -2428,7 +2447,7 @@ def test_reject_duplicate_defaults(self):
         xml2 = """<compiler><MPI_PATH>/path/to/other_default</MPI_PATH></compiler>"""
         with assertRaisesRegex(self,
                 SystemExit,
-                "Variable MPI_PATH is set ambiguously in config_build.xml."):
+                "Variable MPI_PATH is set ambiguously in config_compilers.xml."):
             self.xml_to_tester(xml1+xml2)
 
     def test_reject_duplicates(self):
@@ -2437,7 +2456,7 @@ def test_reject_duplicates(self):
         xml2 = """<compiler><MPI_PATH MPILIB="mpich">/path/to/mpich2</MPI_PATH></compiler>"""
         with assertRaisesRegex(self,
                 SystemExit,
-                "Variable MPI_PATH is set ambiguously in config_build.xml."):
+                "Variable MPI_PATH is set ambiguously in config_compilers.xml."):
             self.xml_to_tester(xml1+xml2)
 
     def test_reject_ambiguous(self):
@@ -2446,7 +2465,7 @@ def test_reject_ambiguous(self):
         xml2 = """<compiler><MPI_PATH DEBUG="FALSE">/path/to/mpi-debug</MPI_PATH></compiler>"""
         with assertRaisesRegex(self,
                 SystemExit,
-                "Variable MPI_PATH is set ambiguously in config_build.xml."):
+                "Variable MPI_PATH is set ambiguously in config_compilers.xml."):
             self.xml_to_tester(xml1+xml2)
 
     def test_compiler_changeable_at_build_time(self):
@@ -2566,7 +2585,7 @@ def test_env_and_shell_command(self):
         tester.assert_variable_equals("FFLAGS", "-O2 -fast", env={"OPT_LEVEL": "2"})
 
     def test_config_variable_insertion(self):
-        """Test that $VAR insert variables from config_build."""
+        """Test that $VAR insert variables from config_compilers."""
         # Construct an absurd chain of references just to sure that we don't
         # pass by accident, e.g. outputting things in the right order just due
         # to good luck in a hash somewhere.
@@ -2619,7 +2638,7 @@ class I_TestCMakeMacros(H_TestMakeMacros):
 
     def xml_to_tester(self, xml_string):
         """Helper that directly converts an XML string to a MakefileTester."""
-        test_xml = _wrap_config_build_xml(xml_string)
+        test_xml = _wrap_config_compilers_xml(xml_string)
         if (NO_CMAKE):
             self.skipTest("Skipping cmake test")
         else: