In between attempting to get the new assembler up and running (currently
dealing with XS issues), Robert Spiers and I have come up with a new
make mechanism.
The syntax may change, and the build mechanism has a ways to go (It's
simply running one step at a time in order, no parallelism or multiple
processes), but the basic idea so far seems sound.

Our replacement for the somewhat non-portable make mechanism relies
entirely on perl, and so far, no external modules are required. The
current version makes allowances for Win32 issues, but has not been
tested on Win32 yet.

Make.pl is a simple perl script that builds a dependency graph and
satisfies a single target recursively. The Makefile it seeks to emulate
follows:

--cut here--
foo.o: foo.c
        cc -c foo.c
bar.o: bar.c
        cc -c bar.c
foo: foo.o bar.o
        cc -o foo foo.o bar.o
--cut here--

This, of course, builds the binary 'foo' from the source files 'foo.c'
and 'bar.c'.

So far, with the exception of the need to explicitly declare a target,
it's a one-to-one translation of the makefile.

The perl translation of the Makefile above follows (with comments):

--cut here--

# Create a compile target for 'foo.o'. "Compile targets" are actually
objects
# that can be queried to determine if they've been completed or not, and
asked
# how they want to be built.

# Although the compile target is named 'CC' this, of course, doesn't
mean that
# 'cc' is used. The compiler name and arguments are determined based on
the
# current platform.

my $foo_o = CC(

  # The 'Object()' syntax here simply takes into account the fact that
  # platforms such as Win32 require different extensions than UNIX.
  # The target is currently declared explicitly, but given the fact that
we
  # already have the input file name, it would be easy to determine the
  # output file name should we want to declare 'output' explicitly.

  output => Object( input => 'foo' ),

  # The input is given an explicit file extension to accommodate for the
fact
  # that the user may want to submit a .C file or .ch file to the
compiler.
  # Should we not need that flexibility, the extension can be removed.

  input => 'foo.c',     # foo.o: foo.c

  # Dependencies are either file names or objects, and as we'll see
later on
  # can be arrays of these as well.

  dependsOn => 'foo.c', #   cc -c foo.c
);

my $bar_o = CC(
  output => Object( input => 'bar' ),
  input => 'bar.c',     # bar.o: bar.c
  dependsOn => 'bar.c', #   cc -c bar.c
);

# The link statement is also platform-sensitive, and introduces another
# platform-dependent directive, 'Executable'. This, of course, accounts
for
# the difference between Win32's '.exe' and UNIX's '' extension for file
# names. In due course, the file name here should be stated without
extension
# of any kind.
my $foo_exe = Link(
  output => Executable( input => 'foo' ),

  # Any directive can take an anonymous array of inputs, and they'll be
  # handled in the order declared. The Object declaration returns the
same
  # name every time, so we could concievably cache these in scalars as
well,
  # but to be pedantic I'm declaring them each time.
  input => [ Object(input=>'foo'),
             Object(input=>'bar') ], # foo: foo.o bar.o

  # And this can be dependent upon one or more files as well,
  # Take special note here that we're depending upon two *objects*, not
files.
  # When time comes to determine if the link target needs to be done,
the
  # script looks through these dependencies to see if 'foo.o' needs to
be
  # rebuilt as well as comparing timestamps with 'foo.exe' to see if the
link
  # needs to be rebuilt.
  dependsOn => [$foo_o, $bar_o],     #   cc -o foo foo.o bar.o
);

# This is simply a directive that explicitly declares that 'foo' is a
target.
# Here, 'foo' is not an executable but a target name, that will be
looked up
# when 'make.pl foo' is run.
$depends->{foo} = Target(
  input => 'foo',
  dependsOn => $foo_exe,
);

--cut here--

This project is currently very much in an alpha state, but on my system
it does handle the very limited set of dependencies you see here very
well. Deleting files and rerunning make.pl does the right thing, as well
as touch'ing files. I haven't expanded the makefile beyond what you see
here yet, although once I'm certain of the foundation the number of
available directives will grow, as will such incidentals as
documentation and more error detection. 

Unlike the UNIX make tool, this application executes the required
actions in linear order in a single process, letting each action
complete before the next one is allowed to begin. In other words, no
parallelism and no determining of which actions can be allowed to
proceed in parallel without worries of race conditions.

I'll commit this later in the week once I make two major alterations to
the structure. The code as it stands builds the list of actions to be
taken in parallel with checking the graph to determine whether a
particular action needs to be taken or not. This needs to be decoupled
so that a sane algorithm for determining which tasks can run in parallel
can be accomplished.

Also, support for multiple make targets such as:
--cut here--
foo bar: foo.c bar.c
--cut here--
doesn't exist yet. This probably needs to either be addressed or worked
around in some fashion, and I admittedly haven't given this matter much
thought.

Also I'm not quite fond of how the back end looks, but I'm willing to
expose the sort-of-OO-guts to let someone else write it as it -should-
be done, but I feel that the important logic is there. It also needs a
few more simple directives, such as 'Perl', 'Header', and 'Delete'. The
latter may require some additional logic for directories, which brings
me to the last item.

Directory standards vary wildly across OSen, and this needs to be
compensated for in some fashion. One approach I'm considering (and
probably will do) is creating a 'FilePath' directive, something along
the lines of C<$encoding_USascii = FilePath(relative=>[qw(encoding
usascii.c)]);> and letting lower-level functions handle the task of
converting the array to a usable path on the appropriate platform.
C<FilePath(absolute=>[])> would do the obvious thing, although we might
have to fall back to using UNIX path syntax for relative paths such as
'../foo/bar.c'.

I will make sure that 'make.pl' contains a translated copy of the
current top-level Makefile.in before release. After the initial release
of the new make.pl, running it will likely EAT YOUR HARD DRIVE. Now that
I've sufficiently scared you, I'll reassure you that I'll comment out
the nasty bits that actually run the dependencies and enable some
debugging so you can watch how make.pl would have actually destroyed
your filesystem had I let it run amok.

Seriously though, with the probable exception of a few things that I
won't be
able to easily translate to the new system, it shouldn't do anything out
of the ordinary. I hope. In any case, uncomment and run at your own
peril.
--
Jeff <[EMAIL PROTECTED]>

Reply via email to