#!/usr/bin/env perl
#
# Copyright © 2012 Nick Bowler
#
# Fixup a non-recursive libltdl makefile fragment.  While libltdl purports to
# support inclusion into non-recursive automake builds, it does a few things
# that are less than ideal.  The most dire problem is that it modifies
# AM_CPPFLAGS and AM_LDFLAGS in a manner that can negatively affect the
# rest of the project.
#
# This script postprocesses Makefile.inc in the libltdl directory and attempts
# to fix these integration problems.  The output can then included normally
# into Makefile.am.
#
# Most of the specific transformations are documented below.
#
# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
# This is free software: you are free to do what the fuck you want to.
# There is NO WARRANTY, to the extent permitted by law.

use strict;
use Getopt::Long;

my $output   = undef;
my $input    = undef;

my $line     = 0;

Getopt::Long::Configure("gnu_getopt", "no_auto_abbrev");
GetOptions(
	"o|output=s"   => \$output,
	"i|input=s"    => \$input,
);

open STDOUT, ">", $output or die "$output: $!\n" if (defined $output);
open STDIN,  "<", $input  or die "$input: $!\n"  if (defined $input);

my $printed_header = 0;
my ($ltdl_dir, $am_dir_prefix);
my (%libtargets, %libtargetflags);
my @libobjfiles;

sub drop {
	undef $_;
	next;
}

sub basename {
	my $file = shift;
	$file =~ m|(?:.+/)?([^/]+)/?|;
	return $1;
}

sub handle_libobj {
	my $distfile = shift;
	my $base;

	return 1 if (!($distfile =~ /(.*)\.c/));
	$base = basename($1);

	die if (!defined $am_dir_prefix);
	print <<EOF;
${am_dir_prefix}libobj_la_SOURCES += $distfile
$ltdl_dir/$base.lo: $ltdl_dir/${am_dir_prefix}libobj_la-$base.lo
\t\$(AM_V_at)(cd \$(\@D) && \$(LN_S) ${am_dir_prefix}libobj_la-\$(\@F) \$(\@F))
EOF

	return 0;
}

sub fixup_libobjs {
	my $raw = shift;

	$raw =~ /([^=]+=)[[:space:]]*(.*)/s;
	my ($left, @right) = ($1, split(/[[:space:]]+/, $2));

	@right = grep(handle_libobj($_), @right);
	return "" if (!@right);
	return join(" ", ($left, @right)) . "\n";
}

while (<STDIN>) {
	$line++;

	# Combine line splices.
	while (s/\\$//) {
		$line++;
		$_ = $_ . <STDIN>
	}

	# I cannot resist...
	drop if (/DO NOT REMOVE THIS LINE/);

	next if (/^#/);

	if (!$printed_header) {
		print "# Postprocessed by ", basename($0), "\n";
		print <<'EOF';
# This trick should define ltdl_orderonly to | iff we're using GNU make.
ltdl_have_orderonly = $(findstring order-only,$(.FEATURES))
ltdl_orderonly = $(ltdl_have_orderonly:order-only=|)
ltdl_core_headers =
ltdl_src_headers = $(ltdl_orderonly) $(ltdl_core_headers)
EOF
		$printed_header = 1;
	}

	# Don't pollute the global AM_CPPFLAGS/AM_LDFLAGS environment with
	# stuff totally specific to libltdl.
	s/^AM_((?:CPPFLAGS|LDFLAGS)[[:space:]]*)\+=/LTDL_\1 =/;

	# Augment references to AM_xFLAGS with the corresponding LTDL_xFLAGS.
	s/\$\(AM_(CPPFLAGS|LDFLAGS)\)/$& \$(LTDL_\1)/;

	# Since some of the targets rely on the automatic use of AM_xFLAGS,
	# we need to track which libraries do not have explicit xFLAGS
	# settings, and add references to LTDL_xFLAGS as appropriate.
	if (/^((lib[[:alpha:]_]+_la_)[[:upper:]]+)/) {
		$libtargetflags{$1} = 1;
		$libtargets{$2} = 1;
	}

	# The use of LIBOBJ makes passing required CPPFLAGS tricky.  We will
	# handle this specially by defining a fake convenience library.
	if (/^([[:alnum:]_]+)libltdl_la_SOURCES[[:space:]]*=/) {
		$am_dir_prefix=$1;
		m|\b([^[:space:]]*)/ltdl\.c\b|s;
		$ltdl_dir = $1;
		print <<EOF;
EXTRA_LTLIBRARIES += $ltdl_dir/libobj.la
\$(${am_dir_prefix}libobj_la_OBJECTS): \$(ltdl_src_headers)
${am_dir_prefix}libobj_la_SOURCES =
${am_dir_prefix}libobj_la_CPPFLAGS = \$(AM_CPPFLAGS) \$(LTDL_CPPFLAGS)
${am_dir_prefix}libobj_la_LINK = false
EOF
	}

	# Handle the relevant LIBOBJ source files, which appear in EXTRA_DIST.
	if (/^EXTRA_DIST[[:space:]]*\+=/) {
		$_ = fixup_libobjs($_);
	}

	# BUILT_SOURCES has similar problems to recursive make: inadequate
	# dependencies lead to incorrect builds.  Collect them into an
	# ordinary variable so we can deal with them later.
	s/BUILT_SOURCES/ltdl_core_headers/;

	# Add explicit dependencies on generated header files to each library.
	if (/^([[:alnum:]_]+)_SOURCES[[:space:]]*=/) {
		print "\$($1_OBJECTS): \$(ltdl_src_headers)\n";
	}
} continue { s/(\n.)/\\\1/g; print; };

print "\n# Automatic default flags for libraries\n";
foreach my $lib (keys %libtargets) {
	foreach my $flag ("CPPFLAGS", "LDFLAGS") {
		if (!$libtargetflags{"$lib$flag"}) {
			print "$lib$flag = \$(AM_$flag) \$(LTDL_$flag)\n";
		}
	}
}
