automake
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

26-makesort.patch


From: Akim Demaille
Subject: 26-makesort.patch
Date: Sun, 04 Feb 2001 14:38:26 +0100

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 address@hidden@'.
          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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]