mirror of
https://github.com/PureDarwin/PureDarwin.git
synced 2026-01-25 12:16:26 +00:00
409 lines
14 KiB
Bash
Executable File
409 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2007-2008 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>
|
|
#
|
|
|
|
#
|
|
# Set up a DOT file representing all dependencies of a port form MacPorts
|
|
#
|
|
# Usage: pd_portviz portname [+/-variant ...]
|
|
#
|
|
|
|
#
|
|
# Changelog
|
|
#
|
|
# 20081209 - License header update - aladin
|
|
# 20081014 - Minor fixes - aladin
|
|
# 20081012 - Process minimized (as the result graphs)
|
|
# Bugs, cosmetic fixed - aladin
|
|
# 20081010 - Variants added - aladin
|
|
# 20081008 - Initial release - aladin
|
|
#
|
|
|
|
i=0
|
|
|
|
function fancy_wait {
|
|
if [ $i -eq 0 ]; then
|
|
echo "-"
|
|
fi
|
|
|
|
if [ $i = 1 ]; then
|
|
echo "\\"
|
|
fi
|
|
|
|
if [ $i = 2 ]; then
|
|
echo "|"
|
|
fi
|
|
|
|
if [ $i = 4 ]; then
|
|
echo "/"
|
|
fi
|
|
|
|
echo ""
|
|
i=$(expr $i + 1)
|
|
}
|
|
|
|
#fancy_wait
|
|
|
|
# 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 portname is given
|
|
if [ "$1" == "" ]; then
|
|
echo "Usage: $(basename $0) portname [+/-variant ...]"
|
|
exit
|
|
fi
|
|
|
|
# Ensure MacPorts in default/recommanded PATH
|
|
if [ ! -x /opt/local/bin/port ]; then
|
|
echo "$(tput bold)Aborting!$(tput reset) /opt/local/bin/port is not executable"
|
|
echo "You must install the MacPorts project (http://www.macports.org/) in order to use \`$(basename $0)'"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure portname existence
|
|
PORT_INFO=`/opt/local/bin/port info $1 2>&1 | grep "Error: Port $1 not found"`
|
|
if [ $? -eq 0 ]; then
|
|
echo "$(tput bold)Aborting!$(tput reset) $PORT_INFO"
|
|
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
|
|
|
|
# Global var sux
|
|
export OUTPUT_FILENAME="$1.dot"
|
|
|
|
# Too lazy to loop
|
|
laziness() {
|
|
# Eating portname ($1)
|
|
shift
|
|
export VARIANTS="$*"
|
|
}
|
|
laziness $*
|
|
|
|
echo
|
|
echo "Generate dependencies graphs of $*"
|
|
echo
|
|
|
|
# DOT begin
|
|
echo "digraph untitled {" > "$OUTPUT_FILENAME"
|
|
|
|
# Do not draw the legend by default
|
|
IS_LEGEND="false"
|
|
# DOT Legend
|
|
if [ "$IS_LEGEND" == "true" ]; then
|
|
cat >>"$OUTPUT_FILENAME"<<EOOF
|
|
subgraph clusterLegend {
|
|
<Root> [shape=circle, style=filled, fillcolor="#FFFFFFCF"];
|
|
<Runtime> [shape=circle, style=filled, fillcolor="#CCCCFFCF"];
|
|
<Library> [shape=circle, style=filled, fillcolor="#9999FFCF"];
|
|
<Build> [shape=circle, style=filled, fillcolor="#6666FFCF"];
|
|
<Not here> [shape=circle, style=filled, fillcolor="#999999CF"];
|
|
<No dependency> [shape=doublecircle, style=filled, fillcolor="#6666FFCF"];
|
|
<Library> -> <Library> [label=" blocker ",headclip=false, fontcolor=red,color=red,style=bold];
|
|
subgraph cluster_$T_VARIANT {
|
|
color=grey;
|
|
fillcolor="lightgrey";
|
|
style="rounded,filled";
|
|
<Runtime> -> <Runtime> [label=" variant ",side="r", fontcolor=honeydew4,color=honeydew4,style=bold];
|
|
<Build> -> <Build> [label=" variant ",side="r", fontcolor=honeydew4,color=honeydew4,style=bold];
|
|
}
|
|
<Root> -> <Library>;
|
|
<Root> -> <Runtime>;
|
|
<Root> -> <Build>;
|
|
<Root> -> <Not here>;
|
|
<Root> -> <No dependency>;
|
|
label="Legend of port dependencies."
|
|
}
|
|
EOOF
|
|
fi
|
|
|
|
# CLI legend
|
|
echo " Legend:"
|
|
echo " 1\t + foo (variant_x1, ..., variant_xn)"
|
|
echo " 2\t |\`. bar (variant_y1, ..., variant_yn) No dependency found"
|
|
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 (variant_z1, ..., variant_zn) Has Dependenc{y|ies}"
|
|
echo " 3\t | |\`- bar () Already processed"
|
|
|
|
echo ""
|
|
echo "Now generating dependencies tree and checking for \`\`purity'', please wait..."
|
|
echo ""
|
|
|
|
echo " <$1> [style=filled, fillcolor=\"#FFFFFFCF\"] " >> "$OUTPUT_FILENAME"
|
|
|
|
# Call himself
|
|
|
|
$0 1 $1
|
|
|
|
# DOT end
|
|
echo "} " >> "$OUTPUT_FILENAME"
|
|
|
|
echo
|
|
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$1_directed.$OUTPUT_FORMAT "$OUTPUT_FILENAME"
|
|
echo "Generation of $1_directed.$OUTPUT_FORMAT $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/circo -T$OUTPUT_FORMAT -o$1_circular.$OUTPUT_FORMAT "$OUTPUT_FILENAME"
|
|
echo "Generation of $1_circular.$OUTPUT_FORMAT $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/twopi -T$OUTPUT_FORMAT -o$1_radial.$OUTPUT_FORMAT "$OUTPUT_FILENAME"
|
|
echo "Generation of $1_radial.$OUTPUT_FORMAT $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/neato -T$OUTPUT_FORMAT -o$1_undirected.$OUTPUT_FORMAT "$OUTPUT_FILENAME"
|
|
echo "Generation of $1_undirected.$OUTPUT_FORMAT $(tput bold)complete$(tput reset)."
|
|
|
|
/opt/local/bin/fdp -T$OUTPUT_FORMAT -o$1_undirectedBIS.$OUTPUT_FORMAT "$OUTPUT_FILENAME"
|
|
echo "Generation of $1_undirectedBIS.$OUTPUT_FORMAT $(tput bold)complete$(tput reset)."
|
|
|
|
exit 0
|
|
fi
|
|
|
|
#
|
|
# Variables
|
|
#
|
|
|
|
# Default port path
|
|
PORT=/opt/local/bin/port
|
|
|
|
|
|
PORT_VARIANTS=""
|
|
PORT_INFO=""
|
|
PORT_INSTALLED=""
|
|
PORT_ACTIVATE=""
|
|
PORT_CONTENTS=""
|
|
PORT_OBJECT_FILE_SHARED_LIBS=""
|
|
PORT_BLOCKER_COUNT=0
|
|
|
|
# Simple blacklist based on https://sites.google.com/a/puredarwin.org/puredarwin/developers/macports/purity
|
|
PORT_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\`+"
|
|
TEXT_PADDING=" \t$TEXT_PADDING\`+"
|
|
else
|
|
#TEXT_PADDING=" $LEVEL\t$TEXT_PADDING +"
|
|
TEXT_PADDING=" \t$TEXT_PADDING +"
|
|
fi
|
|
# portname already found and processed?
|
|
#
|
|
# The dot file is used as a simple cache
|
|
grep "<$1> \[shape = .*circle\];" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
# port installed portname?
|
|
PORT_INSTALLED=`$PORT installed $1 | grep "$1 \@"`
|
|
if [ $? -eq 0 ]; then
|
|
# is the portname active?
|
|
PORT_ACTIVATE=`echo $PORT_INSTALLED | grep '(active)'`
|
|
if [ $? -eq 0 ]; then
|
|
# port contents portname ?
|
|
PORT_CONTENTS=`$PORT contents $1 | grep -v "Port $1 contains"`
|
|
if [ $? -eq 0 ]; then
|
|
# inspecting all the mach-o files of the portname
|
|
for PORT_OBJECT_FILE in $PORT_CONTENTS; do
|
|
# otool map the object file?
|
|
PORT_OBJECT_FILE_SHARED_LIBS=`/usr/bin/otool -L $PORT_OBJECT_FILE 2> /dev/null`
|
|
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 $PORT_OBJECT_FILE_SHARED_LIBS | grep "is not an object file" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
# looking for current blocker(s)
|
|
for PORT_BLOCKER in $PORT_BLOCKERS; do
|
|
echo $PORT_OBJECT_FILE_SHARED_LIBS | grep "$PORT_BLOCKER" > /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
echo "$TEXT_PADDING $1 $(tput bold)impure!$(tput reset) $PORT_BLOCKER -> $PORT_OBJECT_FILE "
|
|
PORT_BLOCKER_COUNT=$[$PORT_BLOCKER_COUNT + 1]
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
if [ $PORT_BLOCKER_COUNT -gt 0 ]; then
|
|
echo " <$1> -> <$1> [headclip=false, label=\" x$PORT_BLOCKER_COUNT \",fontcolor=red,color=red];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
# port contents portname is not available
|
|
else
|
|
echo " <$1> [fillcolor=\"#999999\"];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
# port is not activate
|
|
else
|
|
echo " <$1> [fillcolor=\"#888888\"];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
# port is not installed
|
|
else
|
|
echo " <$1> [fillcolor=\"#777777\"];" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
|
|
# port deps portname?
|
|
#
|
|
# port info portname is used because port deps doesn't reflect consequences of variants added
|
|
PORT_INFO==`$PORT info $1 $VARIANTS | sed "s/,//g" | egrep "Variants: |Build Dependencies|Library Dependencies|Runtime Dependencies" `
|
|
if [ $? -eq 0 ]; then
|
|
RUNTIME_DEPENDENCIES=`echo $PORT_INFO | grep "Runtime Dependencies" | awk -F "Runtime Dependencies: " {'print $2'}`
|
|
if [ "$RUNTIME_DEPENDENCIES" == "" ]; then
|
|
LIBRARY_DEPENDENCIES=`echo $PORT_INFO | grep "Library Dependencies" | awk -F "Library Dependencies: " {'print $2'}`
|
|
if [ "$LIBRARY_DEPENDENCIES" == "" ]; then
|
|
BUILD_DEPENDENCIES=`echo $PORT_INFO | grep "Build Dependencies" | awk -F "Build Dependencies: " {'print $2'}`
|
|
else
|
|
BUILD_DEPENDENCIES=`echo $PORT_INFO | grep "Build Dependencies" | awk -F "Build Dependencies: " {'print $2'} | awk -F "Library Dependencies: " {'print $1'}`
|
|
fi
|
|
else
|
|
LIBRARY_DEPENDENCIES=`echo $PORT_INFO | grep "Library Dependencies" | awk -F "Library Dependencies: " {'print $2'} | awk -F "Runtime Dependencies: " {'print $1'}`
|
|
if [ "$LIBRARY_DEPENDENCIES" == "" ]; then
|
|
BUILD_DEPENDENCIES=`echo $PORT_INFO | grep "Build Dependencies" | awk -F "Build Dependencies: " {'print $2'} | awk -F "Runtime Dependencies: " {'print $1'}`
|
|
else
|
|
BUILD_DEPENDENCIES=`echo $PORT_INFO | grep "Build Dependencies" | awk -F "Build Dependencies: " {'print $2'} | awk -F "Library Dependencies: " {'print $1'}`
|
|
fi
|
|
fi
|
|
fi
|
|
# port variants portname?
|
|
#
|
|
# port info portname is also used in this case (no need to recall port command so)
|
|
PORT_VARIANTS=`$PORT info $1 $VARIANTS | grep 'Variants: '`
|
|
if [ $? -eq 0 ]; then
|
|
PORT_VARIANTS=`echo $PORT_VARIANTS | awk -F "Variants: " {'print $2'} | awk -F ")" {'print $1'}`
|
|
# inspecting all the submitted variant against portname variants
|
|
for v in $VARIANTS; do
|
|
T_VARIANT=`echo $v | sed "s/\+//g" | sed "s/\-//g"` > /dev/null
|
|
# are variants matching?
|
|
echo $PORT_VARIANTS | grep $T_VARIANT > /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
PORT_VARIANTS=`echo $PORT_VARIANTS | sed "s/[( ]$T_VARIANT/$(tput bold)&$(tput reset)/g"`
|
|
echo " subgraph cluster_$T_VARIANT {" >> "$OUTPUT_FILENAME"
|
|
echo " color=grey;" >> "$OUTPUT_FILENAME"
|
|
echo " fillcolor=\"lightgrey\";" >> "$OUTPUT_FILENAME"
|
|
echo " style=\"rounded,filled\";" >> "$OUTPUT_FILENAME"
|
|
#grep "<$1> -> <$1> \[label=\" $v \",fontcolor=honeydew4,color=honeydew4,style=bold\];" $OUTPUT_FILENAME > /dev/null
|
|
#if [ ! $? -eq 0 ]; then
|
|
echo " <$1> -> <$1> [label=\" $v \",fontcolor=honeydew4,color=honeydew4,style=bold];" >> "$OUTPUT_FILENAME"
|
|
#fi
|
|
echo " }" >> "$OUTPUT_FILENAME"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# End of recursion if no dependency found
|
|
if [ "$BUILD_DEPENDENCIES$LIBRARY_DEPENDENCIES$RUNTIME_DEPENDENCIES" = "" ]; then
|
|
echo " <$1> [shape = doublecircle];" >> "$OUTPUT_FILENAME" ;
|
|
echo "$TEXT_PADDING\b. $1 ($PORT_VARIANTS)"
|
|
# Fall recursively into dependencies
|
|
else
|
|
echo "$TEXT_PADDING $1 ($PORT_VARIANTS)"
|
|
echo " <$1> [shape = circle];" >> "$OUTPUT_FILENAME" ;
|
|
|
|
# Iterative look in the build dependencies
|
|
for dep in $BUILD_DEPENDENCIES; do
|
|
# Again, using the dot file to avoid wasting time redoing things already done
|
|
grep " <$1> -> <$dep>;" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
echo " <$dep> [style=filled, fillcolor=\"#6666FFCF\"];" >> "$OUTPUT_FILENAME"
|
|
echo " <$1> -> <$dep>;" >> "$OUTPUT_FILENAME"
|
|
|
|
# Recursive call on each build dep
|
|
$0 $[$LEVEL + 1] $dep
|
|
fi
|
|
done
|
|
|
|
# Iterative look in the lib dependencies
|
|
for dep in $LIBRARY_DEPENDENCIES; do
|
|
# Again, using the dot file to avoid wasting time redoing things already done
|
|
grep " <$1> -> <$dep>;" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
echo " <$dep> [style=filled, fillcolor=\"#9999FFCF\"];" >> "$OUTPUT_FILENAME"
|
|
echo " <$1> -> <$dep>;" >> "$OUTPUT_FILENAME"
|
|
|
|
# Recursive call on each lib dependency
|
|
$0 $[$LEVEL + 1] $dep
|
|
fi
|
|
done
|
|
|
|
# Iterative look in the runtime dependencies
|
|
for dep in $RUNTIME_DEPENDENCIES; do
|
|
# Again, using the dot file to avoid wasting time redoing things already done
|
|
grep " <$1> -> <$dep>;" "$OUTPUT_FILENAME" > /dev/null
|
|
if [ ! $? -eq 0 ]; then
|
|
echo " <$dep> [style=filled, fillcolor=\"#CCCCFFCF\"];" >> "$OUTPUT_FILENAME"
|
|
echo " <$1> -> <$dep>;" >> "$OUTPUT_FILENAME"
|
|
|
|
# Recursive call on each run time dependency
|
|
$0 $[$LEVEL + 1] $dep
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# port has already been processed
|
|
else
|
|
echo "$TEXT_PADDING\b- $1 ($PORT_VARIANTS)"
|
|
fi
|