mirror of
https://github.com/PureDarwin/PureDarwin.git
synced 2026-01-25 12:16:26 +00:00
273 lines
9.0 KiB
Bash
Executable File
273 lines
9.0 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2007-2009 The PureDarwin Project.
|
|
# All rights reserved.
|
|
#
|
|
# @LICENSE_HEADER_START@
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# @LICENSE_HEADER_END@
|
|
#
|
|
|
|
#
|
|
# aladin <aladin@puredarwin.org>
|
|
#
|
|
|
|
#
|
|
# Generate a DOT file, a dependencies tree of one or more given mach-o file
|
|
# based on pd_portviz and pd_kextviz
|
|
#
|
|
# Usage: pd_machviz file [...]
|
|
#
|
|
|
|
#
|
|
# Changelog
|
|
#
|
|
# 20090731 - See Mercurial history (`hg log') from now - aladin
|
|
# 20081209 - License header update - aladin
|
|
# 20081024 - Initial release - aladin
|
|
#
|
|
|
|
# Initialization
|
|
echo $1 | grep '^[0-9]$' > /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
# Recursive level count found in arg1
|
|
LEVEL=$1
|
|
# Eating arg1
|
|
shift
|
|
else
|
|
#
|
|
# Preventive tests
|
|
#
|
|
|
|
# Ensure root exclusivity
|
|
if [ "$UID" -ne 0 ]; then
|
|
echo "$(tput bold)Aborting!$(tput reset) You must be root in order to run \`$(basename $0)'"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure graphviz is available
|
|
if [ ! -x /opt/local/bin/dot ]; then
|
|
echo "$(tput bold)Warning!$(tput reset) /opt/local/bin/dot is not executable"
|
|
echo "You should install \`graphviz' port for graphic output"
|
|
fi
|
|
|
|
# Ensure at least one FILE is given
|
|
if [ "$1" == "" ]; then
|
|
echo "Usage: $(basename $0) file [...]"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $# -gt 1 ]; then
|
|
# Global var sux
|
|
export OUTPUT_FILENAME=SLE.dot
|
|
FILES="$*"
|
|
else
|
|
export OUTPUT_FILENAME=$(basename "$1").dot
|
|
FILES="$1"
|
|
fi
|
|
echo
|
|
echo "Generate dependencies graphs of $*"
|
|
echo
|
|
|
|
# DOT begin
|
|
echo "digraph untitled {" > "$OUTPUT_FILENAME"
|
|
|
|
# Do not draw the legend by default
|
|
IS_LEGEND="false"
|
|
if [ "$IS_LEGEND" == "true" ]; then
|
|
# DOT Legend
|
|
cat >>"$OUTPUT_FILENAME"<<EOOF
|
|
subgraph clusterLegend {
|
|
|
|
<mach-o object file> [shape=none, style="rounded,filled", fillcolor="#6666FFCF"];
|
|
<No dependency> [shape=none, style="rounded,filled", fillcolor="#CCCCFFCF"];
|
|
|
|
<mach-o object file> -> <No dependency>;
|
|
<mach-o object file> -> <mach-o object file> [label=" blocker", headclip=false, fontcolor=red, color=red, style=bold];
|
|
<No dependency> -> <No dependency> [label=" symbols missing", fontcolor=gray, color=gray];
|
|
|
|
label="Legend of mach-o object file dependencies."
|
|
}
|
|
EOOF
|
|
fi
|
|
|
|
# CLI legend
|
|
echo " Legend:"
|
|
echo " 1\t + foo"
|
|
echo " 2\t |\`- bar No more dependency"
|
|
echo " 2\t |\`+ baz $(tput bold)impure!$(tput reset) /Blocker_1 -> file_involved_1 Impurity detected"
|
|
echo " 2\t |\`+ baz $(tput bold)impure!$(tput reset) /Blocker_. -> file_involved_. Impurity detected"
|
|
echo " 2\t |\`+ baz $(tput bold)impure!$(tput reset) /Blocker_n -> file_involved_n Impurity detected"
|
|
echo " 2\t |\`+ baz Dependency found"
|
|
echo " 3\t | |\`. qux Cached file (Already processed)"
|
|
|
|
echo
|
|
echo "Now generating dependencies tree, please wait..."
|
|
echo
|
|
|
|
for FILE in $FILES; do
|
|
# Ensure FILE existence (because of space in filename, mainly..)
|
|
if [ -e "$FILE" ]; then
|
|
# Recursive call
|
|
$0 1 $FILE
|
|
fi
|
|
done
|
|
|
|
# DOT end
|
|
echo "} " >> "$OUTPUT_FILENAME"
|
|
|
|
|
|
echo "Generation of $OUTPUT_FILENAME $(tput bold)complete$(tput reset)."
|
|
echo
|
|
echo "Now drawing graphs from $OUTPUT_FILENAME, please wait..."
|
|
|
|
OUTPUT_FORMAT="png"
|
|
|
|
/opt/local/bin/dot -T$OUTPUT_FORMAT -o "${OUTPUT_FILENAME}_directed.$OUTPUT_FORMAT" "$OUTPUT_FILENAME"
|
|
echo "Generation of ${OUTPUT_FILENAME}_directed.${OUTPUT_FORMAT} $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/circo -T$OUTPUT_FORMAT -o "${OUTPUT_FILENAME}_circular.$OUTPUT_FORMAT" "$OUTPUT_FILENAME"
|
|
echo "Generation of ${OUTPUT_FILENAME}_circular.${OUTPUT_FORMAT} $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/twopi -T$OUTPUT_FORMAT -o "${OUTPUT_FILENAME}_radial.$OUTPUT_FORMAT" "$OUTPUT_FILENAME"
|
|
echo "Generation of ${OUTPUT_FILENAME}_radial.${OUTPUT_FORMAT} $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/neato -T$OUTPUT_FORMAT -o "${OUTPUT_FILENAME}_undirected.$OUTPUT_FORMAT" "$OUTPUT_FILENAME"
|
|
echo "Generation of ${OUTPUT_FILENAME}_undirected.${OUTPUT_FORMAT} $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/fdp -T$OUTPUT_FORMAT -o "${OUTPUT_FILENAME}_undirectedBIS.$OUTPUT_FORMAT" "$OUTPUT_FILENAME"
|
|
echo "Generation of ${OUTPUT_FILENAME}_undirectedBIS.${OUTPUT_FORMAT} $(tput bold)complete$(tput reset)."
|
|
exit 0
|
|
fi
|
|
|
|
#
|
|
# Variables
|
|
#
|
|
|
|
FILE=$1
|
|
|
|
# Simple blacklist based on https://sites.google.com/a/puredarwin.org/puredarwin/developers/macports/purity
|
|
FILE_BLOCKERS="
|
|
/AppKit.framework
|
|
/ApplicationServices.framework
|
|
/Carbon.framework
|
|
/Cocoa.framework
|
|
/CoreServices.framework
|
|
/Foundation.framework
|
|
/Symbolication.framework
|
|
"
|
|
|
|
# Cosmectic padding
|
|
TEXT_PADDING=""
|
|
for ((x=1; x <= $[$LEVEL - 1] ; x++))
|
|
do
|
|
TEXT_PADDING="$TEXT_PADDING |"
|
|
done
|
|
|
|
if [ $LEVEL -gt 1 ]; then
|
|
TEXT_PADDING=" $LEVEL\t$TEXT_PADDING\`+"
|
|
else
|
|
TEXT_PADDING=" $LEVEL\t$TEXT_PADDING +"
|
|
fi
|
|
|
|
# FILE already processed?
|
|
#
|
|
# The dot file is used as a simple cache
|
|
grep " <$FILE> \[label=\"$(basename "$FILE")\", shape=none, style=\"rounded,filled\", fillcolor=\"#" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
# If FILE is a directory, follow..
|
|
FILE_FILES=`/usr/bin/find "$FILE" -name '*'`
|
|
FILE_BLOCKER_COUNT=0
|
|
# inspecting all the mach-o files of the portname
|
|
for FILE_OBJECT_FILE in $FILE_FILES; do
|
|
# otool map the object file?
|
|
FILE_OBJECT_FILE_SHARED_LIBS=`/usr/bin/otool -L "$FILE_OBJECT_FILE" 2> /dev/null | grep -v "$FILE_OBJECT_FILE"`
|
|
if [ $? -eq 0 ]; then
|
|
# is the file an object file? (Because otool return 0 even if it is or if it is not an object file)
|
|
echo $FILE_OBJECT_FILE_SHARED_LIBS | grep "is not an object file" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
# looking for current blocker(s)
|
|
for FILE_BLOCKER in $FILE_BLOCKERS; do
|
|
echo $FILE_OBJECT_FILE_SHARED_LIBS | grep "$FILE_BLOCKER" > /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
echo "$TEXT_PADDING $FILE $(tput bold)impure!$(tput reset) $FILE_BLOCKER -> $FILE_OBJECT_FILE"
|
|
FILE_BLOCKER_COUNT=$[$FILE_BLOCKER_COUNT + 1]
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# Dependency found?
|
|
FILE_DEPENDENCIES=$FILE_OBJECT_FILE_SHARED_LIBS
|
|
if [ "$FILE_DEPENDENCIES" != "" ]; then
|
|
|
|
# cosmetic
|
|
|
|
|
|
if [ $LEVEL -gt 1 ]; then
|
|
echo " <$FILE> [label=\"$(basename "$FILE")\", shape=none, style=\"rounded,filled\", fillcolor=\"#9999FFCF\"];" >> "$OUTPUT_FILENAME"
|
|
else
|
|
echo " <$FILE> [label=\"$(basename "$FILE")\", shape=none, style=\"rounded,filled\", fillcolor=\"#6666FFCF\"];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
|
|
for FILE_BLOCKER in $FILE_BLOCKERS; do
|
|
echo "$FILE" | grep "$FILE_BLOCKER" > /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
echo " <$FILE> [label=\"$(basename "$FILE")\", shape=none, style=\"rounded,filled\", fillcolor=\"#FF6666CF\"];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
done
|
|
|
|
echo "$TEXT_PADDING $FILE"
|
|
|
|
# Iterative loop against dependencies
|
|
for FILE_DEPENDENCY in `echo "$FILE_DEPENDENCIES" | awk -F "(" {'print $1'}`; do
|
|
# FILE -> FILEdependency already processed?
|
|
grep "<$FILE> -> <$FILE_DEPENDENCY>;" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
# Recursive call on each run time dependency with its bundle id
|
|
$0 $[$LEVEL + 1] $FILE_DEPENDENCY
|
|
|
|
# Used as cache + resolv
|
|
echo " <$FILE> -> <$FILE_DEPENDENCY>;" >> "$OUTPUT_FILENAME"
|
|
# FILE -> FILEdependency already resolved
|
|
else
|
|
# cache inside
|
|
echo "$TEXT_PADDING\b\b |\`. $FILE_DEPENDENCY"
|
|
fi
|
|
done
|
|
# No dependency found
|
|
else
|
|
echo " <$FILE> [label=\"$(basename "$FILE")\", shape=none, style=\"rounded,filled\", fillcolor=\"#CCCCFFCF\"];" >> "$OUTPUT_FILENAME"
|
|
echo "$TEXT_PADDING\b- $FILE"
|
|
fi
|
|
done
|
|
if [ $FILE_BLOCKER_COUNT -gt 0 ]; then
|
|
echo " <$FILE> -> <$FILE> [label=\" x$FILE_BLOCKER_COUNT \", headclip=false, fontcolor=red, color=red, style=bold];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
# FILE has already been processed
|
|
else
|
|
echo "$TEXT_PADDING\b. $FILE"
|
|
fi
|
|
|