Repository: cupy/cupy

Issue:
"No supported gcc/g++ host compiler found" error in Ubuntu 17.04
This error happens when I try to install CuPy in Ubuntu 17.04
```
$ python setup.py develop
:
:
NVCC options: ['--generate-code=arch=compute_30,code=compute_30', '--generate-code=arch=compute_50,code=compute_50', '--generate-code=arch=compute_60,code=compute_60', '-O2', '--compiler-options="-fPIC"']
/usr/bin/nvcc -D_GLIBCXX_USE_CXX11_ABI=0 -D_FORCE_INLINES=1 -I/usr/include -I/home/niboshi/anaconda/anaconda3/include/python3.6m -c cupy/cuda/cupy_thrust.cu -o build/temp.linux-x86_64-3.6/cupy/cuda/cupy_thrust.o --generate-code=arch=compute_30,code=compute_30 --generate-code=arch=compute_50,code=compute_50 --generate-code=arch=compute_60,code=compute_60 -O2 --compiler-options="-fPIC"
ERROR: No supported gcc/g++ host compiler found, but clang-3.8 is available.
       Use 'nvcc -ccbin clang-3.8' to use that instead.
error: command '/usr/bin/nvcc' failed with exit status 1
```


Discussion:

Resolving patch:
diff --git a/cupy_setup_build.py b/cupy_setup_build.py
--- a/cupy_setup_build.py
+++ b/cupy_setup_build.py
@@ -433,6 +433,7 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
         _compiler_so = self.compiler_so
         try:
             nvcc_path = build.get_nvcc_path()
+            base_opts = build.get_compiler_base_options()
             self.set_executable('compiler_so', nvcc_path)
 
             cuda_version = build.get_cuda_version()
@@ -441,7 +442,7 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
             print('NVCC options:', postargs)
 
             return unixccompiler.UnixCCompiler._compile(
-                self, obj, src, ext, cc_args, postargs, pp_opts)
+                self, obj, src, ext, base_opts + cc_args, postargs, pp_opts)
         finally:
             self.compiler_so = _compiler_so
 
diff --git a/install/build.py b/install/build.py
--- a/install/build.py
+++ b/install/build.py
@@ -1,4 +1,6 @@
+import contextlib
 import os
+import re
 import shutil
 import subprocess
 import sys
@@ -16,6 +18,16 @@
 minimum_cusolver_cuda_version = 8000
 
 _cuda_path = 'NOT_INITIALIZED'
+_compiler_base_options = None
+
+
+@contextlib.contextmanager
+def _tempdir():
+    temp_dir = tempfile.mkdtemp()
+    try:
+        yield temp_dir
+    finally:
+        shutil.rmtree(temp_dir, ignore_errors=True)
 
 
 def get_cuda_path():
@@ -105,6 +117,77 @@ def get_compiler_setting():
     }
 
 
+def _match_output_lines(output_lines, regexs):
+    # Matches regular expressions `regexs` against `output_lines` and finds the
+    # consecutive matching lines from `output_lines`.
+    # `None` is returned if no match is found.
+    if len(output_lines) < len(regexs):
+        return None
+
+    matches = [None] * len(regexs)
+    for i in range(len(output_lines) - len(regexs)):
+        for j in range(len(regexs)):
+            m = re.match(regexs[j], output_lines[i + j])
+            if not m:
+                break
+            matches[j] = m
+        else:
+            # Match found
+            return matches
+
+    # No match
+    return None
+
+
+def get_compiler_base_options():
+    """Returns base options for nvcc compiler.
+
+    """
+    global _compiler_base_options
+    if _compiler_base_options is None:
+        _compiler_base_options = _get_compiler_base_options()
+    return _compiler_base_options
+
+
+def _get_compiler_base_options():
+    # Try compiling a dummy code.
+    # If the compilation fails, try to parse the output of compilation
+    # and try to compose base options according to it.
+    nvcc_path = get_nvcc_path()
+    with _tempdir() as temp_dir:
+        test_cu_path = os.path.join(temp_dir, 'test.cu')
+        with open(test_cu_path, 'w') as f:
+            f.write('int main() { return 0; }')
+        proc = subprocess.Popen(
+            [nvcc_path, test_cu_path],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdoutdata, stderrdata = proc.communicate()
+        stderrlines = stderrdata.split(b'\n')
+        if proc.returncode != 0:
+
+            # No supported host compiler
+            matches = _match_output_lines(
+                stderrlines,
+                [
+                    b'^ERROR: No supported gcc/g\+\+ host compiler found, but '
+                    b'.* is available.$',
+
+                    b'^ *Use \'nvcc (.*)\' to use that instead.$',
+                ])
+            if matches is not None:
+                base_opts = matches[1].group(1)
+                base_opts = base_opts.decode('utf8').split(' ')
+                return base_opts
+
+            # Unknown error
+            raise RuntimeError(
+                'Encountered unknown error while testing nvcc:\n' +
+                stderrdata.decode('utf8'))
+
+    return []
+
+
 _cuda_version = None
 
 
@@ -217,9 +300,7 @@ def check_cusolver_version(compiler, settings):
 
 def build_shlib(compiler, source, libraries=(),
                 include_dirs=(), library_dirs=()):
-    temp_dir = tempfile.mkdtemp()
-
-    try:
+    with _tempdir() as temp_dir:
         fname = os.path.join(temp_dir, 'a.cpp')
         with open(fname, 'w') as f:
             f.write(source)
@@ -239,15 +320,10 @@ def build_shlib(compiler, source, libraries=(),
             msg = 'Cannot build a stub file.\nOriginal error: {0}'.format(e)
             raise Exception(msg)
 
-    finally:
-        shutil.rmtree(temp_dir, ignore_errors=True)
-
 
 def build_and_run(compiler, source, libraries=(),
                   include_dirs=(), library_dirs=()):
-    temp_dir = tempfile.mkdtemp()
-
-    try:
+    with _tempdir() as temp_dir:
         fname = os.path.join(temp_dir, 'a.cpp')
         with open(fname, 'w') as f:
             f.write(source)
@@ -274,6 +350,3 @@ def build_and_run(compiler, source, libraries=(),
         except Exception as e:
             msg = 'Cannot execute a stub file.\nOriginal error: {0}'.format(e)
             raise Exception(msg)
-
-    finally:
-        shutil.rmtree(temp_dir, ignore_errors=True)


Test patch: