Last week, we started looking at installing software for ourselves. We pick it up again this week, taking it a bit further, looking at common ways things can go south, and some general tips and methods for solving these problems.
The GNU way
- Traditional three steps:
./configuremakesudo make install
- But what if I don’t have
sudoaccess?./configure --prefix=/path/to/installmakemake install
Tips
- Use
make -jto compile in parallel - Use
./configure --helpto see configuration options - Use out-of-source build if you can (though this might not always work)
- Run
make check(ortests) to verify the build works
Out-of-source builds
What?
Build the software in a separate directory to the source, e.g.:
$ mkdir build; cd build
$ ../configure --prefix=/path/to/install
$ make -j && make install
Why?
- Keeps the source tree clean
- Makes it easy to start again – just delete the build directory
- Makes it possible to keep multiple different builds (e.g. debug and production)
What’s going on here?
./configure
- Purpose is to make compilation “portable” – i.e. work on many
different systems, abstract over:
- different compilers
- different shells
- different implementations of features
- different hardware
- Allows user to choose between options
- Tries to find dependencies
- Different distros might install things in different locations
- Creates
Makefilefrom a template, usually with hardcoded paths found during runningconfigure
What’s going on here?
make
- Compile the software
- There may be additional/optional “targets” you can also build – see the README/INSTALL files for information
make install
- Copy the compiled software to the installation directory (specified
with
--prefixtoconfigure- You can also use
make DESTDIR=/path/to/install installto install in a different place thanprefix - This will actually install it under
/path/to/install/usr/local- Due to hardcoded paths set during configure stage
- You can also use
The GNU way – dependencies
- Running just
./configurenormally finds everything that it needs without any extra input - Sometimes want optional features
- Sometimes can’t find dependencies
- Usually 2-3 ways of tell
configureto use an optional package or where to find a needed dependency:--with-<package>– will try and use some built-in method of finding optionalpackage- Typically looks for standard names under
/usr/or/usr/local - Might be more clever and use
pkg-config(see later) - Might check environment variables
- Typically looks for standard names under
--with-<package>=/path/to/install– use this if it still can’t findpackage--with-<package>-libdir=/path/to/lib--with-<package>-include=/path/to/include– use these ifconfigureis looking for some exact path, butlib/andinclude/are in different places
The GNU way – environment variables
- Running
./configure --helpshould also list “Some influential environment variables”:- Which compiler to use:
CC,CXX,FC - Extra compiler flags:
CFLAGS,CXXFLAGS,FFLAGS - Extra linking flags:
LDFLAGS,LDLIBS,LIBS - Preprocessor flags:
CPPFLAGS
- Which compiler to use:
- These allow you to be extra specific about how to compile the software
Oh no, something’s gone wrong!
Find the error first
configureproducesconfig.logby default- Unfortunately, jumping straight to the end won’t show you the last error – it’s a bunch of useless output!
- Instead,
grep -ni error config.logto find line numbers containing the word “error” or “ERROR” - Then,
less config.logfollowed byg Nto go to lineN
makedoesn’t produce logs by default, so instead do:make | tee make.log–teewrites to screen and to file- Error should be at bottom
Typical problems
- Wrong options to
configure(e.g. wrongprefix, missed optional feature you wanted)- Start again! If out-of-source, you can just
rm -r <build dir>and start again - Otherwise, run
make distclean(ifconfigureworked successfully) – this should clean up everything created during the configure process - In general, if you need to change an option to
configure, you need to start completely afresh
- Start again! If out-of-source, you can just
- Missing dependency
- Should be obvious from
configureoutput or readingconfig.log - Bad software will assume a dependency can always be found
- Solution: install it! Check package manager first before trying to do it manually
- Should be obvious from
Typical problems
- Unsupported version of dependency (i.e. software doesn’t work with
this version)
configurewill hopefully tell you!- Otherwise, can be tricky to even diagnose if documentation isn’t explicit about version numbers
- Typically, looks like mismatch in function signatures or object
definitions at compile time – check output of
make - Solution: install it! Might be more difficult if you first need
to establish which version
- Probably going to need to compile and install yourself!
- Wrong version of dependency (i.e. not the version you wanted to
use)
- You make have to be very explicit and use
--with-<package>=/path/to/version - Possibly also need to set
CFLAGS/CPPFLAGS, etc.
- You make have to be very explicit and use
Typical problems
- Couldn’t find
<file>- Could be either at configure-time or compile-time
- Might just be in a weird place:
locate <file>– uses database to quickly find files- Otherwise use
find / -type f -name <file>– this will be slow - Then tell
configurewhere it is
- Might be because dependency is the wrong version
- Or compiled without a feature
- Might be named differently
- Symlinking to the rescue!
- Make a link to the installed version, but with the expected
name, and then point
configureat your link
- If compile-time (
cannot find -l<foo>), then either:- one of the above
- You need to add actual install path to
LD_LIBRARY_PATHvariable
CMake
- Traditional four steps:
mkdir build && cd buildcmake .. -DCMAKE_INSTALL_PREFIX=/path/to/installmake -jmake install
Tips
- CMake always works with out-of-source builds, so you should always use them!
- Use
ccmakeorcmake-guito discover configure-time options - Press
tinccmaketo see advanced options and set paths, etc. - CMake doesn’t make log files by default: use
teeto record output to a log file - Typical problems similar to
configure
The “no method” method
- Typical for smaller projects
- Read all the documentation provided
- Even in this state, it’s probably at least got a Makefile
- Look for
CC,CXX,FC,F77variables in the Makefile – these set the C, C++ and Fortran compilers- You might need to check to see if it’s going to parse these variables and only work for certain compilers
- Particularly bad projects will call this something like
COMPILER
- Look for
CFLAGS,CXXFLAGS,FFLAGSvariables – these set compiler options- These are where you’ll tell the compiler about where include files (headers/.mod files) are located
- Look for
LDFLAGS– this sets linker options- Where library files are located
How do I know what flags I need to use a dependency?
- Read the README!
- Some software uses
pkg-config:pkg-config --list-all | sortto see alphabetical list of packagespkg-config --libs <package>for libraries (i.e.-lpackageflags)pkg-config --cflags <package>for include paths and other flags- If you know a package installs a
<package>.pcfile, then you can setPKG_CONFIG_PATH=/path/to/pkgconfig/dir(normally found under/path/to/package/lib/pkgconfig)
- Other software (e.g. NetCDF, HDF5) provide their own config tools
(e.g.
nc-config,h5cc -show) - Otherwise, find out what files a package uses:
- If system package, you can use either
dpkg -L <package>orrpm -ql <package>to list files installed by a package
- If system package, you can use either
What to do after installation
- Usual layout of an installation directory:
bin/– for executablesinclude/– for headerslib/– for librariesshare/– for data/configuration files
- Default install location is
/usr/local- Probably won’t need to do very much more if installing here
- Custom install location, you’ll then need to tell everything else where you’ve installed it
- Helpful environment variables for runtime:
$PATH– location ofbin/$LD_LIBRARY_PATH– location oflib/
- When compiling:
-I/path/to/include/inCFLAGS/CXXFLAGS/FFLAGS-L/path/to/lib/inLDFLAGS
Help! It’s installed but won’t run!
- Command not found:
- Check your
$PATH: is the install location in it? - Use
locate <file>to check install location
- Check your
error while loading shared libraries: lib<foo>... No such file- Use
ldd <executable>to see what libraries it needs - Missing libraries look like
lib<foo> => not found - Check your
$LD_LIBRARY_PATH: is the install location in it?
- Use
Best practices when installing software
- Always use out-of-source builds when you can – saves many headaches!
- Even if you have
sudorights, not always a good idea to install stuff centrally on your own machine- Might not play well with multiple versions
- Might hide/shadow important system packages
- Instead, install in e.g.
~/Toolsor~/local - Name things like:
package-version-compiler-version- e.g. flat:
netcdf-4.4.1-gcc-5.4 - or nested:
netcdf-4.4.1/gcc-5.4
- e.g. flat:
- Then you can change your
PATHandLD_LIBRARY_PATHto point to the correct version - Expert level: set up an “environment module” system to manage multiple versions for you
Thanks!
- Resources:
- Apt docs: https://help.ubuntu.com/lts/serverguide/apt.html
- CMake docs: https://cmake.org/
- Arch Linux wiki: https://wiki.archlinux.org
- FHS: https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
- FHS: http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/c23.html