# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1518120526 -3600
#      Thu Feb 08 21:08:46 2018 +0100
# Node ID 7de1eb8aaec8ac425a325e11566f3ff98025d70e
# Parent  6a6108db3a6bf52e963a70ac1b80f8115abc2bcf
Add "-blas" and "-lapack" options to "version" (bug #45659).

* defaults.cc, defaults.h: Add new functions "blas_version_str" and
"lapack_version_str".

* toplev.cc (__octave_config_info__): Add fields "blas_version" and
"lapack_version" to returned structure.

* version.m: Add support for "-blas" and "-lapack" options. Make options case
insensitive.

diff -r 6a6108db3a6b -r 7de1eb8aaec8 libinterp/corefcn/defaults.cc
--- a/libinterp/corefcn/defaults.cc	Thu Feb 08 19:56:18 2018 +0100
+++ b/libinterp/corefcn/defaults.cc	Thu Feb 08 21:08:46 2018 +0100
@@ -31,6 +31,7 @@
 #include <string>
 
 #include "dir-ops.h"
+#include "f77-fcn.h"
 #include "file-ops.h"
 #include "oct-env.h"
 
@@ -46,6 +47,7 @@
 #include "default-defs.h"
 
 static bool initialized = false;
+static octave::dynamic_library dyn_libs;
 
 static std::string Voctave_home;
 static std::string Voctave_exec_home;
@@ -93,6 +95,9 @@
 static std::string Vlocal_site_defaults_file;
 static std::string Vsite_defaults_file;
 
+static std::string Vblas_version_str ("");
+static std::string Vlapack_version_str ("");
+
 // Variables that name directories or files are substituted into source
 // files with "${prefix}/" stripped from the beginning of the string.
 
@@ -169,6 +174,131 @@
 }
 
 static void
+set_blas_version_str (void)
+{
+  if (! dyn_libs)
+    {
+      // Prepare to check all loaded library functions
+      dyn_libs = octave::dynamic_library ("");
+      if (! dyn_libs)
+        {
+          Vblas_version_str = "Unknown BLAS";
+          return;
+        }
+    }
+
+  // Check for functions that are specific to certain BLAS implementations.
+
+  // FlexiBLAS
+  typedef void (*flexi_f_type) (int*, int*, int*);
+  flexi_f_type flexi_f_ptr = reinterpret_cast<flexi_f_type>
+                           (dyn_libs.search ("flexiblas_get_version"));
+  if (flexi_f_ptr)
+    {
+      int v_major = 0;
+      int v_minor = 0;
+      int v_patch = 0;
+      flexi_f_ptr (&v_major, &v_minor, &v_patch);
+      std::stringstream s;
+      s << "FlexiBLAS Version "
+        << v_major << "." << v_minor << "." << v_patch;
+      Vblas_version_str = Vblas_version_str + s.str() + "\n";
+    }
+
+  // OpenBLAS
+  typedef char * (*open_fcn_type) (void);
+  open_fcn_type open_f_ptr = reinterpret_cast<open_fcn_type>
+                             (dyn_libs.search ("openblas_get_config"));
+  if (open_f_ptr)
+    Vblas_version_str = Vblas_version_str + "OpenBLAS (config: " +
+                        open_f_ptr () + ")\n";
+
+  // GotoBLAS(2)
+  if (dyn_libs.search ("gotoblas_profile_init"))
+    Vblas_version_str = Vblas_version_str + "GotoBLAS(2)\n";
+
+  // ATLAS
+  // FIXME: If we are really interested, we could use a pipe to redirect the
+  // output of "ATL_buildinfo".
+  if (dyn_libs.search ("ATL_buildinfo") != nullptr)
+    Vblas_version_str = Vblas_version_str + "ATLAS\n";
+
+  // ACML
+  typedef void (*acml_f_type) (int*, int*, int*);
+  acml_f_type acml_f_ptr = reinterpret_cast<acml_f_type>
+                           (dyn_libs.search ("acmlversion"));
+  if (acml_f_ptr)
+    {
+      int v_major = 0;
+      int v_minor = 0;
+      int v_patch = 0;
+      acml_f_ptr (&v_major, &v_minor, &v_patch);
+      std::stringstream s;
+      s << "ACML BLAS Version "
+        << v_major << "." << v_minor << "." << v_patch;
+      Vblas_version_str = Vblas_version_str + s.str() + "\n";
+    }
+
+  // Intel MKL
+  typedef void (*mkl_f_type) (char*, int);
+  mkl_f_type mkl_f_ptr = reinterpret_cast<mkl_f_type>
+                         (dyn_libs.search ("mkl_get_version_string"));
+  if (mkl_f_ptr)
+    {
+      char buf[198];
+      int len = 198;
+      mkl_f_ptr (buf, len);
+      Vblas_version_str = Vblas_version_str + std::string (buf) + "\n";
+    }
+
+  // otherwise
+  if (Vblas_version_str.empty ())
+    Vblas_version_str = "unknown or reference BLAS";
+  else
+    Vblas_version_str.pop_back ();
+}
+
+/* Hack to stringize macro results. */
+#define xSTRINGIZE(x) #x
+#define STRINGIZE(x) xSTRINGIZE(x)
+
+static void
+set_lapack_version_str (void)
+{
+  Vlapack_version_str = "Unknown LAPACK";
+
+  if (! dyn_libs)
+    {
+      // Prepare to check all loaded library functions
+      dyn_libs = octave::dynamic_library ("");
+      if (! dyn_libs)
+        {
+          return;
+        }
+    }
+
+  // query LAPACK version
+  typedef F77_RET_T
+          (*ilaver_fcn_type) (const F77_INT&, const F77_INT&, const F77_INT&);
+  ilaver_fcn_type f_ptr = reinterpret_cast<ilaver_fcn_type>
+                      (dyn_libs.search (STRINGIZE (F77_FUNC (ilaver, ILAVER))));
+  if (f_ptr != nullptr)
+    {
+      int v_major = 0;
+      int v_minor = 0;
+      int v_patch = 0;
+      f_ptr (v_major, v_minor, v_patch);
+      std::stringstream s;
+      s << "Linear Algebra PACKage Version "
+        << v_major << "." << v_minor << "." << v_patch;
+      Vlapack_version_str = s.str();
+      return;
+    }
+
+}
+
+
+static void
 init_defaults (void)
 {
   if (initialized)
@@ -230,8 +360,10 @@
     = octave::config::prepend_octave_home (OCTAVE_STARTUPFILEDIR);
 
   set_local_site_defaults_file ();
+  set_site_defaults_file ();
 
-  set_site_defaults_file ();
+  set_blas_version_str ();
+  set_lapack_version_str ();
 
   initialized = true;
 }
@@ -307,6 +439,9 @@
 
     std::string local_site_defaults_file (void) { RETURN (Vlocal_site_defaults_file); }
     std::string site_defaults_file (void) { RETURN (Vsite_defaults_file); }
+
+    std::string blas_version_str (void) { RETURN (Vblas_version_str); }
+    std::string lapack_version_str (void) { RETURN (Vlapack_version_str); }
   }
 }
 
diff -r 6a6108db3a6b -r 7de1eb8aaec8 libinterp/corefcn/defaults.h
--- a/libinterp/corefcn/defaults.h	Thu Feb 08 19:56:18 2018 +0100
+++ b/libinterp/corefcn/defaults.h	Thu Feb 08 21:08:46 2018 +0100
@@ -96,6 +96,9 @@
 
     extern OCTINTERP_API std::string local_site_defaults_file (void);
     extern OCTINTERP_API std::string site_defaults_file (void);
+
+    extern OCTINTERP_API std::string blas_version_str (void);
+    extern OCTINTERP_API std::string lapack_version_str (void);
   }
 }
 
diff -r 6a6108db3a6b -r 7de1eb8aaec8 libinterp/corefcn/toplev.cc
--- a/libinterp/corefcn/toplev.cc	Thu Feb 08 19:56:18 2018 +0100
+++ b/libinterp/corefcn/toplev.cc	Thu Feb 08 21:08:46 2018 +0100
@@ -374,6 +374,7 @@
 
            { "api_version", OCTAVE_API_VERSION },
            { "archlibdir", octave::config::arch_lib_dir () },
+           { "blas_version", octave::config::blas_version_str () },
            { "bindir", octave::config::bin_dir () },
            { "canonical_host_type", octave::config::canonical_host_type () },
            { "datadir", octave::config::data_dir () },
@@ -384,6 +385,7 @@
            { "imagedir", octave::config::image_dir () },
            { "includedir", octave::config::include_dir () },
            { "infodir", octave::config::info_dir () },
+           { "lapack_version", octave::config::lapack_version_str () },
            { "libdir", octave::config::lib_dir () },
            { "libexecdir", octave::config::libexec_dir () },
            // Each library and executable has its own definition of the hg
diff -r 6a6108db3a6b -r 7de1eb8aaec8 scripts/miscellaneous/version.m
--- a/scripts/miscellaneous/version.m	Thu Feb 08 19:56:18 2018 +0100
+++ b/scripts/miscellaneous/version.m	Thu Feb 08 21:08:46 2018 +0100
@@ -45,10 +45,10 @@
 ## for version information for the linked @sc{fftw},
 ##
 ## @item @qcode{"-blas"}
-## for version information for the linked @sc{blas} (not implemented),
+## for version information for the linked @sc{blas},
 ##
 ## @item @qcode{"-lapack"}
-## for version information for the linked @sc{lapack} (not implemented).
+## for version information for the linked @sc{lapack}.
 ## @end table
 ##
 ## The variant with no input and output argument is an alias for the function
@@ -71,7 +71,7 @@
       d = __octave_config_info__ ("release_date");
     end
   else
-    switch (feature)
+    switch (lower (feature))
       case "-date"
         v = __octave_config_info__ ("release_date");
       case "-description"
@@ -90,16 +90,14 @@
                                  "java.vm.info");
           v = ["Java " jversion " with " jvendor " " jname " " jjitmode];
         catch
-          v = "no java available";
+          v = "no Java available";
         end_try_catch
       case "-fftw"
         v = __octave_config_info__ ("fftw_version");
       case "-blas"
-        v = "";
-        warning ("version: option '-blas' not implemented");
+        v = __octave_config_info__ ("blas_version");
       case "-lapack"
-        v = "";
-        warning ("version: option '-lapack' not implemented");
+        v = __octave_config_info__ ("lapack_version");
       otherwise
         error ("version: invalid FEATURE");
     endswitch
@@ -119,11 +117,11 @@
 
 %!assert (version ("-description"), "")
 %!assert (version ("-release"), "")
+%!assert (ischar (version ("-blas")))
+%!assert (ischar (version ("-LAPACK")))
 
 ## Test input validation
 %!error version ("-date", "-release")
 %!error [v, d] = version ("-date")
 %!error version (1)
-%!warning <option '-blas' not implemented> version ("-blas");
-%!warning <option '-lapack' not implemented> version ("-lapack");
 %!error <invalid FEATURE> version ("-foobar")
