#!/bin/zsh
emulate -L zsh;setopt extendedglob
# "=command" means the full path to the command (for security)
# used when app is initially dropped.
# check for architectures present then exit
if[[ -f = $1]];then
/usr/bin/file $2/**/*(*N)| /usr/bin/grep \(for\ architecture | /usr/bin/sed -E "s/.*architecture ([a-z0-9_A-Z]+)\).*/\1/g"| /usr/bin/sort | /usr/bin/uniq
exit
fi# now we have the actual trimming...
# Check the options - what do we have to do?
while getopts :dstrbl:a: o;docase$oin
d)junk=1;;t)tif=1;;r)res='';;b)bkp=1;;s)sym=1;;l)lang=$OPTARG;;a)arch=$OPTARG;;\?)echo error;exit;;esacdone# Remove options from the arg list.
((OPTIND>1))&& shift $((OPTIND-1))|| {echo FAILED - no app to trim.;exit}
# $* means all the options
for dir in$*;do# the app itself should be a directory (and writable?)
if!{[[ -d $dir]]&&[[ -w $dir]] };then echo "FAILED - check permissions.";exit;fi;
echo "Trimming $dir.\n"# path to the "working copy" of the app in /tmp
tmpapp=/tmp/${dir:t}# create the working copy, excluding junk files if specified
/usr/bin/rsync -Ca${res-E} --exclude-from=- --delete-excluded "$dir/"$tmpapp<<<${junk+'classes.nib
info.nib
data.dependency
.DS_Store
Headers/
PrivateHeaders/
*.h
pbdevelopment.plist'}
echo -n ${junk+"== Removed useless junk files.\n"}${res+"== Cleared extended attributes.\n"}
# into our "working copy"
cd $tmpapp || {echo "FAILED - check permissions.";/bin/rm -rf $tmpapp;exit}
# this removes broken symlinks (if "remove junk is set")
[[ -n $junk ]] && /bin/rm -fv **/*(-@N) 2>/dev/null
# Remove foreign languages.
[[ -n $lang ]]&&{
echo "== Cleared foreign languages."
for line in **/Resources(/N);do
integer count="`print -l $line/*.lproj(/N) | wc -l`"
((count>1))&&{
integer left="`eval print -l "${(q)line}/*.lproj~${(q)line}/($lang)*(/N)" | wc -l`"
integer right="`eval print -l "${(q)line}/*.lproj(N)" | wc -l`"
((left<right))&&eval /bin/rm -vrf "${(q)line}/*.lproj~${(q)line}/($lang)*(/N)" | grep -v lproj/
}
done
}
# lipo universal binaries
[[ -n $arch ]] && {
echo "== Trimmed universal binaries."
# get list of fat files
lines=`/usr/bin/file **/*(*N) |/usr/bin/grep -e 'fat file' -e 'universal binary'|/usr/bin/sed -E 's/: *(set.id )?Mach.*//g'`
# for each fat file...
for line in ${(s.
.)lines}; do
# make sure we're "+w". this may be unnecessary.
[[ -w $line:h ]]||/bin/chmod ug+w $line:h;[[ -w $line]]||/bin/chmod ug+w $line# output to 'log'
echo $line# do lipo to /tmp. "2>&1" means that error messages are printed the standard out (our logview)
if /usr/bin/lipo $line -thin $arch -output "/tmp/${line:t}.lipo"2>&1;then# Copy the lipo to the app
/bin/cp "/tmp/${line:t}.lipo"$line2>&1# Get rid of the temp lipo file
/bin/chmod +w "/tmp/${line:t}.lipo"&& /bin/rm -f "/tmp/${line:t}.lipo"fidone}[[ -n $sym]]&&{
# strip debug symbols. "2>&1" means error messages to the standard out.
/usr/bin/strip -Sur Contents/MacOS/*(*)2>&1
/usr/bin/strip -Sx **/*.dylib(N) **/*.framework/**/*(*N)2>/dev/null
echo "== Stripped debug symbols."
}
[[ -n $tif]]&&{
echo "== Compressed TIFF images."
zmodload zsh/stat # zsh stat is much, much faster
# the (.NLk-500Uw,GI,W):
# "Lk-500" means under 500kb, "Uw,GI,W" checks if we can write. latter may not be necessary now as we use "cp".
for line in **/*.(#i)tif(f|)(.NLk-500Uw,GI,W); do
if /usr/bin/tiffutil -lzw $line -out "/tmp/${line:t}"2>/dev/null;then((`stat -L +size /tmp/${line:t}`<`stat -L +size $line`))&& /bin/cp "/tmp/${line:t}"$line&& echo $line
/bin/chmod +w "/tmp/${line:t}"&& /bin/rm -f "/tmp/${line:t}"fidone
}
# now to copy our changes back to the real app
cd $dir/..
# if we're making a backup, get a name that doesn't exist by adding 'backup' to the end of the name of the app.
appcopy=${dir:t:r}[[ -n $bkp]]&&{
while[[ -d $appcopy.${dir:e}]];do appcopy=$appcopy\ backup;done# the original app becomes the backup
/bin/mv $dir:t "$appcopy.${dir:e}"# out trimmed app takes its place
/bin/mv $tmpapp$dir:t
}||{
# we're not making a backup
# remove the real app and replace with trimmed app
# otherwise if we can't, make our app "appname trimmed.app"
/bin/rm -rf ${dir:t}&& /bin/mv $tmpapp${dir:t}|| {while[[ -d $appcopy.${dir:e}]];do appcopy=$appcopy\ trimmed;done;/bin/mv $tmpapp"$appcopy.${dir:e}";echo "==\nALERT: Could not delete $dir:t - please delete this. Trimmed app saved to '$appcopy.${dir:e}'"}
}
# and we're done.
echo "\n== Trim completed =="done