Search code examples
c++linkerautotoolsautoconfautomake

How to dynamically find and include libraries


I have a library that I am developing the configuration and makefiles for using Autotools suite. The library requires an external library (specifically fftw3) on the system it is being compiled on and I want Autoconf/Automake to automatically find and link/include the external library before/when compiling (Either in configure.ac or Makefile.am).

I currently am just using autoconf "--with-" flags for the user to provide the location of both the library file and the include folder of the external library on their system, but this seems like such a hassle for the user. The library I am building the configure file and makefiles for will be deployed on various shared systems, so it's difficult to assume the necessary external library will always be in the same place.

What's the best way to approach this?


Solution

  • You may be overthinking this. That libraries may be installed in different places on different systems is not in itself an issue. Development toolchains know about the layouts of the systems on which they are used. The base case is fairly robust; just (for example) ...

    AC_CHECK_LIB([fftw3], [fftw_plan_dft])
    

    That will recognize that libfftw3 is available in the search path if indeed it is, and in that case will prepend the appropriate link option to the LIBS variable and define HAVE_LIBFFTW3.

    An issue arises only if you want to provide for use of libraries that are not in the linker's default search path, or where a different version of the library is found earlier in that path. That's the case for providing --with-foo options by which the builder can specify a location. But note that it's a somewhat special-case problem, at least when linking against dynamic libraries, because dev libraries are usually installed alongside runtime libraries, and if a dev lib is not found by the static linker at build time, then typically you need some kind of special provision for the runtime lib to be found by the dynamic linker at run time.

    Nevertheless, if you want to check a list of possible library locations encoded into configure, then that can be done relatively easily. The generated configure is a shell script, and you can write literal code for it into your configure.ac without too much trouble. For example,

    # Preserve the original value of LIBS
    LIBS_save=$LIBS
    
    # This is how we will report out the result
    FFTW3_LIBS=
    
    # Check the default locations first
    AC_CHECK_LIB([fftw3], [fftw_plan_dft], [
      # libfftw3 found in the library search path
      FFTW3_LIBS=-lfftw3
    ], [
      # libfftw3 not found in the library search path; try some other paths
      # make the linker search the chosen path by adding an `-L` option to the LDFLAGS
      LDFLAGS_save=$LDFLAGS
      for fftw_libdir in
          /usr/lib/fftw
          /usr/lib/fftw3
          /usr/local/lib/fftw3
      do
        LDFLAGS="${LDFLAGS_save} -L${fftw_libdir}"
        AC_CHECK_LIB([fftw3], [fftw_plan_dft], [
          # library found
          FFTW3_LIBS="-L${fftw_libdir} -lfftw3"
          break
        ])
      done
    
      # restore the original LDFLAGS
      LDFLAGS=$LDFLAGS_save
    ])
    
    # restore the original LIBS
    LIBS=$LIBS_save
    
    AS_IF([test x = "x${FFTW3_LIBS}"], [
      # configure fails if it does not find libfftw3
      AC_MSG_ERROR([libfftw3 not found])
    ])
    
    # Make FFTW3_LIBS an output variable
    AC_OUTPUT([FFTW3_LIBS])
    

    Notable characteristics of that code include

    • saving and restoring variables used by configure in running tests (LDFLAGS) and reporting results (LIBS)
    • using a shell loop to test various options
    • using the on-success / on-error actions of AC_CHECK_LIB
      • understanding that AC_CHECK_LIB is a macro, not a function, so, for example, you can put a break into its parameters that will exit the loop in which AC_CHECK_LIB is used
    • using an output variable to report the result as a set of link options. You would use that in your Makefile.am by adding $(FFTW3_LIBS) to the appropriate *LIBADD or *LDADD variable(s). (Or if you're not using Automake, then by adding it to the link command in whatever manner is suitable.)

    Of course, you can combine that with using a --with-foo option to support cases that you do not anticipate (left as an exercise).