# Samba Build System
# - create output for Makefile
#
#  Copyright (C) Stefan (metze) Metzmacher 2004
#  Copyright (C) Jelmer Vernooij 2005
#  Released under the GNU GPL

package smb_build::makefile;
use smb_build::output;
use File::Basename;
use strict;

use Cwd 'abs_path';

sub new($$$)
{
	my ($myname, $config, $mkfile) = @_;
	my $self = {};

	bless($self, $myname);

	$self->_set_config($config);

	$self->{output} = "";

	$self->{mkfile} = $mkfile;

	$self->output("################################################\n");
	$self->output("# Autogenerated by build/smb_build/makefile.pm #\n");
	$self->output("################################################\n");
	$self->output("\n");

	if (!$self->{automatic_deps}) {
		$self->output("ALL_PREDEP = proto\n");
		$self->output(".NOTPARALLEL:\n");
	}

	return $self;
}

sub _set_config($$)
{
	my ($self, $config) = @_;

	$self->{config} = $config;

	if (not defined($self->{config}->{srcdir})) {
		$self->{config}->{srcdir} = '.';
	}

	if (not defined($self->{config}->{builddir})) {
		$self->{config}->{builddir}  = '.';
	}

	if ($self->{config}->{prefix} eq "NONE") {
		$self->{config}->{prefix} = $self->{config}->{ac_default_prefix};
	}

	if ($self->{config}->{exec_prefix} eq "NONE") {
		$self->{config}->{exec_prefix} = $self->{config}->{prefix};
	}
}

sub output($$)
{
	my ($self, $text) = @_;

	$self->{output} .= $text;
}

sub _prepare_mk_files($)
{
	my $self = shift;
	my @tmp = ();

	foreach (@smb_build::config_mk::parsed_files) {
		s/ .*$//g;
		push (@tmp, $_);
	}

	if ($self->{gnu_make}) {
		$self->output("
ifneq (\$(MAKECMDGOALS),clean)
ifneq (\$(MAKECMDGOALS),distclean)
ifneq (\$(MAKECMDGOALS),realdistclean)
");
	}

	$self->output("MK_FILES = " . array2oneperline(\@tmp) . "\n");

	if ($self->{gnu_make}) {
		$self->output("
endif
endif
endif
");
	}
}

sub array2oneperline($)
{
	my $array = shift;
	my $output = "";

	foreach (@$array) {
		next unless defined($_);

		$output .= " \\\n\t\t$_";
	}

	return $output;
}

sub _prepare_list($$$)
{
	my ($self,$ctx,$var) = @_;
	my @tmparr = ();

	push(@tmparr, @{$ctx->{$var}}) if defined($ctx->{$var});

	my $tmplist = array2oneperline(\@tmparr);
	return if ($tmplist eq "");

	$self->output("$ctx->{NAME}_$var =$tmplist\n");
}

sub SharedModulePrimitives($$)
{
	my ($self,$ctx) = @_;
	
	#FIXME
}

sub SharedModule($$)
{
	my ($self,$ctx) = @_;

	my $init_obj = "";

	my $sane_subsystem = lc($ctx->{SUBSYSTEM});
	$sane_subsystem =~ s/^lib//;
	
	if ($ctx->{TYPE} eq "PYTHON") {
		$self->output("PYTHON_DSOS += $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}\n");
	} else {
		$self->output("PLUGINS += $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}\n");
		$self->output("installplugins:: $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}\n");
		$self->output("\t\@echo Installing $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME} as \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$ctx->{LIBRARY_REALNAME}\n");
		$self->output("\t\@mkdir -p \$(DESTDIR)\$(modulesdir)/$sane_subsystem/\n");
		$self->output("\t\@cp $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME} \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$ctx->{LIBRARY_REALNAME}\n");
		if (defined($ctx->{ALIASES})) {
			foreach (@{$ctx->{ALIASES}}) {
				$self->output("\t\@rm -f \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$_.\$(SHLIBEXT)\n");
				$self->output("\t\@ln -fs $ctx->{LIBRARY_REALNAME} \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$_.\$(SHLIBEXT)\n");
			}
		}

		$self->output("uninstallplugins::\n");
		$self->output("\t\@echo Uninstalling \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$ctx->{LIBRARY_REALNAME}\n");
		$self->output("\t\@-rm \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$ctx->{LIBRARY_REALNAME}\n");

		if (defined($ctx->{ALIASES})) {
			foreach (@{$ctx->{ALIASES}}) {
				$self->output("\t\@-rm \$(DESTDIR)\$(modulesdir)/$sane_subsystem/$_.\$(SHLIBEXT)\n");
			}
		}
	}

	$self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
	$self->_prepare_list($ctx, "FULL_OBJ_LIST");
	$self->_prepare_list($ctx, "DEPEND_LIST");
	$self->_prepare_list($ctx, "LINK_FLAGS");

	if (defined($ctx->{INIT_FUNCTION}) and $ctx->{TYPE} ne "PYTHON" and 
		$ctx->{INIT_FUNCTION_TYPE} =~ /\(\*\)/) {
		my $init_fn = $ctx->{INIT_FUNCTION_TYPE};
		$init_fn =~ s/\(\*\)/init_module/;
		my $proto_fn = $ctx->{INIT_FUNCTION_TYPE};
		$proto_fn =~ s/\(\*\)/$ctx->{INIT_FUNCTION}/;

		$self->output(<< "__EOD__"
bin/$ctx->{NAME}_init_module.c:
	\@echo Creating \$\@
	\@echo \"#include \\\"includes.h\\\"\" > \$\@
	\@echo \"$proto_fn;\" >> \$\@
	\@echo \"_PUBLIC_ $init_fn\" >> \$\@
	\@echo \"{\" >> \$\@
	\@echo \"	return $ctx->{INIT_FUNCTION}();\" >> \$\@
	\@echo \"}\" >> \$\@
	\@echo \"\" >> \$\@
__EOD__
);
		$init_obj = "bin/$ctx->{NAME}_init_module.o";
	}

	$self->output(<< "__EOD__"
#

$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}: \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST) $init_obj
	\@echo Linking \$\@
	\@mkdir -p $ctx->{SHAREDDIR}
	\@\$(MDLD) \$(LDFLAGS) \$(MDLD_FLAGS) \$(INTERN_LDFLAGS) -o \$\@ \$(INSTALL_LINK_FLAGS) \\
		\$($ctx->{NAME}\_FULL_OBJ_LIST) $init_obj \\
		\$($ctx->{NAME}_LINK_FLAGS)
__EOD__
);

	if (defined($ctx->{ALIASES})) {
		foreach (@{$ctx->{ALIASES}}) {
			$self->output("\t\@rm -f $ctx->{SHAREDDIR}/$_.\$(SHLIBEXT)\n");
			$self->output("\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$_.\$(SHLIBEXT)\n");
		}
	}
	$self->output("\n");
}

sub SharedLibraryPrimitives($$)
{
	my ($self,$ctx) = @_;

	$self->output("$ctx->{NAME}_SOVERSION = $ctx->{SO_VERSION}\n") if (defined($ctx->{SO_VERSION}));
	$self->output("$ctx->{NAME}_VERSION = $ctx->{VERSION}\n") if (defined($ctx->{VERSION}));

	if (not grep(/STATIC_LIBRARY/, @{$ctx->{OUTPUT_TYPE}})) {
		$self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
		$self->_prepare_list($ctx, "FULL_OBJ_LIST");
	}
}

sub SharedLibrary($$)
{
	my ($self,$ctx) = @_;

	$self->output("SHARED_LIBS += $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}\n") if (defined($ctx->{SO_VERSION}));

	$self->_prepare_list($ctx, "DEPEND_LIST");
	$self->_prepare_list($ctx, "LINK_FLAGS");

	my $soarg = "";
	my $lns = "";
	if ($self->{config}->{SONAMEFLAG} ne "#" and defined($ctx->{LIBRARY_SONAME})) {
		$soarg = "$self->{config}->{SONAMEFLAG}$ctx->{LIBRARY_SONAME}";
		if ($ctx->{LIBRARY_REALNAME} ne $ctx->{LIBRARY_SONAME}) {
			$lns .= "\n\t\@test \$($ctx->{NAME}_VERSION) = \$($ctx->{NAME}_SOVERSION) || ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$ctx->{LIBRARY_SONAME}";
		}
	}

	$lns .= "\nifdef $ctx->{NAME}_SOVERSION";
	$lns .= "\n\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$ctx->{LIBRARY_DEBUGNAME}";
	$lns .= "\nendif";

	$self->output(<< "__EOD__"
#
$ctx->{RESULT_SHARED_LIBRARY}: \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST)
	\@echo Linking \$\@
	\@mkdir -p \$(\@D)
	\@\$(SHLD) \$(LDFLAGS) \$(SHLD_FLAGS) \$(INTERN_LDFLAGS) -o \$\@ \$(INSTALL_LINK_FLAGS) \\
		\$($ctx->{NAME}\_FULL_OBJ_LIST) \\
		\$($ctx->{NAME}_LINK_FLAGS) \\
		$soarg$lns
__EOD__
);
	$self->output("\n");
}

sub MergedObj($$)
{
	my ($self, $ctx) = @_;

	return unless defined($ctx->{OUTPUT});

	$self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
	$self->output(<< "__EOD__"
#
$ctx->{RESULT_MERGED_OBJ}: \$($ctx->{NAME}_OBJ_LIST)
	\@echo Partially linking \$@
	\@mkdir -p \$(\@D)
	\$(PARTLINK) -o \$@ \$($ctx->{NAME}_OBJ_LIST)

__EOD__
);
}

sub StaticLibrary($$)
{
	my ($self,$ctx) = @_;

	return unless (defined($ctx->{OBJ_FILES}));

	$self->output("STATIC_LIBS += $ctx->{TARGET_STATIC_LIBRARY}\n") if ($ctx->{TYPE} eq "LIBRARY");

	$self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
	$self->_prepare_list($ctx, "FULL_OBJ_LIST");

	$self->output("$ctx->{RESULT_STATIC_LIBRARY}: \$($ctx->{NAME}_FULL_OBJ_LIST)\n");
}

sub Binary($$)
{
	my ($self,$ctx) = @_;

	unless (defined($ctx->{INSTALLDIR})) {
	} elsif ($ctx->{INSTALLDIR} eq "SBINDIR") {
		$self->output("SBIN_PROGS += bin/$ctx->{BINARY}\n");
	} elsif ($ctx->{INSTALLDIR} eq "BINDIR") {
		$self->output("BIN_PROGS += bin/$ctx->{BINARY}\n");
	}

	$self->output("binaries:: $ctx->{TARGET_BINARY}\n");

	$self->_prepare_list($ctx, "FULL_OBJ_LIST");
	$self->_prepare_list($ctx, "DEPEND_LIST");
	$self->_prepare_list($ctx, "LINK_FLAGS");

$self->output(<< "__EOD__"
$ctx->{RESULT_BINARY}: \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST)
	\@echo Linking \$\@
__EOD__
	);

	if (defined($ctx->{USE_HOSTCC}) && $ctx->{USE_HOSTCC} eq "YES") {
		$self->output(<< "__EOD__"
	\@\$(HOSTLD) \$(HOSTLD_FLAGS) -L\${builddir}/bin/static -o \$\@ \$(INSTALL_LINK_FLAGS) \\
		\$\($ctx->{NAME}_LINK_FLAGS)
__EOD__
		);
	} else {
		$self->output(<< "__EOD__"
	\@\$(BNLD) \$(BNLD_FLAGS) \$(INTERN_LDFLAGS) -o \$\@ \$(INSTALL_LINK_FLAGS) \\
		\$\($ctx->{NAME}_LINK_FLAGS) 

__EOD__
		);
	}
}

sub PythonFiles($$)
{
	my ($self,$ctx) = @_;

	foreach (@{$ctx->{PYTHON_FILES}}) {
		my $target = "bin/python/".basename($_);
		my $source = output::add_dir_str($ctx->{BASEDIR}, $_);
		$self->output("$target: $source\n\n");
		$self->output("PYTHON_PYS += $target\n");
	}
}

sub ProtoHeader($$)
{
	my ($self,$ctx) = @_;

	my $target = "";
	my $comment = "Creating ";

	my $priv = undef;
	my $pub = undef;

	if (defined($ctx->{PRIVATE_PROTO_HEADER})) {
		$priv = output::add_dir_str($ctx->{BASEDIR}, $ctx->{PRIVATE_PROTO_HEADER});
		$target .= $priv;
		$comment .= $priv;
		if (defined($ctx->{PUBLIC_PROTO_HEADER})) {
			$comment .= " and ";
			$target.= " ";
		}
		$self->output("PROTO_HEADERS += $priv\n");
	} else {
		$ctx->{PRIVATE_PROTO_HEADER} = $ctx->{PUBLIC_PROTO_HEADER};
		$priv = output::add_dir_str($ctx->{BASEDIR}, $ctx->{PRIVATE_PROTO_HEADER});
	}

	if (defined($ctx->{PUBLIC_PROTO_HEADER})) {
		$pub = output::add_dir_str($ctx->{BASEDIR}, $ctx->{PUBLIC_PROTO_HEADER});
		$comment .= $pub;
		$target .= $pub;
		$self->output("PROTO_HEADERS += $pub\n");
	} else {
		$ctx->{PUBLIC_PROTO_HEADER} = $ctx->{PRIVATE_PROTO_HEADER};
		$pub = output::add_dir_str($ctx->{BASEDIR}, $ctx->{PUBLIC_PROTO_HEADER});
	}

	$self->output("$pub: $ctx->{MK_FILE} \$($ctx->{NAME}_OBJ_LIST:.o=.c) \$(srcdir)/script/mkproto.pl\n");
	$self->output("\t\@echo \"$comment\"\n");
	$self->output("\t\@\$(PERL) \$(srcdir)/script/mkproto.pl --srcdir=\$(srcdir) --builddir=\$(builddir) --private=$priv --public=$pub \$($ctx->{NAME}_OBJ_LIST)\n\n");
}

sub write($$)
{
	my ($self, $file) = @_;

	$self->output("ALL_OBJS = " . array2oneperline($self->{all_objs}) . "\n");

	$self->_prepare_mk_files();

	$self->output($self->{mkfile});

	if ($self->{automatic_deps}) {
		$self->output("
ifneq (\$(MAKECMDGOALS),clean)
ifneq (\$(MAKECMDGOALS),distclean)
ifneq (\$(MAKECMDGOALS),realdistclean)
ifneq (\$(SKIP_DEP_FILES),yes)
-include \$(DEP_FILES)
endif
endif
endif
endif

ifneq (\$(SKIP_DEP_FILES),yes)
clean::
	\@echo Removing dependency files
	\@find . -name '*.d' -o -name '*.hd' | xargs rm -f
endif
");
	} else {
		$self->output("include \$(srcdir)/static_deps.mk\n");
	}

	open(MAKEFILE,">$file") || die ("Can't open $file\n");
	print MAKEFILE $self->{output};
	close(MAKEFILE);

	print __FILE__.": creating $file\n";
}

1;
