Repository: spack/spack

Issue:
[BUG] : `spack compiler` command doesn't work correctly for multiple architecture
##### TLDR

Updates to one particular architecture in `compilers.yaml` seem to discard the information on all the other architectures.
##### Description

The bug can be reproduced in the following way. In the first shell, assuming `SYS_TYPE=<first>`

``` bash
$ spack compilers
==> Available compilers
-- gcc ----------------------------------------------------------
gcc@4.8

$ spack config get compilers
compilers:
  <first>:
    gcc@4.8:
      cc: /usr/bin/gcc
      cxx: /usr/bin/g++
      f77: /usr/bin/gfortran
      fc: /usr/bin/gfortran
```

On a second shell, within the same spack installation, assuming `SYS_TYPE=<second>`:

``` bash
$ spack compilers
==> Available compilers
-- gcc ----------------------------------------------------------
gcc@4.8

$ spack config get compilers
compilers:
  <first>:
    gcc@4.8:
      cc: /usr/bin/gcc
      cxx: /usr/bin/g++
      f77: /usr/bin/gfortran
      fc: /usr/bin/gfortran
  <second>:
    gcc@4.8:
      cc: /usr/bin/gcc-4.8
      cxx: /usr/bin/g++-4.8
      f77: /usr/bin/gfortran-4.8
      fc: /usr/bin/gfortran-4.8
```

Coming back to the first shell : 

``` bash
$ spack install gcc@5.3.0
...
$ spack load gcc@5.3.0
$ spack compiler add
==> Added 1 new compiler to /home/mculpo/.spack/compilers.yaml
    gcc@5.3.0
$ spack config get compilers
compilers:
  <first>:
    gcc@4.8:
      cc: /usr/bin/gcc
      cxx: /usr/bin/g++
      f77: /usr/bin/gfortran
      fc: /usr/bin/gfortran
    gcc@5.3.0:
      cc: /home/mculpo/PycharmProjects/spack/opt/spack/<first>/gcc-4.8/gcc-5.3.0-n2e5zswyv6xf5evzzj4t3aulvlkwjyno/bin/gcc
      cxx: /home/mculpo/PycharmProjects/spack/opt/spack/<first>/gcc-4.8/gcc-5.3.0-n2e5zswyv6xf5evzzj4t3aulvlkwjyno/bin/g++
      f77: /home/mculpo/PycharmProjects/spack/opt/spack/<first>/gcc-4.8/gcc-5.3.0-n2e5zswyv6xf5evzzj4t3aulvlkwjyno/bin/gfortran
      fc: /home/mculpo/PycharmProjects/spack/opt/spack/<first>/gcc-4.8/gcc-5.3.0-n2e5zswyv6xf5evzzj4t3aulvlkwjyno/bin/gfortran

```

At this point the architecture `<second>` has been removed from the configuration file.



Discussion:

Resolving patch:
diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py
--- a/lib/spack/spack/cmd/compiler.py
+++ b/lib/spack/spack/cmd/compiler.py
@@ -22,19 +22,18 @@
 # along with this program; if not, write to the Free Software Foundation,
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-import sys
 import argparse
+import sys
 
 import llnl.util.tty as tty
-from llnl.util.tty.color import colorize
-from llnl.util.tty.colify import colify
-from llnl.util.lang import index_by
-
 import spack.compilers
-import spack.spec
 import spack.config
-from spack.util.environment import get_path
+import spack.spec
+from llnl.util.lang import index_by
+from llnl.util.tty.colify import colify
+from llnl.util.tty.color import colorize
 from spack.spec import CompilerSpec
+from spack.util.environment import get_path
 
 description = "Manage compilers"
 
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -539,14 +539,16 @@ def update_config(section, update_data, scope=None):
        other yaml-ish structure.
 
     """
+    validate_section_name(section)  # validate section name
+    scope = validate_scope(scope)  # get ConfigScope object from string.
+
     # read in the config to ensure we've got current data
-    get_config(section)
+    configuration = get_config(section)
 
-    validate_section_name(section)       # validate section name
-    scope = validate_scope(scope)   # get ConfigScope object from string.
+    configuration.update(update_data)
 
     # read only the requested section's data.
-    scope.sections[section] = { section : update_data }
+    scope.sections[section] = {section: configuration}
     scope.write_section(section)
 
 


Test patch:
diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py
--- a/lib/spack/spack/test/config.py
+++ b/lib/spack/spack/test/config.py
@@ -33,7 +33,7 @@
 
 # Some sample compiler config data
 a_comps =  {
-    "all": {
+    "x86_64_E5v2_IntelIB": {
         "gcc@4.7.3" : {
             "cc" : "/gcc473",
             "cxx": "/g++473",
@@ -53,7 +53,7 @@
 }
 
 b_comps = {
-    "all": {
+    "x86_64_E5v3": {
         "icc@10.0" : {
             "cc" : "/icc100",
             "cxx": "/icc100",
@@ -85,27 +85,24 @@ def tearDown(self):
         super(ConfigTest, self).tearDown()
         shutil.rmtree(self.tmp_dir, True)
 
-
-    def check_config(self, comps, *compiler_names):
+    def check_config(self, comps, arch, *compiler_names):
         """Check that named compilers in comps match Spack's config."""
         config = spack.config.get_config('compilers')
         compiler_list = ['cc', 'cxx', 'f77', 'fc']
         for key in compiler_names:
             for c in compiler_list:
-                expected = comps['all'][key][c]
-                actual = config['all'][key][c]
+                expected = comps[arch][key][c]
+                actual = config[arch][key][c]
                 self.assertEqual(expected, actual)
 
-
     def test_write_key_in_memory(self):
         # Write b_comps "on top of" a_comps.
         spack.config.update_config('compilers', a_comps, 'test_low_priority')
         spack.config.update_config('compilers', b_comps, 'test_high_priority')
 
         # Make sure the config looks how we expect.
-        self.check_config(a_comps, 'gcc@4.7.3', 'gcc@4.5.0')
-        self.check_config(b_comps, 'icc@10.0', 'icc@11.1', 'clang@3.3')
-
+        self.check_config(a_comps, 'x86_64_E5v2_IntelIB', 'gcc@4.7.3', 'gcc@4.5.0')
+        self.check_config(b_comps, 'x86_64_E5v3', 'icc@10.0', 'icc@11.1', 'clang@3.3')
 
     def test_write_key_to_disk(self):
         # Write b_comps "on top of" a_comps.
@@ -116,5 +113,17 @@ def test_write_key_to_disk(self):
         spack.config.clear_config_caches()
 
         # Same check again, to ensure consistency.
-        self.check_config(a_comps, 'gcc@4.7.3', 'gcc@4.5.0')
-        self.check_config(b_comps, 'icc@10.0', 'icc@11.1', 'clang@3.3')
+        self.check_config(a_comps, 'x86_64_E5v2_IntelIB', 'gcc@4.7.3', 'gcc@4.5.0')
+        self.check_config(b_comps, 'x86_64_E5v3', 'icc@10.0', 'icc@11.1', 'clang@3.3')
+
+    def test_write_to_same_priority_file(self):
+        # Write b_comps in the same file as a_comps.
+        spack.config.update_config('compilers', a_comps, 'test_low_priority')
+        spack.config.update_config('compilers', b_comps, 'test_low_priority')
+
+        # Clear caches so we're forced to read from disk.
+        spack.config.clear_config_caches()
+
+        # Same check again, to ensure consistency.
+        self.check_config(a_comps, 'x86_64_E5v2_IntelIB', 'gcc@4.7.3', 'gcc@4.5.0')
+        self.check_config(b_comps, 'x86_64_E5v3', 'icc@10.0', 'icc@11.1', 'clang@3.3')