# $Id$
# Written 2004, Fabrice Popineau.
# Public domain.
#
package Tpm;
BEGIN {
# $Exporter::Verbose = 1;
use Exporter ();
use Carp;
use XML::DOM;
use File::Path;
use FileUtils;
use Cwd;
@ISA = qw( Exporter );
@EXPORT_OK = qw (
new
$MasterDir
%TexmfTreeOfType %TypeOfTexmfTree
$FtpDir $CurrentArch
@TpmCategories
@TexmfTrees
@ArchList
@StandAlonePackages
$IgnoredFiles
&toRDF &toString
&setAttribute &getAttribute
&setList &getList
&setHash &getHash
&patternsExpand
&patternsUpdate
&buildPatternsPackage
&buildPatternsDocumentation
&getPatterns
&fixDate
&fixRequires
&patternsAuto
&completeUsingCatalogue
&getAllFileList
&getRequiredFileList
&getRequiredTpm
&getFilesFromPatterns
&writeFile
&testSync
&Tpm2Zip
&Clean
&Remove
$Verbose
);
use vars (@ISA);
}
$MasterDir = "c:/Source/TeXLive/Master";
$ZipDir = "c:/InetPub/ftp/fptex/0.7";
$CurrentArch = "all";
$Editor = ($^O =~ m/win32/i ? "notepad": "vi");
#print "$MasterDir $CurrentArch\n";
%TexmfTreeOfType = ( "TLCore" => "texmf",
"Documentation" => "texmf-doc",
"Package" => "texmf-dist");
%TypeOfTexmfTree = &reverse_hash(%TexmfTreeOfType);
@TpmCategories = keys %TexmfTreeOfType;
@TexmfTrees = values %TexmfTreeOfType;
# must match subdir names in Master/bin/ directory.
@ArchList = (
"alpha-linux",
"alpha-osf",
"i386-darwin",
"i386-freebsd",
"i386-linux",
"mips-irix",
"powerpc-aix",
"powerpc-darwin",
"sparc-solaris",
"sparc-linux",
"win32",
"win32-static",
"x86_64-linux"
);
@StandAlonePackages = (
"TLCore/bin-afm2pl",
"TLCore/bin-aleph",
"TLCore/bin-dvipdfm",
"TLCore/bin-dvipdfmx",
"TLCore/bin-dvipsk",
"TLCore/bin-gsftopk",
"TLCore/bin-lcdftypetools",
"TLCore/bin-omega",
"TLCore/bin-pdftex",
"TLCore/bin-metapost",
"TLCore/bin-t1utils",
"TLCore/bin-tex4htk",
"TLCore/bin-windvi"
);
# this list is not up to date, therefore I think it is not needed.
# . "bin/i386-freebsd|bin/i386-openbsd|bin/i386-solaris|bin/mips-irix"
# . "|bin/powerpc-aix|bin/powerpc-darwin|bin/sparc-solaris"
# used both to ignore whole tpm's (?) and individual files?
# must match whole path
$IgnoredFiles = "("
. 'source/.*'
. '|texmf/tpm/(collection-binaries|texlive|xemtex|scheme-.*|.*-static)\.tpm'
. '|texmf(-doc|-dist)?/(ls-R|aliases|lists/.*|README|tpm/tpm.dtd)'
. '|.*/\.svn.*'
. ")";
# The so-called engines
my @engines = ("aleph", "enctex", "eomega", "metafont", "metapost", "omega", "pdftex", "pdfetex", "tex", "vtex",
"bibtex", "context", "dvipdfm", "dvips", "ispell",
"makeindex","mft", "psutils", "tex4ht", "texdoctk",
"ttf2pk");
# The so called formats
my @formats = (
"alatex", "amstex", "context", "cslatex", "csplain", "enctex",
"eplain", "fontinst", "generic", "jadetex", "lambda",
"latex", "latex3",
"mex", "physe", "phyzzx", "plain", "psizzl",
"startex", "texinfo", "texsis", "xetex", "xelatex",
"xmltex", "ytex", );
# Kind of font files
my @fonttypes = (
"afm", "misc", "ofm", "opentype", "ovf", "ovp", "pfb",
"pk", "sfd", "source", "tfm", "truetype", "type1", "vf"
);
# Font vendors
my @vendors = (
"adobe", "amsfonts", "archaic", "arphic",
"bakoma", "bh", "bitstrea", "bluesky",
"cg", ,"cns", "cspsfonts-adobe",
"hoekwater", "ibm", "itc", "jknappen", "jmn", "korean", "lh",
"mathdesign", "misc", "monotype", "paragrap",
"public", "uhc", "urw", "urw35vf", "vntex", "wadalab",
"yandy");
my @fontmodes = (
"ljfour", "ljfive", "cx"
);
my @languages = ("bulgarian", "czechslovak", "dutch", "english",
"finnish", "french", "general",
"german", "greek", "italian", "japanese", "korean",
"mongolian",
"polish", "portuguese", "russian", "slovak", "spanish",
"thai", "turkish", "ukrainian", "vietnamese");
my %dotfiles = (
"texmf-dist/tex/latex/tools/*" => ( "texmf-dist/tex/latex/tools/.tex" ),
"texmf/chktex/*" => ( "texmf/chktex/.chktexrc" )
);
my $CatalogueDir = "${MasterDir}/texmf-doc/doc/english/catalogue";
my $Catalogue;
my $Verbose = 0;
sub reverse_hash {
{
my (%direct) = @_;
my %reversed;
my ($key, $value);
foreach $key (keys %direct) {
$reversed{$direct{$key}} = $key;
}
return %reversed;
}
}
#----------------------------------------------------------------------
# Helper functions
sub getTextField {
my ($doc, $f) = @_;
my $nodelist = $doc->getElementsByTagName("TPM:$f");
my %s = ( );
return %s if ($nodelist->getLength <= 0);
my $node = $nodelist->item(0);
return %s if (! $node);
foreach my $k (@{$node->getAttributes->getValues}) {
$k = $k->getName;
$s{$k} = $node->getAttribute($k);
}
$node = $node->getFirstChild();
return %s if (! $node);
my $str = $node->toString;
$str = $node->expandEntityRefs($str);
$s{"text"} = $str;
return %s;
}
sub getListField {
my ($doc, $f) = @_;
my %s = getTextField($doc, $f);
my $str = $s{"text"};
$str =~ s/^\n*//;
$str =~ s/\n*$//;
$str =~ s/\n/ /gomsx;
@{$s{"text"}} = split(" ", $str);
return %s;
}
sub getMultipleTextField {
my ($doc, $f) = @_;
my $nodelist = $doc->getElementsByTagName("TPM:$f");
my @stringlist = ( );
for (my $i = 0; $i < $nodelist->getLength; $i++) {
my $node = $nodelist->item($i);
my %s = ( );
foreach my $k (@{$node->getAttributes->getValues}) {
$k = $k->getName;
$s{$k} = $node->getAttribute($k);
}
$node = $node->getFirstChild();
if ($node) {
my $text = $node->toString;
$text =~ s/\n/ /gomsx;
push @{$s{"text"}}, split(" ", $text);
}
push @stringlist, \%s;
}
return @stringlist;
}
sub getAttributes {
my ($doc, $f) = @_;
my $nodelist = $doc->getElementsByTagName("TPM:$f");
my %attr = ( );
return %attr if ($nodelist->getLength <= 0);
my $node = $nodelist->item(0);
foreach my $k (@{$node->getAttributes->getValues}) {
$k = $k->getName;
$attr{$k} = $node->getAttribute($k);
}
return %attr;
}
#----------------------------------------------------------------------
sub new {
my $type = shift;
my ($filename) = @_;
my $self = { };
bless $self, $type;
if ($filename) {
$filename =~ s@\\@/@g;
$filename .= ".tpm" if ($filename !~ m@\.tpm$@);
if (! &FileUtils::is_absolute($filename)) {
$filename = "${Tpm::MasterDir}/${filename}";
}
if (! -f $filename) {
$filename =~ m@^.*/(.*)/(.*)$@;
if (&FileUtils::member($1, @TpmCategories)) {
$filename = "${Tpm::MasterDir}/" . $TexmfTreeOfType{$1} . "/tpm/$2";
}
}
die (`pwd` . "$filename not found!\n") if (! -f $filename);
my $parser = new XML::DOM::Parser;
$doc = $parser->parsefile($filename);
my ($type, $name);
$filename =~ m@^(.*/|)([^/]+)[/\\]tpm[/\\]([^/\.]+)\.tpm$@;
$type = $TypeOfTexmfTree{$2}; $name = $3;
$self->initialize($type,$name,$doc);
}
return $self;
}
sub initialize {
my ($self, $type, $name, $doc) = @_;
my $parser = new XML::DOM::Parser;
my $text;
my @list;
my %field;
%field = &getTextField($doc, "Name");
$text = $field{"text"};
if ($text ne $name) {
print "Warning: $filename has wrong Name attribute ($text should be $name) ... fixing it.\n";
}
$self->setAttribute("Name", $name);
%field = &getTextField($doc, "Type");
$text = $field{"text"};
if ($text ne $type) {
print "Warning: $filename has wrong Type attribute ($text should be $type) ... fixing it.\n";
}
$self->setAttribute("Type", $type);
for my $tag ("Date", "Version", "Creator", "Size", "Author", "Title", "Description", "Provides") {
%field = &getTextField($doc, "$tag");
$text = $field{"text"};
$self->setAttribute("$tag", $text);
}
$text = $self->getAttribute("Provides");
if ("$type/$name" ne $text) {
print "Warning: $filename has wrong Provides attribute ($text should be $type/$name) ... fixing it.\n";
}
$self->setAttribute("Provides", "$type/$name");
%field = &getAttributes($doc, "Flags");
$self->setHash("Flags", %field);
# map { print "$_ = $field{$_}\n"; } (keys %field);
for my $tag ("BinPatterns", "RunPatterns", "DocPatterns", "SourcePatterns", "RemotePatterns") {
%field = &getListField($doc, "$tag");
@list = @{$field{"text"}};
$self->setList("$tag", @list);
}
# FIXME ! several architectures !
@list = &getMultipleTextField($doc, "BinFiles");
$self->setList("BinFiles", @list);
for my $tag ("RunFiles", "DocFiles", "SourceFiles", "RemoteFiles") {
%field = &getListField($doc, "$tag");
$self->setHash("$tag", %field);
}
my %requires = ();
for my $tag (@TpmCategories) {
my $nodelist = $doc->getElementsByTagName("TPM:$tag");
for (my $i = 0; $i < $nodelist->getLength; $i = $i+1) {
my $package = $nodelist->item($i)->getAttribute("name");
push @{$requires{$tag}}, $package;
}
}
$self->setHash("Requires",%requires);
# Installation instructions
my @instructions = ();
$nodelist = $doc->getElementsByTagName("TPM:Installation");
if ($nodelist->getLength > 0) {
my $executelist = $doc->getElementsByTagName("TPM:Execute");
for (my $i = 0; $i < $executelist->getLength; $i++) {
my $inst = $executelist->item($i);
my %execute = ();
foreach my $attr (@{$inst->getAttributes->getValues}) {
$attr = $attr->getName;
$execute{$attr} = $inst->getAttribute($attr);
}
push @instructions, \%execute;
}
}
$self->setList("Installation", @instructions);
}
#
# Create a fresh package of $name and $type
#
sub fresh {
my $type = shift;
my $self = { };
bless $self, $type;
my ($provides) = @_;
my $name;
$provides =~ m@^([^/]+)[/\\]([^/\.]+)$@;
$type = $1; $name = $2;
my $texmf = $TexmfTreeOfType{$type};
print "Creating new $type $name tpm file\n";
my $parser = new XML::DOM::Parser;
chomp (my $user = `whoami`); # for Creator field.
my $doc = $parser->parse("\
\
\
\
${name}\
${type}\
1970/01/01 01:00:00\
\
$user\
\
The ${name} package.\
314\
\
\
${texmf}/tpm/${name}.tpm\
\
${texmf}/tpm/${name}.tpm\
${type}/${name}\
\
\
");
$self->initialize($type, $name, $doc);
return $self;
}
sub toRDF {
my ($self) = @_;
my $parser = new XML::DOM::Parser;
$doc = $parser->parse("\n\
\n\n");
my ($node, $child, $father, $nodelist, %attr);
# Add an 'about' field
my $tpmdesc = $doc->createElement("rdf:Description");
my $name = $self->getAttribute("Name");
my $type = $self->getAttribute("Type");
if ($name) {
# Add an about node
$node = $doc->createAttribute("about", "http://texlive.dante.de/texlive/" . $type . "/" . $name . ".zip");
# my $tpmhref = $doc->createAttribute("href", $href);
$tpmdesc->setAttributeNode($node);
# $tpmdesc->setAttributeNode($tpmhref);
}
else {
warn " toRDF(), no Name found!\n" if (! $name);
}
for my $tag ("Name", "Type", "Date", "Version", "Creator", "Title",
"Description", "Author", "Size") {
my $attribute = $self->getAttribute("$tag");
# None of these are optional
$node = $doc->createElement("TPM:$tag");
$child = $doc->createTextNode($attribute);
$node->appendChild($child);
$tpmdesc->appendChild($node);
warn " toRDF($name), no $tag found\n" if ! $attribute && $::opt_warnings;
}
# Flags are optional
$node = $doc->createElement("TPM:Flags");
%attr = $self->getHash("Flags");
if (%attr) {
foreach $key (keys %attr) {
$child = $doc->createAttribute($key, ${attr}{$key});
$node->setAttributeNode($child);
}
# Only if there are attributes
$tpmdesc->appendChild($node);
}
# Globbed expressions
$father = $doc->createElement("TPM:Build");
for my $tag ("BinPatterns", "RunPatterns", "DocPatterns", "SourcePatterns", "RemotePatterns") {
$node = $doc->createElement("TPM:$tag");
$text = $self->getList("$tag");
if ($text ne "" && $text !~ m/^[\s\n]+$/sx) {
$child = $doc->createTextNode($text);
$node->appendChild($child);
$father->appendChild($node);
}
}
$tpmdesc->appendChild($father);
# End of globbed expressions
my @binfiles = $self->getList("BinFiles");
if (@binfiles) {
for (my $i = 0; $i <= $#binfiles; $i++) {
$node = $doc->createElement("TPM:BinFiles");
my %archbin = %{$binfiles[$i]};
my $tpmattr = $doc->createAttribute("arch", $archbin{"arch"});
$node->setAttributeNode($tpmattr);
$tpmattr = $doc->createAttribute("size", $archbin{"size"});
$node->setAttributeNode($tpmattr);
my @files = @{$archbin{"text"}};
if (@files) {
my $strfiles = (join "\n", @files) . "\n";
$child = $doc->createTextNode($strfiles);
$node->appendChild($child);
$tpmdesc->appendChild($node);
}
}
}
for my $tag ("RunFiles", "DocFiles", "SourceFiles", "RemoteFiles") {
$node = $doc->createElement("TPM:$tag");
%field = $self->getHash("$tag");
if (%field) {
my $tpmattr = $doc->createAttribute("size", $field{"size"});
$node->setAttributeNode($tpmattr);
my @files = @{$field{"text"}};
if (@files) {
my $strfiles = (join "\n", @files) . "\n";
$child = $doc->createTextNode($strfiles);
$node->appendChild($child);
$tpmdesc->appendChild($node);
}
}
}
$node = $doc->createElement("TPM:Requires");
my %requires = $self->getHash("Requires");
if (%requires) {
foreach my $k (sort @TpmCategories) {
my @taglist = @{$requires{$k}};
for my $tag (sort @taglist) {
my $tpmbin = $doc->createElement("TPM:$k");
my $a = $doc->createAttribute("name", $tag);
$tpmbin->setAttributeNode($a);
$node->appendChild($tpmbin);
}
}
$tpmdesc->appendChild($node);
}
$node = $doc->createElement("TPM:Installation");
my @installation = $self->getList("Installation");
if (@installation) {
for(my $i = 0 ; $i <= $#installation; $i++) {
my $tpmexec = $doc->createElement("TPM:Execute");
my %execute = %{$installation[$i]};
my $attr = $doc->createAttribute("function", $execute{"function"});
$tpmexec->setAttributeNode($attr);
print " kfunc = $execute{function}\n" if ($::opt_debug);
foreach my $kparam (sort keys %execute) {
print " kparam = $kparam\n" if $::opt_debug;
if ($kparam ne "function") {
$attr = $doc->createAttribute($kparam, $execute{$kparam});
$tpmexec->setAttributeNode($attr);
}
}
$node->appendChild($tpmexec);
}
$tpmdesc->appendChild($node);
}
$node = $doc->createElement("TPM:Provides");
$text = $self->getAttribute("Provides");
$text = $name if (! $text);
if ($text) {
$child = $doc->createTextNode($text);
$node->appendChild($child);
$tpmdesc->appendChild($node);
}
# Set the fragment
$doc->getElementsByTagName("rdf:RDF")->item(0)->appendChild($tpmdesc);
return $doc;
}
sub toString {
my ($self) = @_;
return $self->toRDF()->toString();
}
sub writeFile {
my ($self, $name) = @_;
if (! $name) {
$name = "${MasterDir}/" . $TexmfTreeOfType{$self->getAttribute("Type")} . "/tpm/" . $self->getAttribute("Name") . ".tpm";
}
open (OUT, ">$name") || die "open(>$name) failed: $!";
# rewrite them without ^M
binmode(OUT) if ($^O =~ /MSWin32/);
print OUT $self->toString();
close (OUT) || warn "close(>$name) failed: $!";
}
sub setAttribute {
my ($self, $n, $v) = @_;
$self->{$n} = $v;
}
sub getAttribute {
my ($self, $n) = @_;
return ($self->{$n});
}
sub setList {
my ($self, $n, @v) = @_;
@{$self->{$n}} = @v;
}
sub getFileList {
my ($self, $n) = @_;
my @l = ();
if ($n eq "BinFiles") {
foreach $v (@{$self->{$n}}) {
if (($CurrentArch eq "all" && FileUtils::member(${$v}{"arch"}, @Tpm::ArchList))
|| ${$v}{"arch"} eq ${CurrentArch}) {
my @val = @{${$v}{"text"}};
#print "getfilelist pushing for $v: @val\n";
push @l, @val;
}
}
}
elsif ($n =~ /^(Run|Doc|Source|Remote)Files$/) {
my %v = %{$self->{$n}};
@l = @{$v{"text"}};
}
else {
@l = @{$self->{$n}};
}
if (wantarray) {
#print "getfilelist($n) returning list: @l\n";
#&debug_hash ($n, $self->{$n});
#print "$n {text} = " . @{$n{text}} . "\n";
return @l;
}
else {
#print "getfilelist($n) returning scalar: @l\n";
if (@l) {
return (join "\n", @l);
}
else {
return "";
}
}
}
# Need to test forcycles !
sub getRequiredFileList {
my ($self, $n) = @_;
my @l = ();
# print "name = " . $self->getAttribute('Name') . "\n";
if ($n eq 'all') {
push @l, $self->getAllFileList();
}
else {
push @l, $self->getFileList($n);
}
my %requires = $self->getHash("Requires");
my @reqlist = ();
foreach my $k (keys %requires) {
foreach my $e (@{$requires{$k}}) {
push @reqlist, ${Tpm::TexmfTreeOfType}{$k} . "/tpm/$e.tpm";
}
}
map {
my $tpm = Tpm->new($_);
push @l, $tpm->getRequiredFileList($n);
} @reqlist;
return @l;
}
sub getRequiredTpm {
my ($self, $recursive) = @_;
my %requires = $self->getHash("Requires");
my @reqlist = ();
foreach my $k (keys %requires) {
foreach my $e (@{$requires{$k}}) {
push @reqlist, "$k/$e";
}
}
my @l = ();
if ($recursive) {
while (@reqlist) {
my $tpmname = pop @reqlist;
&FileUtils::push_uniq(\@l, $tpmname);
my $tpm = Tpm->new($tpmname);
print "tpmname = $tpmname\n";
%requires = $tpm->getHash("Requires");
foreach my $k (keys %requires) {
foreach my $e (@{$requires{$k}}) {
&FileUtils::push_uniq(\@reqlist, "$k/$e");
}
}
}
}
else {
@l = @reqlist;
}
print $self->getAttribute("Name") . " requires @l\n";
return @l;
}
sub getList {
my ($self, $n) = @_;
my @l = @{$self->{$n}};
if ($n eq "BinFiles") {
# the elements of BinFiles are hash references; we want to sort by
# the arch name, so the output will be stable.
@l = sort { $a->{"arch"} cmp $b->{"arch"} } @l;
} elsif ($n eq "Installation") {
# Need these alphabetically, too, e.g.,
@l = sort tpm_inst_sort @l;
} else {
@l = sort @l;
}
if (wantarray) {
return @l;
} elsif (@l) {
return (join "\n", @l);
} else {
return "";
}
}
# This function is used to sort the TPM:Installation elements for
# getList. Include both key names and values, e.g.,
#
#
# should be sorted in that order.
#
sub tpm_inst_sort
{
$astr = join (" ", map { $_ . "=" . $a->{$_} } keys %$a);
$bstr = join (" ", map { $_ . "=" . $b->{$_} } keys %$b);
return $astr cmp $bstr;
}
sub setHash {
my ($self, $n, %v) = @_;
%{$self->{$n}} = %v;
}
sub getHash {
my ($self, $n) = @_;
return %{$self->{$n}};
}
sub getPatterns {
my ($self, $recurse) = @_;
my @patterns = ();
warn "Doing " . $self->getAttribute("Name") . "\n";
my $type = $self->getAttribute("Type");
if ($type =~ m/tlcore/i) {
# already there
# push @patterns, $self->getList("RunPatterns");
# push @patterns, $self->getList("DocPatterns");
# push @patterns, $self->getList("SourcePatterns");
}
elsif ($type =~ m/package/i) {
$self->buildPatternsPackage();
# Add them
push @patterns, $self->getList("RunPatterns");
push @patterns, $self->getList("DocPatterns");
push @patterns, $self->getList("SourcePatterns");
$self->setList("RunPatterns", () );
$self->setList("DocPatterns", () );
$self->setList("SourcePatterns", () );
}
elsif ($type =~ m/documentation/i) {
$self->buildPatternsDocumentation();
push @patterns, $self->getList("RunPatterns");
push @patterns, $self->getList("DocPatterns");
push @patterns, $self->getList("SourcePatterns");
$self->setList("RunPatterns", () );
$self->setList("DocPatterns", () );
$self->setList("SourcePatterns", () );
}
if ($recurse) {
my %requires = $self->getHash("Requires");
my @reqlist = ();
foreach my $k (keys %requires) {
foreach my $e (@{$requires{$k}}) {
push @reqlist, ${Tpm::TexmfTreeOfType}{$k} . "/tpm/$e.tpm";
}
}
map {
print "testing $_\n";
if (&FileUtils::member("$_", @patterns)) {
print "Already done: $_\n";
}
else {
my $tpm = Tpm->new("${MasterDir}/$_");
push @patterns, $tpm->getPatterns($recurse);
}
} @reqlist;
}
return @patterns;
}
sub getFilesFromPatterns {
my ($self, $n, $recurse) = @_;
my @patterns = ();
if ($n eq "BinFiles") {
if ($CurrentArch eq "all") {
my @l = $self->getList("BinPatterns");
my @lgen = ();
my @lwin32 = ();
my @lothers = ();
while (@l) {
my $f = shift @l;
if ($f =~ m/\/\$\{ARCH\}\//) {
push @lgen, $f;
}
elsif ($f =~ m/\/win32(-static)?\//) {
push @lwin32, $f;
}
else {
push @lothers, $f;
}
}
foreach my $a (@ArchList) {
# Skip win32, since they are processed separately anyway
next if ($a =~ m/^win32(-static)?/);
my @l = @lgen;
map { $_ =~ s/\$\{ARCH\}/${a}/sxo } @l;
push @patterns, @l;
}
push @patterns, @lwin32;
push @patterns, @lothers;
}
elsif ($CurrentArch =~ m/win32/) {
my @l = grep { /\/${CurrentArch}\// } $self->getList("BinPatterns");
push @patterns, @l;
}
else {
push @patterns, (map {s/\$\{ARCH\}/$CurrentArch/ } $self->getList("BinPatterns"));
push @patterns, (grep { /\/${CurrentArch}\// } $self->getList("BinPatterns"));
}
}
else {
$n =~ s/Files/Patterns/;
my @files = $self->getList($n);
push @patterns, @files;
}
my @files = ();
if (@patterns) {
@files = ();
map {
push @files, $dotfiles{$_};
$_ = "$MasterDir/" . $_ ;
} @patterns;
for my $p (@patterns) {
push @files, &FileUtils::globexpand ($recurse, $p);
#print " files after $p: @files\n" if $::opt_debug;
}
map { $_ =~ s/^${MasterDir}\///; } @files;
@files = &FileUtils::sort_uniq(@files);
}
return @files;
}
sub patternsExpand {
my ($self, $recurse) = @_;
my (%v, $size);
my @allbinfiles = $self->getFilesFromPatterns("BinFiles", 0);
my @files = ();
my $file_number = $#allbinfiles + 1;
foreach my $a (@ArchList) {
my @archbinfiles = grep { /\/$a\// } @allbinfiles;
if (@archbinfiles) {
$size = &FileUtils::calc_file_size($MasterDir, @archbinfiles);
my %v = ( );
$v{"arch"} = $a;
$v{"size"} = $size;
push @{$v{"text"}}, @archbinfiles;
push @files, \%v;
}
}
$self->setList("BinFiles", @files);
#print "binfiles = @files\n";
for my $tag ("RunFiles", "DocFiles", "SourceFiles", "RemoteFiles") {
#print ($self->getAttribute("Name") . ", tag $tag\n") if $::opt_debug;
my %v = ( );
@files = $self->getFilesFromPatterns($tag, $recurse);
#print " files = @files\n" if $::opt_debug;
$file_number += $#files + 1;
$size = &FileUtils::calc_file_size($MasterDir, @files);
$v{"arch"} = $a;
$v{"size"} = $size;
@{$v{"text"}} = @files;
$self->setHash($tag, %v);
}
if ($file_number == 1) {
# No need to complain about the collection tpm's,
# they aren't intended to have files.
my $name = $self->getAttribute("Provides");
print "Package $name has no files !\n"
unless $name =~ m!/(collection-*|scheme-*|xemtex|texlive)!;
}
}
sub compress_bin {
my (@files) = @_;
my @result = ();
# Compute architectures list without win32
my @al = @ArchList;
@al = grep { $_ !~ m@win32(-static)?@ } @al;
# Process patterns one by one
while (@files) {
# First file in the list
my $f = $files[0];
# If it is a win32 file, nothing to do
if ($f =~ m@/win32(-static)?/@) {
push @result, $f;
shift @files;
next;
}
# Else, try to match an architecture in its path
my $re = $f;
my $a; # Keep the architecture that matched
for my $arch (@al) {
# Replace the architecture by a catch all pattern
if ($re =~ s@/(${arch})/@/[^\/]*/@x) {
$re = "^${re}\$";
$a = $1; last;
}
}
# Because of bg5+latex
$re =~ s/\+/\\\+/;
# Compute how many files in the list will match this pattern
my @match = grep {$_ =~ m@${re}@ } @files;
# If all the architectures are present, then do the replacement
if (@match == @al) {
@files = grep { $_ !~ m@${re}@ } @files;
$f =~ s@/${a}/@/\${ARCH}/@;
}
else {
shift @files;
}
push @result, $f;
}
return @result;
}
sub patternsUpdate {
my ($self) = @_;
my @patterns = &compress_bin(&FileUtils::regexpify(0, $MasterDir, $self->getFileList("BinFiles")));
$self->setList("BinPatterns", @patterns);
@patterns = &FileUtils::regexpify(0, $MasterDir, $self->getFileList("DocFiles"));
$self->setList("DocPatterns", @patterns);
@patterns = &FileUtils::regexpify(0, $MasterDir, $self->getFileList("RunFiles"));
$self->setList("RunPatterns", @patterns);
@patterns = &FileUtils::regexpify(0, $MasterDir, $self->getFileList("SourceFiles"));
$self->setList("SourcePatterns", @patterns);
@patterns = &FileUtils::regexpify(0, $MasterDir, $self->getFileList("RemoteFiles"));
$self->setList("RemotePatterns", @patterns);
}
sub testSync {
my ($self) = @_;
my @files_from_patterns = () ;
push @files_from_patterns, $self->getFilesFromPatterns("BinFiles");
push @files_from_patterns, $self->getFilesFromPatterns("RunFiles");
push @files_from_patterns, $self->getFilesFromPatterns("DocFiles");
push @files_from_patterns, $self->getFilesFromPatterns("SourceFiles");
push @files_from_patterns, $self->getFilesFromPatterns("RemoteFiles");
my @files = ();
push @files, $self->getFileList("BinFiles");
push @files, $self->getFileList("RunFiles");
push @files, $self->getFileList("DocFiles");
push @files, $self->getFileList("SourceFiles");
push @files, $self->getFileList("RemoteFiles");
my @l1 = ();
my @l2 = ();
&FileUtils::diff_list(@files_from_patterns, @files, \@l1, \@l2);
if ($#l1 < 0 && $#l2 < 0) {
return 1;
}
else {
print $self->getAttribute("Name") . ": patterns and file lists not in sync\n";
print "Files in patterns not in lists :\n";
map { print "$_\n"; } @l1;
print "Files in lists not in patterns :\n";
map { print "$_\n"; } @l2;
return 0;
}
}
sub formatdate {
return sprintf("%4d/%02d/%02d %02d:%02d:%02d",
$_[5]+1900, $_[4]+1, $_[3], $_[2], $_[1], $_[0]);
}
sub printdate {
my ($strDate) = @_;
my @mytime;
my ($s, $strTime);
($strDate, $strTime) = split " ", $strDate;
# print "strDate = $strDate; strTime = $strTime\n";
if ($strDate =~ m@(\d\d\d\d|\d\d)/(\d\d)/(\d\d)@) {
$mytime[5] = eval $1;
$mytime[4] = eval $2;
$mytime[3] = eval $3;
if ($strTime =~ m@(\d\d):(\d\d):(\d\d)@) {
$mytime[2] = eval $1;
$mytime[1] = eval $2;
$mytime[0] = eval $3;
}
$mytime[5] -= 1900 if ($mytime[5] > 1900);
$mytime[4] -= 1;
}
else {
@mytime = gmtime;
}
return &formatdate(@mytime);
}
sub debug_date
{
my ($str,$date) = @_;
#warn "$str " . &formatdate(gmtime($date)) . "\n";
}
# if any of FILES are newer than OLDDATE, return the newest mtime.
#
sub max_date
{
my ($olddate, @files) = @_;
my $tpmdate = 0;
&debug_date (" max_date files=@files, olddate=", $olddate);
for my $f (@files) {
# although the texmf/tpm/*.tpm files are mostly hand-maintained, it
# still seems best for the TPM:Date to reflect the newest date of
# the actual files in the package; the sizes and such might still
# get autoupdated.
if ($f =~ m,/tpm/.*\.tpm$,) {
$tpmdate = (stat("$MasterDir/$f"))[9];
&debug_date (" tpm itself, found ", $tpmdate);
}
elsif (-f "$MasterDir/$f") {
my @st = stat("$MasterDir/$f");
&debug_date (" file $f is ", $st[9]);
if ($st[9] > $olddate) {
&debug_date (" replacing olddate ", $olddate);
$olddate = $st[9];
}
}
}
if ($olddate == 0 && $tpmdate) {
&debug_date (" max_date using tpm date", $tpmdate);
$olddate = $tpmdate;
}
&debug_date (" max_date returning ", $olddate);
return $olddate;
}
sub fixDate {
my ($self) = @_;
my $newdate = 0;
my @binfiles = $self->getFileList("BinFiles");
#warn "binfiles=@binfiles";
if ($CurrentArch ne "all") {
@binfiles = grep { m@/${CurrentArch}/@ } @binfiles;
warn "arch-filtered for $CurrentArch binfiles=@binfiles";
}
$newdate = &max_date($newdate, @binfiles);
&debug_date (" newdate after bin: ", $newdate);
#
$newdate = &max_date($newdate, $self->getFileList("DocFiles"));
&debug_date (" newdate after doc: ", $newdate);
#
$newdate = &max_date($newdate, $self->getFileList("SourceFiles"));
&debug_date (" newdate after source: " , $newdate);
#
$newdate = &max_date($newdate, $self->getFileList("RemoteFiles"));
&debug_date (" newdate after remote: " , $newdate);
#
# Check the RunFiles last, because it includes the tpm itself, and we
# only want to use that as a last resort.
$rundate = &max_date($newdate, $self->getFileList("RunFiles"));
&debug_date (" newdate after run: ", $newdate);
$self->setAttribute("Date", &formatdate(gmtime($newdate)));
}
sub fixRequires {
my ($self, $test) = @_;
my %requires = $self->getHash("Requires");
if (%requires) {
foreach my $k (@TpmCategories) {
my @taglist = @{$requires{$k}};
my $texmf = $TexmfTreeOfType{$k};
my @newtaglist = ( );
for my $tag (@taglist) {
if (-f "${MasterDir}/${texmf}/tpm/${tag}.tpm") {
push @newtaglist, $tag;
}
elsif ($test == 0) {
print "Requirement ${MasterDir}/${texmf}/tpm/${tag}.tpm is not found.\n";
}
}
# @{$requires{$k}} = @newtaglist;
}
# if ($test >= 1) {
# $self->setHash("Requires",%requires);
# }
}
}
#
# This function will print every text node under given nodes
# and catenate the result.
#
sub myToText {
my (@nodes) = @_;
return
join '', ( map {
if ($_->isTextNode) {
my $s =$_->toString; chomp($s); $s;
}
else {
if ($_->hasChildNodes) {
myToText($_->getChildNodes) . " ";
}
else {
'';
}
}
} @nodes ) ;
}
sub trim {
my ($str) = @_;
$str =~ s/^[\n\s]+//;
$str =~ s/[\n\s]+$//;
return $str;
}
#
# Look into the Catalogue to find any supplementary information
# Get the license information, version and release numbers
#
sub completeUsingCatalogue {
my ($self) = @_;
my($author, $version, $license, $title, $description);
my $pkgname = $self->getAttribute("Name");
$pkgname =~ s/^(bin-|lib-|tex-)//;
# FIXME : we should handle several cases where the Catalogue name
# is not the package name...
print STDERR "Looking for $pkgname in the Catalogue.\n" if $Verbose;
my $fletter = substr($pkgname, 0, 1);
my $catname = "${CatalogueDir}/entries/$fletter/${pkgname}.xml";
return if (! -f $catname);
# print "catname = $catname\n";
my $parser = new XML::DOM::Parser;
my $catdoc = $parser->parsefile($catname);
my $nodelist = $catdoc->getElementsByTagName("author");
$author = '';
for (my $i = 0; $i < $nodelist->getLength; $i++) {
if ($nodelist->item($i)->getElementsByTagName("name")->item(0)->getFirstChild) {
$author .= ($i == 0 ? "" : " and ") . $nodelist->item($i)->getElementsByTagName("name")->item(0)->getFirstChild->toString;
}
}
# print "author = $author \n";
$nodelist = $catdoc->getElementsByTagName("version")->item(0);
if ($nodelist && $nodelist->getElementsByTagName("number")->item(0)) {
$version = $nodelist->getElementsByTagName("number")->item(0)->getFirstChild;
if ($version) {
$version = $version->toString;
# print "version = $version\n";
}
}
my $node = $catdoc->getElementsByTagName("license")->item(0);
if ($node) {
$license = $node->getAttribute("type");
}
$node = $catdoc->getElementsByTagName("caption")->item(0);
if ($node) {
$title = &trim($node->getFirstChild->toString);
}
$node = $catdoc->getElementsByTagName("description")->item(0);
if ($node) {
my $abstract = $node->getElementsByTagName("abstract")->item(0);
$node = $abstract if ($abstract);
$description = myToText( $node );
# $description = join '', (map { ($_->isTextNode ? $_->toString : '') } $node->getChildNodes);
$description = &trim($node->expandEntityRefs($description));
# print "description = |$description|\n";
}
my $old_author = &trim($self->getAttribute("Author"));
my $old_version = &trim($self->getAttribute("Version"));
my $old_title = &trim($self->getAttribute("Title"));
my $old_description = &trim($self->getAttribute("Description"));
if ($author && $author ne $old_author) {
$self->setAttribute("Author", $author);
print "Replacing $old_author by $author\n";
}
if ($version && $version ne $old_version) {
$self->setAttribute("Version", $version);
print "Replacing $old_version by $version\n";
}
if ($title && $title ne $old_title) {
$self->setAttribute("Title", $title);
print "Replacing $old_title by $title\n";
}
if ($description && ($description ne $old_description)) {
$self->setAttribute("Description", $description);
print "Replacing $old_description by $description\n";
}
}
sub buildPatternsPackage {
my ($self) = @_;
my $type = $self->getAttribute("Type");
return unless $type eq 'Package';
my $name = $self->getAttribute("Name");
my $texmf = $TexmfTreeOfType{$type};
# set run patterns
my @run_patterns = ( );
my @doc_patterns = ( );
my @source_patterns = ( );
# Usually the package name and the directory name match.
# Here are the special cases when they don't.
if (&FileUtils::member(${name}, @engines)) {
print "special engine patterns for $name\n" if $::opt_debug;
# If our $name is one of the engines
push @run_patterns, (
$texmf . "/${name}/base/*",
$texmf . "/${name}/data/*", # for context
$texmf . "/${name}/misc/*",
$texmf . "/${name}/config/*",
$texmf . "/metapost/${name}/*", # also for context
$texmf . "/tex/${name}/*"
);
push @doc_patterns, ( $texmf . "/doc/${name}/base/*" );
push @source_patterns, ( $texmf . "/source/${name}/base/*" );
# Shouldn't we chose between the previous patterns
# and these ones?
map {
push @run_patterns, $texmf . "/tex/$_/${name}/*";
push @doc_patterns, ( $texmf . "/doc/$_/${name}/*" );
push @source_patterns, ( $texmf . "/source/$_/${name}/*" );
} @formats;
# Exception for dvips and ttf2pk !
if (${name} eq 'dvips' || ${name} eq 'ttf2pk') {
push @run_patterns,
( $texmf . "/fonts/map/${name}/base/*", $texmf . "/fonts/map/${name}/config/*",
$texmf . "/fonts/enc/${name}/base/*", $texmf . "/fonts/enc/${name}/config/*" );
# Exception for metapost !
} elsif (${name} eq 'metapost') {
push @run_patterns, $texmf . "/metapost/support/*";
# Exception for tex4ht !
} elsif (${name} eq 'tex4ht') {
push @run_patterns, $texmf . "/tex4ht/ht-fonts/*";
# Exception for omega !
} elsif (${name} eq 'omega') {
push @run_patterns,
( $texmf . "/tex/generic/encodings/*",
$texmf . "/tex/generic/omegahyph/*",
$texmf . "/omega/otp/char2uni/*",
$texmf . "/omega/otp/uni2char/*",
$texmf . "/omega/ocp/char2uni/*",
$texmf . "/omega/ocp/uni2char/*" );
# Exception for vtex -- extra map files.
} if (${name} eq 'vtex') {
push @run_patterns, $texmf . "/fonts/map/${name}/*";
}
} elsif (&FileUtils::member(${name}, @formats)) {
print "special format patterns for $name\n" if $::opt_debug;
# if our $name is one of the formats
map {
my $e = $_;
push @run_patterns, ( $texmf . "/$e/${name}/base/*",
$texmf . "/$e/${name}/config/*",
);
push @run_patterns, $texmf . "/$e/${name}/*" if ($_ ne 'tex' && $_ ne 'omega');
} @engines;
map {
push @run_patterns, $texmf . "/tex/$_/${name}/*";
} @formats;
push @doc_patterns, ( $texmf . "/doc/${name}/base/*" );
push @source_patterns, ( $texmf . "/source/${name}/base/*" );
# Exception for Context !
if (${name} eq 'context') {
push @run_patterns, $texmf . "/tex/context/*";
# exception for texinfo since it has no subdirs.
} elsif (${name} eq 'texinfo') {
push @run_patterns, $texmf . "/tex/texinfo/*";
# Exception for fontinst, since it has lots of subdirs, including misc.
# cyrfinst is really a separate package, but let's not clean that up now.
} elsif (${name} eq 'fontinst') {
push @run_patterns, $texmf . "/tex/fontinst/*/*";
}
} elsif (&FileUtils::member(${name}, @vendors)) {
print "special vendor patterns for $name\n" if $::opt_debug;
push @run_patterns, $texmf . "/dvips/${name}/*";
map { push @run_patterns, $texmf . "/fonts/$_/${name}/*"; } @fonttypes;
map {
my $e = $_;
map {
push @run_patterns, $texmf . "/$e/$_/${name}/*"
unless $name eq "misc" && $_ eq "fontinst";
# keep fontinst/misc in fontinst.
} @formats;
} @engines;
# Exception for amsfonts -- te/staw decided in June 2005 to use ams
# for the directory name within fonts, but amsfonts elsehwere.
if (${name} eq 'amsfonts') {
push @run_patterns,
( $texmf . "/dvips/ams/config.*",
$texmf . "/fonts/map/*/ams/*",
$texmf . "/fonts/*/bluesky/ams/*",
$texmf . "/fonts/*/public/ams/*",
);
push @source_patterns,
( $texmf . "/source/latex/amsfonts/*" );
}
# Exception for lh: also have source/latex/lh.
push @source_patterns, ( $texmf . "/source/latex/$name/*" ); # lh
# Exception for mathdesign -- doc is in doc/latex instead of doc/fonts.
push @doc_patterns, ( $texmf . "/doc/latex/$name/*" ); # mathdesign
# Exception for vntex -- doc is in doc/generic instead of doc/fonts.
push @doc_patterns, ( $texmf . "/doc/generic/$name/*" ); # vntex
} else {
print "normal patterns for $name\n" if $::opt_debug;
map {
my $e = $_;
push @run_patterns, $texmf . "/$e/${name}/*";
push @doc_patterns, $texmf . "/doc/$e/${name}/*";
push @source_patterns, $texmf . "/source/$e/${name}/*";
map {
push @run_patterns, $texmf . "/$e/$_/${name}/*";
} @formats;
} @engines;
map {
push @run_patterns, $texmf . "/tex/$_/${name}/*";
push @doc_patterns, $texmf . "/doc/$_/${name}/*";
push @source_patterns, $texmf . "/source/$_/${name}/*";
} @formats;
push @doc_patterns, $texmf . "/doc/${name}/*";
push @source_patterns, $texmf . "/source/${name}/*";
# one more special case, fontname has its own odd map files.
if ($name eq 'fontname') {
push @run_patterns, $texmf . "/fonts/map/${name}/*";
}
}
map {
my $v = $_;
map {
push @run_patterns, $texmf . "/fonts/$_/$v/${name}/*";
} @fonttypes;
map {
push @run_patterns, $texmf . "/fonts/pk/$_/$v/${name}/*";
} @fontmodes;
} @vendors;
push @run_patterns, $texmf . "/scripts/${name}/*";
push @run_patterns, $texmf . "/dvips/${name}/*";
my $bibe = (${name} eq 'bibtex' ? 'base' : ${name});
push @run_patterns,
( $texmf . "/bibtex/bib/${bibe}/*",
$texmf . "/bibtex/bst/${bibe}/*",
$texmf . "/bibtex/csf/${bibe}/*" );
push @run_patterns,
( $texmf . "/fonts/map/dvips/${name}/*",
$texmf . "/fonts/map/dvipdfm/${name}/*",
$texmf . "/fonts/map/pdftex/${name}/*",
$texmf . "/fonts/map/ttf2pk/${name}/*",
$texmf . "/fonts/enc/dvips/${name}/*",
$texmf . "/fonts/enc/dvipdfm/${name}/*",
$texmf . "/fonts/enc/pdftex/${name}/*",
$texmf . "/fonts/enc/ttf2pk/${name}/*" );
push @run_patterns, "usergrps/$name/*";
push @doc_patterns, $texmf . "/doc/fonts/${name}/*";
push @run_patterns, $texmf . "/omega/ocp/${name}/*";
push @run_patterns, $texmf . "/omega/otp/${name}/*";
push @source_patterns, $texmf . "/source/fonts/${name}/*";
push @run_patterns, $texmf. "/tpm/$name.tpm";
$self->setList("RunPatterns", @run_patterns);
$self->setList("DocPatterns", @doc_patterns);
$self->setList("SourcePatterns", @source_patterns);
}
sub autoPatternsCore {
my ($self) = @_;
return if ($self->getAttribute("Type") ne 'TLCore');
my $type = $self->getAttribute("Type");
my $name = $self->getAttribute("Name");
my $texmf = $TexmfTreeOfType{$type};
}
sub buildPatternsDocumentation {
my ($self) = @_;
return if ($self->getAttribute("Type") ne 'Documentation');
my $type = $self->getAttribute("Type");
my $name = $self->getAttribute("Name");
my $texmf = $TexmfTreeOfType{$type};
# set run patterns
my @run_patterns = ( );
my @doc_patterns = ( );
my @source_patterns = ( );
map {
push @doc_patterns, $texmf . "/doc/$_/${name}/*";
push @source_patterns, $texmf . "/source/$_/${name}/*";
} @languages;
push @run_patterns, $texmf. "/tpm/$name.tpm";
$self->setList("RunPatterns", @run_patterns);
$self->setList("DocPatterns", @doc_patterns);
$self->setList("SourcePatterns", @source_patterns);
}
sub autoPatternsPackage {
my ($self) = @_;
# map { print "$_\n"; } @run_patterns;
# map { print "$_\n"; } @doc_patterns;
# map { print "$_\n"; } @source_patterns;
$self->buildPatternsPackage();
$self->patternsExpand(1);
$self->setList("RunPatterns", () );
$self->setList("DocPatterns", () );
$self->setList("SourcePatterns", () );
}
sub autoPatternsDocumentation {
my ($self) = @_;
$self->buildPatternsDocumentation();
$self->patternsExpand(1);
$self->setList("RunPatterns", () );
$self->setList("DocPatterns", () );
$self->setList("SourcePatterns", () );
}
sub patternsAuto {
my ($self) = @_;
my $type = $self->getAttribute("Type");
if ($type =~ m/tlcore/i) {
$self->autoPatternsCore();
}
elsif ($type =~ m/package/i) {
$self->autoPatternsPackage();
}
elsif ($type =~ m/documentation/i) {
$self->autoPatternsDocumentation();
}
}
#
# Get all files, optionnaly only for architecture $arch
#
sub getAllFileList {
my ($self, $arch) = @_;
my @files = ();
# print "Getting all file list for " . $self->getAttribute("Name") . "\n";
($arch = $CurrentArch) if (undef $arch);
push @files, $self->getFileList("BinFiles");
push @files, $self->getFileList("RunFiles");
push @files, $self->getFileList("DocFiles");
push @files, $self->getFileList("SourceFiles");
push @files, $self->getFileList("RemoteFiles");
return @files;
}
sub fixSize {
my ($self, $arch) = @_;
my $size = 0;
my @files = $self->getList("BinFiles");
foreach my $f (@files) {
$size += ${$f}{"size"};
}
foreach my $tag ("RunFiles", "DocFiles", "SourceFiles", "RemoteFiles") {
my %v = $self->getHash("$tag");
$size += $v{"size"};
}
if ($size != $self->getAttribute("Size")) {
my $name = $self->getAttribute("Name");
my $old_size = $self->getAttribute("Size");
print " $name\t size=$size\t old size=$old_size\t diff="
. ($size - $old_size) . "\n";
$self->setAttribute("Size", $size);
}
return $size;
}
sub Tpm2Zip {
my ($self, $destdir, $full, $standalone) = @_;
if (! $destdir) {
$destdir = $ZipDir;
}
my $name = $self->getAttribute("Name");
my $type = $self->getAttribute("Type");
my $version = $self->getAttribute("Version");
my @files = ();
if ($full eq "full") {
push @files, $self->getRequiredFileList("RunFiles");
push @files, $self->getRequiredFileList("DocFiles");
push @files, $self->getRequiredFileList("SourceFiles");
}
else {
push @files, $self->getFileList("RunFiles");
push @files, $self->getFileList("DocFiles");
push @files, $self->getFileList("SourceFiles");
}
my ($zipname, $tpmname, $zipcmd, $nul);
# Create zip files for all $arch if type = binary
# First, common files
if ($#files >=0) {
# if ($name =~ m/-static$/) {
if ($standalone && &FileUtils::member("$type/$name", @StandAlonePackages)) {
# static packages are expected to have more complete names
if ($full eq "full") {
push @files, $self->getRequiredFileList("BinFiles");
} else {
push @files, $self->getFileList("BinFiles");
}
$zipname = "$destdir/../standalone/$name";
$zipname .= "-${version}-${CurrentArch}.zip";
}
else {
$tpmname = "$destdir/$type/$name.tpm";
$zipname = "$destdir/$type/$name.zip";
}
if ($^O =~ /MSWin32/) {
$nul = "nul";
}
else {
$nul = "/dev/null";
}
@files = &FileUtils::sort_uniq(@files);
if ($zipname =~ /\/binary/ && $^O !~ /MSWin32/) {
$zipcmd = "| zip -9\@ory "
}
else {
$zipcmd = "| zip -9\@or "
}
&mkpath(&FileUtils::dirname($zipname)) if (! -d &FileUtils::dirname($zipname));
my $cwd = &getcwd;
chdir($MasterDir);
unlink $zipname;
print $zipcmd . $zipname . " > $nul\n" if ($::opt_debug);
open ZIP, $zipcmd . $zipname . " > $nul";
map {
if (! -f $_) {
print STDERR "!!!Error: non-existent $_\n";
} else {
print ZIP "$_\n";
}
} @files;
close ZIP;
print "Done $zipname\n" if ($::opt_debug);
}
if (! $standalone) {
# Binaries
my $DoCurrentArch = ${CurrentArch};
foreach my $arch (@{ArchList}) {
if (${DoCurrentArch} eq "all" || ${DoCurrentArch} eq ${arch}) {
${CurrentArch} = $arch;
my @binfiles;
if ($full eq "full") {
@binfiles = $self->getRequiredFileList("BinFiles");
}
else {
@binfiles = $self->getFileList("BinFiles");
}
$zipname = "$destdir/$type/$name-$arch.zip";
if ($#binfiles >=0) {
&mkpath(&FileUtils::dirname($zipname)) if (! -d &FileUtils::dirname($zipname));
my $cwd = &getcwd;
chdir($MasterDir);
unlink $zipname;
print $zipcmd . $zipname . " > $nul\n" if ($::opt_debug);
open ZIP, $zipcmd . $zipname . " > $nul";
map {
if (! -f $_) {
print STDERR "!!!Error: non-existent $_\n";
} else {
print ZIP "$_\n";
}
} @binfiles;
close ZIP;
print "Done $zipname\n" if ($::opt_debug);
}
}
}
${CurrentArch} = ${DoCurrentArch};
}
# Write the tpm file together with the zip file in the current scheme
$self->writeFile($tpmname) if ($tpmname);
chdir($cwd);
}
sub Clean {
my ($self, $patterns, $fixreq) = @_;
# Update the Date to the date of the latest file in the package
$self->fixDate();
# Find missing information in the Catalogue if possible
$self->completeUsingCatalogue();
# Compute the overall size
$self->fixSize();
# Fix the tpm file
my @run_patterns = $self->getList("RunPatterns");
# First remove all tpm file present in the package
#print "run_patterns before remove_list = @run_patterns\n";
&FileUtils::remove_list(\@run_patterns, "\.tpm\$");
#print "run_patterns after remove_list = @run_patterns\n";
# Second, add the right one
my $name = $self->getAttribute("Name");
my $type = $self->getAttribute("Type");
push @run_patterns, ${Tpm::TexmfTreeOfType}{$type} . "/tpm/$name.tpm";
$self->setList("RunPatterns", @run_patterns);
# Fix the Title
if (! $self->getAttribute("Title")) {
$self->setAttribute("Title", "The " . $self->getAttribute("Name") . " package.");
}
# Big step, get fiels from patterns.
if ($patterns eq 'auto') {
$self->patternsAuto();
} elsif ($patterns eq 'to') {
# Update patterns
$self->patternsUpdate();
} elsif ($patterns eq 'from') {
$self->patternsExpand(0);
}
# Fix Requires field
$self->fixRequires(undef $fixreq || $fixreq == 0 || $fixreq eq '' ? 0 : 1);
$self->setList("RunPatterns", &FileUtils::sort_uniq($self->getList("RunPatterns")));
$self->setList("DocPatterns", &FileUtils::sort_uniq($self->getList("DocPatterns")));
$self->setList("SourcePatterns", &FileUtils::sort_uniq($self->getList("SourcePatterns")));
# Alternatively you could expand patterns if for example you have just edited them
# See the 'process2_tpm' function below
# Test that patterns and files list are n sync
if ($self->testSync()) {
print "Writing $type/$name\n";
$self->writeFile();
}
else {
print "ERROR: out of sync between patterns and files in $tpmname (not written).\n";
}
}
sub Remove {
my ($self, $patterns, $dry) = @_;
my @run_patterns = $self->getList("RunPatterns");
# First remove all tpm file present in the package
# print "run_patterns = @run_patterns\n";
&FileUtils::remove_list(\@run_patterns, "\.tpm\$");
# print "run_patterns = @run_patterns\n";
# Second, add the right one
my $name = $self->getAttribute("Name");
my $type = $self->getAttribute("Type");
push @run_patterns, ${Tpm::TexmfTreeOfType}{$type} . "/tpm/$name.tpm";
$self->setList("RunPatterns", @run_patterns);
if ($patterns eq 'auto') {
$self->patternsAuto();
}
elsif ($patterns eq 'to') {
# Update patterns
$self->patternsUpdate();
}
elsif ($patterns eq 'from') {
$self->patternsExpand(0);
}
$self->setList("RunPatterns", &FileUtils::sort_uniq($self->getList("RunPatterns")));
$self->setList("DocPatterns", &FileUtils::sort_uniq($self->getList("DocPatterns")));
$self->setList("SourcePatterns", &FileUtils::sort_uniq($self->getList("SourcePatterns")));
map {
my $file = "${MasterDir}/$_";
if ($dry) {
print "would unlink $file\n";
} else {
unlink($file) || warn "unlink($file) failed: $!";
print "unlinked $file\n";
}
} $self->getAllFileList();
}
# Log LABEL followed by hash elements, all on one line.
#
sub debug_hash
{
my ($label) = shift;
my (%hash) = (ref $_[0] && $_[0] =~ /.*HASH.*/) ? %{$_[0]} : @_;
my $str = "$label: {";
my @items = ();
for my $key (sort keys %hash) {
my $val = $hash{$key};
$key =~ s/\n/\\n/g;
$val =~ s/\n/\\n/g;
push (@items, "$key:$val");
}
$str .= join (",", @items);
$str .= "}";
print "$str\n";
}
1;