diff options
author | Karl Berry <karl@freefriends.org> | 2006-02-13 00:49:47 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-02-13 00:49:47 +0000 |
commit | 045f872c421bb96d9e680ccd7f79c823cef10521 (patch) | |
tree | 20483574ec8846ce1adb25e905f4a0b4e2505a41 /Build/tools/Tpm.pm | |
parent | a1e47d103e66e2a947105dd7378e8df3918c3f62 (diff) |
move Master/Tools to Build/tools
git-svn-id: svn://tug.org/texlive/trunk@1514 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/tools/Tpm.pm')
-rw-r--r-- | Build/tools/Tpm.pm | 1754 |
1 files changed, 1754 insertions, 0 deletions
diff --git a/Build/tools/Tpm.pm b/Build/tools/Tpm.pm new file mode 100644 index 00000000000..f620b611092 --- /dev/null +++ b/Build/tools/Tpm.pm @@ -0,0 +1,1754 @@ +# $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", "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", + "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", "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("\ +<!DOCTYPE rdf:RDF SYSTEM \"../../Tools/tpm.dtd\">\ +<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:TPM=\"http://texlive.dante.de/\">\ + <rdf:Description about=\"http://texlive.dante.de/texlive/tlcore/${name}.zip\">\ + <TPM:Name>${name}</TPM:Name>\ + <TPM:Type>${type}</TPM:Type>\ + <TPM:Date>1970/01/01 01:00:00</TPM:Date>\ + <TPM:Version></TPM:Version>\ + <TPM:Creator>$user</TPM:Creator>\ + <TPM:Author></TPM:Author>\ + <TPM:Title>The ${name} package.</TPM:Title>\ + <TPM:Size>864</TPM:Size>\ + <TPM:Description></TPM:Description>\ + <TPM:Build>\ + <TPM:RunPatterns>${texmf}/tpm/${name}.tpm</TPM:RunPatterns>\ + </TPM:Build>\ + <TPM:RunFiles size=\"679\">${texmf}/tpm/${name}.tpm</TPM:RunFiles>\ + <TPM:Provides>${type}/${name}</TPM:Provides>\ + </rdf:Description>\ +</rdf:RDF>\ +"); + $self->initialize($type, $name, $doc); + return $self; +} + +sub toRDF { + my ($self) = @_; + my $parser = new XML::DOM::Parser; + + $doc = $parser->parse("<!DOCTYPE rdf:RDF\n\ + SYSTEM \"../../Tools/tpm.dtd\">\n\ +<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns\#\"\n\ + xmlns:TPM=\"http://texlive.dante.de/\">\n</rdf:RDF>\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 (@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}) { + push @l, @{${$v}{"text"}}; + } + } + } + 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}); + 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., +# <TPM:Execute function="addMap" mode="mixed" parameter="cm-super-t1.map"/> +# <TPM:Execute function="addMap" mode="mixed" parameter="cm-super-x2.map"/> +# 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 max_date +{ + my ($olddate, @files) = @_; + my ($f, $tpmdate); + #print " olddate was " . &formatdate(gmtime($oldate)) . ", files=@files\n"; + foreach $f (@files) { + if ($f =~ m@^texmf-dist/tpm/.*\.tpm@) { + $tpmdate = (stat("$MasterDir/$f"))[9]; + #print " tpm itself, found $tpmdate\n"; + } + else { + my @st = stat("$MasterDir/$f"); + #print " file $f is " . &formatdate(gmtime($st[9])) . "\n"; + if ($st[9] > $olddate) { + #print " replacing\n"; + $olddate = $st[9]; + } + } + } + if ($olddate == 0) { + $olddate = $tpmdate; + } +# print " newdate is " . &formatdate(gmtime($oldate)) . "\n"; + return $olddate; +} + +sub fixDate { + my ($self) = @_; + my $newdate = 0; + foreach my $s (@{$self->getFileList("BinFiles")}) { + if ($CurrentArch eq "all" || $s =~ m@/${CurrentArch}/@) { + $newdate = &max_date($newdate, ${$s}{"text"}); + } + } + #print " newdate after bin: " . &formatdate(gmtime($newdate)) . "\n"; + $newdate = &max_date($newdate, $self->getFileList("RunFiles")); + #print " newdate after run: " . &formatdate(gmtime($newdate)) . "\n"; + $newdate = &max_date($newdate, $self->getFileList("DocFiles")); + #print " newdate after doc: " . &formatdate(gmtime($newdate)) . "\n"; + $newdate = &max_date($newdate, $self->getFileList("SourceFiles")); + #print " newdate after source: " . &formatdate(gmtime($newdate)) . "\n"; + $newdate = &max_date($newdate, $self->getFileList("RemoteFiles")); + #print " newdate after remote: " . &formatdate(gmtime($newdate)) . "\n"; + $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 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; |