This message introduces makesort.pl which is quite handy when you
change to much the layout of Makefiles, just like my previous patch
does: it becomes nearly impossible to just diff.
--------------------------------------------------
#! /usr/bin/perl -w
# -*- perl -*-
# @configure_input@
eval 'exec @PERL@ -S $0 ${1+"$@"}'
if 0;
require 5;
# String constants.
$IGNORE_PATTERN = "^##([^#].*)?\$";
# $WHITE_PATTERN = "^[ \t]*\$";
$COMMENT_PATTERN = "^#";
$TARGET_PATTERN="[\$a-zA-Z_.][-.a-zA-Z0-9_(){}/\$]*";
# An action starts with a tab not preceded by an escaped new lines,
# and runs to the end of the paragraph.
# $ACTION_PATTERN="(?<!\\)\n\t.*\$";
# A rule has three parts: a list of targets, a list of dependencies,
# and optionally actions.
$RULE_PATTERN =
"^($TARGET_PATTERN(?:(?:\\\\\n|\\s)+$TARGET_PATTERN)*) *:([^=].*|)\$";
# $SUFFIX_RULE_PATTERN = "^\\.([a-zA-Z0-9]+)\\.([a-zA-Z0-9]+)\$";
# Only recognize leading spaces, not leading tabs. If we recognize
# leading tabs here then we need to make the reader smarter, because
# otherwise it will think rules like `foo=bar; \' are errors.
$MACRO_PATTERN = "^ *([A-Za-z0-9_]+)[ \t]*([:+]?)=[ \t]*(.*)\$";
local $verbose = 0;
my $diff = 0;
my @files = ();
foreach (@ARGV)
{
if ($_ eq '--diff' || $_ eq '-d')
{
$diff = "diff";
next;
}
elsif ($_ eq '--unidiff' || $_ eq '-u')
{
$diff = "diff -u";
next;
}
# Remove `.sort', it makes it easier to use shell history:
# ./makesort Makefile.in.old.sort Makefile.in.new.sort
# diff -u Makefile.in.old.sort Makefile.in.new.sort | less
s/\.sort$//;
push (@files, "$_.sort");
&process_file ($_);
}
if ($diff)
{
system ("$diff @files | less\n");
}
################################################################
sub process_file
{
my ($in) = @_;
my $out = "$in.sort";
local (*OUT);
local %variables = ();
local %dependencies = ();
local %actions = ();
local @scories = ();
open (OUT, ">$out") || die "open $out: $!\n";
file_contents ($in);
print OUT "## Variables ##\n";
foreach (sort keys %variables)
{
print OUT &pretty ("$_ =", "\t", split (' ' , $variables{$_}));
print OUT "\n";
}
print OUT "\n\n";
print OUT "## Rules ## \n";
foreach (sort target_cmp keys %dependencies)
{
print OUT &pretty ("$_:", "\t", @{$dependencies{$_}});
print OUT $actions{$_}
if defined $actions{$_};
print OUT "\n";
}
print OUT "\n\n";
print OUT "## Scories ##\n";
foreach (sort @scories)
{
print OUT "$_\n";
print OUT "\n";
}
print OUT "\n\n";
close (OUT) || die "close $out: $!\n";
}
################################################################
# &target_cmp ($A, $B)
# --------------------
# Subroutine for &handle_factored_dependencies to let `.PHONY' be last.
sub target_cmp
{
return
if $a eq $b;
return -1
if $b eq '.PHONY';
return 1
if $a eq '.PHONY';
return $a cmp $b;
}
# Pretty-print something. HEAD is what should be printed at the
# beginning of the first line, FILL is what should be printed at the
# beginning of every subsequent line.
sub pretty
{
my ($head, $fill, @values) = @_;
my ($column) = length ($head);
my ($result) = $head;
# Fill length is number of characters. However, each Tab
# character counts for eight. So we count the number of Tabs and
# multiply by 7.
my ($fill_length) = length ($fill);
$fill_length += 7 * ($fill =~ tr/\t/\t/d);
my ($bol) = ($head eq '');
foreach (@values)
{
# "75" because we also print a space, and diff add two chars.
if ($column + length ($_) > 75)
{
$result .= " \\\n" . $fill;
$column = $fill_length;
$bol = 1;
}
$result .= ' ' unless ($bol);
$result .= $_;
$column += length ($_) + 1;
$bol = 0;
}
$result .= "\n";
return $result;
}
# $FLATTENED
# &flatten ($STRING)
# ------------------
# Flatten the $STRING and return the result.
sub flatten ($)
{
($_) = @_;
s/\\\n//somg;
s/\s+/ /g;
s/^ //;
s/ $//;
return $_;
}
sub verbose (@)
{
print STDERR "@_\n"
if $verbose;
}
# $CONTENTS
# &file_contents ($FILENAME)
# --------------------------
# Return contents of a file from $am_dir, automatically skipping
# macros or rules which are already known. Runs command on each line
# as it is read; this command can modify $_.
sub file_contents
{
my ($file, $command) = @_;
open (FC_FILE, $file)
|| die "automake: installation error: cannot open \`$file'\n";
# Swallow into $CONTENTS the whole content of the file, after
# having performed the $COMMAND, and removed Automake comments.
my ($contents) = '';
while (<FC_FILE>)
{
# Merely delete comments beginning with two hashes.
next if /$IGNORE_PATTERN/o || /$COMMENT_PATTERN/o;
$contents .= $_;
}
close (FC_FILE);
# We don't need more than two consecutive new-lines.
$contents =~ s/\n{3,}/\n\n/g;
# Process each Make `paragraph'.
#
# A Make `paragraph' is delimited by a new line which is not
# escaped, and not followed by a tab or a comment.
#
# Frankly, unless you like fighting with Perl (you're a freak!),
# if I were you I would not try some other implementation, it's
# very easy falling either into extremely low RE matching
# (backtracking...), or worse yet: infloop... For instance, (my)
# perl goes loopy if you try to
#
# $result_rules =~ /^($TARGET_PATTERN *)+: ($TARGET_PATTERN *)+\n\n/sm
my ($comment) = '';
foreach (split (/(?<!\\)\n(?![\t\#])/, $contents))
{
next
if /^\s*$/om;
verbose "------------- Studying ----------------\n";
verbose "$_\n";
if (/$RULE_PATTERN/mso)
{
# Separate relationship from optional actions: the first
# `new-line tab" not preceded by backslash (continuation
# line).
# I'm quite shoked! It seems that (\\\n|[^\n]) is not the
# same as `([^\n]|\\\n)!!! Don't swap it, it breaks.
my ($relationship, $actions) =
/^((?:\\\n|[^\n])*)(?:\n(\t.*))?$/som;
# Separate targets from dependencies: the first colon.
$relationship =~ /^([^:]+) *: *(.*)$/som;
my ($targets, $dependencies) = (&flatten ($1), &flatten ($2));
verbose "------------- Targets -----------------\n";
verbose "$targets\n";
if ($dependencies)
{
verbose "------------- Dependencies ------------\n";
verbose "$dependencies\n";
}
if ($actions)
{
verbose "------------- Actions -----------------\n";
verbose "$actions\n";
}
verbose "\n\n";
# For the time being, we don't split targets, consider they
# are one.
push (@{$dependencies{$targets}}, split (' ', $dependencies));
$actions{$targets} = $actions;
}
elsif (/$MACRO_PATTERN/mso)
{
$variables{$1} = &flatten ($3);
&prog_error (".am macro \`$1' with trailing backslash at $file:$.")
if /\\$/;;
}
else
{
# This isn't an error; it is probably some tokens which
# configure is supposed to replace, such as `@SET-MAKE@'.
push (@scories, $_);
}
}
}
--------------------------------------------------
Sorry, I'm at home, and I'm editing a plain file, no attachement.
Here are the diff of CVS Automake's Makefiles.in before and after the
previous patch. diff -u is less readable here.
---------------------------------------- Makefile.in
~/src/am % diff /tmp/am/Makefile.in.sort . Err 2 remo
275,276d274
< all-recursive install-data-recursive install-exec-recursive check-recursive
installcheck-recursive info-recursive dvi-recursive:
<
309c307
< clean-am: clean-vti clean-aminfo clean-tags clean-generic mostlyclean-am
---
> clean-am: clean-generic clean-recursive mostlyclean-am
313,316d310
< clean-tags:
<
< clean-vti:
<
388,389c382
< distclean-am: distclean-vti distclean-aminfo distclean-tags \
< distclean-generic clean-am
---
> distclean-am: clean-am distclean-generic distclean-recursive distclean-tags
396,397d388
< distclean-vti:
<
562,563c553,555
< maintainer-clean-am: maintainer-clean-vti maintainer-clean-aminfo \
< maintainer-clean-tags maintainer-clean-generic distclean-am
---
> maintainer-clean-am: distclean-am maintainer-clean-aminfo \
> maintainer-clean-generic maintainer-clean-recursive \
> maintainer-clean-vti
577,578d568
< maintainer-clean-tags:
<
583,584c573,574
< mostlyclean-am: mostlyclean-vti mostlyclean-aminfo mostlyclean-tags \
< mostlyclean-generic
---
> mostlyclean-am: mostlyclean-aminfo mostlyclean-generic mostlyclean-recursive \
> mostlyclean-vti
618,619d607
< mostlyclean-tags:
<
678,688c666,674
< clean clean-aminfo clean-generic clean-recursive clean-tags \
< clean-vti distclean distclean-aminfo distclean-generic \
< distclean-recursive distclean-tags distclean-vti distdir dvi dvi-am \
< dvi-recursive info info-am info-recursive install install-am \
< install-binSCRIPTS install-data install-data-am \
< install-data-recursive install-dist_pkgdataDATA \
< install-dist_scriptDATA install-exec install-exec-am \
< install-exec-recursive install-info-am install-recursive \
< install-strip installcheck installcheck-am installcheck-local \
< installcheck-recursive installdirs installdirs-am \
< installdirs-recursive maintainer-clean maintainer-clean-aminfo \
---
> clean clean-generic clean-recursive distclean distclean-generic \
> distclean-recursive distclean-tags distdir dvi dvi-am dvi-recursive \
> info info-am info-recursive install install-am install-binSCRIPTS \
> install-data install-data-am install-data-recursive \
> install-dist_pkgdataDATA install-dist_scriptDATA install-exec \
> install-exec-am install-exec-recursive install-info-am \
> install-recursive install-strip installcheck installcheck-am \
> installcheck-local installcheck-recursive installdirs \
> installdirs-am installdirs-recursive maintainer-clean \
690,691c676
< maintainer-clean-recursive maintainer-clean-tags \
< maintainer-clean-vti mostlyclean mostlyclean-aminfo \
---
> maintainer-clean-recursive maintainer-clean-vti mostlyclean \
693,694c678,679
< mostlyclean-tags mostlyclean-vti tags tags-recursive uninstall \
< uninstall-am uninstall-binSCRIPTS uninstall-dist_pkgdataDATA \
---
> mostlyclean-vti tags tags-recursive uninstall uninstall-am \
> uninstall-binSCRIPTS uninstall-dist_pkgdataDATA \
----------------------------------------------------------------------
---------------------------------------- m4/Makefile.in
~/src/am % diff /tmp/am/m4/Makefile.in.sort m4 Err 1 remo
159c159
< distclean-am: distclean-generic clean-am
---
> distclean-am: clean-am distclean-generic
215c215
< maintainer-clean-am: maintainer-clean-generic distclean-am
---
> maintainer-clean-am: distclean-am maintainer-clean-generic
----------------------------------------------------------------------
This one shows a problem: the -local targets are not properly handled.
I don't know why, I do believe I have the right code. But I will fix
this. This patch is already too big... Please, let's go by step.
Let it in.
---------------------------------------- tests/Makefile.in
~/src/am % diff /tmp/am/tests/Makefile.in.sort tests 14:29 remo
257c257
< distclean: distclean-am
---
> distclean: distclean-am distclean-local
259c259
< distclean-am: distclean-generic clean-am distclean-local
---
> distclean-am: clean-am distclean-generic
308c308
< maintainer-clean-am: maintainer-clean-generic distclean-am
---
> maintainer-clean-am: distclean-am maintainer-clean-generic