#!/usr/bin/env bash # if you don't have bash on your system, you could try ksh, pdksh, or # sh. case "$(uname)" in SunOS|Linux|AIX|OSF1|IRIX*|DragonFly|Darwin) ;; *) echo 'Warning: running find-sym on an untested platform.' 1>&2 echo 'There may be anomalies! :)' 1>&2 echo 1>&2 ;; esac function usage { retval="$1" case "$retval" in 0) ;; *) exec 1>&2 ;; esac echo Usage: "$0 [-l] [-i] [-v] [-h] [-s symbol] [-p] [-h]" echo '-q says to only list -L, -I and -l options - be quiet' echo '-l says to only check for libraries including the symbol' echo '-h says to only check for header files including the symbol' echo '-v says to run (very) verbosely' echo '-s symbol says to check for only one symbol' echo '-p says to parse stdin for unresolved external messages from' echo ' gcc, and search for each of them' echo echo '-l and -h are mutually exclusive' echo '-s and -p are mutually exclusive' echo echo 'Run with no arguments to get help' exit "$retval" } # shellcheck disable=SC2046 set -- $(getopt vhps:ilq "$@") if test $? != 0 then usage 1 fi for arg do case "$arg" in -q) QUIET=true shift ;; -l) LIBSONLY=true shift ;; -i) INCLUDESONLY=true shift ;; -v) VERBOSE=true shift ;; -h) shift usage 0 ;; -s) if [ "$syms" = "" ] then syms="$2" shift shift else echo 'Sorry, -s can only be specified once' 1>&2 shift shift usage 1 fi ;; -p) PARSE=true shift ;; --) shift remainder="$*" break ;; esac done if [ "$remainder" != "" ] then echo Trailing garbage at the end of the command: "$@" 1>&2 usage 1 fi if [ "$syms" = "" ] && [ "$PARSE" = "" ] then echo Must specify either -s or -p usage 1 fi if [ "$INCLUDESONLY" = "true" ] && [ "$LIBSONLY" = "true" ] then echo You can only specify one of -i or -l 1>&2 usage 1 fi PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/ccs/bin export PATH DEBUG=false case "$DEBUG" in false) LIBSEARCHDIRS="/lib /usr/lib /usr/sfw /usr/lib64 /usr/local /usr/X11R6/lib /usr/X11/lib /usr/lib/X11 /usr/openwin/lib /opt/freeware/lib /usr/lpp" INCSEARCHDIRS="/usr/include /usr/local /usr/X11R6/include /usr/include/X11 /usr/X11/include /usr/openwin/include /opt /usr/lpp" ;; true) LIBSEARCHDIRS="/lib" INCSEARCHDIRS="" #set -x ;; esac if [ "$syms" = "" ] then if [ "$VERBOSE" = true ] then echo Deriving symbols to look for from stdin 1>&2 fi syms="$(grep -Ei 'undefined reference to `[a-zA-Z0-9_]*'"'" | \ sed 's/^.*`\([a-zA-Z0-9_]*\)'"'"'.*$/\1/' | sort | \ uniq)" if [ "$VERBOSE" = true ] then echo "Symbols found: $syms" 1>&2 fi fi if [ "$INCLUDESONLY" != true ] then for symbol in $syms do if [ "$VERBOSE" = true ] then echo "Searching for libraries containing $symbol" 1>&2 fi for dir in $LIBSEARCHDIRS do if [ "$VERBOSE" = true ] then echo " Searching in $dir" 1>&2 fi ( # skip symlinks! find $dir -name 'lib*.a' -print find $dir -name 'lib*.so\.*' -print find $dir -name 'lib*.dylib' -print ) 2> /dev/null | \ while read -r libpath do if [ "$VERBOSE" = true ] then echo " Searching in $libpath" 1>&2 fi #echo libpath is $libpath, uname is `uname` 1>&2 #set -x case "$(uname)" in IRIX*) nm -B "$libpath" | sed "s#^#$libpath:#" ;; SunOS|OSF1) # here we're munging Sun format around into linux # format nm -P "$libpath" | awk ' { print $3,$2,$1 } ' | \ sed "s#^#$libpath:#" ;; Linux|DragonFly|Darwin) nm -o "$libpath" ;; AIX) #AIX: .popen T 0430424 14 # -X bits specifies the bitwidth for the library # -p says don't sort - Much faster nm -X 32 -p -o "$libpath" | awk ' { print $3,$2,$1 } ' | \ sed "s#^#$libpath:#" nm -X 64 -p -o "$libpath" | awk ' { print $3,$2,$1 } ' | \ sed "s#^#$libpath:#" ;; esac 2> /dev/null | \ grep "$symbol" | \ case "$(uname)" in AIX) awk ' { if (($2 == "t" || $2 == "T") && ($3 == "'"$symbol"'" || $3 == "'".$symbol"'")) print }' ;; OSF1) awk ' { if (($2 == "t" || $2 == "T") && ($3 == "'"$symbol"'" || $3 == "'"__$symbol"'")) print }' ;; DragonFly) awk ' { if ($2 == "T" && $3 == "'"$symbol"'") print }' ;; Darwin) awk ' { if (($3 == "t" || $3 == "T") && $4 == "'"_$symbol"'") print }' ;; Linux*|SunOS|IRIX*|*) awk ' { if (($2 == "t" || $2 == "T" || $2 == "D") && ($3 == "'"$symbol"'" || $3 == "'"_$symbol"'")) print }' ;; esac | \ awk -F':' ' { print $1 }' | \ while read -r lib do case "$lib" in *lib*.a) add="-L$(dirname "$libpath") -l$(basename "$lib" | sed -e 's/^lib//' -e 's/\.a$//')" ;; *lib*.so\.*) add="-L$(dirname "$libpath") -l$(basename "$lib" | sed -e 's/^lib//' -e 's/\.so\..*$//')" ;; *lib*.dylib) add="-L$(dirname "$libpath") -l$(basename "$lib" | sed -e 's/^lib//' -e 's/\.dylib$//')" ;; *) echo "Internal error: unrecognized library filename form: $lib" 1>&2 exit 1 ;; esac if [ "$QUIET" = true ] then echo "$add" else # shellcheck disable=SC2016 echo 'Library candidate for addition to your $CC' echo 'link line: '"$add"' You may also want' echo "-R$(dirname "$libpath")"' (on Sun) or -Wl,-rpath' echo -Wl,"$(dirname "$libpath")"' (most other unixes' echo 'and linuxes) to build the' echo 'relevant directory into the resulting' echo 'executable'"'"'s search path - this is' echo 'generally preferred to using' # shellcheck disable=SC2016 echo '$LD_LIBRARY_PATH or similar, though using' # shellcheck disable=SC2016 echo '$LD_LIBRARY_PATH from a wrapper script is not' echo 'that bad.' case "$(uname)" in AIX) echo 'You may need to add -X32 or -X64 to your' echo 'compile and link options, since this is AIX' ;; esac echo '_____' fi done done done done fi if [ "$LIBSONLY" != 'true' ] then for symbol in $syms do if [ "$VERBOSE" = true ] then echo "Searching for includes containing $symbol" 1>&2 fi for dir in $INCSEARCHDIRS do if [ "$VERBOSE" = true ] then echo " Searching in $dir" 1>&2 fi if [ -d "$dir" ] then cd "$dir" || exit 1 find "." -name '*.h' -print 2> /dev/null | \ while read -r headerfile do if [ "$VERBOSE" = true ] then echo ' '"Searching in $headerfile" 1>&2 fi if grep -E "#[ ]*define[ ][ ]*$symbol\>" "$headerfile" > /dev/null 2>&1 then if [ "$QUIET" = true ] then # shellcheck disable=SC2001 echo "$headerfile" and "#include <$(echo "$headerfile" | sed 's#^\./##')>" else # shellcheck disable=SC2001 echo Header candidate: "#include <$(echo "$headerfile" | sed 's#^\./##')>" echo Must concommittantly add -I"$dir" to your # shellcheck disable=SC2016 echo '$CC' compile line for the .c file. Please echo note that it is possible that this headerfile echo only -conditionally- defines this symbol. If echo this symbol is conditionally defined, echo 'then you may have to include some other header' echo first, '#define' another symbol, or add a echo suitable -D for some other symbol to your # shellcheck disable=SC2016 echo '$CC' compile line echo _____ fi fi done fi done done fi echo The cleanest way of adding some flags to your link line is to add # shellcheck disable=SC2016 echo them to '$LDFLAGS', remove ./config.cache if there is one, and echo rerun ./configure. echo echo The same is true for compilation flags: the cleanest way is to add # shellcheck disable=SC2016 echo them to your '$CFLAGS'. echo echo 'However, I have found a surprising number of applications that do' echo not use these variables. echo echo In that case, I have found that a larger number of applications # shellcheck disable=SC2016 echo will accept your additional flags by adding them to your '$CC' echo variable. echo _____ echo echo This is a -very- simplistic heuristic, but if you find yourself echo with lots of library choices to choose between, you will often be echo best off using the directory and library with the shortest name. ':)' echo _____ echo