The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict ;
use warnings ;
use Data::Dumper ;

use PBS::Output ;
use PBS::Constants ;
use PBS::Shell ;

#-------------------------------------------------------------------------------

die "this is an old module and most certainly doesn't work with the latest Pbs version" ;

#-------------------------------------------------------------------------------

sub Multiple_O_Compile
{
my $package            = shift  ;
my $pbs_config     = shift  ;

my @build_sequence = @_ ;
my ($build_result, $build_message) = (BUILD_FAILED, '') ;

PrintInfo("\n** Sequence Miner: 'Multiple Object Files Compile' **\n") unless $pbs_config->{DISPLAY_NO_STEP_HEADER} ;

# Handle the '.c' files that are in the build sequence
# we could also handle only the file that are dependencies for our '.o' files
my (@c_nodes, @not_c_nodes, @o_nodes, @not_o_nodes) ;

for my $node (@build_sequence)
	{
	if($node->{__NAME} =~ /\.c$/)
		{
		push @c_nodes, $node ;
		}
	else
		{
		push @not_c_nodes, $node ;
		}
	}

if(@c_nodes)
	{
	PrintInfo("Found C files in the build sequence, attempting to build them.\n") ;
	($build_result, $build_message) = PBS::Build::BuildSequence
														(
														  $package
														, $pbs_config
														, \@c_nodes
														) ;
														
	if($build_result == BUILD_FAILED)
		{
		PrintInfo("Sequence Miner: 'Multiple Object Files Compile', Aborting.\n") ;
		return ($build_result, $build_message) ;
		}
	}
	
for my $node (@not_c_nodes)
	{
	my $node_will_be_compiled_with_other_nodes = 0 ;
	
	if($node->{__NAME} =~ /\.o$/)
		{
		my $number_of_dependencies = 0 ;
		my $c_dependency  ;
		
		#~ print "********* Checking : $node->{__BUILD_NAME}\n" ;
		for my $key (keys %$node)
			{
			next if($key =~  /^__/) ;
			#~ print "********* dependency $node->{__BUILD_NAME} -> $key\n" ;
			
			$number_of_dependencies++ ;
			if($number_of_dependencies == 2)
				{
				#~ print "!!!!!!!! more than one dependency.\n" ;
				last  ;
				}
			
			$c_dependency = $key if($key =~ /\.c$/) ;
			}
			
		}
		
	unless($node_will_be_compiled_with_other_nodes)
		{
		push @not_o_nodes, $node ;
		}
	}

my %sorted_nodes ;
for my $node (@o_nodes)
	{
	my ($basename, $path, $ext) = File::Basename::fileparse($node->{__BUILD_NAME}, ('\..*')) ;
	
	use Digest::MD5 qw(md5_hex) ;
	
	my $node_digest = GetDigest($node) ;
	
	for my $key (keys %$node_digest)
		{
		# could delete the Pbsfile md5!
		
		delete $node_digest->{$key} unless $key =~ /^__/ ;
		}
		
	my $config_md5 = md5_hex($node_digest) ;
	
	$node->{__MULTIPLE_OBJECT_FILES_COMPILE_PATH} = $path ;
	push @{$sorted_nodes{$path}{$config_md5}}, $node ;
	}

for my $path (keys %sorted_nodes)
	{
	my $command;
	
	for my $config_md5 (keys %{$sorted_nodes{$path}})
		{
		my $first_node = $sorted_nodes{$path}{$config_md5}[0] ;
		my $cc         = $first_node->{__CONFIG}{MULTIPLE_CC} || $first_node->{__CONFIG}{CC} || '! No CC defined' ;
		my $c_flags    = $first_node->{__CONFIG}{MULTIPLE_CFLAGS} || $first_node->{__CONFIG}{CFLAGS} || '' ;
		my $path       = $first_node->{__MULTIPLE_OBJECT_FILES_COMPILE_PATH} ;
		
		#~ my $files_to_build     = join ' ', map({$_->{__BUILD_NAME}} @{$sorted_nodes{$path}{$config_md5}}) ;
		my $c_files_to_compile = join ' ', map({$_->{__MULTIPLE_OBJECT_FILES_COMPILE_C_SOURCE}} @{$sorted_nodes{$path}{$config_md5}}) ;
		
		local $PBS::Shell::silent_commands = 1 ;
		
		if(defined $pbs_config->{CREATE_NODE_PATH})
			{
			use File::Path ;
			mkpath($path) unless(-e $path) ;
			}
			
		PrintInfo("Processing Object files in directory '$path'.\n") ;
		$command = "cd $path && $cc $c_flags -c $c_files_to_compile";
		eval
			{
			PBS::Shell::RunShellCommands($command);
				
			for my $node (@{$sorted_nodes{$path}{$config_md5}})
				{
				# the following  is very important
				# if you _don't_ use -j when building, the build sequence is ordered
				# and everything works fine. But when using the -j switch, PBS reorders
				# the build to a more effective parallel sequence. The parallel sequence 
				# used the parent/child relationship. Children must be tagged as already build.
				$node->{__BUILD_DONE} = "'Multiple OBject Files Compile'" ;
				
#				if(@{$node->{__POST_BUILD_COMMANDS}})
#					{
#					PrintError("Multiple Object Files Compiler: Ignoring post build commands for node '$node->{__BUILD_NAME}'. (Not supported!)\n") ;
#					}
				}
			} ;
			
		#check results
		if($@)
			{
			if($@->isa('PBS::Shell'))
				{
				$build_result = BUILD_FAILED ;
				
				$build_message= "\n\t" . $@->{error} . "\n" ;
				$build_message .= "\tCommand   : '" . $@->{command} . "'\n" ;
				$build_message .= "\tErrno     : " . $@->{errno} . "\n" ;
				$build_message .= "\tErrno text: " . $@->{errno_string} . "\n" ;
				
				PrintError("BUILD_FAILED : $build_message\n") ;
				
				return($build_result, $build_message, @not_o_nodes) ;
				}
			else
				{
				PrintError("'Multiple Object Files Compiler' running '$command': Exception: $@\n") ;
				die ;
				}
			}
		
		PBS::Digest::GenerateNodeDigest($_) for (@o_nodes) ;
		}
	}
	
PrintInfo("\n** Sequence Miner: 'Multiple Object Files Compile' Done **\n") unless $pbs_config->{DISPLAY_NO_STEP_HEADER} ;

#~return($build_result, $build_message, @not_o_nodes) ;
return(1, "ok", @not_o_nodes) ;
}

#-------------------------------------------------------------------------------
1;