Closed kriegaex closed 12 years ago
This patch breaks the test suite because the _find method is used to look up exact match by design. Maybe we should explicitly pass a Perl regex object to claim that we need a fuzzy match here?
Sounds good to me. What would the code look like then? And how would I use it? (Attention, I do not speak Perl, I am just calling your script!) Currently I am doing something like this with the patched version above:
# Helper script for shell script visualise_make
#
# Author: Alexander Kriegisch (http://scrum-master.de), 2008-02-10
use Makefile::GraphViz;
# Parse makefile
$parser = Makefile::GraphViz->new;
$parser->parse($ARGV[0]);
# Let GraphViz generate an image layout and make sure to exclude "|" as a
# target (will be falsely regarded as one sometimes) and do not display
# dependencies of ".config" (lots of "Config.in" files)
$gv = $parser->plot(
$ARGV[1],
trim_mode => true,
init_args => { rankdir => true, },
exclude => [ "^\\|\$", "/[.](unpack|configur)ed\$" ],
end_with => [ "^[.]config\$", "^Config[.]in[.]cache\$", "^dl\$", "^download-toolchain\$", "^glib2-precompiled\$" ]
);
# Save as PNG bitmap
$gv->as_png($ARGV[2]);
The Perl script is called by a shell script which prepares and cleans up a big makefile with recursive includes like this, because your script cannot deal with what I need correctly:
#!/bin/bash
# Visualise makefile dependencies using Perl module Makefile::GraphViz, see
# http://search.cpan.org/~agent/Makefile-GraphViz/lib/Makefile/GraphViz.pm
#
# If not installed, you will probably see an error like this:
# Creating dependency graph image 'foo.png' for target 'foo' ...
# Can't locate GraphViz.pm in @INC (@INC contains: ...)
# at tools/visualise_make.pl line 5.
#
# So you need to install the module. But before you do, make sure you install
# GraphViz (Debian package "graphviz") first, otherwise the Perl module's
# installation will fail later because of this missing dependency.
#
# You might want to install the module and its dependencies using
# Debian/Ubuntu package "cpanminus" and then issuing the shell command
# sudo cpanm Makefile::GraphViz
#
# If tests fail for Makefile::GraphViz, retry and force installation after
# failed tests when prompted. In my case, the module worked flawlessly anyway:
# sudo cpanm Makefile::GraphViz --prompt
#
# Author: Alexander Kriegisch (http://scrum-master.de), 2008-02-10
CMD=$(basename $0)
usage()
{
cat << EOF
Usage: $CMD [options] target [log-target]
More help: $CMD -?
EOF
}
help()
{
cat << EOF
$CMD - create PNG image visualising a Make target's dependency graph
Usage: $CMD [options] target [log-target]
-? print this usage help
-a [lfica] perform specified actions (default: lfi)
l create log via "make -p"
f filter log as a preparation for visualisation
i create dependency graph image for specified target
c clean up: delete log and filtered file before exiting
a all: equal to "lfic"
-l <file> log file name (default: $CMD.log); contains complete
output of: LC_ALL=C make -j1 -pns log-target
-f <file> filtered log file name (default: $CMD.flt); contains only the
main makefile's targets (no sub-makefile contents in case of
recursive make calls) without variables, implicit rules,
actions, comments etc., i.e. just the data needed for the
dependency graph
-i <file> alternative image file name (default: <target>.png, but dots,
spaces and slashes in target names will be replaced by
underscores, condensing consecutive underscores to one, i.e.
target "../src/foo_/bar/zot.h" becomes image file name
"_src_foo_bar_zot_h.png")
target target to be visualised, resulting in <target>.png (mandatory)
log-target target for log file creation (optional); default: none, i.e.
makefile's default target (usually "all"); specify only if you
wish to create the log-file from a target other than the
default one.
Examples:
$CMD my_target
create + filter log file, possibly overwriting existing *.log, *.flt
files; then create my_target.png containing visual representation of
my_target's dependency graph; do not delete *.log, *.flt so they can be
re-used with another target
$CMD -a i my_other_target
create my_other_target.png, re-using existing $CMD.flt
$CMD -a fi -l my_dir/saved_make.log -i graph-03.png my_3rd_target
filter existing make log into $CMD.flt, then create
graph-03.png for specified target
$CMD -a a -l make.log -f make.flt my_target big_target
create + filter log file, using custom file names make.log, make.flt,
but do not use default make target for log file creation, but big_target;
then create visual representation of my_target's dependency graph;
finally, clean up make.log, make.flt
$CMD -a c
delete $CMD.log and $CMD.flt, do nothing else
$CMD -?
print usage help
Hints for creating custom log files:
- You may create your own make logs via "make -p".
- If so, take care to use "-j1" so as to get a clean log file with
sequential entries. Multi-threaded make may create garbled output.
If your makefile contains -j2 or greater, change it there temporarily,
because the command line switch might not override the makefile setting.
- If you just want to simulate a make run in order to create a log file,
but do not want to actually build files, use "make -n" (dry-run).
Combined with log output generation this would be "make -pn".
- This tool can only filter English make logs, so please use
"LC_ALL=C make ..." so as to avoid locale-specific output.
- This tool only works well for top-level makefiles, so if you have a
build system using recursive make calls, just modify the desired
sub-build's "make" call so as to create a log file of its own,
subsequently filtering it by
$CMD -a fi -l custom_make.log my_target
The reason it does not work for recursive make is that target names can
repeat themselves (e.g. often-used standard targets like all, install,
clean, menuconfig, distclean). To avoid problems, the filter action
always eliminates sub-make output when creating *.flt from *.log.
EOF
}
clean_up()
{
rm -rf "$LOG_FILE"
rm -rf "$FILTER_FILE"
}
# No args -> short usage help
if [ $# -eq 0 ]; then
usage >&2
exit 1
fi
# First arg "-?" -> verbose help
if [ "$1" == "-?" ]; then
help
exit 0
fi
# Set defaults
DO_LOG=y
DO_FILTER=y
DO_IMAGE=y
unset DO_CLEAN
LOG_FILE=$CMD.log
FILTER_FILE=$CMD.flt
while getopts "a:l:f:i:" opt; do
case "$opt" in
a)
ACTIONS="$OPTARG"
# Actions explicitly specified -> only use these, reset others
unset DO_LOG DO_FILTER DO_IMAGE DO_CLEAN
[ "${ACTIONS##*l*}" ] || DO_LOG=y
[ "${ACTIONS##*f*}" ] || DO_FILTER=y
[ "${ACTIONS##*i*}" ] || DO_IMAGE=y
[ "${ACTIONS##*c*}" ] || DO_CLEAN=y
[ "${ACTIONS##*a*}" ] || { DO_LOG=y; DO_FILTER=y; DO_IMAGE=y; DO_CLEAN=y; }
;;
l)
LOG_FILE="$OPTARG"
;;
f)
FILTER_FILE="$OPTARG"
;;
i)
IMAGE_FILE=$(echo "$OPTARG" | sed -r 's#[_/. \t]+#_#g').png
;;
*)
usage >&2
exit 1
;;
esac
done
shift $(($OPTIND - 1))
# Special case: clean up is the only action, no target specified
if [ $# -eq 0 -a "$DO_CLEAN" -a ! "$DO_LOG" -a ! "$DO_FILTER" -a ! "$DO_IMAGE" ]; then
clean_up
exit 0
fi
# Now there should be 1 or 2 positional parameters left (target, log-target)
if [ $# -ne 1 -a $# -ne 2 ]; then
usage >&2
exit 1
fi
TARGET="$1"
LOG_TARGET="$2"
IMAGE_FILE=$(echo "$TARGET" | sed -r 's#[_/. \t]+#_#g').png
# Step 1: generate log file
if [ "$DO_LOG" ]; then
echo -n "Creating make log '$LOG_FILE' from log target '$LOG_TARGET' ... "
LC_ALL=C make -j1 -pnsq $LOG_TARGET > "$LOG_FILE" 2> /dev/null
echo "done."
fi
# Step 2: filter log file
if [ "$DO_FILTER" ]; then
echo -n "Filtering make log '$LOG_FILE' into '$FILTER_FILE'... "
# Filter make log, removing stuff unnecessary for visualisation
cat "$LOG_FILE" |
# Remove everything above files section
sed -r '/^# GNU Make/,/^# Files$/d' |
# Remove comments
sed -r '/^#.*/d' |
# Remove indented lines (recipes)
sed -r '/^[[:space:]]+.*/d' |
# Remove everything else not containing a ":" (target definitions)
sed -r '/^[^:]*$/d' |
# Remove pipe ('|') characters used in order-only prerequisites,
# because the Makefile::GraphViz library would mistake the pipe for a
# target and show it in graphs
tr -d '|' \
> "$FILTER_FILE"
echo "done."
fi
# Step 3: visualise dependency graph as PNG image
if [ "$DO_IMAGE" ]; then
echo -n "Creating dependency graph image '$IMAGE_FILE' for target '$TARGET' ... "
perl $(dirname $0)/$CMD.pl "$FILTER_FILE" $TARGET "$IMAGE_FILE" #> /dev/null 2>&1
#perl $(dirname $0)/$CMD.pl Makefile.makesimple $TARGET "$IMAGE_FILE" #> /dev/null 2>&1
[ $? -ne 0 ] && echo "FAILED" && exit 1
echo "done."
fi
# Step 4: clean up log + filter files
if [ "$DO_CLEAN" ]; then
echo -n "Cleaning up log/filter files ... "
clean_up > /dev/null 2>&1
echo "done."
fi
exit 0
Semi off-topic: It is not just that I do not speak Perl, I also do not speak Dot/GraphViz. Having said that, do you happen to know if after I have created a big Dot file once, can I render just parts of the graph (starting from a specific target or regmatched sets of targets) somehow? After a quick search I did not find any options for the GraphViz command line tools permitting any kind of filtering. It would be nice not to have to run make everytime I want to document any of my many, many subtargets.
Ah, talking about the tests you mentioned: They fail upon installation via cpanminus all the time, so I have to skip them to install the tool anyway.
On Thu, Mar 15, 2012 at 2:55 PM, Alexander Kriegisch < reply@reply.github.com
wrote:
$gv = $parser->plot( $ARGV[1], trim_mode => true, init_args => { rankdir => true, }, exclude => [ "^|\$", "/.ed\$" ], end_with => [ "^[.]config\$", "^Config[.]in[.]cache\$", "^dl\$", "^download-toolchain\$", "^glib2-precompiled\$" ] );
Just pass regex objects to the "exclude" and "end_with" options, like
exclude => [ qr{^\\|\$}, qr{/[.](unpack|configur)ed\$} ],
end_with => [ qr{^[.]config\$}, qr{^Config[.]in[.]cache\$},
qr{^dl\$}, qr{^download-toolchain\$}, qr{^glib2-precompiled\$} ]
And then in the _find method, we should check if the pattern is a plain string or a regex object:
if (ref $_) {
return 1 if $elem =~ $_;
}
return 1 if $elem eq $_;
Regards, -agentzh
On Thu, Mar 15, 2012 at 2:56 PM, Alexander Kriegisch < reply@reply.github.com
wrote:
Ah, talking about the tests you mentioned: They fail upon installation via cpanminus all the time, so I have to skip them to install the tool anyway.
What errors are you getting while running the test suite on your side?
-agentzh
On Thu, Mar 15, 2012 at 3:06 PM, agentzh agentzh@gmail.com wrote:
And then in the _find method, we should check if the pattern is a plain string or a regex object:
if (ref $_) { return 1 if $elem =~ $_; } return 1 if $elem eq $_;
I've already committed this patch to the git master branch.
Could you check if it works for you?
Thanks! -agentzh
Actually, you forgot to unescape the regexes which formerly were strings, so it rather looks like this:
exclude => [ qr{^\|$}, qr{/[.](unpack|configur)ed$} ],
end_with => [ qr{^[.]config$}, qr{^Config[.]in[.]cache$}, qr{^dl$}, qr{^download-toolchain$}, qr{^glib2-precompiled$} ]
This works with the change you suggested (I did it manually, because I have not checked out your repo).
By the way, while you are at it, could you please make sure that the "|" separator between normal and order-only targets is not considered a target, consequently not creating a graph node for it anymore? You would save me the filtering (see scripts above, currently I am filtering it via regex and also in the shell script so as for you to see the two ways it can be done).
As for the failing tests, I will be busy now for a few hours. I need to un-/re-install the module to see the error messages again, I guess. I will let you know ASAP.
On Thu, Mar 15, 2012 at 3:56 PM, Alexander Kriegisch < reply@reply.github.com
wrote:
Actually, you forgot to unescape the regexes which formerly were strings, so it rather looks like this:
exclude => [ qr{^\|$}, qr{/[.](unpack|configur)ed$} ], end_with => [ qr{^[.]config$}, qr{^Config[.]in[.]cache$}, qr{^dl$}, qr{^download-toolchain$}, qr{^glib2-precompiled$} ]
Right.
This works with the change you suggested (I did it manually, because I have not checked out your repo).
Cool :)
By the way, while you are at it, could you please make sure that the "|" separator between normal and order-only targets is not considered a target, consequently not creating a graph node for it anymore? You would save me the filtering (see scripts above, currently I am filtering it via regex and also in the shell script so as for you to see the two ways it can be done).
Have you tried the makesimple script shipped with the Makefile::Parser project? See http://search.cpan.org/dist/Makefile-Parser/
It's recommended to use makesimple to simplify your Makefiles and then do gvmake on that makefile output :)
Regards, -agentzh
I tried to uninstall the module in cpanp and reinstall it, but no more test errors. On two machines the tests only failed upon initial installation (which also happened to be the first CPAN module installations on those machines at all). Any idea how I can reproduce a fresh install?
I am going to try makesimple later. As you can see in my shell script above (makefile name "Makefile.makesimple" in commented line), I experimented with it a while ago, but got interrupted and forgot about it. I will let you know if it helps.
What about the order-only thing ("|" separator)? Will makesimple take care of it or are you planning to do something about it in this module? Would be nice and probably easy for you, knowing your own code.
On Fri, Mar 16, 2012 at 1:57 AM, Alexander Kriegisch reply@reply.github.com wrote:
I tried to uninstall the module in cpanp and reinstall it, but no more test errors. On two machines the tests only failed upon initial installation (which also happened to be the first CPAN module installations on those machines at all). Any idea how I can reproduce a fresh install?
Installing perl from source?
I am going to try makesimple later. As you can see in my shell script above (makefile name "Makefile.makesimple" in commented line), I experimented with it a while ago, but got interrupted and forgot about it. I will let you know if it helps.
Cool :)
What about the order-only thing ("|" separator)? Will makesimple take care of it or are you planning to do something about it in this module? Would be nice and probably easy for you, knowing your own code.
To be honest, I cannot remember the exact details because this piece of code was written many years ago :P It may help here or may not.
Best, -agentzh
I tried to uninstall the module in cpanp and reinstall it, but no more test errors. On two machines the tests only failed upon initial installation (which also happened to be the first CPAN module installations on those machines at all). Any idea how I can reproduce a fresh install?
Installing perl from source?
I guess I will rather not.
I am going to try makesimple later. As you can see in my shell script above (makefile name "Makefile.makesimple" in commented line), I experimented with it a while ago, but got interrupted and forgot about it. I will let you know if it helps.
Cool :)
As I just wrote in the other ticket, it runs for 20 minutes which is way too slow for my purposes.
What about the order-only thing ("|" separator)? Will makesimple take care of it or are you planning to do something about it in this module? Would be nice and probably easy for you, knowing your own code.
To be honest, I cannot remember the exact details because this piece of code was written many years ago :P It may help here or may not.
I just checked: makesimple also creates files with "|" in the output, which is correct. But I guess the right place to filter it would be Makefile::GraphViz anyway because it is only during visualisation that the "|" should not be displayed (at least not as a target node).
Another question. It is OT because it is a GraphViz question, but maybe you know it anyway. I did not find anything in GV's documentation: Is here a way to render nodes for long targets in a way which does not cut the path names? Just as an option. How would I configure Makefile::GraphViz to do that?
On Fri, Mar 16, 2012 at 5:30 PM, Alexander Kriegisch reply@reply.github.com wrote:
I just checked: makesimple also creates files with "|" in the output, which is correct. But I guess the right place to filter it would be Makefile::GraphViz anyway because it is only during visualisation that the "|" should not be displayed (at least not as a target node).
Right, Makefile::GraphViz should handle this.
Another question. It is OT because it is a GraphViz question, but maybe you know it anyway. I did not find anything in GV's documentation: Is here a way to render nodes for long targets in a way which does not cut the path names? Just as an option. How would I configure Makefile::GraphViz to do that?
Makefile::GraphViz cuts those long target names. I think we should expose a configure option to disable that.
Regards, -agentzh
I just checked: makesimple also creates files with "|" in the output, which is correct. But I guess the right place to filter it would be Makefile::GraphViz anyway because it is only during visualisation that the "|" should not be displayed (at least not as a target node).
Right, Makefile::GraphViz should handle this.
Cool :)
Another question. It is OT because it is a GraphViz question, but maybe you know it anyway. I did not find anything in GV's documentation: Is here a way to render nodes for long targets in a way which does not cut the path names? Just as an option. How would I configure Makefile::GraphViz to do that?
Makefile::GraphViz cuts those long target names. I think we should expose a configure option to disable that.
Double cool :)
Another question: I often have graphs for targets like "mc-precompiled"
which depend on other targets ".-precompiled". So I use stuff like
qr{^glib2-precompiled$}
in my script. Because I must edit this for
each and every target, I would prefer to use `qr{^.-precompiled$}`, but
this also makes the script end with my target. It would be nice if your
script would either automatically not end with the target(s) given or if
there was another category "no_end_with" or in which to specify
exceptions (also optionally as regexes). I would also need an exception
list for "exclude", maybe something called "no_exclude".
On Fri, Mar 16, 2012 at 6:15 PM, Alexander Kriegisch reply@reply.github.com wrote:
Another question: I often have graphs for targets like "mc-precompiled" which depend on other targets ".-precompiled". So I use stuff like
qr{^glib2-precompiled$}
in my script. Because I must edit this for each and every target, I would prefer to use `qr{^.-precompiled$}`, but this also makes the script end with my target. It would be nice if your script would either automatically not end with the target(s) given or if there was another category "no_end_with" or in which to specify exceptions (also optionally as regexes). I would also need an exception list for "exclude", maybe something called "no_exclude".
These two options make sense. Would you provide patches?
Thanks! -agentzh
The following patch contains both changes discussed here (regmatch for _find plus new options "no_exclude" and "no_end_with". I am not sure if the first use of "no_exclude" is correct, though. You better double-check. Please note that I have not updated the help text. I suggest you update it the way you want. You have your own style of writing.
--- /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-16 11:38:12.027600173 +0100
+++ /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-16 11:39:44.543601642 +0100
@@ -68,6 +68,9 @@
sub _find ($@) {
my $elem = shift;
foreach (@_) {
+ if (ref $_) {
+ return 1 if $elem =~ $_;
+ }
return 1 if $elem eq $_;
}
return undef;
@@ -124,11 +127,19 @@
$val = $opts{end_with};
my @end_with = ($val and ref $val) ? @$val : ();
+ # process the ``no_end_with'' option:
+ $val = $opts{no_end_with};
+ my @no_end_with = ($val and ref $val) ? @$val : ();
+
# process the ``exclude'' option:
$val = $opts{exclude};
my @exclude = ($val and ref $val) ? @$val : ();
- return $gv if _find($root_name, @exclude);
+ # process the ``no_exclude'' option:
+ $val = $opts{no_exclude};
+ my @no_exclude = ($val and ref $val) ? @$val : ();
+
+ return $gv if _find($root_name, @exclude) and !_find($root_name, @no_exclude);
if (!$gv) {
$gv = GraphViz->new(%init_args);
@@ -152,7 +163,7 @@
$is_virtual = 1;
}
- if (!@roots or _find($root_name, @end_with)) {
+ if (!@roots or _find($root_name, @end_with) and !_find($root_name, @no_end_with)) {
$gv->add_node(
$root_name,
label => $short_name,
@@ -194,7 +205,7 @@
my @prereqs = $root->prereqs;
foreach (@prereqs) {
#warn "$_\n";
- next if _find($_, @exclude);
+ next if _find($_, @exclude) and !_find($_, @no_exclude);
$gv->add_edge(
$_ => $lower_node,
$is_virtual ? (style => 'dashed') : ());
Now I can call Makefile::GraphViz like this:
# Helper script for shell script visualise_make
#
# Author: Alexander Kriegisch (http://scrum-master.de), 2008-02-10
use Makefile::GraphViz;
# Parse makefile
$parser = Makefile::GraphViz->new;
$parser->parse($ARGV[0]);
# Let GraphViz generate an image layout and make sure to exclude "|" as a
# target (will be falsely regarded as one sometimes) and do not display
# dependencies of ".config" (lots of "Config.in" files)
$gv = $parser->plot(
$ARGV[1],
trim_mode => true,
init_args => { rankdir => true, },
exclude => [ qr{^\|$}, qr{/[.](unpack|configur)ed$} ],
end_with => [ qr{^[.]config$}, qr{^Config[.]in[.]cache$}, qr{^dl$}, qr{^download-toolchain$}, qr{^.*-precompiled$} ],
no_end_with => [ $ARGV[1] ]
);
# Save as PNG bitmap
$gv->as_png($ARGV[2]);
As you can see, I am now using qr{^.*-precompiled$}
in "end_with" plus $ARGV[1]
in "no_end_with". This does what I want. :)
BTW: It would be nice if you would push out a new release to CPAN after you have updated the documentation and tests for those two changes. This way I could use the script out of the box in my project without having to instruct all users to patch Makefile::GraphViz. I do not know how busy you are (probably pretty much), so whenever you get around to doing that, it will be appreciated.
P.S.: I forgot about the "|" node issue and about the hard-wired target name shortening. I will be busy for a while, but maybe later I can provide patches for those two things as well. But beware! I still do not speak Perl, the code will probably be horrible.
Okay, I just had a minute to fix the "|" issue:
@@ -194,7 +207,7 @@
my @prereqs = $root->prereqs;
foreach (@prereqs) {
#warn "$_\n";
- next if _find($_, @exclude);
+ next if $_ eq "|" or (_find($_, @exclude) and !_find($_, @no_exclude));
$gv->add_edge(
$_ => $lower_node,
$is_virtual ? (style => 'dashed') : ());
On Fri, Mar 16, 2012 at 6:57 PM, Alexander Kriegisch < reply@reply.github.com
wrote:
BTW: It would be nice if you would push out a new release to CPAN after you have updated the documentation and tests for those two changes. This way I could use the script out of the box in my project without having to instruct all users to patch Makefile::GraphViz. I do not know how busy you are (probably pretty much), so whenever you get around to doing that, it will be appreciated.
Thank you very much! I'll apply all your patches and make a new CPAN release after you confirm everything is straight :)
Thanks! -agentzh
Sorry to bother you, but I am just trying to do something like this:
my @NodeTrimCmds = [
qr{s/.+(.{5}[\\\/].*)$/...$1/o},
qr{s/\\/\\\\/g}
];
I am getting the error: Use of uninitialized value $1 in regexp compilation at /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm line 46.
I really have no idea about how to use regexes in Perl even though I know quite a lot about regexes in general. Can you please tell me how to put regexes containing a search-replace op into an array without having them precompiled in a way which raises this exception? And how to use them later in a loop? I searched a while, but probably not for the right thing. I found a lot of infor about perlre, but not exactly what I was looking for. And I do not want to spend three days learning everything there is to learn about perlre.
Well, I thought I asked a simple question, but if I look at this, it seems to be a bit more complicated: http://stackoverflow.com/questions/125171/passing-a-regex-substitution-as-a-variable-in-perl
I guess to modify the behaviour of _trim_cmd
and _trim_path
we can just as well have the user pass a complete function instead of an array or quoted or cut-in-two replacement expressions. Working on it later, I am needed elsewhere now.
Okay, here is yet another iteration of my code extension. Now it is also possible to set node_trim_fct
and cmd_trim_fct
if a user wants to take care of trimming target paths (node) and commands (cmd) by herself. It took me a while to find out how code references work in Perl. I hope I have done it right. At least my test case seems to work.
+++ /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-17 09:30:02.673576095 +0100
+++ /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-17 09:30:02.673576095 +0100
@@ -68,6 +68,9 @@
sub _find ($@) {
my $elem = shift;
foreach (@_) {
+ if (ref $_) {
+ return 1 if $elem =~ $_;
+ }
return 1 if $elem eq $_;
}
return undef;
@@ -120,15 +123,31 @@
my $trim_mode = $opts{trim_mode};
#warn "TRIM MODE: $trim_mode\n";
+ # process the ``node_trim_fct'' option:
+ $val = $opts{node_trim_fct};
+ my $node_trim_fct = ($val and ref $val) ? $val : \&_trim_path;
+
+ # process the ``cmd_trim_fct'' option:
+ my $cmd_trim_fct = ($val and ref $val) ? $val : \&_trim_cmd;
+ $val = $opts{cmd_trim_fct};
+
# process the ``end_with'' option:
$val = $opts{end_with};
my @end_with = ($val and ref $val) ? @$val : ();
+ # process the ``no_end_with'' option:
+ $val = $opts{no_end_with};
+ my @no_end_with = ($val and ref $val) ? @$val : ();
+
# process the ``exclude'' option:
$val = $opts{exclude};
my @exclude = ($val and ref $val) ? @$val : ();
- return $gv if _find($root_name, @exclude);
+ # process the ``no_exclude'' option:
+ $val = $opts{no_exclude};
+ my @no_exclude = ($val and ref $val) ? @$val : ();
+
+ return $gv if _find($root_name, @exclude) and !_find($root_name, @no_exclude);
if (!$gv) {
$gv = GraphViz->new(%init_args);
@@ -145,17 +164,17 @@
my @roots = ($root_name and ref $root_name) ?
$root_name : ($self->target($root_name));
- my $short_name = _trim_path($root_name);
+ my $short_name = $node_trim_fct->($root_name);
if ($normal_nodes{$root_name}) {
$is_virtual = 0;
} elsif ($vir_nodes{$root_name} or @roots and !$roots[0]->commands) {
$is_virtual = 1;
}
- if (!@roots or _find($root_name, @end_with)) {
+ if (!@roots or _find($root_name, @end_with) and !_find($root_name, @no_end_with)) {
$gv->add_node(
$root_name,
- label => $short_name,
+ label => "[" . $short_name . "]",
$is_virtual ? %vir_node_style : ()
);
return $gv;
@@ -181,7 +200,7 @@
my @cmds = $root->commands;
if (!$trim_mode and @cmds) {
$lower_node = _gen_id();
- my $cmds = join("\n", map { _trim_cmd($_); } @cmds);
+ my $cmds = join("\n", map { $cmd_trim_fct->($_); } @cmds);
$gv->add_node($lower_node, label => $cmds, %cmd_style);
$gv->add_edge(
$lower_node => $root_name,
@@ -194,7 +213,7 @@
my @prereqs = $root->prereqs;
foreach (@prereqs) {
#warn "$_\n";
- next if _find($_, @exclude);
+ next if $_ eq "|" or (_find($_, @exclude) and !_find($_, @no_exclude));
$gv->add_edge(
$_ => $lower_node,
$is_virtual ? (style => 'dashed') : ());
I was also thinking about whether it might be a good idea to have a special node style for end_with
nodes/targets to visualise that the node is actually a subtree, not a leave. But maybe this is not so important because exclude
nodes are also (by definition) not visible and there is not indicator that they exist at all.
Update: I decided to print labels for end_with
nodes like this if label equals "foobar": [foobar]
. I do not know if you like this, but I think it is simple, yet clear, if documented properly.
On Sat, Mar 17, 2012 at 3:07 PM, Alexander Kriegisch reply@reply.github.com wrote:
Okay, here is yet another iteration of my code extension.
Thank you for your contribution! I'll find some time to merge your patches :)
Thanks! -agentzh
To do for documentation update:
exclude
and end_with
, also mixed with simple stringsno_exclude
and no_end_with
: arrays of exceptions (string, regex) for exclude
and end_with
node_trim_fct
and cmd_trim_fct
: optional subroutines to replace the normal length trimming behaviourend_with
nodes are now surrounded by square brackets: [foobar]
All new/changed features also need code examples.
Update: end_with
nodes printed as [ foobar ]
(see above).
On Sat, Mar 17, 2012 at 3:18 PM, Alexander Kriegisch reply@reply.github.com wrote:
To do for documentation update:
[...]
All new/changed features also need code examples.
Do you mind providing some POD documentation patches for these? ;)
Best, -agentzh
"\\l"
in the join command instead of "\n"
and once more at the end of the joined string, i.e. once per line, is there a better way to declare left-justified layout for %CmdStyle
globally?Until lately, I have always preprocessed my makefile, removing all recipes (indented code) or replacing it by a dummy command like "@echo", so as not to run into problems with your script. I also do not intend to use trim_mode => 0
very often, but at the moment I am trying to do so, so now I do not remove the recipes anymore. But now I am getting errors like these:
Bareword found where operator expected at (eval 557) line 1, near "s/^(\S*)(\S*)$/"${1}$(NCURSES_TARGET_DIR)/lib"
Bareword found where operator expected at (eval 604) line 1, near "s/^(\S*)(\S*)$/"${1}$(NTFS_DEST_DIR)/usr"
String found where operator expected at (eval 604) line 1, at end of line
(Missing semicolon on previous line?)
(...)
I dunno how to do with it: sane-backends-dirclean--int:
(...)
Basically there are three types of errors:
Okay, I found out why the error messages were appearing:
Can you tell me where to fix these two issues?
Okay, here is the next version with note-style, left-justified code blocks (not nicely done, as I said, so if you know a better way, let me know), a spelling fix in Parser.pm and still no documentation update. Sorry for my inflationary posting, but I want you to know what I am doing and how I am making progress and/or finding problems.
+++ /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-17 10:37:54.865679438 +0100
+++ /usr/local/share/perl/5.12.4/Makefile/GraphViz.pm 2012-03-17 10:37:54.865679438 +0100
@@ -33,9 +33,9 @@
my %CmdStyle =
(
- shape => 'ellipse',
+ shape => 'note',
style => 'filled',
- fillcolor => '#c7f77c',
+ fillcolor => '#dddddd',
);
my %InitArgs = (
@@ -68,6 +68,9 @@
sub _find ($@) {
my $elem = shift;
foreach (@_) {
+ if (ref $_) {
+ return 1 if $elem =~ $_;
+ }
return 1 if $elem eq $_;
}
return undef;
@@ -120,15 +123,31 @@
my $trim_mode = $opts{trim_mode};
#warn "TRIM MODE: $trim_mode\n";
+ # process the ``node_trim_fct'' option:
+ $val = $opts{node_trim_fct};
+ my $node_trim_fct = ($val and ref $val) ? $val : \&_trim_path;
+
+ # process the ``cmd_trim_fct'' option:
+ $val = $opts{cmd_trim_fct};
+ my $cmd_trim_fct = ($val and ref $val) ? $val : \&_trim_cmd;
+
# process the ``end_with'' option:
$val = $opts{end_with};
my @end_with = ($val and ref $val) ? @$val : ();
+ # process the ``no_end_with'' option:
+ $val = $opts{no_end_with};
+ my @no_end_with = ($val and ref $val) ? @$val : ();
+
# process the ``exclude'' option:
$val = $opts{exclude};
my @exclude = ($val and ref $val) ? @$val : ();
- return $gv if _find($root_name, @exclude);
+ # process the ``no_exclude'' option:
+ $val = $opts{no_exclude};
+ my @no_exclude = ($val and ref $val) ? @$val : ();
+
+ return $gv if _find($root_name, @exclude) and !_find($root_name, @no_exclude);
if (!$gv) {
$gv = GraphViz->new(%init_args);
@@ -145,17 +164,17 @@
my @roots = ($root_name and ref $root_name) ?
$root_name : ($self->target($root_name));
- my $short_name = _trim_path($root_name);
+ my $short_name = $node_trim_fct->($root_name);
if ($normal_nodes{$root_name}) {
$is_virtual = 0;
} elsif ($vir_nodes{$root_name} or @roots and !$roots[0]->commands) {
$is_virtual = 1;
}
- if (!@roots or _find($root_name, @end_with)) {
+ if (!@roots or _find($root_name, @end_with) and !_find($root_name, @no_end_with)) {
$gv->add_node(
$root_name,
- label => $short_name,
+ label => "[" . $short_name . "]",
$is_virtual ? %vir_node_style : ()
);
return $gv;
@@ -181,8 +200,8 @@
my @cmds = $root->commands;
if (!$trim_mode and @cmds) {
$lower_node = _gen_id();
- my $cmds = join("\n", map { _trim_cmd($_); } @cmds);
- $gv->add_node($lower_node, label => $cmds, %cmd_style);
+ my $cmds = join("\\l", map { $cmd_trim_fct->($_); } @cmds);
+ $gv->add_node($lower_node, label => $cmds . "\\l", %cmd_style);
$gv->add_edge(
$lower_node => $root_name,
$is_virtual ? (style => 'dashed') : ()
@@ -194,7 +213,7 @@
my @prereqs = $root->prereqs;
foreach (@prereqs) {
#warn "$_\n";
- next if _find($_, @exclude);
+ next if $_ eq "|" or (_find($_, @exclude) and !_find($_, @no_exclude));
$gv->add_edge(
$_ => $lower_node,
$is_virtual ? (style => 'dashed') : ());
+++ /usr/local/share/perl/5.12.4/Makefile/Parser.pm 2012-03-17 12:36:47.337857384 +0100
+++ /usr/local/share/perl/5.12.4/Makefile/Parser.pm 2012-03-17 12:36:47.337857384 +0100
@@ -201,7 +201,7 @@
$Error = "syntax error: line $.: $_\n";
return undef;
} else {
- warn "I dunno how to do with it: $_\n";
+ warn "I dunno what to do with it: $_\n";
}
}
$self->{_tars} = \%tars;
The next new idea: Would you like a vertically separated record shape for trim_mode => 0
instead of a separate node? I think it would be more intuitive. See http://www.graphviz.org/doc/info/shapes.html#record for more info about record shapes. I imagine the target name in yellow and centered on top and below a separator the (optional) code in gray and left-justified, possibly in a monospace font if I ever find out how to do that. All in all it would look like a box with two vertical compartments.
Now that you made me a committer, I guess we can safely close this issue. Our discussion went way beyong the original inquiry anyway. I have pushed some changes related to our discussion upstream, so the ticket is closed.
Hi!
I am using your nice script to visualise makefiles, but sometimes want to match targets in a pattern-based way for "exclude" or "end_with". So I did this little, useful change. I would be happy if you included it upstream: