# 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::env;
use smb_build::output;
use File::Basename;
use strict;

use base 'smb_build::env';
use Cwd 'abs_path';

sub new($$$)
{
	my ($myname, $config, $mkfile) = @_;
	my $self = new smb_build::env($config);
	
	bless($self, $myname);

	$self->{manpages} = [];
	$self->{sbin_progs} = [];
	$self->{bin_progs} = [];
	$self->{static_libs} = [];
	$self->{python_dsos} = [];
	$self->{python_pys} = [];
	$self->{shared_libs} = [];
	$self->{headers} = [];
	$self->{plugins} = [];
	$self->{pc_files} = [];
	$self->{proto_headers} = [];
	$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 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") {
		push (@{$self->{python_dsos}}, 
			"$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}");
	} else {
		push (@{$self->{plugins}}, "$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}");
		$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) = @_;

	push (@{$self->{shared_libs}}, $ctx->{RESULT_SHARED_LIBRARY}) 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}";
		}
	}

	if (defined($ctx->{LIBRARY_SONAME})) {
		$lns .= "\n\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$ctx->{LIBRARY_DEBUGNAME}";
	}

	$self->output(<< "__EOD__"
#
$ctx->{RESULT_SHARED_LIBRARY}: \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST)
	\@echo Linking \$\@
	\@mkdir -p $ctx->{SHAREDDIR}
	\@\$(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 bin/mergedobj
	\$(PARTLINK) -o \$@ \$($ctx->{NAME}_OBJ_LIST)

__EOD__
);
}

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

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

	push (@{$self->{static_libs}}, $ctx->{RESULT_STATIC_LIBRARY}) if ($ctx->{TYPE} eq "LIBRARY");

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

	$self->output(<< "__EOD__"
#
$ctx->{RESULT_STATIC_LIBRARY}: \$($ctx->{NAME}_FULL_OBJ_LIST)
	\@echo Linking \$@
	\@rm -f \$@
	\@mkdir -p $ctx->{STATICDIR}
	\@\$(STLD) \$(STLD_FLAGS) \$@ \$($ctx->{NAME}_FULL_OBJ_LIST)

__EOD__
);
}

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

	foreach (@{$ctx->{PUBLIC_HEADERS}}) {
		push (@{$self->{headers}}, output::add_dir_str($ctx->{BASEDIR}, $_));
	}
}

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

	my $extradir = "";

	unless (defined($ctx->{INSTALLDIR})) {
	} elsif ($ctx->{INSTALLDIR} eq "SBINDIR") {
		push (@{$self->{sbin_progs}}, $ctx->{RESULT_BINARY});
	} elsif ($ctx->{INSTALLDIR} eq "BINDIR") {
		push (@{$self->{bin_progs}}, $ctx->{RESULT_BINARY});
	}

	$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" .
					  "\tmkdir -p \$(builddir)/bin/python\n" .
		              "\tcp $source \$@\n\n");
		push (@{$self->{python_pys}}, $target);
	}
}

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

	my $path = output::add_dir_str($ctx->{BASEDIR}, $ctx->{MANPAGE});
	push (@{$self->{manpages}}, $path);
}

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.= " ";
		}
		push (@{$self->{proto_headers}}, $priv);
	} 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;
		push (@{$self->{proto_headers}}, $pub);
	} 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("MANPAGES = " . array2oneperline($self->{manpages})."\n");
	$self->output("BIN_PROGS = " . array2oneperline($self->{bin_progs}) . "\n");
	$self->output("SBIN_PROGS = " . array2oneperline($self->{sbin_progs}) . "\n");
	$self->output("STATIC_LIBS = " . array2oneperline($self->{static_libs}) . "\n");
	$self->output("SHARED_LIBS = " . array2oneperline($self->{shared_libs}) . "\n");
	$self->output("PYTHON_DSOS = " . array2oneperline($self->{python_dsos}) . "\n");
	$self->output("PYTHON_PYS = " . array2oneperline($self->{python_pys}) . "\n");
	$self->output("PUBLIC_HEADERS = " . array2oneperline($self->{headers}) . "\n");
	$self->output("PC_FILES = " . array2oneperline($self->{pc_files}) . "\n");
	$self->output("ALL_OBJS = " . array2oneperline($self->{all_objs}) . "\n");
	$self->output("PROTO_HEADERS = " . array2oneperline($self->{proto_headers}) .  "\n");
	$self->output("PLUGINS = " . array2oneperline($self->{plugins}) . "\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;