Repository: spack/spack

Issue:
spack install suite-sparse fails with appleclang
See gist here https://gist.github.com/goxberry/e2fdb6dca02bb4ab132f3515a6e02f18 for full details. 

It seems like the crash in building `suite-sparse` is due to the way `spack` handles OpenMP flags: for `appleclang`, it assumes that lack of OpenMP is a hard failure for all packages.

Would adding an `openmp` variant to packages that can disable OpenMP, like `suite-sparse`, be accepted in a pull request?


Discussion:
Hmmm. Judging from suite-sparse package.py file, I kinda think lack of OpenMP is a hard-failure for suite-sparse. I mean, it appears to *always* set CFOPENMP flag to self.compiler.openmp_flag. So, maybe just have it raise install error if self.compiler.openmp_flag is empty? But, that assumes that every OpenMP enabled compiler actually needs a flag to enable OpenMP features. Maybe that isn't true of all compilers? Or, maybe it won't always be true in distant future? I dunno.
Generally, I think packages should have flags to enable/disable openmp regardless of compiler. That's because sometimes u don't want thread parallelism in a dependency. So I think it's perfectly fine to add a variant. 
What about packages that actually don't work *without* openmp? It is my impression from looking at suite-sparse that it seems to want openmp by fiat. If that turns out to be false and openmp for suite-sparse is optional, I agree with @goxberry and @davydden, add the variant for it.
If you download and unzip the archive, the SuiteSparse default for `CFOPENMP` is the empty string, and [version 4.5.4 builds on a Mac with clang](https://github.com/Homebrew/homebrew-science/blob/master/suite-sparse.rb), so it should be just fine to build SuiteSparse without OpenMP. It looks like no one's added the variant yet. I've already started, so I should have a notional PR in an hour or two.
as for Apple's Clang, to my knowledge it did not support openmp (as of late 2016). So throwing an error is perfectly legitimate IMO.
@davydden I agree. I'm adding `conflicts('+openmp', when='%appleclang')` as a precaution.
> What about packages that actually don't work without openmp?

I am not aware of such packages. Suite-sparse is certainly not one of them. Hard requirement of Openmp there was introduced recently and is a BUG. I have it installed on my Sierra for ages.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/suite-sparse/package.py b/var/spack/repos/builtin/packages/suite-sparse/package.py
--- a/var/spack/repos/builtin/packages/suite-sparse/package.py
+++ b/var/spack/repos/builtin/packages/suite-sparse/package.py
@@ -40,6 +40,7 @@ class SuiteSparse(Package):
     variant('tbb',  default=False, description='Build with Intel TBB')
     variant('pic',  default=True,  description='Build position independent code (required to link with shared libraries)')
     variant('cuda', default=False, description='Build with CUDA')
+    variant('openmp', default=False, description='Build with OpenMP')
 
     depends_on('blas')
     depends_on('lapack')
@@ -63,11 +64,12 @@ def install(self, spec, prefix):
         # logic in it. Any kind of customization will need to go through
         # filtering of that file
 
-        make_args = ['INSTALL=%s' % prefix]
+        pic_flag  = self.compiler.pic_flag if '+pic' in spec else ''
 
-        make_args.extend([
+        make_args = [
+            'INSTALL=%s' % prefix,
             # By default, the Makefile uses the Intel compilers if
-            # they are found. This flag disables this behavior,
+            # they are found. The AUTOCC flag disables this behavior,
             # forcing it to use Spack's compiler wrappers.
             'AUTOCC=no',
             # CUDA=no does NOT disable cuda, it only disables internal search
@@ -75,47 +77,44 @@ def install(self, spec, prefix):
             # completely disabled. See
             # [SuiteSparse/SuiteSparse_config/SuiteSparse_config.mk] for more.
             'CUDA=no',
-            'CUDA_PATH={0}'.format(
-                spec['cuda'].prefix if '+cuda' in spec else ''
-            ),
-            'CFOPENMP={0}'.format(self.compiler.openmp_flag)
-        ])
-
-        if '+pic' in spec:
-            make_args.extend([
-                'CFLAGS={0}'.format(self.compiler.pic_flag),
-                'FFLAGS={0}'.format(self.compiler.pic_flag)
-            ])
+            'CUDA_PATH=%s' % (spec['cuda'].prefix if '+cuda' in spec else ''),
+            'CFOPENMP=%s' % (self.compiler.openmp_flag
+                             if '+openmp' in spec else ''),
+            'CFLAGS=-O3 %s' % pic_flag,
+            # Both FFLAGS and F77FLAGS are used in SuiteSparse makefiles;
+            # FFLAGS is used in CHOLMOD, F77FLAGS is used in AMD and UMFPACK.
+            'FFLAGS=%s' % pic_flag,
+            'F77FLAGS=%s' % pic_flag,
+            # use Spack's metis in CHOLMOD/Partition module,
+            # otherwise internal Metis will be compiled
+            'MY_METIS_LIB=%s' % spec['metis'].libs.ld_flags,
+            'MY_METIS_INC=%s' % spec['metis'].prefix.include,
+            # Make sure Spack's Blas/Lapack is used. Otherwise System's
+            # Blas/Lapack might be picked up. Need to add -lstdc++, following
+            # with the TCOV path of SparseSuite 4.5.1's Suitesparse_config.mk,
+            # even though this fix is ugly
+            'BLAS=%s' % (spec['blas'].libs.ld_flags + (
+                '-lstdc++' if '@4.5.1' in spec else '')),
+            'LAPACK=%s' % spec['lapack'].libs.ld_flags,
+        ]
+
+        # SuiteSparse defaults to using '-fno-common -fexceptions' in
+        # CFLAGS, but not all compilers use the same flags for these
+        # optimizations
+        if any([x in spec
+                for x in ('%clang', '%gcc', '%intel')]):
+            make_args += ['CFLAGS+=-fno-common -fexceptions']
+        elif '%pgi' in spec:
+            make_args += ['CFLAGS+=--exceptions']
 
         if '%xl' in spec or '%xl_r' in spec:
-            make_args.extend(['CFLAGS+=-DBLAS_NO_UNDERSCORE'])
-
-        # use Spack's metis in CHOLMOD/Partition module,
-        # otherwise internal Metis will be compiled
-        make_args.extend([
-            'MY_METIS_LIB=-L%s -lmetis' % spec['metis'].prefix.lib,
-            'MY_METIS_INC=%s' % spec['metis'].prefix.include,
-        ])
+            make_args += ['CFLAGS+=-DBLAS_NO_UNDERSCORE']
 
         # Intel TBB in SuiteSparseQR
         if 'tbb' in spec:
-            make_args.extend([
+            make_args += [
                 'SPQR_CONFIG=-DHAVE_TBB',
                 'TBB=-L%s -ltbb' % spec['tbb'].prefix.lib,
-            ])
-
-        # Make sure Spack's Blas/Lapack is used. Otherwise System's
-        # Blas/Lapack might be picked up.
-        blas = spec['blas'].libs.ld_flags
-        lapack = spec['lapack'].libs.ld_flags
-        if '@4.5.1' in spec:
-            # adding -lstdc++ is clearly an ugly way to do this, but it follows
-            # with the TCOV path of SparseSuite 4.5.1's Suitesparse_config.mk
-            blas += ' -lstdc++'
-
-        make_args.extend([
-            'BLAS=%s' % blas,
-            'LAPACK=%s' % lapack
-        ])
+            ]
 
         make('install', *make_args)


Test patch: