Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added LAPACK library detection mechanisms to compile and test library… #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Commits on Jul 27, 2022

  1. Added LAPACK library detection mechanisms to compile and test library…

    … success.
    
    Unified @pkgs list into @libsets in find_libs() to group libraries by
    dependency.
    
    Compatibility with the 0.33 release is important across multiple UNIX
    deployments.  The first section discusses how the new code is equivalent to the
    old code in terms of functionality, while extending the detection to make
    actual FORTRAN calls to test that the libraries work.  In addition, libraries
    with multiple dependencies are tested together instead _and_ independently,
    instead of _only_ independently as previously designed.
    
    This commit is documented in sections
    
    1. Overview of the current implementaiton
    2. Design goals for the new implementation
    3. Devel::CheckLib limitations
    4. Implementation details
    5. Validation
    
    ORIGINAL IMPLEMENTATION
    =======================
    
    The previous code was as follows.  The indented lines are original code, unindented lines are commentary. There are 3 major parts to the original library detection mechanism:
    
    	#  Part 1: This section tries to find LAPACK by searching
    	# for -llapack and -lblas.  If neither PkgConfig->find(),
    	# ExtUtils::PkgConfig->libs, nor `pkg-config` can find the
    	# libraries, then '-L/usr/lib/atlas -llapack -lblas -latlas'
    	# is forced as a default:
    	my @pkgs = qw(lapack blas);
    	our $libs0 = (
    	  eval {require PkgConfig; join ' ', map PkgConfig->find($_)->get_ldflags, @pkgs} ||
    	  eval {require ExtUtils::PkgConfig; join ' ', map ExtUtils::PkgConfig->libs($_), @pkgs} ||
    	  `pkg-config @pkgs --libs` ||
    	  '-L/usr/lib/atlas -llapack -lblas -latlas'
    
    	# Finally, any FORTRAN libraries are added to $lib0:
    	) . ' ' . ExtUtils::F77->runtime;
    
    	# The next anonymous code block is in two parts:
    	our $inc = '-DF77_USCORE='. (ExtUtils::F77->trail_ ? '_' : '');
    	{
    
    	#  Part 2a: use check_lib() to strip any libraries added above
    	# (lapack, blas) that fail to link as tested by check_lib().
    	# They may fail to link for one of two reasons.  Either (a) they
    	# do not exist on the host, or (b) that fail to link independently
    	# (ie, lapack may depend on blas or vice-versa):
    
    	  # work around Devel::CheckLib not doing Text::ParseWords::shellwords
    	  use Text::ParseWords qw(shellwords);
    	  my @libpath = grep -d, map {my $r=$_;$r=~s/^-L//?$r:()} my @sw = shellwords $libs0;
    	  my @lib = grep check_lib(lib=>$_), map {my $r=$_;$r=~s/^-l//?$r:()} @sw;
    	  $libs0 = join ' ', (map qq{-L"$_"}, @libpath), (map "-l$_", @lib);
    
    	#  Part 2b: Emit a warning and `exit 0` if compilation fails.
    	# This is a sticky issue, because Devel::CheckLib has some very old
    	# known bugs.  Notably, RT75803: (10 years ago): "Allow function
    	# option to be used without lib option".	Simply stated, if 'lib=>'
    	# is not passed to check_lib_or_exit() then it will do nothing,
    	# and return success.  PDL::LinearAlgebra commit 1d25c1b _does_
    	# have a 'lib=>' option, but _it was removed_ in e1b6576, at
    	# which point the following code may as well have been deleted:
    
    	  my $f77_uscore = (ExtUtils::F77->trail_ ? '_' : '');
    	  check_lib_or_exit(
    		ldflags => $libs0,
    		header => [($^O =~ /MSWin/ ? 'float.h' : ()), qw(stdio.h math.h)],
    		function => ...
    	  )
    	}
    
    So we can distill the above code down to two items:
    
    1. Add the FORTRAN libs determined by ExtUtils::F77
    2. Add -llapack, -lblas, and -latlas if `pkg-config` cannot find them.
    
    NEW DESIGN
    ==========
    
    The new design intends to satisfy the following:
      1. Define an array of reasonable library combinations that are compatible with existing UNIX deployments
      2. By default, the first successful library is selected.
         - A library is "successful" if it can compile and link FORTRAN functions.
      3. Allow users who have multiple LAPACK/BLAS implementations to select a specific implementation if they wish.
      4. Provide adequate debug output for troubleshooting test reports.
      5. Linux and other UNIXen that support compling and linking with `-l<lib>` and `-L<dir>` linker options are treated the same.  While there may be special cases for libraries (openblas vs atlas), there is no special-case for UNIX-like operating systems.
      6. Library detection/selection for Windows will not be modified.
      7. Safety: Use shell_quote/shellwords to make sure library paths are clean.
    
    BUGS IN Devel::CheckLib
    =======================
    
    (They are commented in the code as well, so pasted here for good visibility)
    
    In addition, Devel::CheckLib fails to provide `argc` to main() before about v1.11, so a Devel::CheckLib version requirement was added.
    
    IMPLEMENTATION
    ==============
    
    We have written a wrapper `new_check_lib()` around Devel::CheckLib::check_lib() to work around the issues above. This is the comment in the code:
    
    Configurability with environment variables:
    
      1. PDL_LA_LDFLAGS='-L<dir> -l<lib>...':
      	The user can set this to specify their own linker flags (for example, to link with the Intel MKL library).
    
      2. PDL_LA_DEBUG=1: Pass `debug=>1` to Devel::CheckLib::check_lib()
    
      3. PDL_LA_LIB_INDEX=<n>: Select a LAPACK/BLAS library.
    
    	When Makefile.PL runs it emits output like the following.  The user can specify PDL_LA_LIB_INDEX=<n> to choose the implementation that succeeded.  This defaults to 0 if not specified:
    	  [0] found (openblaso): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -lopenblaso
    		  missing (openblas_openmp)
    	  [1] found (tatlas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -L/usr/lib64/atlas/ -lgfortran -lm -ltatlas
    	  [2] found (openblasp): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -lopenblasp
    		  missing (openblas_pthreads)
    	  [3] found (openblas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -lopenblas
    		  missing (openblas_serial)
    	  [4] found (satlas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -L/usr/lib64/atlas/ -lgfortran -lm -lsatlas
    	  [5] found (atlas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -L/usr/lib64/atlas/ -lgfortran -lm -lsatlas
    		  missing (lapack_atlas)
    	  [6] found (lapack): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -llapack
    		  missing (blas)
    	  [7] found (lapack blas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -llapack -lblas
    	  [8] found (lapack blas atlas): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -L/usr/lib64/atlas/ -lgfortran -lm -llapack -lblas -lsatlas
    	  [9] found (mkl_rt): -Llibgfortran.a -L/usr/lib -L/opt/intel/compilers_and_libraries_2020.4.304/linux/mkl/lib/intel64_lin/ -lgfortran -lm -lmkl_rt
    
    Safety:
      - -L flags are passed before -l flags so the linker can know the paths before hunting for the libraries.
      - Any linker options that match neither `-l<lib>` nor `-L<dir>` are passed verbatim.
      - Options are handled as lists except when generating an LDFLAGS-style output, in which case `shell_quote` and `shellwords` are used to properly parse and escape the input and output.
    
    The library detection code at the top of `Makefile.PL` is now a single line:
    
    	our $libs0 = find_libs(ExtUtils::F77->runtime, $ENV{PDL_LA_LDFLAGS});
    
    The list of available library combinations are in `@libsets` atop of `find_libs()`:
      my @libsets = (
          # Empty case if PDL_LA_LDFLAGS satisfies the build
          [],
    
          # Threaded OpenBLAS/ATLAS
          ['openblaso'],
          ['tatlas'],
    
          # [ ... many others ...]
    
          # And finally the options from the original implementation if none of the above options match:
          ['blas'],
          ['lapack', 'blas'],
          ['lapack', 'blas', 'atlas'],
    
          # Experimental Intel Math Kernel Library support:
          ['mkl_rt'],
        );
    
    Notable changes:
    
    FORTRAN check function:
    	Added library code to test `dgebal_` existence
    	Added note about compile problems when missing a leading empty line
    	Added 'return 0' to the end
    	Calls to ExtUtils::F77->trail_ ? '_' : '' were replaced with $f77_uscore
    
    Notes:
      The windows library detection was not modified in this patch.
      The 'shellwords' work-around was not changed, but the diff looks that way
    
    VALIDATION
    ==========
    
    The following library combinations pass `make test` on my Oracle Linux 8
    deployment:
    
    	for i in {0..8}; do export i; echo === $i; (PDL_LA_LIB_INDEX=$i perl Makefile.PL && make -j100 && make test) 2>&1 | grep -P 'will use|ok$' -A1; done
    
     1. -Llibgfortran.a -L/usr/lib -lgfortran -lm -lopenblaso
     2. -Llibgfortran.a -L/usr/lib -L/usr/lib64/atlas/ -lgfortran -lm -ltatlas
     3. -Llibgfortran.a -L/usr/lib -lgfortran -lm -lopenblasp
     4. -Llibgfortran.a -L/usr/lib -lgfortran -lm -lopenblas
     5. -Llibgfortran.a -L/usr/lib -L/usr/lib64/atlas/ -lgfortran -lm -lsatlas
     6. -Llibgfortran.a -L/usr/lib -L/usr/lib64/atlas/ -lgfortran -lm -lsatlas
     7. -Llibgfortran.a -L/usr/lib -lgfortran -lm -llapack
     8. -Llibgfortran.a -L/usr/lib -lgfortran -lm -llapack -lblas
     9. -Llibgfortran.a -L/usr/lib -L/usr/lib64/atlas/ -lgfortran -lm -llapack -lblas -lsatlas
    
    	t/1.t ....... ok
    	t/cgtsv.t ... ok
    	t/gtsv.t .... ok
    	t/legacy.t .. ok
    	All tests successful.
    
    In addition, the following Ubuntu/Debian distributions have been tested, all
    you need to install is `libopenblas-dev` to build LAPACK+BLAS successfully:
    
        grep -B4  'All tests' */root/pdl*/build.log
    
    bionic-i386/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    bionic-i386/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    bionic-i386/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    bionic-i386/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    bionic-i386/root/pdl-linearalgebra/build.log:All tests successful.
    --
    bionic/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    bionic/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    bionic/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    bionic/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    bionic/root/pdl-linearalgebra/build.log:All tests successful.
    --
    bullseye-i386/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    bullseye-i386/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    bullseye-i386/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    bullseye-i386/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    bullseye-i386/root/pdl-linearalgebra/build.log:All tests successful.
    --
    bullseye/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    bullseye/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    bullseye/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    bullseye/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    bullseye/root/pdl-linearalgebra/build.log:All tests successful.
    --
    buster-i386/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    buster-i386/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    buster-i386/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    buster-i386/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    buster-i386/root/pdl-linearalgebra/build.log:All tests successful.
    --
    buster/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    buster/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    buster/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    buster/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    buster/root/pdl-linearalgebra/build.log:All tests successful.
    --
    focal/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    focal/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    focal/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    focal/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    focal/root/pdl-linearalgebra/build.log:All tests successful.
    --
    xenial-i386/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    xenial-i386/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    xenial-i386/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    xenial-i386/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    xenial-i386/root/pdl-linearalgebra/build.log:All tests successful.
    --
    xenial/root/pdl-linearalgebra/build.log-t/1.t ....... ok
    xenial/root/pdl-linearalgebra/build.log-t/cgtsv.t ... ok
    xenial/root/pdl-linearalgebra/build.log-t/gtsv.t .... ok
    xenial/root/pdl-linearalgebra/build.log-t/legacy.t .. ok
    xenial/root/pdl-linearalgebra/build.log:All tests successful.
    Eric Wheeler committed Jul 27, 2022
    Configuration menu
    Copy the full SHA
    485d38d View commit details
    Browse the repository at this point in the history

Commits on Jul 30, 2022

  1. Added String::ShellQuote dependency

    Eric Wheeler committed Jul 30, 2022
    Configuration menu
    Copy the full SHA
    7ba193b View commit details
    Browse the repository at this point in the history