Skip to content

Instantly share code, notes, and snippets.

@b-adkins
Last active December 21, 2015 21:29
Show Gist options
  • Save b-adkins/6368918 to your computer and use it in GitHub Desktop.
Save b-adkins/6368918 to your computer and use it in GitHub Desktop.
Script that generates include graphs from Robot Operating System .launch files.
digraph graphname {
"sim_stage.launch" -> "environment_stage.launch";
"sim_stage.launch" -> "sim_to_map_stage.launch";
"navigation_stage.launch" -> "asdf.launch";
"nav_stack_stage.launch" -> "navigation_stage.launch";
"nav_stack_stage.launch" -> "tf_robot_base2rel_teleop_wasd.launch";
"environment_stage.launch" -> "asdf_node.xml";
"environment_stage.launch" -> "move_base_stage.xml";
"run_all_stage.launch" -> "sim.launch";
"run_all_stage.launch" -> "sim_stage.launch";
"run_all_stage.launch" -> "nav_stack_stage.launch";
"run_all_stage.launch" -> "teleop_keyboard.launch";
"run_all_stage.launch" -> "view_sim.launch";
"tf_robot_base2rel.launch" -> "kinect.launch";
"tf_robot_base2rel.launch" -> "tf_base_link_to_laser.launch";
"tf_robot_base2rel_teleop_wasd.launch" -> "teleop_wasd.launch";
"tf_robot_base2rel_teleop_wasd.launch" -> "tf_robot_base2rel.launch";
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
#!/bin/bash
##
# Generates dot graphs launch files/directory given.
#
# @author Bea Adkins
# @date 08-27-2013
#
## Default file regex.
DEFAULT_PATTERN='.*'
usage()
{
echo "Generates include graphs from ROS .launch files."
echo "USAGE: $0 [flags] package path(s)"
echo "Parameters:"
echo " package ROS package."
echo " path File/directory path relative to package. File patterns"
echo " supported (e.g. adsf.launch, *, run*.launch)."
echo "Flags:"
echo " -f Forces $0 to overwrite outfile."
echo " -o outfile Name of output file."
echo " -e pattern Regex (in quotes) to restrict matching files. Default: '$DEFAULT_PATTERN'."
}
#
# Parse arguments
#
# Default values
OUT_FILE=graph.pdf
PATTERN=$DEFAULT_PATTERN
FORCE=false
# Parse flags
while getopts o:e:f option; do
case $option in
f) FORCE=true;;
o) OUT_FILE=$OPTARG;;
e) PATTERN=$OPTARG;;
\?) echo "Invalid option: -$OPTARG." >&2; exit 1;;
:) echo "Option -$OPTARG requires an argument." >&2; exit 1;;
esac
done
# Shift flags out of argument list
shift $(expr $OPTIND - 1)
# Insufficient positional arguments
if [ $# -lt 2 ]; then
usage
exit 0
fi
#
# Determine paths.
#
# Find absolute package path
PACKAGE=$1
PACKAGE_ROOT=$(rospack find $PACKAGE)
# Abort if not found. Assumes rospack prints its own error message.
if [ -z "$PACKAGE_ROOT" ]; then
exit 1
fi
# Get file pattern.
LAUNCH_PATH=$PACKAGE_ROOT/$2
# Prepare scratch files
TMP_PATH=/tmp/$(basename $OUT_FILE).dot
TMP_PATH_2=/tmp/$(basename $OUT_FILE)_uniq.dot
rm -f $TMP_PATH $TMP_PATH_2 # Silently clean up, if needed.
#
# Find .launch files.
#
# File exists and is directory, start with files matching pattern in directory
if [ -d "$LAUNCH_PATH" ]; then
FILES=$(ls -d -1 $LAUNCH_PATH/*.* | grep "$PATTERN")
# File pattern/file.
else
FILES=$(echo "$LAUNCH_PATH" | grep "$PATTERN")
fi
# Debugging for argument/file path parsing.
# echo "Package: $PACKAGE"
# echo "Package root: $PACKAGE_ROOT"
# echo "Launch path: $LAUNCH_PATH"
# echo "Output file: $OUT_FILE"
# echo "Pattern: $PATTERN"
# echo "Files:"
# for FILE in $FILES; do
# echo "$FILE"
# done
# exit 0
# No matching files found, fail.
if [ -z "$FILES" ]; then
# Generate error message.
MSG="No files found matching '$LAUNCH_PATH'"
# if [ "$PATTERN" != "$DEFAULT_PATTERN" ]; then
MSG="$MSG and pattern '$PATTERN'"
# fi
MSG="$MSG."
echo $MSG
exit 1
fi
# Output file exists, abort if not forced.
if ! $FORCE && [ -f $OUT_FILE ]; then
echo "Output file '$OUT_FILE' exists!"
exit 1
fi
#
# Recursively outputs the includes of this file and its children as DOT graph edges.
#
# @param ROS package of file. Only used for printing.
# @param Single absolute file path.
# @param Output file.
#
extract_dot_edges()
{
#
# Check parameters
#
# Needs one parameter
if [ $# -lt 3 ]; then
echo "Insufficient parameters. Given:$#. Needed: 3. Parameters: $@"
return
fi
# Parse parameters, executing file path.
PACKAGE_NAME=$1
FILE_PATH="$2"
OUT_PATH=$3
# File doesn't exist, return empty
if [ ! -f $FILE_PATH ]; then
return
fi
#
# Extract includes.
#
# 1) grep extracts <include> tags
# 2) sed strips <!-- XML comments -->
# 3) sed extracts 'file' attribute of <include> tags
# 4) sed gets package from "find" commands, placing it in front of the path
#
INCLUDES=$(grep "<include.*>" $FILE_PATH | sed -ne '/<!--/ { :c; /-->/! { N; b c; }; /-->/s/<!--.*-->//g }; /^ *$/!p;' | sed 's/ *<include\( \+.*\)* \+file=["\x27]\(.*\)["\x27]\( \+.*\)*\/*>/\2/' | sed 's!\$(find \([a-zA-Z0-9_]\+\))[/\\]!\1::!g' )
# Dot edges: parent -> child
for INCLUDE in $INCLUDES; do
echo " \"$PACKAGE_NAME::$(basename $FILE_PATH)\" -> \"$(basename $INCLUDE)\";" >> "$OUT_PATH"
done
# Escape condition: no includes found
if [ -z "$INCLUDES" ]; then
return
fi
# For each include recursively call self
for INCLUDE in $INCLUDES; do
INCLUDE_TOK=$(echo $INCLUDE | tr ':' ' ') # Tokenize include by ':'
INCLUDE_PACKAGE_NAME=$(echo $INCLUDE_TOK | cut -f1 -d\ -)
INCLUDE_PATH=$(rospack find $INCLUDE_PACKAGE_NAME)/$(echo $INCLUDE_TOK | cut -f2 -d\ -)
extract_dot_edges "$INCLUDE_PACKAGE_NAME" "$INCLUDE_PATH" "$OUT_PATH"
done
}
# Start the traversing the tree(s).
for FILE in $FILES; do
extract_dot_edges $PACKAGE $FILE $TMP_PATH
done
# Remove duplicates
sort -u $TMP_PATH > $TMP_PATH_2
#
# Print graph boilerplate
#
echo 'digraph graphname {' > $TMP_PATH
echo '}' | cat $TMP_PATH_2 - >> $TMP_PATH
# Generate PDF
dot -Tpdf $TMP_PATH -o $OUT_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment