# autolatex - Make.pm
# Copyright (C) 2013-2016  Stephane Galland <galland@arakhne.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

=pod

=head1 NAME

Make.pm - Make-like Tools

=head1 DESCRIPTION

Provides tools that are close to the Make tool.

To use this library, type C<use AutoLaTeX::Make::Make;>.

=head1 GETTING STARTED

=head2 Initialization

To create a Make tool, say something like this:

    use AutoLaTeX::Make::Make;

    my $make = AutoLaTeX::Make::Make->new($configuration) ;

...or something similar. Acceptable parameters to the constructor are:

=over

=item * C<configuration> is an associative array that contains all the configuration of AutoLaTeX.

=back

=head1 METHOD DESCRIPTIONS

This section contains only the methods in TeXParser.pm itself.

=over

=cut
package AutoLaTeX::Make::Make;

our @ISA = qw( Exporter );
our @EXPORT = qw( );
our @EXPORT_OK = qw();

require 5.014;
use strict;
use utf8;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
use Exporter;
use Class::Struct;
use File::Basename;
use File::Spec;
use Carp;

use AutoLaTeX::Core::Util;
use AutoLaTeX::Core::IntUtils;
use AutoLaTeX::Core::Config;
use AutoLaTeX::Core::OS;
use AutoLaTeX::Core::Progress;
use AutoLaTeX::TeX::BibCitationAnalyzer;
use AutoLaTeX::TeX::TeXDependencyAnalyzer;
use AutoLaTeX::TeX::IndexAnalyzer;
use AutoLaTeX::TeX::GlossaryAnalyzer;

our $VERSION = '34.0';

my $EXTENDED_WARNING_CODE = <<'ENDOFTEX';
	%*************************************************************
	% CODE ADDED BY AUTOLATEX TO CHANGE THE OUPUT OF THE WARNINGS
	%*************************************************************
	\makeatletter
	\newcount\autolatex@@@lineno
	\newcount\autolatex@@@lineno@delta
	\xdef\autolatex@@@mainfile@real{::::REALFILENAME::::}
	\def\autolatex@@@mainfile{autolatex_autogenerated.tex}
	\xdef\autolatex@@@filename@stack{{\autolatex@@@mainfile}{\autolatex@@@mainfile}}
	\global\let\autolatex@@@currentfile\autolatex@@@mainfile
	\def\autolatex@@@filename@stack@push#1{%
		\xdef\autolatex@@@filename@stack{{#1}\autolatex@@@filename@stack}%
	}
	\def\autolatex@@@filename@stack@pop@split#1#2\@nil{%
		\gdef\autolatex@@@currentfile{#1}%
		\gdef\autolatex@@@filename@stack{#2}%
	}
	\def\autolatex@@@filename@stack@pop{%
		\expandafter\autolatex@@@filename@stack@pop@split\autolatex@@@filename@stack\@nil}
	\def\autolatex@@@update@filename{%
		\ifx\autolatex@@@mainfile\autolatex@@@currentfile%
			\edef\autolatex@@@warning@filename{\autolatex@@@mainfile@real}%
			\global\autolatex@@@lineno@delta=::::AUTOLATEXHEADERSIZE::::\relax%
		\else%
			\edef\autolatex@@@warning@filename{\autolatex@@@currentfile}%
			\global\autolatex@@@lineno@delta=0\relax%
		\fi%
		{\filename@parse{\autolatex@@@warning@filename}\global\let\autolatex@@@filename@ext\filename@ext}%
		\xdef\autolatex@@@generic@warning@beginmessage{!!!![BeginWarning]\autolatex@@@warning@filename:\ifx\autolatex@@@filename@ext\relax.tex\fi:}%
		\xdef\autolatex@@@generic@warning@endmessage{!!!![EndWarning]\autolatex@@@warning@filename}%
	}
	\def\autolatex@@@openfile#1{%
		\expandafter\autolatex@@@filename@stack@push{\autolatex@@@currentfile}%
		\xdef\autolatex@@@currentfile{#1}%
		\autolatex@@@update@filename%
	}
	\def\autolatex@@@closefile{%
		\autolatex@@@filename@stack@pop%
		\autolatex@@@update@filename%
	}
	\let\autolatex@@@InputIfFileExists\InputIfFileExists
	\long\def\InputIfFileExists#1#2#3{%
		\autolatex@@@openfile{#1}%
		\autolatex@@@InputIfFileExists{#1}{#2}{#3}%
		\autolatex@@@closefile%
	}
	\let\autolatex@@@input\@input
	\long\def\@input#1{%
		\autolatex@@@openfile{#1}%
		\autolatex@@@input{#1}%
		\autolatex@@@closefile%
	}
	\global\DeclareRobustCommand{\GenericWarning}[2]{%
		\global\autolatex@@@lineno\inputlineno\relax%
		\global\advance\autolatex@@@lineno\autolatex@@@lineno@delta\relax%
		\begingroup
		\def\MessageBreak{^^J#1}%
		\set@display@protect
		\immediate\write\@unused{^^J\autolatex@@@generic@warning@beginmessage\the\autolatex@@@lineno: #2\on@line.^^J\autolatex@@@generic@warning@endmessage^^J}%
		\endgroup
	}
	\autolatex@@@update@filename
	\makeatother
	%*************************************************************
ENDOFTEX

my %COMMAND_DEFINITIONS = (
	'pdflatex' => {
		'cmd' => 'pdflatex',
		'flags' => ['-halt-on-error', '-interaction', 'batchmode', '-file-line-error'],
		'to_dvi' => ['-output-format=dvi'],
		'to_ps' => undef,
		'to_pdf' => ['-output-format=pdf'],
		'synctex' => '-synctex=1',
		'jobname' => '-jobname',
		'ewarnings' => $EXTENDED_WARNING_CODE,
	},
	'latex' => {
		'cmd' => 'latex',
		'flags' => ['-halt-on-error', '-interaction', 'batchmode', '-file-line-error'],
		'to_dvi' => ['-output-format=dvi'],
		'to_ps' => undef,
		'to_pdf' => ['-output-format=pdf'],
		'synctex' => '-synctex=1',
		'jobname' => '-jobname',
		'ewarnings' => $EXTENDED_WARNING_CODE,
	},
	'xelatex' => {
		'cmd' => 'xelatex',
		'flags' => ['-halt-on-error', '-interaction', 'batchmode', '-file-line-error'],
		'to_dvi' => ['-no-pdf'],
		'to_ps' => undef,
		'to_pdf' => [],
		'synctex' => '-synctex=1',
		'jobname' => '-jobname',
		'ewarnings' => $EXTENDED_WARNING_CODE,
	},
	'lualatex' => {
		'cmd' => 'lualatex',
		'flags' => ['-halt-on-error', '-interaction', 'batchmode', '-file-line-error'],
		'to_dvi' => ['-output-format=dvi'],
		'to_ps' => undef,
		'to_pdf' => ['-output-format=pdf'],
		'synctex' => '-synctex=1',
		'jobname' => '-jobname',
		'ewarnings' => $EXTENDED_WARNING_CODE,
	},
	'bibtex' => {
		'cmd' => 'bibtex',
		'flags' => [],
	},
	'biber' => {
		'cmd' => 'biber',
		'flags' => [],
	},
	'makeindex' => {
		'cmd' => 'makeindex',
		'flags' => [],
	},
	'makeglossaries' => {
		'cmd' => 'makeglossaries',
		'flags' => [],
	},
	'dvi2ps' => {
		'cmd' => 'dvips',
		'flags' => [],
	},
);

struct( Entry => [
		'file' => '$',
		'go_up' => '$',
		'rebuild' => '$',
		'parent' => '$',
] );

sub newEntry($$) {
	my $e = Entry->new;
	@$e = ($_[0],0,0,$_[1]);
	return $e;
}

#------------------------------------------------------
#
# Constructor
#
#------------------------------------------------------

sub new(\%) : method {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $parent = ref($proto) && $proto ;

	my $self ;
	if ( $parent ) {
		%{$self} = %{$parent} ;
	}
	else {
		$self = { 
			'configuration' => $_[0],
			'files' => {},
			'rootFiles' => [],
			'is_extended_warning_enable' => 0,
			'is_biblio_enable' => 1,
			'is_makeindex_enable' => 1,
			'is_makeglossaries_enable' => 1,
			'warning_level' => 1,
			'generation_type' => 'pdf',
			'latex_cmd' => [],
			'bibtex_cmd' => [],
			'biber_cmd' => [],
			'makeindex_cmd' => [],
			'makeglossaries_cmd' => [],
			'dvi2ps_cmd' => [],
		};
	}
	bless( $self, $class );

	#
	# Build the different commands according to the current configuration
	#
	$self->{'type'} = $_[0]->{'generation.generation type'} || 'pdf';
	my $compiler = $_[0]->{'generation.tex compiler'} || 'pdflatex';
	$self->{'compiler_definition'} = $COMMAND_DEFINITIONS{"$compiler"};
	my $def = $self->{'compiler_definition'};
	confess("No command definition for '$compiler'") unless ($def);

	# LaTeX
	if ($_[0]->{'generation.latex_cmd'}) {
		push @{$self->{'latex_cmd'}}, $_[0]->{'generation.latex_cmd'};
	}
	else {		
		push @{$self->{'latex_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
		confess("No command definition for '$compiler/".$self->{'type'}."'") unless (exists $def->{'to_'.$self->{'type'}});

		# Support of SyncTeX
		if (cfgBoolean($_[0]->{'generation.synctex'}) && $def->{'synctex'}) {
			push @{$self->{'latex_cmd'}}, $def->{'synctex'};
		}

		my $target = $def->{'to_'.$self->{'type'}};
		if (defined($target)) {
			push @{$self->{'latex_cmd'}}, @{$target};
		}
		elsif ($self->{'type'} eq 'ps') {
			push @{$self->{'latex_cmd'}}, @{$def->{'to_dvi'}};
		}
		else {
			confess('invalided Maker state: cannot find the command line to compile TeX files.');
		}
	}

	if ($_[0]->{'generation.latex_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.latex_flags'}));
		push @{$self->{'latex_cmd'}}, @params;
	}

	# Change the warning level
	if (defined($_[0]->{'__private__'}{'CLI.warning level'})) {
		$self->{'warning_level'} = int($_[0]->{'__private__'}{'CLI.warning level'});
	}

	# Support of extended warnings
	if (($_[0]->{'__private__'}{'CLI.is extended tex warnings'} || ($self->{'warning_level'}>1))
	    && $def->{'ewarnings'}) {
		my $code = $def->{'ewarnings'} || '';
		$code =~ s/^\s+//gm;
		$code =~ s/\s+$//gm;
		my $s = - countLinesIn($code);
		$code =~ s/\Q::::AUTOLATEXHEADERSIZE::::\E/$s/sg;
		$self->{'latex_warning_code'} = $code;
		$self->{'is_extended_warning_enable'} = 1;
	}

	# BibTeX
	if ($_[0]->{'generation.bibtex_cmd'}) {
		push @{$self->{'bibtex_cmd'}}, $_[0]->{'generation.bibtex_cmd'};
	}
	else {
		$def = $COMMAND_DEFINITIONS{'bibtex'};
		confess("No command definition for 'bibtex'") unless ($def);
		push @{$self->{'bibtex_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
	}

	if ($_[0]->{'generation.bibtex_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.bibtex_flags'}));
		push @{$self->{'bibtex_cmd'}}, @params;
	}

	# Biber
	if ($_[0]->{'generation.biber_cmd'}) {
		push @{$self->{'biber_cmd'}}, $_[0]->{'generation.biber_cmd'};
	}
	else {
		$def = $COMMAND_DEFINITIONS{'biber'};
		confess("No command definition for 'biber'") unless ($def);
		push @{$self->{'biber_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
	}

	if ($_[0]->{'generation.biber_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.biber_flags'}));
		push @{$self->{'biber_cmd'}}, @params;
	}

	# MakeIndex
	if ($_[0]->{'generation.makeindex_cmd'}) {
		push @{$self->{'makeindex_cmd'}}, $_[0]->{'generation.makeindex_cmd'};
	}
	else {
		$def = $COMMAND_DEFINITIONS{'makeindex'};
		confess("No command definition for 'makeindex'") unless ($def);
		push @{$self->{'makeindex_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
	}

	if ($_[0]->{'generation.makeindex_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.makeindex_flags'}));
		push @{$self->{'makeindex_cmd'}}, @params;
	}

	# MakeGlossaries
	if ($_[0]->{'generation.makeglossaries_cmd'}) {
		push @{$self->{'makeglossaries_cmd'}}, $_[0]->{'generation.makeglossaries_cmd'};
	}
	else {
		$def = $COMMAND_DEFINITIONS{'makeglossaries'};
		confess("No command definition for 'makeglossaries'") unless ($def);
		push @{$self->{'makeglossaries_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
	}

	if ($_[0]->{'generation.makeglossaries_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.makeglossaries_flags'}));
		push @{$self->{'makeglossaries_cmd'}}, @params;
	}

	# dvi2ps
	if ($_[0]->{'generation.dvi2ps_cmd'}) {
		push @{$self->{'dvi2ps_cmd'}}, $_[0]->{'generation.dvi2ps_cmd'};
	}
	else {
		$def = $COMMAND_DEFINITIONS{'dvi2ps'};
		confess("No command definition for 'dvi2ps'") unless ($def);
		push @{$self->{'dvi2ps_cmd'}}, $def->{'cmd'}, @{$def->{'flags'}};
	}

	if ($_[0]->{'generation.dvi2ps_flags'}) {
		my @params = split(/\s+/, ($_[0]->{'generation.dvi2ps_flags'}));
		push @{$self->{'dvi2ps_cmd'}}, @params;
	}

	return $self;
}

=pod

=item * makeRelativePath($)

Make the path relative to the current directory.

=cut
sub makeRelativePath($) : method {
	my $self = shift;
	my $relativePath = File::Spec->abs2rel($_[0]);
	return $relativePath;
}


=pod

=item * reset()

Reset this make tool.

=cut
sub reset() : method {
	my $self = shift;
	$self->{'files'} = {};
	$self->{'rootFiles'} = [];
	return undef;
}

=pod

=item * addTeXFile($)

Add the given TeX file into the building process.
Takes 1 arg:

=over

=item * file (string)

is the name of the TeX file to read.

=back

=cut
sub addTeXFile($) : method {
	my $self = shift;
	my $rootfile = shift;
	$rootfile = File::Spec->rel2abs($rootfile);
	my $rootbasename = basename($rootfile, '.tex');
	my $roottemplate = File::Spec->catfile(dirname($rootfile), "$rootbasename");

	my $pdfFile = "$roottemplate.pdf";
	$self->{'files'}{$pdfFile} = {
		'type' => 'pdf',
		'dependencies' => { $rootfile => undef },
		'change' => lastFileChange($pdfFile),
		'mainFile' => $rootfile,
	};
	push @{$self->{'rootFiles'}}, "$pdfFile";

	return undef;
}

sub _computeDependenciesForRootFile($) : method {
	my $self = shift;
	my $pdfFile = shift;
	my $rootfile = $self->{'files'}{$pdfFile}{'mainFile'};
	my $rootdir = dirname($rootfile);
	my $rootbasename = basename($rootfile, '.tex');
	my $roottemplate = File::Spec->catfile(dirname($rootfile), "$rootbasename");

	my @files = ( $rootfile );
	while (@files) {
		my $file = shift @files;
		printDbgFor(2, formatText(_T("Parsing '{}'"), $file));
		if (-f "$file" ) {
			printDbgIndent();
			printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$file)));
			$self->{'files'}{$file} = {
				'type' => 'tex',
				'dependencies' => {},
				'change' => lastFileChange($file),
			};
			my %deps = getDependenciesOfTeX($file,$rootdir);
			if (%deps) {
				my $dir = dirname($file);

				#
				# INCLUDED FILES
				#
				foreach my $cat ('tex', 'sty', 'cls') {
					if ($deps{$cat}) {
						foreach my $dpath (@{$deps{$cat}}) {
							if (!File::Spec->file_name_is_absolute($dpath)) {
								$dpath = File::Spec->catfile($dir, $dpath);
							}
							if ($dpath !~ /\.$cat/) {
								$dpath .= ".$cat";
							}
							printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$dpath)));
							$self->{'files'}{$dpath} = {
								'type' => $cat,
								'dependencies' => {},
								'change' => lastFileChange($dpath),
							};
							$self->{'files'}{$pdfFile}{'dependencies'}{$dpath} = undef;
							if ($cat eq 'tex') {
								push @files, $dpath;
							}
						}
					}
				}

				#
				# BIBLIOGRAPHY CALLED FROM THE TEX
				#
				if ($deps{'biblio'}) {
					while (my ($bibdb,$bibdt) = each(%{$deps{'biblio'}})) {
						my $dir = dirname($file);
						if ($rootdir ne $dir) {
							$bibdb = $rootbasename;
						}
						my $bblfile = File::Spec->catfile("$rootdir", "$bibdb.bbl");
						printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$bblfile)));
						$self->{'files'}{"$bblfile"} = {
							'type' => 'bbl',
							'dependencies' => {},
							'change' => lastFileChange("$bblfile"),
							'use_biber' => $deps{'biber'},
						};
						$self->{'files'}{$pdfFile}{'dependencies'}{$bblfile} = undef;
						foreach my $cat ('bib', 'bst', 'bbc', 'cbx') {
							if ($bibdt->{$cat}) {
								foreach my $dpath (@{$bibdt->{$cat}}) {
									if (!File::Spec->file_name_is_absolute($dpath)) {
										$dpath = File::Spec->catfile("$rootdir", $dpath);
									}
									if ($dpath !~ /\.$cat/) {
										$dpath .= ".$cat";
									}
									printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$dpath)));
									$self->{'files'}{$dpath} = {
										'type' => $cat,
										'dependencies' => {},
										'change' => lastFileChange($dpath),
									};
									$self->{'files'}{"$bblfile"}{'dependencies'}{$dpath} = undef;
								}
							}
						}
					}					
				}

				#
				# INDEX
				#
				if ($deps{'idx'}) {
					for my $idxdep (@{$deps{'idx'}}) {
						my $idxbasefilename;
						if ($idxdep) {
							$idxbasefilename = "$idxdep";
						} else {
							$idxbasefilename = "$roottemplate";
						}
						my $idxfile = "$idxbasefilename.idx";
						printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$idxfile)));
						$self->{'files'}{"$idxfile"} = {
							'type' => 'idx',
							'dependencies' => {},
							'change' => lastFileChange("$idxfile"),
						};
						my $indfile = "$idxbasefilename.ind";
						printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$indfile)));
						$self->{'files'}{"$indfile"} = {
							'type' => 'ind',
							'dependencies' => { $idxfile => undef },
							'change' => lastFileChange("$indfile"),
						};
						$self->{'files'}{$pdfFile}{'dependencies'}{$indfile} = undef;
					}
				}

				#
				# GLOSSARIES
				#
				if ($deps{'glo'}) {
					my $glofile = "$roottemplate.glo";
					printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$glofile)));
					$self->{'files'}{"$glofile"} = {
						'type' => 'glo',
						'dependencies' => {},
						'change' => lastFileChange("$glofile"),
					};
					my $glsfile = "$roottemplate.gls";
					printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$glsfile)));
					$self->{'files'}{"$glsfile"} = {
						'type' => 'gls',
						'dependencies' => { $glofile => undef },
						'change' => lastFileChange("$glsfile"),
					};
					$self->{'files'}{$pdfFile}{'dependencies'}{$glsfile} = undef;
				}
			}
			printDbgUnindent();
		}
	}

	printDbgFor(2, formatText(_T("Parsing auxiliary files")));
	printDbgIndent();

	#
	# BIBLIOGRAPHY FROM INSIDE AUXILIARY FILES (MULTIBIB...)
	#
	local *DIR;
	opendir(*DIR, "$rootdir") or printErr("$rootdir: $!");
	while (my $dir = readdir(*DIR)) {
		if ((!isIgnorableDirectory($dir)) && $dir =~ /^(.+?)\.aux$/) {
			my $bibdb = "$1";
			if ($bibdb ne "$rootbasename") {
				my $auxfile = File::Spec->catfile("$rootdir", "$dir");
				my %data = getAuxBibliographyData("$auxfile");
				if ($data{'databases'} || $data{'styles'}) {
					my $bblfile = File::Spec->catfile("$rootdir", "$bibdb.bbl");
					printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$bblfile)));
					$self->{'files'}{"$bblfile"} = {
						'type' => 'bbl',
						'dependencies' => {},
						'change' => lastFileChange("$bblfile"),
					};
					$self->{'files'}{$pdfFile}{'dependencies'}{$bblfile} = undef;
					if ($data{'styles'}) {
						foreach my $style (@{$data{'styles'}}) {
							my $bstfile = File::Spec->catfile("$rootdir", "$style.bst");
							if (-r "$bstfile") {
								printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$bstfile)));
								$self->{'files'}{"$bstfile"} = {
									'type' => 'bst',
									'dependencies' => {},
									'change' => lastFileChange("$bstfile"),
								};
								$self->{'files'}{$bblfile}{'dependencies'}{$bstfile} = undef;
							}
						}
					}
					if ($data{'databases'}) {
						foreach my $db (@{$data{'databases'}}) {
							my $bibfile = File::Spec->catfile("$rootdir", "$db.bib");
							if (-r "$bibfile") {
								printDbgFor(3, formatText(_T("Adding file '{}'"), removePathPrefix($rootdir,$bibfile)));
								$self->{'files'}{"$bibfile"} = {
									'type' => 'bib',
									'dependencies' => {},
									'change' => lastFileChange("$bibfile"),
								};
								$self->{'files'}{$bblfile}{'dependencies'}{$bibfile} = undef;
							}
						}
					}
				}
			}
		}
	}
	closedir(*DIR);

	printDbgUnindent();

	return undef;
}

=pod

=item * runLaTeX()

Launch pdfLaTeX once time.

=over 4

=item * C<file> is the name of the PDF or the TeX file to compile.

=item * C<enableLoop> (optional boolean) indicates if this function may loop on the LaTeX compilation when it is requested by the LaTeX tool.

=item * C<buffering_warnings> (optional boolean) indicates if the warnings are buffered or not.

=back

=cut
sub runLaTeX($;$$) : method {
	my $self = shift;
	my $file = shift;
	my $linenumber = 0;
	my $enableLoop = shift;
	my $buffering_warnings = shift;
	if ($self->{'files'}{$file}{'mainFile'}) {
		$file = $self->{'files'}{$file}{'mainFile'};
	}
	my $logFile = File::Spec->catfile(dirname($file), basename($file, '.tex').'.log');
	my $minNumberOfLaunchs = $self->{'configuration'}{'generation.post compilation runs'} || 1;
	my $numberOfRuns = 0;
	my $continueToCompile;
	do {
		printDbg(formatText(_T('{}: {}'), 'PDFLATEX', basename($file))); 
		$continueToCompile = 0;
		$self->{'buffered_warnings'} = [];
		$self->{'warnings'} = {};
		unlink($logFile);
		my $exitcode;
		if ($self->{'is_extended_warning_enable'}) {
			local *OUTFILE;
			open(*OUTFILE, ">autolatex_autogenerated.tex") or printErr("autolatex_ewarnings.tex: $!");
			my $code = $self->{'latex_warning_code'};
			$code =~ s/\Q::::REALFILENAME::::\E/$file/sg;
			print OUTFILE $code."\n";
			print OUTFILE readFileLines($file);
			close(*OUTFILE);
			$exitcode = runCommandSilently(
				@{$self->{'latex_cmd'}},
				$self->{'compiler_definition'}{'jobname'},
				basename($file, '.tex'),
				'autolatex_autogenerated.tex');
			unlink('autolatex_autogenerated.tex') if ($exitcode==0);
		}
		else {
			$exitcode = runCommandSilently(@{$self->{'latex_cmd'}},
				$self->makeRelativePath($file));
		}
		
		local *LOGFILE;

		if ($exitcode!=0) {
			printDbg(formatText(_T("{}: Error when processing {}"), 'PDFLATEX', basename($file)));

			# Parse the log to extract the blocks of messages
			my $line;
			my $fatal_error = undef;
			my @log_blocks = ();
			my $current_log_block = '';
			my $re_fatal_error = "\Q==>\E\\s*f\\s*a\\s*t\\s*a\\s*l\\s+e\\s*r\\s*r\\s*o\\s*r";
			open(*LOGFILE, "< $logFile") or printErr("$logFile: $!");
			while (*LOGFILE && ($line = <LOGFILE>) && !$fatal_error) {
				my $is_empty_line = (!$line || $line =~/^\s*$/);
				if ($is_empty_line) {
					# Empty line => break the block
					if ($current_log_block) {
					    if ($current_log_block =~ /^(.+):([0-9]+):/m) {
						if ($current_log_block =~ /^\!\s*$re_fatal_error/si) {
							$fatal_error = "\Q!\E[^!]";
						}
						elsif ($current_log_block =~ /^(.+:[0-9]+:)\s*$re_fatal_error/si) {
							$fatal_error = $1;
						}
						else {
							push @log_blocks, $current_log_block;
						}
					    }
					    elsif ($current_log_block =~ /^\!/m) {
						push @log_blocks, $current_log_block;
					    }
					}
					$current_log_block = '';
				}
				else {
					$current_log_block .= $line;
				}
			}
			close(*LOGFILE);
			if ($current_log_block) {
			    if ($current_log_block =~ /^.+:[0-9]+:/m) {
				if ($current_log_block =~ /^\!\s*$re_fatal_error/si) {
					$fatal_error = "\Q!\E[^!]";
				}
				elsif ($current_log_block =~ /^(.+:[0-9]+:)\s*$re_fatal_error/si) {
					$fatal_error = $1;
				}
				else {
					push @log_blocks, $current_log_block;
				}
			    }
			    elsif ($current_log_block =~ /^\!/m) {
				if ($current_log_block =~ /^\!\s*$re_fatal_error/si) {
					$fatal_error = "\Q!\E[^!]";
				}
				else {
					push @log_blocks, $current_log_block;
				}
			    }
			}
			# Search the fatal error inside the blocks
			my $extracted_message = '';
			if ($fatal_error) {
				# Parse the fatal error block to extract the filename
				# where the error occured
				if ($fatal_error =~ /^(.+?)\:([0-9]+)\:$/s) {
					my ($candidate, $post) = ($1,$2);
					my @candidates = split(/[\n\r]+/, $candidate);
					$candidate = pop @candidates;
					my $candidate_pattern = "\Q$candidate\E";
					while ($candidate && @candidates && ! -f "$candidate") {
						my $l = pop @candidates;
						$candidate_pattern = "\Q$l\E[\n\r]+$candidate_pattern";
						$candidate = $l.$candidate;
					}
					if ($candidate && -e "$candidate") {
						$linenumber = int($post);
						# Search the error message in the log.
						$candidate_pattern .= "\Q:$post:\E";
						# Filtering the 'autogenerated' file
						if ($self->{'is_extended_warning_enable'} &&
						    basename($candidate) eq 'autolatex_autogenerated.tex') {
							my $code = $self->{'latex_warning_code'};
							$candidate = $file;
							$linenumber -= countLinesIn($code);
						}
						my $i = 0; 
						while (!$extracted_message && $i<@log_blocks) {
							my $block = $log_blocks[$i];
							if ($block =~ /$candidate_pattern(.*)$/s) {
								my $message_text = $1 || '';
								$extracted_message = trim($candidate.':'.$linenumber.':'.$message_text);
							}
							$i++;
						}
						if ($extracted_message) {
							if (int($post)!=$linenumber) {
								$extracted_message =~ s/^\Ql.$post\E/l.$linenumber/gm;
							}
							# Do not cut the words with carriage returns
							$extracted_message =~ s/([a-z])[\n\r\f]([a-z])/$1$2/sgi;
							$extracted_message =~ s/([a-z]) [\n\r\f]([a-z])/$1 $2/sgi;
							$extracted_message =~ s/([a-z])[\n\r\f] ([a-z])/$1 $2/sgi;
						}
					}
				}
				else {
					# Search the error message in the log.
					my $candidate_pattern .= "$fatal_error";
					my $i = 0; 
					while (!$extracted_message && $i<@log_blocks) {
						my $block = $log_blocks[$i];
						if ($block =~ /(?:^|\n|\r)$candidate_pattern\s*(.*)$/s) {
							my $message = $1;
							$linenumber = 0;
							if ($message =~ /line\s+([0-9]+)/i) {
								$linenumber = int($1);
							}
							$extracted_message = trim("$file:$linenumber: $message");
						}
						$i++;
					}
				}
			}

			# Display the message
			if ($extracted_message) {

				# Test if the message is an emergency stop
				if ($extracted_message =~ /^.*?:[0-9]+:\s*emergency\s+stop\./i) {
					foreach my $block (@log_blocks) {
						if ($block =~ /^\s*!\s*(.*?)\s*$/s) {
							my $errmsg = "$1";
							$extracted_message .= "\n$errmsg";
						}
					}
				}

				printDbg(formatText(_T("{}: The first error found in the log file is:"), 'PDFLATEX'));
				print STDERR "$extracted_message\n";
				printDbg(formatText(_T("{}: End of error log."), 'PDFLATEX'));
			}
			else {
				print STDERR (formatText(_T("{}: Unable to extract the error from the log. Please read the log file."), 'PDFLATEX'))."\n";
			}

			exit(255);
		}
		elsif ($enableLoop) {
			$numberOfRuns ++;
			if ($numberOfRuns < $minNumberOfLaunchs) {
				# Force a new run of the LaTeX tool.
				printDbg(formatText(_T('{}: Forcing a new launch to reach {} on {}'), 'PDFLATEX', ($numberOfRuns + 1), $minNumberOfLaunchs)); 
				$continueToCompile = 1;
			}
			else {
				($continueToCompile,$enableLoop) = $self->_testLaTeXWarningInFile(
					$logFile,$continueToCompile,$enableLoop);
			}
		}
	}
	while ($continueToCompile);

	if (!$buffering_warnings && $self->{'buffered_warnings'}) {
		foreach my $w (@{$self->{'buffered_warnings'}}) {
			print STDERR "$w";
		}
		$self->{'buffered_warnings'} = [];
	}

	return 0;
}

sub _printWarning($$$$) : method {
	my $self = shift;
	my $filename = shift;
	my $extension = shift;
	my $line = shift;
	my $message = shift;
	if ($message =~ /^\s*latex\s+warning\s*\:\s*(.*)$/i) {
		$message = "$1";
	}
	if (!$self->{'buffered_warnings'}) {
		$self->{'buffered_warnings'} = [];
	}
	push @{$self->{'buffered_warnings'}},
		"$filename:$line:warning: $message\n";
}

sub _testLaTeXWarningInFile($$$) : method {
	my $self = shift;
	my $logFile = shift;
	my $continueToCompile = shift;
	my $enableLoop = shift;
	my $line;
	my $warning = 0;
	open(*LOGFILE, "< $logFile") or printErr("$logFile: $!");
	my $lastline = '';
	my $current_log_block = '';
	while (!$continueToCompile && ($line = <LOGFILE>)) {
		$lastline .= $line;
		if ($lastline =~ /\.\s*$/) {
			if ($self->_testLaTeXWarningOn($lastline)) {
				$continueToCompile = $enableLoop;
			}
			$lastline = '';
		}
		# Parse and output the detailled warning messages
		if ($self->{'warning_level'}>1) {
			if ($warning) {
				if ($line =~ /^\Q!!!![EndWarning]\E/) {
					if ($current_log_block =~ /^(.*?):([^:]*):([0-9]+):\s*(.*?)\s*$/) {
						my ($filename, $extension, $line, $message) = ($1, $2, $3, $4);
						$self->_printWarning($filename, $extension, $line, $message);
					}
					$warning = 0;
					$current_log_block = '';
				}
				else {
					my $l = $line;
					if ($l !~ /\.\n+$/) {
						$l =~ s/\s+//;
					}
					$current_log_block .= $l;
				}
			}
			elsif ($line =~ /^\Q!!!![BeginWarning]\E(.*)$/) {
				my $l = "$1";
				if ($l !~ /\.\n+$/) {
					$l =~ s/\s+//;
				}
				$current_log_block = $l;
				$warning = 1;
			}
		}
	}
	if ($lastline =~ /\.\s*$/ && $self->_testLaTeXWarningOn($lastline)) {
		$continueToCompile = $enableLoop;
	}
	close(*LOGFILE);
	# Output the detailled wanring message that was not already output
	if ($warning && $current_log_block) {
		if ($current_log_block =~ /^(.*?):([^:]*):([0-9]+):\s*(.*?)\s*$/) {
			my ($filename, $extension, $line, $message) = ($1, $2, $3, $4);
						$self->_printWarning($filename, $extension, $line, $message);
		}
	}
	$self->{'warnings'}{'done'} = 1;
	return ($continueToCompile,$enableLoop);
}

sub _testLaTeXWarningOn($) : method {
	my $self = shift;
	my $line = shift;
	my $oline = "$line";
	$line =~ s/[\n\r\t \f]+//g;
	if ($line =~ /Warning.*re\-?run\s/i) {
		return 1;
	}
	elsif ($line =~ /Warning:Therewereundefinedreferences/i) {
		$self->{'warnings'}{'undefined_reference'} = 1;
	}
	elsif ($line =~ /Warning:Citation.+undefined/i) {
		$self->{'warnings'}{'undefined_citation'} = 1;
	}
	elsif ($line =~ /Warning:Thereweremultiply\-definedlabels/i) {
		$self->{'warnings'}{'multiple_definition'} = 1;
	}
	elsif ($line =~ /(?:\s|^)Warning/im) {
		$self->{'warnings'}{'other_warning'} = 1;
	}
	return 0;
}

=pod

=item * build($)

Build all the root files.

=over 4

=item B<progress> (optional) is the progress indicator to use.

=back

=cut
sub build(;$) : method {
	my $self = shift;
	my $progress = shift;

	my $progValue;
	if ($progress) {
		my $numberOfRootFiles = @{$self->{'rootFiles'}};
		$progress->setMax($numberOfRootFiles*100);
		$progValue = 0;
	}

	foreach my $rootFile (@{$self->{'rootFiles'}}) {

		my $sprogress = undef;
		if ($progress) {
			$progress->setComment(formatText(_T("Generating {}"), basename($rootFile)));
			$sprogress = $progress->subProgress(100);
			$sprogress->setMax(1000);
		}

		# Read building stamps
		$self->_readBuildStamps($rootFile);

		$sprogress->setValue(10) if ($sprogress);

		# Launch at least one LaTeX compilation
		$self->runLaTeX($rootFile,0,1);

		$sprogress->setValue(210) if ($sprogress);

		# Compute the dependencies of the file
		$self->_computeDependenciesForRootFile($rootFile);

		$sprogress->setValue(260) if ($sprogress);

		# Construct the build list and launch the required builds
		my @builds = $self->_buildExecutionList("$rootFile");

		$sprogress->setValue(310) if ($sprogress);

		# Build the files
		if (@builds) {
			my $sprogStep = 600 / @builds;
			foreach my $file (@builds) {
				if ($sprogress) {
					$sprogress->setComment(formatText(_T("Compiling {}"), basename($file)));
				}
				$self->_build($rootFile, $file);
				$sprogress->increment($sprogStep) if ($sprogress);
			}
		}

		# Output the warnings from the last TeX builds
		if ($self->{'buffered_warnings'}) {
			foreach my $w (@{$self->{'buffered_warnings'}}) {
				print STDERR "$w";
			}
			$self->{'buffered_warnings'} = [];
		}

		$sprogress->setValue(910) if ($sprogress);

		# Write building stamps
		$self->_writeBuildStamps($rootFile);

		# Generate the Postscript file when requested
		if (($self->{'configuration'}{'generation.generation type'}||'pdf') eq 'ps') {
			my $dirname = dirname($rootFile);
			my $basename = basename($rootFile, '.pdf', '.ps', '.dvi', '.xdv');
			my $dviFile = File::Spec->catfile($dirname, $basename.'.dvi');
			my $dviDate = lastFileChange("$dviFile");
			if (defined($dviDate)) {
				my $psFile = File::Spec->catfile($dirname, $basename.'.ps');
				my $psDate = lastFileChange("$psFile");
				if (!$psDate || ($dviDate>=$psDate)) {
					if ($sprogress) {
						$sprogress->setComment(formatText(_T("Generating {}"), basename($psFile)));
					}
					printDbg(formatText(_T('{}: {}'), 'DVI2PS', basename($dviFile))); 
					runCommandOrFail(@{$self->{'dvi2ps_cmd'}}, 
						$self->makeRelativePath($dviFile));
				}
			}
		}

		if ($sprogress) {
			$sprogress->setComment(formatText(_T("Analyzing logs for {}"), basename($rootFile)));
		}

		# Compute the log filename
		my $texFile = $self->{'files'}{$rootFile}{'mainFile'};
		my $logFile = File::Spec->catfile(dirname($texFile), basename($texFile, '.tex').'.log');

		# Detect warnings if not already done
		if (!%{$self->{'warnings'}}) {
			$self->_testLaTeXWarningInFile($logFile, 0, 0);
		}

		# Output the last LaTeX warning indicators.
		if ($self->{'warning_level'}>0) {
			if ($self->{'warnings'}{'multiple_definition'}) {
				my $s = _T("LaTeX Warning: There were multiply-defined labels.\n");
				if ($self->{'is_extended_warning_enable'}) {
					print STDERR "!!$logFile:W1: $s";
				}
				else {
					print STDERR "$s";
				}
			}
			if ($self->{'warnings'}{'undefined_reference'}) {
				my $s = _T("LaTeX Warning: There were undefined references.\n");
				if ($self->{'is_extended_warning_enable'}) {
					print STDERR "!!$logFile:W2: $s";
				}
				else {
					print STDERR "$s";
				}
			}
			if ($self->{'warnings'}{'undefined_citation'}) {
				my $s = _T("LaTeX Warning: There were undefined citations.\n");
				if ($self->{'is_extended_warning_enable'}) {
					print STDERR "!!$logFile:W3: $s";
				}
				else {
					print STDERR "$s";
				}
			}
			if ($self->{'warnings'}{'other_warning'}) {
				my $texFile = $rootFile;
				if ($self->{'files'}{$rootFile}{'mainFile'}) {
					$texFile = $self->{'files'}{$rootFile}{'mainFile'};
				}
				print STDERR formatText(_T("LaTeX Warning: Please look inside {} for the other the warning messages.\n"),
						basename($logFile));
			}
		}

		if ($progress) {
			$progValue += 100;
			$progress->setValue($progValue);
		}
	}

	$progress->stop() if ($progress);

	return undef;
}

=pod

=item * buildBiblio($)

Launch the Biblio only.

=over 4

=item B<progress> (optional) is the progress indicator to use.

=back

=cut
sub buildBiblio(;$) : method {
	my $self = shift;
	my $progress = shift;

	my $progValue;
	if ($progress) {
		my $numberOfRootFiles = @{$self->{'rootFiles'}};
		$progress->setMax($numberOfRootFiles*100);
		$progValue = 0;
	}

	foreach my $rootFile (@{$self->{'rootFiles'}}) {

		my $sprogress = undef;
		if ($progress) {
			$sprogress = $progress->subProgress(100);
			$sprogress->setMax(1000);
		}

		# Read building stamps
		$self->_readBuildStamps($rootFile);

		$sprogress->setValue(10) if ($sprogress);

		# Compute the dependencies of the file
		$self->_computeDependenciesForRootFile($rootFile);

		$sprogress->setValue(60) if ($sprogress);

		# Construct the build list and launch the required builds
		my @builds = $self->_buildExecutionList("$rootFile",1);

		$sprogress->setValue(110) if ($sprogress);

		if (@builds) {
			foreach my $file (@builds) {
				if (exists $self->{'files'}{$file}) {
					my $type = $self->{'files'}{$file}{'type'};
					if ($type eq 'bbl') {
						my $func = $self->can('__build_'.lc($type));
						if ($func) {
							$func->($self, $rootFile, $file, $self->{'files'}{$file});
						}
					}
				}
			}
		}
		else {
			printDbgFor(2, formatText(_T('{} is up-to-date.'), basename($rootFile)));
		}

		$sprogress->setValue(990) if ($sprogress);

		# Write building stamps
		$self->_writeBuildStamps($rootFile);

		if ($progress) {
			$progValue += 100;
			$progress->setValue($progValue);
		}
	}

	$progress->stop() if ($progress);

	return undef;
}

=pod

=item * buildMakeGlossaries($)

Launch the MakeGlossaries only.

=over 4

=item B<progress> (optional) is the progress indicator to use.

=back

=cut
sub buildMakeGlossaries(;$) : method {
	my $self = shift;
	my $progress = shift;

	my $progValue;
	if ($progress) {
		my $numberOfRootFiles = @{$self->{'rootFiles'}};
		$progress->setMax($numberOfRootFiles*100);
		$progValue = 0;
	}

	foreach my $rootFile (@{$self->{'rootFiles'}}) {

		my $sprogress = undef;
		if ($progress) {
			$sprogress = $progress->subProgress(100);
			$sprogress->setMax(1000);
		}

		# Read building stamps
		$self->_readBuildStamps($rootFile);

		$sprogress->setValue(10) if ($sprogress);

		# Compute the dependencies of the file
		$self->_computeDependenciesForRootFile($rootFile);

		$sprogress->setValue(60) if ($sprogress);

		# Construct the build list and launch the required builds
		my @builds = $self->_buildExecutionList("$rootFile",1);

		$sprogress->setValue(110) if ($sprogress);

		if (@builds) {
			foreach my $file (@builds) {
				if (exists $self->{'files'}{$file}) {
					my $type = $self->{'files'}{$file}{'type'};
					if ($type eq 'gls') {
						my $func = $self->can('__build_'.lc($type));
						if ($func) {
							$func->($self, $rootFile, $file, $self->{'files'}{$file});
							return undef;
						}
					}
				}
			}
		}
		else {
			printDbgFor(2, formatText(_T('{} is up-to-date.'), basename($rootFile)));
		}

		$sprogress->setValue(990) if ($sprogress);

		# Write building stamps
		$self->_writeBuildStamps($rootFile);

		if ($progress) {
			$progValue += 100;
			$progress->setValue($progValue);
		}
	}

	$progress->stop() if ($progress);

	return undef;
}

=pod

=item * buildMakeIndex($)

Launch the MakeIndex only.

=over 4

=item B<progress> (optional) is the progress indicator to use.

=back

=cut
sub buildMakeIndex(;$) : method {
	my $self = shift;
	my $progress = shift;

	my $progValue;
	if ($progress) {
		my $numberOfRootFiles = @{$self->{'rootFiles'}};
		$progress->setMax($numberOfRootFiles*100);
		$progValue = 0;
	}

	foreach my $rootFile (@{$self->{'rootFiles'}}) {

		my $sprogress = undef;
		if ($progress) {
			$sprogress = $progress->subProgress(100);
			$sprogress->setMax(1000);
		}

		# Read building stamps
		$self->_readBuildStamps($rootFile);

		$sprogress->setValue(10) if ($sprogress);

		# Compute the dependencies of the file
		$self->_computeDependenciesForRootFile($rootFile);

		$sprogress->setValue(60) if ($sprogress);

		# Construct the build list and launch the required builds
		my @builds = $self->_buildExecutionList("$rootFile",1);

		$sprogress->setValue(110) if ($sprogress);

		if (@builds) {
			foreach my $file (@builds) {
				if (exists $self->{'files'}{$file}) {
					my $type = $self->{'files'}{$file}{'type'};
					if ($type eq 'ind') {
						my $func = $self->can('__build_'.lc($type));
						if ($func) {
							$func->($self, $rootFile, $file, $self->{'files'}{$file});
							return undef;
						}
					}
				}
			}
		}
		else {
			printDbgFor(2, formatText(_T('{} is up-to-date.'), basename($rootFile)));
		}

		$sprogress->setValue(990) if ($sprogress);

		# Write building stamps
		$self->_writeBuildStamps($rootFile);

		if ($progress) {
			$progValue += 100;
			$progress->setValue($progValue);
		}
	}

	$progress->stop() if ($progress);

	return undef;
}

# Read the building stamps.
# This function puts the stamps in $self->{'stamps'}.
# Parameter:
# $_[0] = path to the root TeX file.
# Result: nothing
sub _readBuildStamps($) : method {
	my $self = shift;
	my $rootFile = shift;
	my $stampFile = File::Spec->catfile(dirname($rootFile), '.autolatex_stamp');
	if (exists $self->{'stamps'}) {
		delete $self->{'stamps'};
	}
	if (-r "$stampFile") {
		local *FILE;
		open(*FILE, "< $stampFile") or printErr("$stampFile: $!");
		while (my $line = <FILE>) {
			if ($line =~ /^BIB\(([^)]+?)\)\:(.+)$/) {
				my ($k,$n) = ($1,$2);
				$self->{'stamps'}{'bib'}{$n} = $k;
			}
			if ($line =~ /^IDX\(([^)]+?)\)\:(.+)$/) {
				my ($k,$n) = ($1,$2);
				$self->{'stamps'}{'idx'}{$n} = $k;
			}
			if ($line =~ /^GLS\(([^)]+?)\)\:(.+)$/) {
				my ($k,$n) = ($1,$2);
				$self->{'stamps'}{'gls'}{$n} = $k;
			}
		}
		close(*FILE);
	}
}

# Write the building stamps.
# This function gets the stamps from $self->{'stamps'}.
# Parameter:
# $_[0] = path to the root TeX file.
# Result: nothing
sub _writeBuildStamps($) : method {
	my $self = shift;
	my $rootFile = shift;
	my $stampFile = File::Spec->catfile(dirname($rootFile), '.autolatex_stamp');
	local *FILE;
	open(*FILE, "> $stampFile") or printErr("$stampFile: $!");
	if ($self->{'stamps'}{'bib'}) {
		while (my ($k,$v) = each(%{$self->{'stamps'}{'bib'}})) {
			print FILE "BIB($v):$k\n";
		}
	}
	if ($self->{'stamps'}{'idx'}) {
		while (my ($k,$v) = each(%{$self->{'stamps'}{'idx'}})) {
			print FILE "IDX($v):$k\n";
		}
	}
	if ($self->{'stamps'}{'gls'}) {
		while (my ($k,$v) = each(%{$self->{'stamps'}{'gls'}})) {
			print FILE "GLS($v):$k\n";
		}
	}
	close(*FILE);
}

# Static function that is testing if the timestamp a is
# more recent than the timestamp b.
# Parameters:
# $_[0] = a.
# $_[1] = b.
# Result: true if a is more recent than b, or not defined;
#         false otherwise.
sub _a_more_recent_than_b($$) {
	my $a = shift;
	my $b = shift;
	return (!defined($a) || (defined($b) && $a>$b));
}

# Test if the specified file is needing to be rebuild.
# Parameters:
# $_[0] = timestamp of the root file.
# $_[1] = filename of the file to test.
# $_[2] = parent element of the file, of type Entry.
# $_[3] = is the description of the file to test.
# Result: true if the file is needing to be rebuild,
#         false if the file is up-to-date.
sub _need_rebuild($$$$) : method {
	my $self = shift;
	my $rootchange = shift;
	my $filename = shift;
	my $parent = shift;
	my $file = shift;
	if (!defined($file->{'change'}) || (!-f "$filename")) {
		return 1;
	}

	if ($filename =~ /(\.[^.]+)$/) {
		my $ext = $1;
		if ($ext eq '.bbl') {
			if ($file->{'use_biber'}) {
				# Parse the BCF file to detect the citations
				my $bcfFile = File::Spec->catfile(dirname($filename), basename($filename, '.bbl').'.bcf');
				my $currentMd5 = makeBcfBibliographyCitationMd5($bcfFile) || '';
				my $oldMd5 = $self->{'stamps'}{'bib'}{$bcfFile} || '';
				if ($currentMd5 ne $oldMd5) {
					$self->{'stamps'}{'bib'}{$bcfFile} = $currentMd5;
					return 1;
				}
			}
			else {
				# Parse the AUX file to detect the citations
				my $auxFile = File::Spec->catfile(dirname($filename), basename($filename, '.bbl').'.aux');
				my $currentMd5 = makeAuxBibliographyCitationMd5($auxFile) || '';
				my $oldMd5 = $self->{'stamps'}{'bib'}{$auxFile} || '';
				if ($currentMd5 ne $oldMd5) {
					$self->{'stamps'}{'bib'}{$auxFile} = $currentMd5;
					return 1;
				}
			}
			return 0;
		}
		elsif ($ext eq '.ind') {
			# Parse the IDX file to detect the index definitions
			my $idxFile = File::Spec->catfile(dirname($filename), basename($filename, '.ind').'.idx');
			my $currentMd5 = makeIdxIndexDefinitionMd5($idxFile) || '';
			my $oldMd5 = $self->{'stamps'}{'idx'}{$idxFile} || '';
			if ($currentMd5 ne $oldMd5) {
				$self->{'stamps'}{'idx'}{$idxFile} = $currentMd5;
				return 1;
			}
			return 0;
		}
		elsif ($ext eq '.gls') {
			# Parse the GLS file to detect the index definitions
			my $glsFile = File::Spec->catfile(dirname($filename), basename($filename, '.pdf').'.gls');
			my $currentMd5 = makeGlsIndexDefinitionMd5($glsFile) || '';
			my $oldMd5 = $self->{'stamps'}{'gls'}{$glsFile} || '';
			if ($currentMd5 ne $oldMd5) {
				$self->{'stamps'}{'gls'}{$glsFile} = $currentMd5;
				return 1;
			}
			return 0;
		}
	}

	return _a_more_recent_than_b( $file->{'change'}, $rootchange );
}

# Build the list of the files to be build.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = boolean value that permits to force to consider all the files has changed.
# Result: the build list.
sub _buildExecutionList($;$) : method {
	my $self = shift;
	my $rootfile = shift;
	my $forceChange = shift;
	my @builds = ();

	# Go through the dependency tree with en iterative algorithm

	my $rootchange = $self->{'files'}{$rootfile}{'change'};
	my $element = newEntry($rootfile,undef) ;
	my $child;
	my @iterator = ( $element );	
	while (@iterator) {
		$element = pop @iterator;
		my $deps = $self->{'files'}{$element->file}{'dependencies'};
		if ($element->go_up || !%$deps) {
			if (	$forceChange ||
				$element->rebuild ||
				$self->_need_rebuild(
					$rootchange,
					$element->file,
					$element->parent,
					$self->{'files'}{$element->file})) {

				if ($element->parent) {
					$element->parent->rebuild(1);
				}

				if ($self->can('__build_'.lc($self->{'files'}{$element->file}{'type'}))) {
					push @builds, $element->file;
				}

			}
		}
		else {
			push @iterator, $element;
			foreach my $dep (keys %$deps) {
				$child = newEntry($dep,$element);
				push @iterator, $child;
			}
			$element->go_up(1);
		}
	}
	return @builds;
}

# Run the building process.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = name of the file to build (the root file or one of its dependencies).
# Result: nothing.
sub _build($$) : method {
	my $self = shift;
	my $rootFile = shift;
	my $file = shift;

	if (exists $self->{'files'}{$file}) {
		my $type = $self->{'files'}{$file}{'type'};
		if ($type) {
			my $func = $self->can('__build_'.lc($type));
			if ($func) {
				$func->($self, $rootFile, $file, $self->{'files'}{$file});
				return undef;
			}
		}
	}

	# Default building behavior: do nothing
	return undef;
}

sub __find_file_with_basename($) {
	my $self = shift;
	my $basename = shift;
	if (%{$self->{'files'}}) {
		foreach my $k (keys %{$self->{'files'}}) {
			my $bn = basename($k);
			if ($bn eq $basename) {
				return File::Spec->abs2rel($k,
					$self->{'configuration'}{'__private__'}{'input.project directory'});
			}
		}
	}
	return $basename;
}

# Callback to build a BBL file.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = name of the file to build (the root file or one of its dependencies).
# $_[2] = description of the file to build.
# Result: nothing.
sub __build_bbl($$$) : method {
	my $self = shift;
	my $rootFile = shift;
	my $file = shift;
	my $filedesc = shift;
	if ($self->{'is_biblio_enable'}) {
		my $basename = basename($file,'.bbl');
		if ($filedesc->{'use_biber'}) {
			####################################
			# BIBER
			####################################
			printDbg(formatText(_T('{}: {}'), 'BIBER', basename($basename))); 
			my $retcode = runCommandRedirectToInternalLogs(
					@{$self->{'biber_cmd'}}, "$basename");
			# Output the log from the bibliography tool
			if ($retcode!=0) {
				printDbg(formatText(_T("{}: Error when processing {}"), 'BIBER', $basename));
				local *INFILE;
				open(*INFILE, "<autolatex_exec_stdout.log") or printErr("autolatex_exec_stdout.log: $!");
				while (my $line = <INFILE>) {
					if ($line =~ /^\s*ERROR\s*\-\s*.*subsystem:\s*(.+?),\s*line\s+([0-9]+),\s*(.*?)\s*$/i) {
						my ($filename, $linenumber, $message) = ($1, $2, $3);
						if ($filename =~ /^(.+)_[0-9]+\.[a-zA-Z0-9_-]+$/) {
							$filename = $1;
						}
						$filename = $self->__find_file_with_basename(basename($filename));
						print STDERR "$filename:$linenumber: $message\n";
					}
					elsif ($line =~ /^\s*ERROR\s*\-\s*(.+?)\s*$/i) {
						my $message = $1;
						print STDERR "$message\n";
					}
				}
				close(*INFILE);
				exit(255);
			}
			else {
				unlink("autolatex_exec_stdout.log");
				unlink("autolatex_exec_stderr.log");
			}
		}
		else {
			####################################
			# BIBTEX
			####################################
			my $auxFile = File::Spec->catfile(dirname($file),"$basename.aux");
			printDbg(formatText(_T('{}: {}'), 'BIBTEX', basename($auxFile))); 
			my $retcode = runCommandRedirectToInternalLogs(
					@{$self->{'bibtex_cmd'}},
						$self->makeRelativePath("$auxFile"));

			# Output the log from the bibliography tool
			if ($retcode!=0) {

				printDbg(formatText(_T("{}: Error when processing {}"), 'BIBTEX', basename($auxFile)));
				local *INFILE;
				open(*INFILE, "<autolatex_exec_stdout.log") or printErr("autolatex_exec_stdout.log: $!");
				my %currentError = ();
				my $previousline = '';
				while (my $line = <INFILE>) {
					if (%currentError) {
						if ($line =~ /^\s*:\s*(.*?)\s*$/) {
							$currentError{'message'} .= " $1";
						}
						else {
							print STDERR $currentError{'filename'}.':'.$currentError{'lineno'}.': '.$currentError{'message'}."\n";
							%currentError = ();
						}
					}
					elsif ($line =~ /^\s*(.*?)\s*\-\-\-line\s+([0-9]+)\s+of\s+file\s+(.*?)\s*$/i) {
						my ($message, $linenumber, $filename) = ($1, $2, $3);
						if (!$message) {
							$message = $previousline;
							$message =~ s/^\s+//s;
							$message =~ s/\s+$//s;
						}
						%currentError = (
							'filename' => $filename,
							'lineno' => $linenumber,
							'message' => $message,
						);
						$previousline = '';
					}
					else {
						$previousline = $line;
						%currentError = ();
					}
				}
				close(*INFILE);
				if (%currentError) {
					print STDERR $currentError{'filename'}.':'.
						$currentError{'lineno'}.': '.$currentError{'message'}."\n";
				}
				exit(255);
			}
			else {
				unlink("autolatex_exec_stdout.log");
				unlink("autolatex_exec_stderr.log");
			}
		}
	}
}

# Callback to build a IND file.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = name of the file to build (the root file or one of its dependencies).
# $_[2] = description of the file to build.
# Result: nothing.
sub __build_ind($$$) : method {
	my $self = shift;
	my $rootFile = shift;
	my $file = shift;
	my $filedesc = shift;
	if ($self->{'is_makeindex_enable'}) {
		my $basename = basename($file,'.ind');
		my $idxFile = File::Spec->catfile(dirname($file),"$basename.idx");
		if (-f "$idxFile") {
			printDbg(formatText(_T('{}: {}'), 'MAKEINDEX', basename($idxFile))); 
			my @styleArgs = ();
			my $istFile = $self->{'configuration'}{'__private__'}{'output.ist file'};
			if ($istFile && -f "$istFile") {
				printDbgFor(2, formatText(_T('Style file: {}'), $istFile)); 
				push @styleArgs, '-s', "$istFile";
			}
			runCommandOrFail(@{$self->{'makeindex_cmd'}}, @styleArgs, 
				$self->makeRelativePath("$idxFile"));
		}
	}
}

# Callback to build a GLS file.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = name of the file to build (the root file or one of its dependencies).
# $_[2] = description of the file to build.
# Result: nothing.
sub __build_gls($$$) : method {
	my $self = shift;
	my $rootFile = shift;
	my $file = shift;
	my $filedesc = shift;
	if ($self->{'is_makeglossaries_enable'}) {
		my $filename = File::Spec->catfile(dirname($rootFile), basename($rootFile,'.pdf'));
		$filename = $self->makeRelativePath("$filename");
		printDbg(formatText(_T('{}: {}'), 'MAKEGLOSSARIES', basename($rootFile))); 
		runCommandOrFail(@{$self->{'makeglossaries_cmd'}}, "$filename");
	}
}

# Callback to build a PDF file.
# Parameters:
# $_[0] = name of the root file that should be build.
# $_[1] = name of the file to build (the root file or one of its dependencies).
# $_[2] = description of the file to build.
# Result: nothing.
sub __build_pdf($$$) : method {
	my $self = shift;
	my $rootFile = shift;
	my $file = shift;
	my $filedesc = shift;
	my $runs = 2;
	my $majorFailure = 0;
	do {
		$runs--;
		$self->runLaTeX($file,1,1);
		$majorFailure = (exists $self->{'warnings'}{'multiple_definition'}) ||
				(exists $self->{'warnings'}{'undefined_reference'}) ||
				(exists $self->{'warnings'}{'undefined_citation'});
	}
	while ($majorFailure && $runs>0);
}

=pod

=item * enableBiblio

Enable or disable the call to bibtex/biber.
If this function has a parameter, the flag is changed.

=over

=item * isEnable (optional boolean)

=back

I<Returns:> the value of the enabling flag.

=cut
sub enableBiblio : method {
	my $self = shift;
	if (@_) {
		$self->{'is_biblio_enable'} = $_[0];
	}
	return $self->{'is_biblio_enable'};
}

=pod

=item * enableMakeIndex

Enable or disable the call to makeindex.
If this function has a parameter, the flag is changed.

=over

=item * isEnable (optional boolean)

=back

I<Returns:> the vlaue of the enabling flag.

=cut
sub enableMakeIndex : method {
	my $self = shift;
	if (@_) {
		$self->{'is_makeindex_enable'} = $_[0];
	}
	return $self->{'is_makeindex_enable'};
}

=pod

=item * enableMakeGlossaries

Enable or disable the call to makeglossaries.
If this function has a parameter, the flag is changed.

=over

=item * isEnable (optional boolean)

=back

I<Returns:> the vlaue of the enabling flag.

=cut
sub enableMakeGlossaries : method {
	my $self = shift;
	if (@_) {
		$self->{'is_makeglossaries_enable'} = $_[0];
	}
	return $self->{'is_makeglossaries_enable'};
}

=pod

=item * generationType

Get or change the type of generation.
If this function has a parameter, the type is changed.

=over

=item * type (optional string)

C<"pdf"> to use pdflatex, C<"dvi"> to use latex, C<"ps"> to use latex and dvips, C<"pspdf"> to use latex, dvips and ps2pdf.

=back

I<Returns:> the generation type.

=cut
sub generationType : method {
	my $self = shift;
	if (@_) {
		my $type = $_[0] || 'pdf';
		if ($type ne 'dvi' && $type ne 'ps' && $type ne 'pdf') {
			$type = 'pdf';
		}
		$self->{'generation_type'} = $type;
	}
	return $self->{'generation_type'};
}

1;
__END__
=back

=head1 BUG REPORT AND FEEDBACK

To report bug, provide feedback, suggest new features, etc. visit the AutoLaTeX Project management page at <http://www.arakhne.org/autolatex/> or send email to the author at L<galland@arakhne.org>.

=head1 LICENSE

S<GNU Public License (GPL)>

=head1 COPYRIGHT

S<Copyright (c) 2013-2016 Stéphane Galland E<lt>galland@arakhne.orgE<gt>>

=head1 SEE ALSO

L<autolatex-dev>
