#!/usr/bin/env perl
########################################################################
# mkadrules - Write .c.o Makefile rules that also do autodependency
#   generation to $adf for each source subdirectory named on the command
#   line to avoid the need to manually maintain these near-identical
#   Makefile rule sets.
#
#   Each *.o file gets a *.d file generated by the compiler listing the
#   object file's dependencies as discovered by the compiler at build
#   time, using the same build options used to generate the *.o file.
#   These automatically generated dependency rule sets are therefore
#   inherently accurate and comprehensive, which hand-written rules
#   virtually never are, particularly for software that builds on
#   multiple platforms, where out-of-tree dependencies (e.g. the
#   location of stdio.h) vary from one platform to another.
#
#   This mechanism depends on the -M feature of GCC and Clang, which
#   means our build system is probably not as widely portable as it
#   might otherwise be.
#
#   Based on http://scottmcpeak.com/autodepend/autodepend.html
#
# Copyright © 2017 Warren Young
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the names of the authors above
# shall not be used in advertising or otherwise to promote the sale,
# use or other dealings in this Software without prior written
# authorization from those authors.
########################################################################

use strict;
use warnings;

use Cwd;

# The basic rule set we generate, which is repeatedly modified and
# written to the output file.
my $template = <<'TEMPLATE';
obj/%.o: SRCDIR/src/%.c
	$(CC)  -c $(CFLAGS) SRCDIR/src/$*.c -o obj/$*.o
	$(CC) -MM $(CFLAGS) SRCDIR/src/$*.c  > obj/$*.d
	@mv -f obj/$*.d obj/$*.d.tmp
	@sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d
	@sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \
		sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d
	@rm -f obj/$*.d.tmp
TEMPLATE

# Parse command line
die "usage: $0 <srcdir> <paths...>\n\n" unless @ARGV >= 2;
my $srcdir = shift @ARGV;
die "Source tree $srcdir does not exist!\n" unless -d $srcdir;
my @dirs = @ARGV;
for (@dirs) {
    die "Source subdir $_ does not exist!\n" unless -d "$srcdir/$_";
}

# Create the output file
my $adf = 'adrules.mk';
unlink $adf;
open my $ad, '>', $adf, or die "Could not write to $adf: $!\n";
print $ad "## DO NOT MODIFY. GENERATED BY tools/mkadrules! ##\n";
print $ad "## -------------------------------------------- ##\n\n";

# If we're building in-tree, we can simplify $srcdir.
my $intree = $srcdir eq getcwd;
if ($intree) {
    $srcdir = '.';
}

# Write rule set for each dir passed
for my $sdir (@dirs) {
    my $rule = $template;

    my $odir = 'obj/' . $sdir;
    $odir =~ s{/src}{};

    my $cflags = uc $sdir;
    $cflags =~ s{SRC}{};
    $cflags =~ s{^/}{};
    $cflags =~ s{/}{_}g;
    $cflags = $cflags ? "${cflags}_CFLAGS" : 'SIM_CFLAGS';

    $rule =~ s{obj/}{$odir/}g;
    $rule =~ s{src/}{$sdir/}g;
    $rule =~ s{CFLAGS}{$cflags}g;
    $rule =~ s{SRCDIR}{$srcdir}g;
    $rule =~ s{ \./}{ }g;
    print $ad $rule, "\n";
}

# If we're building out-of-tree, some of the *.c files were written
# by autosetup from $srcdir/*.c.in, so we need an additional copy of
# the top src dir rule set especially for them.
unless ($intree) {
    my $rule = $template;
    $rule =~ s{SRCDIR/}{}g;
    print $ad $rule, "\n";
}

# Finish up
close $ad;
chmod 0440, $adf;
print "Generated $adf for ", join(', ', @dirs), ".\n";
