You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
16 KiB
471 lines
16 KiB
#!/usr/bin/env bash |
|
# |
|
|
|
# print version and license |
|
# is hereby part of the code and also displayed to the user |
|
version() { |
|
cat <<EOF >&2 |
|
|
|
Welcome to $( basename $0)! |
|
Authors: Lars Holger Engelhard - DL5RCW (2016) |
|
Tiancheng "Timothy" Gu (2014) |
|
|
|
Version: 1.3 |
|
|
|
# This file is part of the MXE Project, sponsored by the named authors |
|
# it supports the shared build approach by providing an easy way to |
|
# check for library dependencies in a recursive manner |
|
|
|
# Copyright (c) 2016 Lars Holger Engelhard - DL5RCW |
|
# (c) 2014 Tiancheng "Timothy" Gu |
|
|
|
# |
|
# Permission is hereby granted, free of charge, to any person obtaining |
|
# a copy of this software and associated documentation files (the |
|
# "Software"), to deal in the Software without restriction, including |
|
# without limitation the rights to use, copy, modify, merge, publish, |
|
# distribute, sublicense, and/or sell copies of the Software, and to |
|
# permit persons to whom the Software is furnished to do so, subject |
|
# to the following conditions: |
|
# |
|
# The above copyright notice and this permission notice shall be |
|
# included in all copies or substantial portions of the Software. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
EOF |
|
} |
|
|
|
# default application objdump |
|
# you can specify your own objdump with --objdump or -o |
|
OBJDUMP=objdump |
|
|
|
# create a temp directory |
|
tmp=$( mktemp -d ) |
|
|
|
# print an help menu |
|
help() { |
|
cat <<EOF >&2 |
|
|
|
|
|
Usage: $0 -c -d DEST -s SRC [-f FILE|-F DIR] |
|
or: $0 -p [-f FILE|-F DIR] |
|
or: $0 -c -p -d DEST -s SRC [-f FILE|-F DIR] |
|
or: $0 -p -d DEST -R SRC [-f FILE|-F DIR] |
|
or: $0 -p -d DEST -S "SRC_1 SRC_2 ... SRC_n" [-f FILE|-F DIR] |
|
or: $0 -c -d DEST -R SRC_1 -e SRC_2 [-f FILE|-F DIR] |
|
|
|
|
|
Copy executable FILE(s) and their DLL dependencies for SRC directory to a |
|
DEST directory, and/or print the recursive dependencies. |
|
|
|
Operating modes: |
|
-c, --copy print and copy dependencies |
|
-p, --print print dependencies (no copy action) |
|
|
|
Operating options: |
|
-d, --destdir Destination directory - a single destination folder |
|
-f, --infile [ multiCall ] The input executable file or DLL. |
|
-F, --infiles, --indir [ multiCall ] The input directory of executable files and/or DLLs. |
|
-s, --srcdir [ multiCall ] The directory with DLLs that can be copied. |
|
-S, --srcdirs [ multiCall ] List of directories with DLLs that can be copied. Put "" around them, e.g. "/dir1 /root/dir2 /root/dir3" |
|
-R, --recursivesrcdir [ multiCall ] Target directory for recursive search of folders containing *dll files |
|
-X, --excludepattern [ multiCall ] Exclude any path that contains such pattern, e.g. /(PREFIX)/(TARGET)/apps/ |
|
|
|
Optional binary settings: |
|
-o, --objdump Specify the path or name of your objdump application |
|
-e, --enforcedir [ multiCall ] Enforce executable files and/or DLLs of a specific directory |
|
It will be entirely copied - flat, non recursive. assumes *.dll and *.exe in the top level directory |
|
It will copy those into a directory in DESTDIR! |
|
e.g. <path_to_mxe>/mxe/usr/<compiler>/qt5/plugins/platforms/ - for qwindows.dll becomes |
|
DESTDIR/platforms/ containing qwindows.dll |
|
Other options: |
|
-h,-H, --help Display this message and exit |
|
-v,-V, --version Display version of this application |
|
-l,-L, --loglevel Display more output - default is 1 |
|
|
|
multiCall => you can specify this option multiple times! |
|
|
|
Authors: Lars Holger Engelhard - DL5RCW |
|
Tiancheng "Timothy" Gu |
|
EOF |
|
} |
|
|
|
# terminate the application |
|
# print an error message |
|
# and clean the tmp directory |
|
die() { |
|
echo $1 >&2 |
|
rm -rf "$tmp" |
|
help |
|
exit 1 |
|
} |
|
|
|
# find all directories containing dll files |
|
# you can pass a list (array) of directories |
|
# and findAllSrcDirectories will hunt for dlls in each one recursively |
|
# it will return a sorted list and duplicates are removed |
|
findAllSrcDirectories(){ |
|
ar_recursiveDirList=${!1} |
|
string="" |
|
for curPath in "${ar_recursiveDirList[@]}"; do |
|
for element in $(find $curPath $excludePattern -iname "*.dll"); do |
|
#ar_list+="$(dirname $element) " |
|
string+="$(dirname $element) " |
|
done |
|
done |
|
string=$(echo "$string" | tr -s ' ' | tr ' ' '\n' | nl | sort -u -k2 | sort -n | cut -f2-) |
|
echo $string #returns the string |
|
} |
|
|
|
while [ $# -gt 0 ]; do |
|
key="$1" |
|
shift |
|
|
|
case $key in |
|
-f|--infile) |
|
infile+=" $1" |
|
shift |
|
;; |
|
-F|--indir|--infiles) |
|
indir+=" $1" |
|
shift |
|
;; |
|
-s|--srcdir) |
|
srcdir+=" $1" |
|
shift |
|
;; |
|
-d|--destdir) |
|
destdir="$1" |
|
shift |
|
;; |
|
-S|--srcdirs) |
|
srcdirs+=" $1" |
|
shift |
|
;; |
|
-R|--recursivesrcdir) |
|
recursivesrcdir+=" $1" |
|
shift |
|
;; |
|
-X|--excludepattern) |
|
excludepattern+=" $1" |
|
shift |
|
;; |
|
-o|--objdump) |
|
OBJDUMP="$1" |
|
shift |
|
;; |
|
-e|--enforcedir) |
|
enforcedir+=" $1" |
|
shift |
|
;; |
|
-l|-L|--loglevel) |
|
loglevel="$1" |
|
shift |
|
;; |
|
-p|--print) |
|
opmode="print" |
|
;; |
|
-c|--copy) |
|
opmode="copy" |
|
;; |
|
-h|-H|--help) |
|
help |
|
exit 0 |
|
;; |
|
-v|-V|--version) |
|
version |
|
exit 0 |
|
;; |
|
*) |
|
echo "unknown option $key ignored" >&2 |
|
;; |
|
esac |
|
done |
|
|
|
# setting default values if no arguments were given |
|
if ! [ "$loglevel" ]; then |
|
loglevel=0 |
|
fi |
|
|
|
if ! [ "$opmode" ]; then |
|
opmode="copy" # used as default in productive |
|
#opmode="print" # used as default in development |
|
fi |
|
|
|
if ! [ "$destdir" ]; then |
|
die '--destdir is not specified.' |
|
fi |
|
|
|
if [ -n "$(ls -A $destdir 2>/dev/null)" ]; then |
|
echo 'Warning: --destdir already exists and contains files.' >&2 |
|
else |
|
mkdir -p "$destdir" |
|
echo "info: created --destdir $destdir" |
|
fi |
|
|
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "filelist=$filelist" |
|
echo "opmode=$opmode" |
|
fi |
|
|
|
excluePattern="" # building an exclude command consisting of patterns. We still contain the first hit of find |
|
if [ ! -z "$excludepattern" ]; then |
|
for curString in $( echo "$excludepattern" | tr -s ' ' | tr ' ' '\n' ); do |
|
excludePattern+=" ! -path *$( echo "$curString" | tr -d ' ' )* " |
|
done |
|
fi |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "\$excluePattern: $excludePattern" |
|
fi |
|
|
|
str_inputFileList="" |
|
if [ "$indir" ]; then |
|
for curPath in $( echo "${indir}" | tr -s ' ' | tr ' ' '\n' ); do |
|
if [ `uname -s` == "Darwin" ]; then |
|
curList=$( find $curPath -iname *.exe -or -iname *.dll | tr '\n' ' ' ) |
|
else curList=$( find $curPath -iregex '.*\(dll\|exe\)' | tr '\n' ' ' ) |
|
fi |
|
str_inputFileList+=" $curList" |
|
done |
|
fi |
|
if [ "$infile" ]; then |
|
for curFile in $( echo "${infile}" | tr -s ' ' | tr ' ' '\n' ); do |
|
if [ `uname -s` == "Darwin" ]; then |
|
curString=$( find $curPath -iname *.exe -or -iname *.dll | tr '\n' ' ' ) |
|
else curString=$( find $curPath -iregex '.*\(dll\|exe\)' | tr '\n' ' ' ) |
|
fi |
|
str_inputFileList+=" $curString" |
|
done |
|
fi |
|
if [ -z "$str_inputFileList" ]; then |
|
die 'there was no input defined. use --indir and/or --infile in your command' |
|
fi |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "str_inputFileList=$str_inputFileList" |
|
echo "opmode=$opmode" |
|
fi |
|
|
|
str_srcDirList="" |
|
if [ "$srcdir" ]; then |
|
str_srcDirList+=" $srcdir" |
|
fi |
|
if [ "$srcdirs" ]; then |
|
str_srcDirList+=" $srcdirs" |
|
fi |
|
if [ "$recursivesrcdir" ]; then |
|
result="$( findAllSrcDirectories recursivesrcdir )" |
|
str_srcDirList+=" $result" |
|
fi |
|
if [ -z "$str_srcDirList" ]; then |
|
die 'there was no source directory defined. use --srcdirs or --srcdir or --recursivesrcdir in your command' |
|
fi |
|
if [ "$loglevel" -gt 1 ]; then |
|
#echo "infiles: filelist=$filelist" |
|
echo "infiles: str_inputFileList=$str_inputFileList" |
|
echo " opmode: $opmode" |
|
fi |
|
|
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "list for sources: str_srcDirList=${str_srcDirList}" |
|
echo "using OBJDUMP=$OBJDUMP in Version $( $OBJDUMP -V)" |
|
fi |
|
|
|
if [ "$loglevel" -gt 1 ]; then |
|
## during development, I like to interrupt here to check the above output and skip the rest |
|
echo "starting in 5 seconds" && sleep 5 |
|
fi |
|
|
|
# introducing a whitelist of well known DLLs |
|
str_whiteListDlls="advapi32.dll kernel32.dll msvcrt.dll user32.dll ws2_32.dll gdi32.dll shell32.dll d3d9.dll ole32.dll winmm.dll mpr.dll opengl32.dll" |
|
|
|
# function to append dependencies (recursively) |
|
append_deps() { |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "\$1=$1 + \$2=$2 " |
|
sleep 2 |
|
fi |
|
local bn="$( basename $1 )" |
|
if [ -e "$tmp/$bn" ]; then |
|
return 0 |
|
fi |
|
if [ $# -eq 2 ]; then |
|
path="$1" |
|
else |
|
path="" |
|
for curPath in $( echo "${str_srcDirList}" | tr -s ' ' | tr ' ' '\n' ); do |
|
counter=0 |
|
result="" |
|
result=$(find $curPath $excludePattern -iname "$bn" -type f | tail -n 1) |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "complete find command in append_deps(): # find $curPath $excludePattern -iname $bn -type f | tail -n 1 # " |
|
fi |
|
if [ ! -z "$result" ];then |
|
path="$result" |
|
counter=$(expr $counter + 1) |
|
fi |
|
if [ $counter == 0 ]; then |
|
#echo "ERROR: could not find \$path for dll $bn, \$counter=$counter: searched $curPath" |
|
str_test="1" |
|
else |
|
echo "OKAY: found path for dll $bn = $path, \$counter=$counter: searched $curPath" |
|
fi |
|
if [ "$loglevel" -gt 1 ]; then |
|
if [ $counter == 0 ]; then |
|
echo "could not find \$path for dll $bn, \$counter=$counter: searched $curPath" |
|
else |
|
echo "found path for dll $bn = $path, \$counter=$counter: searched $curPath" |
|
fi |
|
fi |
|
done |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "path for dll $bn now is $path" |
|
sleep 2 |
|
fi |
|
fi |
|
echo "Processing $1" >&2 |
|
if ! [ -e "$path" ]; then |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "path=$path| and we touch $tmp/$bn -> non existent in our src directories!" |
|
sleep 4 |
|
fi |
|
touch "$tmp/$bn" |
|
return 0 |
|
fi |
|
$OBJDUMP -p "$path" | grep 'DLL Name:' | cut -f3 -d' ' > "$tmp/$bn" |
|
echo "executing: $OBJDUMP -p "$path" | grep 'DLL Name:' | cut -f3 -d' ' > "$tmp/$bn"" |
|
for dll in $( cat "$tmp/$bn" | tr '\n' ' ' ); do |
|
append_deps "$dll" |
|
done |
|
alldeps=$(printf "$alldeps\n%s" "$(cat $tmp/$bn)" | sort | uniq) |
|
} |
|
|
|
process_enforced_deps(){ |
|
enforcedDirectory=$1 |
|
if [ ! -d $enforcedDirectory ]; then |
|
echo "warning! \$enforcedDirectory=$enforcedDirectory is not valid" |
|
if [ "$loglevel" -gt 1 ]; then |
|
sleep 10 |
|
fi |
|
fi |
|
# first we append the path to enforced dir to our list of source directories |
|
# if we would do this file recursively, we should loop to find those and append them all to the list |
|
str_srcDirList+=" $enforcedDirectory" |
|
# now we search for the dll and exe files to be included |
|
if [ `uname -s` == "Darwin" ]; then |
|
string=$( find $enforcedDirectory -maxdepth 1 -iname *.exe -or -iname *.dll | tr '\n' ' ' ) |
|
else string=$( find $enforcedDirectory -maxdepth 1 -iregex '.*\(dll\|exe\)' | tr '\n' ' ' ) |
|
fi |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "enforcedDirectory=$enforcedDirectory" |
|
echo "we found dlls and exes:$string" |
|
sleep 4 |
|
fi |
|
# we hard copy it to DEST |
|
if [ `uname -s` == "Darwin" ]; then |
|
cp -av "${enforcedDirectory}" "$destdir" |
|
else cp -dpRxv "${enforcedDirectory}" "$destdir" |
|
fi |
|
} |
|
|
|
# beginning of the main function |
|
# we start with the enforced dlls and exe |
|
if [ ! -z "$enforcedir" ]; then |
|
for curFile in $( echo "${enforcedir}" | tr -s ' ' | tr ' ' '\n'); do |
|
echo "startig for file $curFile in enforce section" |
|
append_deps "$curFile" rel |
|
process_enforced_deps "$curFile" |
|
done |
|
fi |
|
|
|
# then we start with our indir or infile list |
|
for file in $str_inputFileList; do |
|
echo "starting for file $file" |
|
#sleep 4 |
|
append_deps "$file" rel |
|
done |
|
|
|
echo "I will now search for \$alldeps" |
|
for debugOut in $( echo $alldeps | tr -s ' ' | tr '\n' ' '); do |
|
echo "debugOut: $debugOut" |
|
done |
|
if [ "$loglevel" -eq 1 ]; then |
|
echo "waiting 10 seconds until I proceed - so you can read my debugOut" |
|
sleep 10 |
|
|
|
tmpStr=${str_srcDirList} |
|
echo "\$alldeps has ${#alldeps[@]} elements" |
|
echo "and \$str_srcDirList has ${#str_srcDirList} elements" |
|
fi |
|
|
|
str_summary="Here is the summary:" |
|
str_summary="${str_summary} # ==== 8< ==== START ==== 8< ==== " |
|
if [ $opmode == "copy" ]; then |
|
echo "copying files from \${curFolder} to \$destdir:" |
|
elif [ $opmode == "print" ]; then |
|
echo "printing files:" |
|
fi |
|
for dll in $( echo $alldeps | tr '\n' ' ' ); do |
|
counter=0 |
|
lowerDll=$( echo $dll | tr '[:upper:]' '[:lower:]' ) |
|
if [ $lowerDll == $dll ]; then |
|
lowerDll="" |
|
fi |
|
for curFolder in $( echo "${str_srcDirList}" | tr -s ' ' | tr ' ' '\n'); do |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "search for dll $dll in curFolder $curFolder" |
|
sleep 1 |
|
fi |
|
for curDll in $dll $lowerDll; do |
|
if [ -e "${curFolder}/${curDll}" ]; then |
|
counter=$( expr $counter + 1 ) |
|
if [ $opmode == "copy" ]; then |
|
if [ `uname -s` == "Darwin" ]; then |
|
cp -av "${curFolder}/${curDll}" "$destdir" |
|
else cp -dpRxv "${curFolder}/${curDll}" "$destdir" |
|
fi |
|
|
|
elif [ $opmode == "print" ]; then |
|
echo "found $dll in: ${curFolder}/${curDll}" |
|
else |
|
echo "unknown opmode=$opmode" |
|
fi |
|
fi |
|
done |
|
done |
|
if [ $counter == 0 ]; then |
|
lowerDll=$( echo $dll | tr '[:upper:]' '[:lower:]' ) |
|
str_whiteListDlls=$( echo ${str_whiteListDlls} | tr '[:upper:]' '[:lower:]' ) # make whiteListDlls lower case to ensure we find the match (case insensitive) |
|
if [ -z "${str_whiteListDlls/*${lowerDll}*}" ]; then |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "Info: \"$dll\" not found - but it is white-listed. That means: it is well known by Windows - do not worry too much. " |
|
fi |
|
str_summary="${str_summary} # Info: \"$dll\" not found - but it is white-listed. That means: it is well known by Windows - do not worry too much. " |
|
else |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "Warn: \"$dll\" NOT found. \$counter=$counter." |
|
fi |
|
str_summary="${str_summary} # Warn: \"$dll\" NOT found. \$counter=$counter." |
|
fi |
|
else |
|
if [ "$loglevel" -gt 1 ]; then |
|
echo "Good: \"$dll\" found in the list. \$counter=$counter" |
|
fi |
|
str_summary="${str_summary} # Good: \"$dll\" Found in the list. \$counter=$counter" |
|
fi |
|
done |
|
str_summary="${str_summary} # ==== 8< ==== END ==== 8< ==== " |
|
echo "Job is done." |
|
# print the summary now |
|
for curLine in "$( echo "${str_summary}" | tr -s '#' | tr '#' '\n' )"; do # convert # to a linebreak - string ecomes an array that can be processed in for loop |
|
echo "$curLine" |
|
done |
|
|
|
# clean up the temp directory stored in $tmp |
|
rm -rf "$tmp"
|
|
|