Repository: easybuilders/easybuild-framework

Issue:
EasyBuild crashes when run on a Raspberry Pi
Seems to be an issue with the architecture detection code. ;-) 

```
pi@pihead ~ $ eb goalf-1.1.0-no-OFED.eb
== temporary log file in case of crash /tmp/easybuild-8ocRBM.log
Traceback (most recent call last):
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/main.py", line 1294, in <module>
    main()
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/main.py", line 255, in main
    validate=validate_easyconfigs))
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/main.py", line 402, in process_easyconfig
    'module': (ec.name, ec.get_installversion()),
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/framework/easyconfig/easyconfig.py", line 392, in get_installversion
    return det_installversion(self['version'], self.toolchain.name, self.toolchain.version,
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/framework/easyconfig/easyconfig.py", line 381, in toolchain
    tc.set_options({})
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/tools/toolchain/compiler.py", line 135, in set_options
    self._set_compiler_toolchainoptions()
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/tools/toolchain/compiler.py", line 162, in _set_compiler_toolchainoptions
    self._get_optimal_architecture()
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/tools/toolchain/compiler.py", line 262, in _get_optimal_architecture
    self.arch = systemtools.get_cpu_vendor()
  File "/home/pi/.local/lib/python2.7/site-packages/easybuild/tools/systemtools.py", line 103, in get_cpu_vendor
    arch = regexp.search(txt).groupdict()['vendorid']
AttributeError: 'NoneType' object has no attribute 'groupdict'
```

Probably this should be assigned a very low priority, but I figured I'd try EasyBuild out on my RPi cluster...

OS is Raspbian Wheezy (Debian variant).



Discussion:
Is this EasyBuild v1.5?

We could certainly make it spit out a nicer error message instead of going down like this...

I agree the get_cpu_vendor function is way too crude, it should except almost any error, and never throw one, just return "Unknown"

Can you give me the contents of `/proc/cpuinfo` and the output of `sysctl -n hw.model` on this machine?
And also please run the code on https://github.com/hpcugent/easybuild/wiki/OS_flavor_name_version

Yes, this is EasyBuild v1.5.

`/proc/cpuinfo`:

```pi@pihead ~/hpl-2.1 $ cat /proc/cpuinfo
Processor       : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS        : 464.48
Features        : swp half thumb fastmult vfp edsp java tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xb76
CPU revision    : 7

Hardware        : BCM2708
Revision        : 000e
Serial          : 00000000532b1613```

`sysctl -n hw.model`:

`pi@pihead ~/hpl-2.1 $ sysctl -n hw.model
sysctl: cannot stat /proc/sys/hw/model: No such file or directory`

OS flavor code:

`Python version: ['2.7.3 (default, Jan 13 2013, 11:20:46) ', '[GCC 4.6.3]']
dist: ('debian', '7.0', '')
linux_distribution: ('debian', '7.0', '')
system: Linux
machine: armv6l
platform: Linux-3.6.11+-armv6l-with-debian-7.0
uname: ('Linux', 'pihead', '3.6.11+', '#371 PREEMPT Thu Feb 7 16:31:35 GMT 2013', 'armv6l', '')
version: #371 PREEMPT Thu Feb 7 16:31:35 GMT 2013
mac_ver: ('', ('', '', ''), '')`


Resolving patch:
diff --git a/easybuild/tools/systemtools.py b/easybuild/tools/systemtools.py
--- a/easybuild/tools/systemtools.py
+++ b/easybuild/tools/systemtools.py
@@ -39,17 +39,19 @@
 
 INTEL = 'Intel'
 AMD = 'AMD'
+ARM = 'ARM'
+UNKNOWN = 'UNKNOWN'
 
 
 class SystemToolsException(Exception):
     """raised when systemtools fails"""
 
+
 def get_core_count():
     """Try to detect the number of virtual or physical CPUs on this system.
 
     inspired by http://stackoverflow.com/questions/1006289/how-to-find-out-the-number-of-cpus-in-python/1006301#1006301
     """
-
     # Python 2.6+
     try:
         from multiprocessing import cpu_count
@@ -60,7 +62,6 @@ def get_core_count():
     # POSIX
     try:
         cores = int(os.sysconf('SC_NPROCESSORS_ONLN'))
-
         if cores > 0:
             return cores
     except (AttributeError, ValueError):
@@ -69,27 +70,26 @@ def get_core_count():
     # Linux
     txt = read_file('/proc/cpuinfo', log_error=False)
     if txt is not None:
-        res = txt.count('processor\t:')
+        # sometimes this is uppercase
+        res = txt.lower().count('processor\t:')
         if res > 0:
             return res
-
     # BSD
     try:
         out, _ = run_cmd('sysctl -n hw.ncpu')
         cores = int(out)
-
         if cores > 0:
             return cores
-    except (ValueError):
+    except ValueError:
         pass
 
-
     raise SystemToolsException('Can not determine number of cores on this system')
 
+
 def get_cpu_vendor():
     """Try to detect the cpu identifier
 
-    will return INTEL or AMD constant
+    will return INTEL, ARM or AMD constant
     """
     regexp = re.compile(r"^vendor_id\s+:\s*(?P<vendorid>\S+)\s*$", re.M)
     VENDORS = {
@@ -100,10 +100,18 @@ def get_cpu_vendor():
     # Linux
     txt = read_file("/proc/cpuinfo", log_error=False)
     if txt is not None:
-        arch = regexp.search(txt).groupdict()['vendorid']
+        result = regexp.search(txt).groupdict()
+        arch = result.get('vendorid', UNKNOWN)
         if arch in VENDORS:
             return VENDORS[arch]
 
+        # some embeded linux on arm behaves differently (e.g. raspbian)
+        regexp = re.compile(r"^Processor\s+:\s*(?P<vendorid>ARM\S+)\s*$", re.M)
+        result = regexp.search(txt).groupdict()
+        arch = result.get('vendorid', UNKNOWN)
+        if ARM in arch:
+            return ARM
+
     # Darwin (OS X)
     out, exitcode = run_cmd("sysctl -n machdep.cpu.vendor")
     out = out.strip()
@@ -116,7 +124,8 @@ def get_cpu_vendor():
     if not exitcode and out:
         return out.split(' ')[0]
 
-    raise SystemToolsException("Could not detect cpu vendor")
+    return UNKNOWN
+
 
 def get_cpu_model():
     """
@@ -137,6 +146,7 @@ def get_cpu_model():
 
     return 'UNKNOWN'
 
+
 def get_kernel_name():
     """Try to determine kernel name
 
@@ -149,6 +159,7 @@ def get_kernel_name():
     except OSError, err:
         raise SystemToolsException("Failed to determine kernel name: %s" % err)
 
+
 def get_os_type():
     """Determine system type, e.g., 'Linux', 'Darwin', 'Java'."""
     os_type = platform.system()
@@ -157,6 +168,7 @@ def get_os_type():
     else:
         raise SystemToolsException("Failed to determine system name using platform.system().")
 
+
 def get_shared_lib_ext():
     """Determine extention for shared libraries
 
@@ -175,6 +187,7 @@ def get_shared_lib_ext():
         raise SystemToolsException("Unable to determine extention for shared libraries,"
                                    "unknown system name: %s" % os_type)
 
+
 def get_platform_name(withversion=False):
     """Try and determine platform name
     e.g., x86_64-unknown-linux, x86_64-apple-darwin
@@ -198,6 +211,7 @@ def get_platform_name(withversion=False):
 
     return platform_name
 
+
 def get_os_name():
     """
     Determine system name, e.g., 'redhat' (generic), 'centos', 'debian', 'fedora', 'suse', 'ubuntu',
@@ -207,7 +221,7 @@ def get_os_name():
         # platform.linux_distribution is more useful, but only available since Python 2.6
         # this allows to differentiate between Fedora, CentOS, RHEL and Scientific Linux (Rocks is just CentOS)
         os_name = platform.linux_distribution()[0].strip().lower()
-    except AttributeError, err:
+    except AttributeError:
         # platform.dist can be used as a fallback
         # CentOS, RHEL, Rocks and Scientific Linux may all appear as 'redhat' (especially if Python version is pre v2.6)
         os_name = platform.dist()[0].strip().lower()
@@ -225,6 +239,7 @@ def get_os_name():
     else:
         return "UNKNOWN_SYSTEM_NAME"
 
+
 def get_os_version():
     """Determine system version."""
     os_version = platform.dist()[1]
@@ -234,12 +249,12 @@ def get_os_version():
             # SLES subversions can only be told apart based on kernel version,
             # see http://wiki.novell.com/index.php/Kernel_versions
             version_suffixes = {
-                                "11": [
-                                       ('2.6.27', ''),
-                                       ('2.6.32', '_SP1'),
-                                       ('3.0', '_SP2'),
-                                      ],
-                               }
+                "11": [
+                    ('2.6.27', ''),
+                    ('2.6.32', '_SP1'),
+                    ('3.0', '_SP2'),
+                ],
+            }
 
             # append suitable suffix to system version
             if os_version in version_suffixes.keys():
@@ -258,4 +273,3 @@ def get_os_version():
         return os_version
     else:
         return "UNKNOWN_SYSTEM_VERSION"
-


Test patch: