Last active
December 21, 2015 21:29
-
-
Save b-adkins/6368918 to your computer and use it in GitHub Desktop.
Script that generates include graphs from Robot Operating System .launch files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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